From 22df238d4a642a4553ebf7b91325189be48b139d Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Tue, 19 Jul 2022 14:20:12 -0700 Subject: [PATCH] [Symbolizer] Implement data symbolizer markup element. This connects the Symbolizer to the markup filter and enables the first working end-to-end flow using the filter. Reviewed By: peter.smith Differential Revision: https://reviews.llvm.org/D130187 --- llvm/docs/CommandGuide/llvm-symbolizer.rst | 12 +-- llvm/docs/SymbolizerMarkupFormat.rst | 2 +- .../llvm/DebugInfo/Symbolize/MarkupFilter.h | 11 ++- llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp | 90 +++++++++++++++++++--- ...mbolize-filter-markup-context-line-elision.test | 2 +- .../DebugInfo/symbolize-filter-markup-data.test | 35 +++++++++ .../symbolize-filter-markup-error-location.test | 6 +- .../DebugInfo/symbolize-filter-markup-mmap.test | 12 +-- .../DebugInfo/symbolize-filter-markup-module.test | 6 +- .../DebugInfo/symbolize-filter-markup-reset.test | 6 +- llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp | 28 +++---- 11 files changed, 159 insertions(+), 51 deletions(-) create mode 100644 llvm/test/DebugInfo/symbolize-filter-markup-data.test diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst index 33b5fa5..3fff88a 100644 --- a/llvm/docs/CommandGuide/llvm-symbolizer.rst +++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst @@ -251,13 +251,13 @@ OPTIONS 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 index 95ac5d8..319a330 100644 --- a/llvm/docs/SymbolizerMarkupFormat.rst +++ b/llvm/docs/SymbolizerMarkupFormat.rst @@ -195,7 +195,7 @@ human-readable symbolic form. {{{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 index 2668614..3a2c2bf 100644 --- 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 @@ private: 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 @@ private: bool tryPresentation(const MarkupNode &Node); bool trySymbol(const MarkupNode &Node); + bool tryData(const MarkupNode &Node); bool trySGR(const MarkupNode &Node); @@ -107,11 +112,13 @@ private: 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 index 91a5148..2bf2e17 100644 --- 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,10 +97,10 @@ bool MarkupFilter::tryMMap(const MarkupNode &Node, 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); + << formatv("overlapping mmap: #{0:x} [{1:x}-{2:x}]\n", M->Mod->ID, + M->Addr, M->Addr + M->Size - 1); reportLocation(Node.Fields[0].begin()); return true; } @@ -182,9 +185,9 @@ void MarkupFilter::endAnyModuleInfoLine() { return A->Addr < B->Addr; }); for (const MMap *M : MIL->MMaps) { - OS << (M == MIL->MMaps.front() ? ' ' : '-'); + OS << (M == MIL->MMaps.front() ? ' ' : ','); highlightValue(); - OS << formatv("{0:x}", M->Addr); + OS << formatv("[{0:x}-{1:x}]", M->Addr, M->Addr + M->Size - 1); highlight(); OS << '('; highlightValue(); @@ -210,7 +213,9 @@ void MarkupFilter::filterNode(const MarkupNode &Node) { } 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 @@ bool MarkupFilter::trySymbol(const MarkupNode &Node) { 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->Mod->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::checkTag(const MarkupNode &Node) const { 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 @@ bool MarkupFilter::checkNumFieldsAtLeast(const MarkupNode &Element, 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,8 @@ void MarkupFilter::reportLocation(StringRef::iterator Loc) const { // 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 +542,20 @@ const MarkupFilter::MMap *MarkupFilter::overlappingMMap(const MMap &Map) const { 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 +563,8 @@ StringRef MarkupFilter::lineEnding() const { 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-context-line-elision.test b/llvm/test/DebugInfo/symbolize-filter-markup-context-line-elision.test index af187f8..acba42e 100644 --- a/llvm/test/DebugInfo/symbolize-filter-markup-context-line-elision.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-context-line-elision.test @@ -3,7 +3,7 @@ RUN: llvm-symbolizer --filter-markup < %t/log | \ RUN: FileCheck --match-full-lines --implicit-check-not {{.}} \ RUN: --strict-whitespace %s -CHECK:keep[[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=ab 0x0(r)[[END:\]{3}]] +CHECK:keep[[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=ab [0x0-0x0](r)[[END:\]{3}]] CHECK:keep[[BEGIN]]ELF module #0x1 "b.o"; BuildID=cd[[END]] ;--- log diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-data.test b/llvm/test/DebugInfo/symbolize-filter-markup-data.test new file mode 100644 index 0000000..3ce1bae --- /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-0x4](r),[0x10-0x11](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 long, 4 +byte: + .byte 0x42 + .size byte, 1 diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-error-location.test b/llvm/test/DebugInfo/symbolize-filter-markup-error-location.test index 400131d..4c7a7e5 100644 --- 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: split-file %s %t 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 index 506d7a9..37e159e 100644 --- a/llvm/test/DebugInfo/symbolize-filter-markup-mmap.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-mmap.test @@ -4,19 +4,19 @@ RUN: FileCheck %s --input-file=%t.out --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=abb50d82b6bdc861 0x0(rwx)-0x1(r)-0x2(w)-0x3(x)-0x4(rwx)-0xa(r)[[END:\]{3}]] +CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=abb50d82b6bdc861 [0x0-0x0](rwx),[0x1-0x1](r),[0x2-0x2](w),[0x3-0x3](x),[0x4-0x4](rwx),[0xa-0xb](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 '' ERR: error: expected mode; found 'g' ERR: error: expected mode; found 'wr' -ERR: error: overlapping mmap: #0x0 [0xa,0xc) -ERR: error: overlapping mmap: #0x0 [0xa,0xc) -ERR: error: overlapping mmap: #0x0 [0xa,0xc) +ERR: error: overlapping mmap: #0x0 [0xa-0xb] +ERR: error: overlapping mmap: #0x0 [0xa-0xb] +ERR: error: overlapping mmap: #0x0 [0xa-0xb] ;--- log {{{module:0:a.o:elf:abb50d82b6bdc861}}} diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-module.test b/llvm/test/DebugInfo/symbolize-filter-markup-module.test index 74d6347..38ef796 100644 --- a/llvm/test/DebugInfo/symbolize-filter-markup-module.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-module.test @@ -7,12 +7,12 @@ RUN: FileCheck %s --check-prefix=ERR -input-file=%t.err --match-full-lines CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=ab[[END:\]{3}]] CHECK: [[BEGIN]]ELF module #0x1 "b.o"; BuildID=abb50d82b6bdc861[[END]] CHECK: [[BEGIN]]ELF module #0x2 "c.o"; BuildID=cd[[END]] -CHECK: [[BEGIN]]ELF module #0x1 "b.o"; adds 0x0(r)[[END]] +CHECK: [[BEGIN]]ELF module #0x1 "b.o"; adds [0x0-0x98967f](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 index 1abb905..75ffd5d 100644 --- a/llvm/test/DebugInfo/symbolize-filter-markup-reset.test +++ b/llvm/test/DebugInfo/symbolize-filter-markup-reset.test @@ -4,11 +4,11 @@ RUN: FileCheck %s --input-file=%t.out --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=ab 0x0(r)[[END:\]{3}]] +CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "a.o"; BuildID=ab [0x0-0x0](r)[[END:\]{3}]] CHECK: {{ }}[[BEGIN]]reset[[END]] -CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "b.o"; BuildID=cd 0x1(r)[[END:\]{3}]] +CHECK: [[BEGIN:\[{3}]]ELF module #0x0 "b.o"; BuildID=cd [0x1-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 index 7ec70e4..34c93be6 100644 --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -366,8 +366,8 @@ static SmallVector parseBuildIDArg(const opt::InputArgList &Args, } // 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)); std::string InputString; while (std::getline(std::cin, InputString)) { InputString += '\n'; @@ -437,8 +437,19 @@ int main(int argc, char **argv) { } } + 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; } @@ -458,17 +469,6 @@ int main(int argc, char **argv) { } 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); -- 2.7.4