Add support for DWARF embedded source to llvm-symbolizer.
authorAlex Orlov <aorlov@accesssoftek.com>
Thu, 20 May 2021 17:40:28 +0000 (21:40 +0400)
committerAlex Orlov <aorlov@accesssoftek.com>
Thu, 20 May 2021 17:40:28 +0000 (21:40 +0400)
This patch adds DWARF embedded source printout to llvm-symbolizer.

Reviewed By: jhenderson, dblaikie

Differential Revision: https://reviews.llvm.org/D102355

llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp
llvm/test/tools/llvm-symbolizer/source.ll [new file with mode: 0644]

index a482ab7..555d29f 100644 (file)
@@ -82,6 +82,29 @@ public:
         FirstLine(std::max(static_cast<int64_t>(1), Line - Lines / 2)),
         LastLine(FirstLine + Lines - 1),
         PrunedSource(pruneSource(load(FileName, EmbeddedSource))) {}
+
+  void format(raw_ostream &OS) {
+    if (!PrunedSource)
+      return;
+    size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine));
+    int64_t L = FirstLine;
+    for (size_t Pos = 0; Pos < PrunedSource->size(); ++L) {
+      size_t PosEnd = PrunedSource->find('\n', Pos);
+      StringRef String = PrunedSource->substr(
+          Pos, (PosEnd == StringRef::npos) ? StringRef::npos : (PosEnd - Pos));
+      if (String.endswith("\r"))
+        String = String.drop_back(1);
+      OS << format_decimal(L, MaxLineNumberWidth);
+      if (L == Line)
+        OS << " >: ";
+      else
+        OS << "  : ";
+      OS << String << '\n';
+      if (PosEnd == StringRef::npos)
+        break;
+      Pos = PosEnd + 1;
+    }
+  }
 };
 
 void PlainPrinterBase::printHeader(uint64_t Address) {
@@ -95,27 +118,7 @@ void PlainPrinterBase::printHeader(uint64_t Address) {
 
 // Prints source code around in the FileName the Line.
 void PlainPrinterBase::printContext(SourceCode SourceCode) {
-  if (!SourceCode.PrunedSource)
-    return;
-
-  StringRef Source = *SourceCode.PrunedSource;
-  std::string SourceCopy;
-  if (*Source.end() != '\0') {
-    SourceCopy = Source.str();
-    Source = SourceCopy;
-  }
-
-  size_t MaxLineNumberWidth = std::ceil(std::log10(SourceCode.LastLine));
-  for (line_iterator I = line_iterator(MemoryBufferRef(Source, ""), false);
-       !I.is_at_eof(); ++I) {
-    int64_t L = SourceCode.FirstLine + I.line_number() - 1;
-    OS << format_decimal(L, MaxLineNumberWidth);
-    if (L == SourceCode.Line)
-      OS << " >: ";
-    else
-      OS << "  : ";
-    OS << *I << '\n';
-  }
+  SourceCode.format(OS);
 }
 
 void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
@@ -131,7 +134,8 @@ void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
 void LLVMPrinter::printSimpleLocation(StringRef Filename,
                                       const DILineInfo &Info) {
   OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n';
-  printContext(SourceCode(Filename, Info.Line, Config.SourceContextLines));
+  printContext(
+      SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
 }
 
 void GNUPrinter::printSimpleLocation(StringRef Filename,
@@ -140,7 +144,8 @@ void GNUPrinter::printSimpleLocation(StringRef Filename,
   if (Info.Discriminator)
     OS << " (discriminator " << Info.Discriminator << ')';
   OS << '\n';
-  printContext(SourceCode(Filename, Info.Line, Config.SourceContextLines));
+  printContext(
+      SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
 }
 
 void PlainPrinterBase::printVerbose(StringRef Filename,
@@ -291,7 +296,7 @@ void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) {
   json::Array Array;
   for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) {
     const DILineInfo &LineInfo = Info.getFrame(I);
-    Array.push_back(json::Object(
+    json::Object Object(
         {{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString
                               ? LineInfo.FunctionName
                               : ""},
@@ -305,7 +310,15 @@ void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) {
           LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""},
          {"Line", LineInfo.Line},
          {"Column", LineInfo.Column},
-         {"Discriminator", LineInfo.Discriminator}}));
+         {"Discriminator", LineInfo.Discriminator}});
+    SourceCode SourceCode(LineInfo.FileName, LineInfo.Line,
+                          Config.SourceContextLines, LineInfo.Source);
+    std::string FormattedSource;
+    raw_string_ostream Stream(FormattedSource);
+    SourceCode.format(Stream);
+    if (!FormattedSource.empty())
+      Object["Source"] = std::move(FormattedSource);
+    Array.push_back(std::move(Object));
   }
   json::Object Json = toJSON(Request);
   Json["Symbol"] = std::move(Array);
diff --git a/llvm/test/tools/llvm-symbolizer/source.ll b/llvm/test/tools/llvm-symbolizer/source.ll
new file mode 100644 (file)
index 0000000..8a12c85
--- /dev/null
@@ -0,0 +1,57 @@
+;; This test checks output of the DWARF embedded source.
+
+; REQUIRES: x86-registered-target
+
+; RUN: llc -filetype=obj -o %t.o %s 
+
+;; Check LLVM style output.
+; RUN: llvm-symbolizer --print-source-context-lines=3 --obj=%t.o 0 | \
+; RUN:   FileCheck %s --check-prefixes=COMMON,LLVM --strict-whitespace --match-full-lines --implicit-check-not={{.}}
+
+;; Check GNU output style.
+; RUN: llvm-symbolizer --print-source-context-lines=3 --obj=%t.o 0 --output-style=GNU | \
+; RUN:   FileCheck %s --check-prefixes=COMMON,GNU --strict-whitespace --match-full-lines --implicit-check-not={{.}}
+
+;      COMMON:foo
+;   LLVM-NEXT:/source.c:3:13
+;    GNU-NEXT:/source.c:3
+; COMMON-NEXT:2  : // Line 2
+; COMMON-NEXT:3 >: void foo() {}
+; COMMON-NEXT:4  : // Line 4
+
+;; Check JSON style output.
+; RUN: llvm-symbolizer --print-source-context-lines=3 --obj=%t.o 0 --output-style=JSON | \
+; RUN:   FileCheck %s --check-prefix=JSON --strict-whitespace --match-full-lines --implicit-check-not={{.}}
+; JSON:[{"Address":"0x0","ModuleName":"{{.*}}.o","Symbol":[{"Column":13,"Discriminator":0,"FileName":"/source.c","FunctionName":"foo","Line":3,"Source":"2  : // Line 2\n3 >: void foo() {}\n4  : // Line 4\n","StartAddress":"0x0","StartFileName":"/source.c","StartLine":3}]}]
+
+;; Generated from the following source:
+;; // Line 1
+;; // Line 2
+;; void foo() {}
+;; // Line 4
+;; // Line 5
+;; clang --target=x86_64-pc-linux -gdwarf-5 -gembed-source -g -emit-llvm -S source.c -o source.ll
+
+source_filename = "source.c"
+target triple = "x86_64-pc-linux"
+
+define dso_local void @foo() #0 !dbg !7 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "source.c", directory: "/", source: "// Line 1\0A// Line 2\0Avoid foo() {}\0A// Line 4\0A// Line 5\0A")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 5}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 12.0.0"}
+!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !DILocation(line: 3, column: 13, scope: !7)