diff --git a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h --- a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h @@ -74,7 +74,8 @@ void printFunctionName(StringRef FunctionName, bool Inlined); virtual void printSimpleLocation(StringRef Filename, const DILineInfo &Info) = 0; - void printContext(StringRef FileName, int64_t Line); + void printContext(const Optional &Source, StringRef FileName, + int64_t Line); void printVerbose(StringRef Filename, const DILineInfo &Info); virtual void printFooter() {} 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 @@ -40,32 +40,41 @@ } // Prints source code around in the FileName the Line. -void PlainPrinterBase::printContext(StringRef FileName, int64_t Line) { +void PlainPrinterBase::printContext(const Optional &Source, + StringRef FileName, int64_t Line) { if (Config.SourceContextLines <= 0) return; - ErrorOr> BufOrErr = - MemoryBuffer::getFile(FileName); - if (!BufOrErr) - return; + std::unique_ptr Buf; + line_iterator I; + if (Source) { + I = line_iterator(MemoryBufferRef(*Source, FileName), false); + } else { + ErrorOr> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return; + Buf = std::move(*BufOrErr); + I = line_iterator(*Buf, false); + } - std::unique_ptr Buf = std::move(BufOrErr.get()); int64_t FirstLine = std::max(static_cast(1), Line - Config.SourceContextLines / 2); - int64_t LastLine = FirstLine + Config.SourceContextLines; + int64_t LastLine = FirstLine + Config.SourceContextLines - 1; size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine)); - for (line_iterator I = line_iterator(*Buf, false); - !I.is_at_eof() && I.line_number() <= LastLine; ++I) { + for (; !I.is_at_eof(); ++I) { int64_t L = I.line_number(); - if (L >= FirstLine && L <= LastLine) { - OS << format_decimal(L, MaxLineNumberWidth); - if (L == Line) - OS << " >: "; - else - OS << " : "; - OS << *I << "\n"; - } + if (L < FirstLine) + continue; + if (L > LastLine) + break; + OS << format_decimal(L, MaxLineNumberWidth); + if (L == Line) + OS << " >: "; + else + OS << " : "; + OS << *I << '\n'; } } @@ -82,7 +91,7 @@ void LLVMPrinter::printSimpleLocation(StringRef Filename, const DILineInfo &Info) { OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n'; - printContext(Filename, Info.Line); + printContext(Info.Source, Filename, Info.Line); } void GNUPrinter::printSimpleLocation(StringRef Filename, @@ -91,7 +100,7 @@ if (Info.Discriminator) OS << " (discriminator " << Info.Discriminator << ')'; OS << '\n'; - printContext(Filename, Info.Line); + printContext(Info.Source, Filename, Info.Line); } void PlainPrinterBase::printVerbose(StringRef Filename, @@ -233,7 +242,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 : ""}, @@ -245,7 +254,28 @@ LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""}, {"Line", LineInfo.Line}, {"Column", LineInfo.Column}, - {"Discriminator", LineInfo.Discriminator}})); + {"Discriminator", LineInfo.Discriminator}}); + if (LineInfo.Source && Config.SourceContextLines > 0) { + int64_t FirstLine = + std::max(static_cast(1), + int64_t(LineInfo.Line) - Config.SourceContextLines / 2); + int64_t LastLine = FirstLine + Config.SourceContextLines - 1; + size_t FirstLinePos = StringRef::npos, Pos = 0; + for (int64_t Line = 1; Line <= LastLine; ++Line, ++Pos) { + if (Line == FirstLine) + FirstLinePos = Pos; + Pos = LineInfo.Source->find('\n', Pos); + if (Pos == StringRef::npos) + break; + } + if (FirstLinePos != StringRef::npos) + Object["Source"] = LineInfo.Source + ->substr(FirstLinePos, (Pos == StringRef::npos) + ? StringRef::npos + : Pos - FirstLinePos) + .str(); + } + 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":"// Line 2\nvoid foo() {}\n// 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)