diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp --- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -131,7 +131,8 @@ 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 +141,8 @@ 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, @@ -282,7 +284,7 @@ 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 : ""}, @@ -294,7 +296,33 @@ 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); + if (SourceCode.PrunedSource) { + StringRef Source = *SourceCode.PrunedSource; + std::string SourceCopy; + if (*Source.end() != '\0') { + SourceCopy = Source.str(); + Source = SourceCopy; + } + std::string FormattedSource; + raw_string_ostream Stream(FormattedSource); + 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; + Stream << format_decimal(L, MaxLineNumberWidth); + if (L == SourceCode.Line) + Stream << " >: "; + else + Stream << " : "; + Stream << *I << '\n'; + } + 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 --- /dev/null +++ b/llvm/test/tools/llvm-symbolizer/source.ll @@ -0,0 +1,51 @@ +;; This test checks printout of the DWARF embedded source. + +; REQUIRES: x86-registered-target + +; RUN: llc -filetype=obj -o %t.o %s + +;; Check the source context plain printout. +; RUN: llvm-symbolizer --print-source-context-lines=3 "CODE %t.o 0" | \ +; RUN: FileCheck %s --strict-whitespace --match-full-lines --implicit-check-not={{.}} +; CHECK:foo +; CHECK-NEXT:/source.c:3:13 +; CHECK-NEXT:2 : // Line 2 +; CHECK-NEXT:3 >: void foo() {} +; CHECK-NEXT:4 : // Line 4 + +;; Check the source context printout to JSON. +; RUN: llvm-symbolizer --print-source-context-lines=3 "CODE %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","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)