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 @@ -82,6 +82,29 @@ FirstLine(std::max(static_cast(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 @@ // 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 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 @@ 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 +287,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 +299,15 @@ 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 --- /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)