diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst --- a/llvm/docs/CommandGuide/llvm-symbolizer.rst +++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst @@ -251,13 +251,13 @@ Reads from standard input, converts contained :doc:`Symbolizer Markup ` into human-readable form, - and prints the results to standard output. Presently, only the following - markup elements are supported: + and prints the results to standard output. The following markup elements are + not yet supported: - * ``{{symbol}}`` - * ``{{reset}}`` - * ``{{module}}`` - * ``{{mmap}}`` + * ``{{pc}}`` + * ``{{bt}}`` + * ``{{hexdict}}`` + * ``{{dumpfile}}`` .. _llvm-symbolizer-opt-f: diff --git a/llvm/docs/SymbolizerMarkupFormat.rst b/llvm/docs/SymbolizerMarkupFormat.rst --- a/llvm/docs/SymbolizerMarkupFormat.rst +++ b/llvm/docs/SymbolizerMarkupFormat.rst @@ -195,7 +195,7 @@ {{{pc:0x12345678}}} {{{pc:0xffffffff9abcdef0}}} -``{{{data:%p}}}`` [#not_yet_implemented]_ +``{{{data:%p}}}`` Here ``%p`` is the memory address of a data location. It might be presented as the name of a global variable at that location. diff --git a/llvm/include/llvm/DebugInfo/Symbolize/MarkupFilter.h b/llvm/include/llvm/DebugInfo/Symbolize/MarkupFilter.h --- a/llvm/include/llvm/DebugInfo/Symbolize/MarkupFilter.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/MarkupFilter.h @@ -26,11 +26,14 @@ namespace llvm { namespace symbolize { +class LLVMSymbolizer; + /// Filter to convert parsed log symbolizer markup elements into human-readable /// text. class MarkupFilter { public: - MarkupFilter(raw_ostream &OS, Optional ColorsEnabled = llvm::None); + MarkupFilter(raw_ostream &OS, LLVMSymbolizer &Symbolizer, + Optional ColorsEnabled = llvm::None); /// Filters a line containing symbolizer markup and writes the human-readable /// results to the output stream. @@ -57,6 +60,7 @@ uint64_t ModuleRelativeAddr; bool contains(uint64_t Addr) const; + uint64_t getModuleRelativeAddr(uint64_t Addr) const; }; // An informational module line currently being constructed. As many mmap @@ -83,6 +87,7 @@ bool tryPresentation(const MarkupNode &Node); bool trySymbol(const MarkupNode &Node); + bool tryData(const MarkupNode &Node); bool trySGR(const MarkupNode &Node); @@ -107,11 +112,13 @@ void reportTypeError(StringRef Str, StringRef TypeName) const; void reportLocation(StringRef::iterator Loc) const; - const MMap *overlappingMMap(const MMap &Map) const; + const MMap *getOverlappingMMap(const MMap &Map) const; + const MMap *getContainingMMap(uint64_t Addr) const; StringRef lineEnding() const; raw_ostream &OS; + LLVMSymbolizer &Symbolizer; const bool ColorsEnabled; MarkupParser Parser; diff --git a/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp b/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp --- a/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/DebugInfo/Symbolize/Markup.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Debuginfod/Debuginfod.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Object/ObjectFile.h" @@ -32,9 +33,11 @@ using namespace llvm; using namespace llvm::symbolize; -MarkupFilter::MarkupFilter(raw_ostream &OS, Optional ColorsEnabled) - : OS(OS), ColorsEnabled(ColorsEnabled.value_or( - WithColor::defaultAutoDetectFunction()(OS))) {} +MarkupFilter::MarkupFilter(raw_ostream &OS, LLVMSymbolizer &Symbolizer, + Optional ColorsEnabled) + : OS(OS), Symbolizer(Symbolizer), + ColorsEnabled( + ColorsEnabled.value_or(WithColor::defaultAutoDetectFunction()(OS))) {} void MarkupFilter::filter(StringRef Line) { this->Line = Line; @@ -94,7 +97,7 @@ if (!ParsedMMap) return true; - if (const MMap *M = overlappingMMap(*ParsedMMap)) { + if (const MMap *M = getOverlappingMMap(*ParsedMMap)) { WithColor::error(errs()) << formatv("overlapping mmap: #{0:x} [{1:x},{2:x})\n", M->Mod->ID, M->Addr, M->Addr + M->Size); @@ -210,7 +213,9 @@ } bool MarkupFilter::tryPresentation(const MarkupNode &Node) { - return trySymbol(Node); + if (trySymbol(Node)) + return true; + return tryData(Node); } bool MarkupFilter::trySymbol(const MarkupNode &Node) { @@ -225,6 +230,47 @@ return true; } +bool MarkupFilter::tryData(const MarkupNode &Node) { + if (Node.Tag != "data") + return false; + if (!checkNumFields(Node, 1)) + return true; + Optional Addr = parseAddr(Node.Fields[0]); + if (!Addr) + return true; + + const auto PrintRaw = [&]() { + highlight(); + OS << "[[[data:"; + highlightValue(); + OS << "0x" << toHex(*Addr, /*LowerCase=*/true); + highlight(); + OS << "]]]\n"; + restoreColor(); + }; + + const MMap *MMap = getContainingMMap(*Addr); + if (!MMap) { + WithColor::error() << "no mmap covers address\n"; + reportLocation(Node.Fields[0].begin()); + PrintRaw(); + return true; + } + + Expected Symbol = Symbolizer.symbolizeData( + MMap->Module->BuildID, {MMap->getModuleRelativeAddr(*Addr)}); + if (!Symbol) { + WithColor::defaultErrorHandler(Symbol.takeError()); + PrintRaw(); + return true; + } + + highlight(); + OS << Symbol->Name; + restoreColor(); + return true; +} + bool MarkupFilter::trySGR(const MarkupNode &Node) { if (Node.Text == "\033[0m") { resetColor(); @@ -442,7 +488,7 @@ bool MarkupFilter::checkNumFields(const MarkupNode &Element, size_t Size) const { if (Element.Fields.size() != Size) { - WithColor::error(errs()) << "expected " << Size << " fields; found " + WithColor::error(errs()) << "expected " << Size << " field(s); found " << Element.Fields.size() << "\n"; reportLocation(Element.Tag.end()); return false; @@ -454,7 +500,7 @@ size_t Size) const { if (Element.Fields.size() < Size) { WithColor::error(errs()) - << "expected at least " << Size << " fields; found " + << "expected at least " << Size << " field(s); found " << Element.Fields.size() << "\n"; reportLocation(Element.Tag.end()); return false; @@ -479,7 +525,7 @@ // Checks for an existing mmap that overlaps the given one and returns a // pointer to one of them. -const MarkupFilter::MMap *MarkupFilter::overlappingMMap(const MMap &Map) const { +const MarkupFilter::MMap *MarkupFilter::getOverlappingMMap(const MMap &Map) const { // If the given map contains the start of another mmap, they overlap. auto I = MMaps.upper_bound(Map.Addr); if (I != MMaps.end() && Map.contains(I->second.Addr)) @@ -495,6 +541,20 @@ return nullptr; } +// Returns the MMap that contains the given address or nullptr if none. +const MarkupFilter::MMap *MarkupFilter::getContainingMMap(uint64_t Addr) const { + // Find the first mmap starting >= Addr. + auto I = MMaps.lower_bound(Addr); + if (I != MMaps.end() && I->second.contains(Addr)) + return &I->second; + + // The previous mmap is the last one starting < Addr. + if (I == MMaps.begin()) + return nullptr; + --I; + return I->second.contains(Addr) ? &I->second : nullptr; +} + StringRef MarkupFilter::lineEnding() const { return Line.endswith("\r\n") ? "\r\n" : "\n"; } @@ -502,3 +562,8 @@ bool MarkupFilter::MMap::contains(uint64_t Addr) const { return this->Addr <= Addr && Addr < this->Addr + Size; } + +// Returns the module-relative address for a given virtual address. +uint64_t MarkupFilter::MMap::getModuleRelativeAddr(uint64_t Addr) const { + return Addr - this->Addr + ModuleRelativeAddr; +} diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-data.test b/llvm/test/DebugInfo/symbolize-filter-markup-data.test new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/symbolize-filter-markup-data.test @@ -0,0 +1,35 @@ +REQUIRES: x86-registered-target +RUN: split-file %s %t +RUN: mkdir -p %t/.build-id/ab +RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t/asm.s \ +RUN: -o %t/.build-id/ab/cdef.debug +RUN: llvm-symbolizer --debug-file-directory=%t --filter-markup < %t/input \ +RUN: > %t.output 2> %t.err +RUN: FileCheck %s --input-file=%t.output --match-full-lines \ +RUN: --implicit-check-not {{.}} +RUN: FileCheck %s --check-prefix=ERR --input-file=%t.err --match-full-lines + +CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=abcdef 0x0(r)-0x10(r)[[END:\]{3}]] +CHECK: long long byte +CHECK: long byte +CHECK: [[BEGIN]]data:0x05[[END]] + +ERR: error: expected 1 field(s); found 0 +ERR: error: no mmap covers address + +;--- input +{{{module:0:a.o:elf:abcdef}}} +{{{mmap:0:5:load:0:r:0}}} +{{{mmap:0x10:2:load:0:r:0x3}}} +{{{data:0x0}}} {{{data:0x1}}} {{{data:0x4}}} +{{{data:0x10}}} {{{data:0x11}}} + +{{{data}}} +{{{data:0x5}}} +;--- asm.s +long: + .long 0x11223344 + .size l, 4 +byte: + .byte 0x42 + .size b, 1 diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-error-location.test b/llvm/test/DebugInfo/symbolize-filter-markup-error-location.test --- a/llvm/test/DebugInfo/symbolize-filter-markup-error-location.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-error-location.test @@ -2,13 +2,13 @@ RUN: llvm-symbolizer --filter-markup < %t/log > /dev/null 2> %t.err RUN: FileCheck %s -input-file=%t.err --match-full-lines --strict-whitespace -CHECK:error: expected 1 fields; found 0 +CHECK:error: expected 1 field(s); found 0 CHECK:[[BEGIN:[{]{3}]]symbol[[END:[}]{3}]] CHECK: ^ -CHECK:error: expected 1 fields; found 0 +CHECK:error: expected 1 field(s); found 0 CHECK:foo[[BEGIN]]symbol[[END]]bar[[BEGIN]]symbol[[END]]baz CHECK: ^ -CHECK:error: expected 1 fields; found 0 +CHECK:error: expected 1 field(s); found 0 CHECK:foo[[BEGIN]]symbol[[END]]bar[[BEGIN]]symbol[[END]]baz CHECK: ^ diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-mmap.test b/llvm/test/DebugInfo/symbolize-filter-markup-mmap.test --- a/llvm/test/DebugInfo/symbolize-filter-markup-mmap.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-mmap.test @@ -6,9 +6,9 @@ CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=abb50d82b6bdc861 0x0(rwx)-0x1(r)-0x2(w)-0x3(x)-0x4(rwx)-0xa(r)[[END:\]{3}]] -ERR: error: expected at least 3 fields; found 0 +ERR: error: expected at least 3 field(s); found 0 ERR: error: unknown mmap type -ERR: error: expected 6 fields; found 3 +ERR: error: expected 6 field(s); found 3 ERR: error: expected address; found '1' ERR: error: expected size; found '-1' ERR: error: expected mode; found '' diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-module.test b/llvm/test/DebugInfo/symbolize-filter-markup-module.test --- a/llvm/test/DebugInfo/symbolize-filter-markup-module.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-module.test @@ -9,10 +9,10 @@ CHECK: [[BEGIN]]ELF module #0x2 "c.o"; BuildID=cd[[END]] CHECK: [[BEGIN]]ELF module #0x1 "b.o"; adds 0x0(r)[[END]] -ERR: error: expected at least 3 fields; found 0 +ERR: error: expected at least 3 field(s); found 0 ERR: error: unknown module type ERR: error: duplicate module ID -ERR: error: expected 4 fields; found 3 +ERR: error: expected 4 field(s); found 3 ;--- log {{{module:0:a.o:elf:ab}}} diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-reset.test b/llvm/test/DebugInfo/symbolize-filter-markup-reset.test --- a/llvm/test/DebugInfo/symbolize-filter-markup-reset.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-reset.test @@ -8,7 +8,7 @@ CHECK: {{ }}[[BEGIN]]reset[[END]] CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "b.o"; BuildID=cd 0x1(r)[[END:\]{3}]] -ERR: error: expected 0 fields; found 1 +ERR: error: expected 0 field(s); found 1 ;--- log {{{reset}}} diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -366,8 +366,8 @@ } // Symbolize markup from stdin and write the result to stdout. -static void filterMarkup(const opt::InputArgList &Args) { - MarkupFilter Filter(outs(), parseColorArg(Args)); +static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) { + MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args)); for (std::string InputString; std::getline(std::cin, InputString);) { InputString += '\n'; Filter.filter(InputString); @@ -436,8 +436,19 @@ } } + LLVMSymbolizer Symbolizer(Opts); + + // A debuginfod lookup could succeed if a HTTP client is available and at + // least one backing URL is configured. + bool ShouldUseDebuginfodByDefault = + HTTPClient::isAvailable() && + !ExitOnErr(getDefaultDebuginfodUrls()).empty(); + if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, + ShouldUseDebuginfodByDefault)) + enableDebuginfod(Symbolizer); + if (Args.hasArg(OPT_filter_markup)) { - filterMarkup(Args); + filterMarkup(Args, Symbolizer); return 0; } @@ -457,17 +468,6 @@ } SmallVector BuildID = parseBuildIDArg(Args, OPT_build_id_EQ); - LLVMSymbolizer Symbolizer(Opts); - - // A debuginfod lookup could succeed if a HTTP client is available and at - // least one backing URL is configured. - bool ShouldUseDebuginfodByDefault = - HTTPClient::isAvailable() && - !ExitOnErr(getDefaultDebuginfodUrls()).empty(); - if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, - ShouldUseDebuginfodByDefault)) - enableDebuginfod(Symbolizer); - std::unique_ptr Printer; if (Style == OutputStyle::GNU) Printer = std::make_unique(outs(), errs(), Config);