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 @@ -242,6 +242,12 @@ name `_Z3bazv` becomes `baz()`, whilst the non-mangled name `foz` is printed as is). Defaults to true. +.. option:: --dump-context, --no-dump-context + + Only valid with `--filter-markup`. Emits a JSON representation of the + encountered contexts (i.e., process layouts) instead of symbolizing the + markup. + .. option:: --dwp Use the specified DWP file at ```` for any CUs that have split DWARF 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 @@ -20,6 +20,7 @@ #include #include "llvm/ADT/DenseMap.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -32,8 +33,11 @@ /// text. class MarkupFilter { public: + /// @param DumpContext If true, emit JSON for the contexts instead of the + /// symbolized log. MarkupFilter(raw_ostream &OS, LLVMSymbolizer &Symbolizer, - std::optional ColorsEnabled = std::nullopt); + std::optional ColorsEnabled = std::nullopt, + bool DumpContext = false); /// Filters a line containing symbolizer markup and writes the human-readable /// results to the output stream. @@ -100,6 +104,8 @@ bool tryBackTrace(const MarkupNode &Node); bool tryData(const MarkupNode &Node); + bool tryTrigger(const MarkupNode &Node); + bool trySGR(const MarkupNode &Node); void highlight(); @@ -110,6 +116,8 @@ void printRawElement(const MarkupNode &Element); void printValue(Twine Value); + void dumpContext(); + std::optional parseModule(const MarkupNode &Element) const; std::optional parseMMap(const MarkupNode &Element) const; @@ -137,6 +145,7 @@ StringRef lineEnding() const; raw_ostream &OS; + std::unique_ptr JOS; LLVMSymbolizer &Symbolizer; const bool ColorsEnabled; 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 @@ -28,6 +28,8 @@ #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include @@ -36,10 +38,16 @@ using namespace llvm::symbolize; MarkupFilter::MarkupFilter(raw_ostream &OS, LLVMSymbolizer &Symbolizer, - std::optional ColorsEnabled) + std::optional ColorsEnabled, bool DumpContext) : OS(OS), Symbolizer(Symbolizer), ColorsEnabled( - ColorsEnabled.value_or(WithColor::defaultAutoDetectFunction()(OS))) {} + ColorsEnabled.value_or(WithColor::defaultAutoDetectFunction()(OS))) { + if (DumpContext) { + ColorsEnabled = false; + JOS = std::make_unique(OS, /*Indent=*/2); + JOS->arrayBegin(); + } +} void MarkupFilter::filter(StringRef Line) { this->Line = Line; @@ -70,6 +78,11 @@ filterNode(*Node); endAnyModuleInfoLine(); resetColor(); + if (JOS) { + dumpContext(); + JOS->arrayEnd(); + OS << '\n'; + } Modules.clear(); MMaps.clear(); } @@ -116,7 +129,8 @@ for (const MarkupNode &Node : DeferredNodes) filterNode(Node); beginModuleInfoLine(MMap.Mod); - OS << "; adds"; + if (!JOS) + OS << "; adds"; } MIL->MMaps.push_back(&MMap); return true; @@ -129,13 +143,19 @@ if (!checkNumFields(Node, 0)) return true; + if (JOS) + dumpContext(); + if (!Modules.empty() || !MMaps.empty()) { endAnyModuleInfoLine(); for (const MarkupNode &Node : DeferredNodes) filterNode(Node); - highlight(); - OS << "[[[reset]]]" << lineEnding(); - restoreColor(); + + if (!JOS) { + highlight(); + OS << "[[[reset]]]" << lineEnding(); + restoreColor(); + } Modules.clear(); MMaps.clear(); @@ -164,39 +184,45 @@ for (const MarkupNode &Node : DeferredNodes) filterNode(Node); beginModuleInfoLine(&Module); - OS << "; BuildID="; - printValue(toHex(Module.BuildID, /*LowerCase=*/true)); + if (!JOS) { + OS << "; BuildID="; + printValue(toHex(Module.BuildID, /*LowerCase=*/true)); + } return true; } void MarkupFilter::beginModuleInfoLine(const Module *M) { - highlight(); - OS << "[[[ELF module"; - printValue(formatv(" #{0:x} ", M->ID)); - OS << '"'; - printValue(M->Name); - OS << '"'; + if (!JOS) { + highlight(); + OS << "[[[ELF module"; + printValue(formatv(" #{0:x} ", M->ID)); + OS << '"'; + printValue(M->Name); + OS << '"'; + } MIL = ModuleInfoLine{M}; } void MarkupFilter::endAnyModuleInfoLine() { if (!MIL) return; - llvm::stable_sort(MIL->MMaps, [](const MMap *A, const MMap *B) { - return A->Addr < B->Addr; - }); - for (const MMap *M : MIL->MMaps) { - OS << (M == MIL->MMaps.front() ? ' ' : ','); - OS << '['; - printValue(formatv("{0:x}", M->Addr)); - OS << '-'; - printValue(formatv("{0:x}", M->Addr + M->Size - 1)); - OS << "]("; - printValue(M->Mode); - OS << ')'; + if (!JOS) { + llvm::stable_sort(MIL->MMaps, [](const MMap *A, const MMap *B) { + return A->Addr < B->Addr; + }); + for (const MMap *M : MIL->MMaps) { + OS << (M == MIL->MMaps.front() ? ' ' : ','); + OS << '['; + printValue(formatv("{0:x}", M->Addr)); + OS << '-'; + printValue(formatv("{0:x}", M->Addr + M->Size - 1)); + OS << "]("; + printValue(M->Mode); + OS << ')'; + } + OS << "]]]" << lineEnding(); + restoreColor(); } - OS << "]]]" << lineEnding(); - restoreColor(); MIL.reset(); } @@ -204,6 +230,8 @@ void MarkupFilter::filterNode(const MarkupNode &Node) { if (!checkTag(Node)) return; + if (JOS) + return; if (tryPresentation(Node)) return; if (trySGR(Node)) @@ -400,6 +428,32 @@ return true; } +void MarkupFilter::dumpContext() { + JOS->object([&] { + JOS->attributeArray("modules", [&] { + for (const auto &[_, Module] : Modules) { + JOS->objectBegin(); + JOS->attribute("id", Module->ID); + JOS->attribute("type", "elf"); + JOS->attribute("buildID", toHex(Module->BuildID, /*LowerCase=*/true)); + JOS->objectEnd(); + } + }); + JOS->attributeArray("mmaps", [&] { + for (const auto &[_, Map] : MMaps) { + JOS->objectBegin(); + JOS->attribute("address", Map.Addr); + JOS->attribute("size", Map.Size); + JOS->attribute("type", "load"); + JOS->attribute("moduleID", Map.Mod->ID); + JOS->attribute("mode", Map.Mode); + JOS->attribute("moduleRelativeAddress", Map.ModuleRelativeAddr); + JOS->objectEnd(); + } + }); + }); +} + bool MarkupFilter::trySGR(const MarkupNode &Node) { if (Node.Text == "\033[0m") { resetColor(); diff --git a/llvm/test/DebugInfo/symbolize-filter-markup-dump-context.test b/llvm/test/DebugInfo/symbolize-filter-markup-dump-context.test new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/symbolize-filter-markup-dump-context.test @@ -0,0 +1,68 @@ +RUN: split-file %s %t +RUN: llvm-symbolizer --filter-markup --dump-context < %t/log > %t.out +RUN: FileCheck %s --input-file=%t.out --match-full-lines \ +RUN: --implicit-check-not {{.}} + +CHECK: [ +CHECK: { +CHECK: "modules": [ +CHECK: { +CHECK: "id": 0, +CHECK: "type": "elf", +CHECK: "buildID": "ab" +CHECK: }, +CHECK: { +CHECK: "id": 1, +CHECK: "type": "elf", +CHECK: "buildID": "cd" +CHECK: } +CHECK: ], +CHECK: "mmaps": [ +CHECK: { +CHECK: "address": 16, +CHECK: "size": 16, +CHECK: "type": "load", +CHECK: "moduleID": 1, +CHECK: "mode": "r", +CHECK: "moduleRelativeAddress": 2 +CHECK: }, +CHECK: { +CHECK: "address": 32, +CHECK: "size": 48, +CHECK: "type": "load", +CHECK: "moduleID": 1, +CHECK: "mode": "w", +CHECK: "moduleRelativeAddress": 3 +CHECK: }, +CHECK: { +CHECK: "address": 80, +CHECK: "size": 96, +CHECK: "type": "load", +CHECK: "moduleID": 0, +CHECK: "mode": "rx", +CHECK: "moduleRelativeAddress": 4 +CHECK: } +CHECK: ] +CHECK: }, +CHECK: { +CHECK: "modules": [ +CHECK: { +CHECK: "id": 0, +CHECK: "type": "elf", +CHECK: "buildID": "ef" +CHECK: } +CHECK: ], +CHECK: "mmaps": [] +CHECK: } +CHECK: ] + + +;--- log +{{{module:1:a.o:elf:cd}}} +{{{module:0:b.o:elf:ab}}} +{{{mmap:0x10:0x10:load:1:r:0x2}}} +{{{mmap:0x20:0x30:load:1:w:0x3}}} +{{{mmap:0x50:0x60:load:0:rx:0x4}}} +{{{pc:0x20}}} +{{{reset}}} +{{{module:0:c.o:elf:ef}}} diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td --- a/llvm/tools/llvm-symbolizer/Opts.td +++ b/llvm/tools/llvm-symbolizer/Opts.td @@ -31,6 +31,7 @@ : Eq<"default-arch", "Default architecture (for multi-arch objects)">, Group; defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">; +defm dump_context : B<"dump-context", "Only dump symbolizer markup context as JSON.", "Don't dump markup context.">; def filter_markup : Flag<["--"], "filter-markup">, HelpText<"Filter symbolizer markup from stdin.">; def functions : F<"functions", "Print function name for a given address">; def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">; 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 @@ -371,7 +371,9 @@ // Symbolize markup from stdin and write the result to stdout. static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) { - MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args)); + MarkupFilter Filter( + outs(), Symbolizer, parseColorArg(Args), + Args.hasFlag(OPT_dump_context, OPT_no_dump_context, false)); std::string InputString; while (std::getline(std::cin, InputString)) { InputString += '\n';