]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp
Merge llvm-project main llvmorg-15-init-17485-ga3e38b4a206b
[FreeBSD/FreeBSD.git] / contrib / llvm-project / llvm / lib / DebugInfo / Symbolize / MarkupFilter.cpp
1 //===-- lib/DebugInfo/Symbolize/MarkupFilter.cpp -------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file defines the implementation of a filter that replaces symbolizer
11 /// markup with human-readable expressions.
12 ///
13 /// See https://llvm.org/docs/SymbolizerMarkupFormat.html
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #include "llvm/DebugInfo/Symbolize/MarkupFilter.h"
18
19 #include "llvm/ADT/None.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/ADT/StringSwitch.h"
23 #include "llvm/DebugInfo/Symbolize/Markup.h"
24 #include "llvm/Debuginfod/Debuginfod.h"
25 #include "llvm/Demangle/Demangle.h"
26 #include "llvm/Object/ObjectFile.h"
27 #include "llvm/Support/Error.h"
28 #include "llvm/Support/FormatVariadic.h"
29 #include "llvm/Support/WithColor.h"
30 #include "llvm/Support/raw_ostream.h"
31
32 using namespace llvm;
33 using namespace llvm::symbolize;
34
35 MarkupFilter::MarkupFilter(raw_ostream &OS, Optional<bool> ColorsEnabled)
36     : OS(OS), ColorsEnabled(ColorsEnabled.value_or(
37                   WithColor::defaultAutoDetectFunction()(OS))) {}
38
39 void MarkupFilter::filter(StringRef Line) {
40   this->Line = Line;
41   resetColor();
42
43   Parser.parseLine(Line);
44   SmallVector<MarkupNode> DeferredNodes;
45   // See if the line is a contextual (i.e. contains a contextual element).
46   // In this case, anything after the contextual element is elided, or the whole
47   // line may be elided.
48   while (Optional<MarkupNode> Node = Parser.nextNode()) {
49     // If this was a contextual line, then summarily stop processing.
50     if (tryContextualElement(*Node, DeferredNodes))
51       return;
52     // This node may yet be part of an elided contextual line.
53     DeferredNodes.push_back(*Node);
54   }
55
56   // This was not a contextual line, so nothing in it should be elided.
57   endAnyModuleInfoLine();
58   for (const MarkupNode &Node : DeferredNodes)
59     filterNode(Node);
60 }
61
62 void MarkupFilter::finish() {
63   Parser.flush();
64   while (Optional<MarkupNode> Node = Parser.nextNode())
65     filterNode(*Node);
66   endAnyModuleInfoLine();
67   resetColor();
68   Modules.clear();
69   MMaps.clear();
70 }
71
72 // See if the given node is a contextual element and handle it if so. This may
73 // either output or defer the element; in the former case, it will first emit
74 // any DeferredNodes.
75 //
76 // Returns true if the given element was a contextual element. In this case,
77 // DeferredNodes should be considered handled and should not be emitted. The
78 // rest of the containing line must also be ignored in case the element was
79 // deferred to a following line.
80 bool MarkupFilter::tryContextualElement(
81     const MarkupNode &Node, const SmallVector<MarkupNode> &DeferredNodes) {
82   if (tryMMap(Node, DeferredNodes))
83     return true;
84   if (tryReset(Node, DeferredNodes))
85     return true;
86   return tryModule(Node, DeferredNodes);
87 }
88
89 bool MarkupFilter::tryMMap(const MarkupNode &Node,
90                            const SmallVector<MarkupNode> &DeferredNodes) {
91   if (Node.Tag != "mmap")
92     return false;
93   Optional<MMap> ParsedMMap = parseMMap(Node);
94   if (!ParsedMMap)
95     return true;
96
97   if (const MMap *M = overlappingMMap(*ParsedMMap)) {
98     WithColor::error(errs())
99         << formatv("overlapping mmap: #{0:x} [{1:x},{2:x})\n", M->Mod->ID,
100                    M->Addr, M->Addr + M->Size);
101     reportLocation(Node.Fields[0].begin());
102     return true;
103   }
104
105   auto Res = MMaps.emplace(ParsedMMap->Addr, std::move(*ParsedMMap));
106   assert(Res.second && "Overlap check should ensure emplace succeeds.");
107   MMap &MMap = Res.first->second;
108
109   if (!MIL || MIL->Mod != MMap.Mod) {
110     endAnyModuleInfoLine();
111     for (const MarkupNode &Node : DeferredNodes)
112       filterNode(Node);
113     beginModuleInfoLine(MMap.Mod);
114     OS << "; adds";
115   }
116   MIL->MMaps.push_back(&MMap);
117   return true;
118 }
119
120 bool MarkupFilter::tryReset(const MarkupNode &Node,
121                             const SmallVector<MarkupNode> &DeferredNodes) {
122   if (Node.Tag != "reset")
123     return false;
124   if (!checkNumFields(Node, 0))
125     return true;
126
127   if (!Modules.empty() || !MMaps.empty()) {
128     endAnyModuleInfoLine();
129     for (const MarkupNode &Node : DeferredNodes)
130       filterNode(Node);
131     highlight();
132     OS << "[[[reset]]]" << lineEnding();
133     restoreColor();
134
135     Modules.clear();
136     MMaps.clear();
137   }
138   return true;
139 }
140
141 bool MarkupFilter::tryModule(const MarkupNode &Node,
142                              const SmallVector<MarkupNode> &DeferredNodes) {
143   if (Node.Tag != "module")
144     return false;
145   Optional<Module> ParsedModule = parseModule(Node);
146   if (!ParsedModule)
147     return true;
148
149   auto Res = Modules.try_emplace(
150       ParsedModule->ID, std::make_unique<Module>(std::move(*ParsedModule)));
151   if (!Res.second) {
152     WithColor::error(errs()) << "duplicate module ID\n";
153     reportLocation(Node.Fields[0].begin());
154     return true;
155   }
156   Module &Module = *Res.first->second;
157
158   endAnyModuleInfoLine();
159   for (const MarkupNode &Node : DeferredNodes)
160     filterNode(Node);
161   beginModuleInfoLine(&Module);
162   OS << "; BuildID=";
163   highlightValue();
164   OS << toHex(Module.BuildID, /*LowerCase=*/true);
165   highlight();
166   return true;
167 }
168
169 void MarkupFilter::beginModuleInfoLine(const Module *M) {
170   highlight();
171   OS << "[[[ELF module";
172   highlightValue();
173   OS << formatv(" #{0:x} \"{1}\"", M->ID, M->Name);
174   highlight();
175   MIL = ModuleInfoLine{M};
176 }
177
178 void MarkupFilter::endAnyModuleInfoLine() {
179   if (!MIL)
180     return;
181   llvm::stable_sort(MIL->MMaps, [](const MMap *A, const MMap *B) {
182     return A->Addr < B->Addr;
183   });
184   for (const MMap *M : MIL->MMaps) {
185     OS << (M == MIL->MMaps.front() ? ' ' : '-');
186     highlightValue();
187     OS << formatv("{0:x}", M->Addr);
188     highlight();
189     OS << '(';
190     highlightValue();
191     OS << M->Mode;
192     highlight();
193     OS << ')';
194   }
195   OS << "]]]" << lineEnding();
196   restoreColor();
197   MIL.reset();
198 }
199
200 // Handle a node that is known not to be a contextual element.
201 void MarkupFilter::filterNode(const MarkupNode &Node) {
202   if (!checkTag(Node))
203     return;
204   if (tryPresentation(Node))
205     return;
206   if (trySGR(Node))
207     return;
208
209   OS << Node.Text;
210 }
211
212 bool MarkupFilter::tryPresentation(const MarkupNode &Node) {
213   return trySymbol(Node);
214 }
215
216 bool MarkupFilter::trySymbol(const MarkupNode &Node) {
217   if (Node.Tag != "symbol")
218     return false;
219   if (!checkNumFields(Node, 1))
220     return true;
221
222   highlight();
223   OS << llvm::demangle(Node.Fields.front().str());
224   restoreColor();
225   return true;
226 }
227
228 bool MarkupFilter::trySGR(const MarkupNode &Node) {
229   if (Node.Text == "\033[0m") {
230     resetColor();
231     return true;
232   }
233   if (Node.Text == "\033[1m") {
234     Bold = true;
235     if (ColorsEnabled)
236       OS.changeColor(raw_ostream::Colors::SAVEDCOLOR, Bold);
237     return true;
238   }
239   auto SGRColor = StringSwitch<Optional<raw_ostream::Colors>>(Node.Text)
240                       .Case("\033[30m", raw_ostream::Colors::BLACK)
241                       .Case("\033[31m", raw_ostream::Colors::RED)
242                       .Case("\033[32m", raw_ostream::Colors::GREEN)
243                       .Case("\033[33m", raw_ostream::Colors::YELLOW)
244                       .Case("\033[34m", raw_ostream::Colors::BLUE)
245                       .Case("\033[35m", raw_ostream::Colors::MAGENTA)
246                       .Case("\033[36m", raw_ostream::Colors::CYAN)
247                       .Case("\033[37m", raw_ostream::Colors::WHITE)
248                       .Default(llvm::None);
249   if (SGRColor) {
250     Color = *SGRColor;
251     if (ColorsEnabled)
252       OS.changeColor(*Color);
253     return true;
254   }
255
256   return false;
257 }
258
259 // Begin highlighting text by picking a different color than the current color
260 // state.
261 void MarkupFilter::highlight() {
262   if (!ColorsEnabled)
263     return;
264   OS.changeColor(Color == raw_ostream::Colors::BLUE ? raw_ostream::Colors::CYAN
265                                                     : raw_ostream::Colors::BLUE,
266                  Bold);
267 }
268
269 // Begin highlighting a field within a highlighted markup string.
270 void MarkupFilter::highlightValue() {
271   if (!ColorsEnabled)
272     return;
273   OS.changeColor(raw_ostream::Colors::GREEN, Bold);
274 }
275
276 // Set the output stream's color to the current color and bold state of the SGR
277 // abstract machine.
278 void MarkupFilter::restoreColor() {
279   if (!ColorsEnabled)
280     return;
281   if (Color) {
282     OS.changeColor(*Color, Bold);
283   } else {
284     OS.resetColor();
285     if (Bold)
286       OS.changeColor(raw_ostream::Colors::SAVEDCOLOR, Bold);
287   }
288 }
289
290 // Set the SGR and output stream's color and bold states back to the default.
291 void MarkupFilter::resetColor() {
292   if (!Color && !Bold)
293     return;
294   Color.reset();
295   Bold = false;
296   if (ColorsEnabled)
297     OS.resetColor();
298 }
299
300 // This macro helps reduce the amount of indirection done through Optional
301 // below, since the usual case upon returning a None Optional is to return None.
302 #define ASSIGN_OR_RETURN_NONE(TYPE, NAME, EXPR)                                \
303   auto NAME##Opt = (EXPR);                                                     \
304   if (!NAME##Opt)                                                              \
305     return None;                                                               \
306   TYPE NAME = std::move(*NAME##Opt)
307
308 Optional<MarkupFilter::Module>
309 MarkupFilter::parseModule(const MarkupNode &Element) const {
310   if (!checkNumFieldsAtLeast(Element, 3))
311     return None;
312   ASSIGN_OR_RETURN_NONE(uint64_t, ID, parseModuleID(Element.Fields[0]));
313   StringRef Name = Element.Fields[1];
314   StringRef Type = Element.Fields[2];
315   if (Type != "elf") {
316     WithColor::error() << "unknown module type\n";
317     reportLocation(Type.begin());
318     return None;
319   }
320   if (!checkNumFields(Element, 4))
321     return None;
322   ASSIGN_OR_RETURN_NONE(SmallVector<uint8_t>, BuildID,
323                         parseBuildID(Element.Fields[3]));
324   return Module{ID, Name.str(), std::move(BuildID)};
325 }
326
327 Optional<MarkupFilter::MMap>
328 MarkupFilter::parseMMap(const MarkupNode &Element) const {
329   if (!checkNumFieldsAtLeast(Element, 3))
330     return None;
331   ASSIGN_OR_RETURN_NONE(uint64_t, Addr, parseAddr(Element.Fields[0]));
332   ASSIGN_OR_RETURN_NONE(uint64_t, Size, parseSize(Element.Fields[1]));
333   StringRef Type = Element.Fields[2];
334   if (Type != "load") {
335     WithColor::error() << "unknown mmap type\n";
336     reportLocation(Type.begin());
337     return None;
338   }
339   if (!checkNumFields(Element, 6))
340     return None;
341   ASSIGN_OR_RETURN_NONE(uint64_t, ID, parseModuleID(Element.Fields[3]));
342   ASSIGN_OR_RETURN_NONE(std::string, Mode, parseMode(Element.Fields[4]));
343   auto It = Modules.find(ID);
344   if (It == Modules.end()) {
345     WithColor::error() << "unknown module ID\n";
346     reportLocation(Element.Fields[3].begin());
347     return None;
348   }
349   ASSIGN_OR_RETURN_NONE(uint64_t, ModuleRelativeAddr,
350                         parseAddr(Element.Fields[5]));
351   return MMap{Addr, Size, It->second.get(), std::move(Mode),
352               ModuleRelativeAddr};
353 }
354
355 // Parse an address (%p in the spec).
356 Optional<uint64_t> MarkupFilter::parseAddr(StringRef Str) const {
357   if (Str.empty()) {
358     reportTypeError(Str, "address");
359     return None;
360   }
361   if (all_of(Str, [](char C) { return C == '0'; }))
362     return 0;
363   if (!Str.startswith("0x")) {
364     reportTypeError(Str, "address");
365     return None;
366   }
367   uint64_t Addr;
368   if (Str.drop_front(2).getAsInteger(16, Addr)) {
369     reportTypeError(Str, "address");
370     return None;
371   }
372   return Addr;
373 }
374
375 // Parse a module ID (%i in the spec).
376 Optional<uint64_t> MarkupFilter::parseModuleID(StringRef Str) const {
377   uint64_t ID;
378   if (Str.getAsInteger(0, ID)) {
379     reportTypeError(Str, "module ID");
380     return None;
381   }
382   return ID;
383 }
384
385 // Parse a size (%i in the spec).
386 Optional<uint64_t> MarkupFilter::parseSize(StringRef Str) const {
387   uint64_t ID;
388   if (Str.getAsInteger(0, ID)) {
389     reportTypeError(Str, "size");
390     return None;
391   }
392   return ID;
393 }
394
395 // Parse a build ID (%x in the spec).
396 Optional<SmallVector<uint8_t>> MarkupFilter::parseBuildID(StringRef Str) const {
397   std::string Bytes;
398   if (Str.empty() || Str.size() % 2 || !tryGetFromHex(Str, Bytes)) {
399     reportTypeError(Str, "build ID");
400     return None;
401   }
402   ArrayRef<uint8_t> BuildID(reinterpret_cast<const uint8_t *>(Bytes.data()),
403                             Bytes.size());
404   return SmallVector<uint8_t>(BuildID.begin(), BuildID.end());
405 }
406
407 // Parses the mode string for an mmap element.
408 Optional<std::string> MarkupFilter::parseMode(StringRef Str) const {
409   if (Str.empty()) {
410     reportTypeError(Str, "mode");
411     return None;
412   }
413
414   // Pop off each of r/R, w/W, and x/X from the front, in that order.
415   StringRef Remainder = Str;
416   if (!Remainder.empty() && tolower(Remainder.front()) == 'r')
417     Remainder = Remainder.drop_front();
418   if (!Remainder.empty() && tolower(Remainder.front()) == 'w')
419     Remainder = Remainder.drop_front();
420   if (!Remainder.empty() && tolower(Remainder.front()) == 'x')
421     Remainder = Remainder.drop_front();
422
423   // If anything remains, then the string wasn't a mode.
424   if (!Remainder.empty()) {
425     reportTypeError(Str, "mode");
426     return None;
427   }
428
429   // Normalize the mode.
430   return Str.lower();
431 }
432
433 bool MarkupFilter::checkTag(const MarkupNode &Node) const {
434   if (any_of(Node.Tag, [](char C) { return C < 'a' || C > 'z'; })) {
435     WithColor::error(errs()) << "tags must be all lowercase characters\n";
436     reportLocation(Node.Tag.begin());
437     return false;
438   }
439   return true;
440 }
441
442 bool MarkupFilter::checkNumFields(const MarkupNode &Element,
443                                   size_t Size) const {
444   if (Element.Fields.size() != Size) {
445     WithColor::error(errs()) << "expected " << Size << " fields; found "
446                              << Element.Fields.size() << "\n";
447     reportLocation(Element.Tag.end());
448     return false;
449   }
450   return true;
451 }
452
453 bool MarkupFilter::checkNumFieldsAtLeast(const MarkupNode &Element,
454                                          size_t Size) const {
455   if (Element.Fields.size() < Size) {
456     WithColor::error(errs())
457         << "expected at least " << Size << " fields; found "
458         << Element.Fields.size() << "\n";
459     reportLocation(Element.Tag.end());
460     return false;
461   }
462   return true;
463 }
464
465 void MarkupFilter::reportTypeError(StringRef Str, StringRef TypeName) const {
466   WithColor::error(errs()) << "expected " << TypeName << "; found '" << Str
467                            << "'\n";
468   reportLocation(Str.begin());
469 }
470
471 // Prints two lines that point out the given location in the current Line using
472 // a caret. The iterator must be within the bounds of the most recent line
473 // passed to beginLine().
474 void MarkupFilter::reportLocation(StringRef::iterator Loc) const {
475   errs() << Line;
476   WithColor(errs().indent(Loc - Line.begin()), HighlightColor::String) << '^';
477   errs() << '\n';
478 }
479
480 // Checks for an existing mmap that overlaps the given one and returns a
481 // pointer to one of them.
482 const MarkupFilter::MMap *MarkupFilter::overlappingMMap(const MMap &Map) const {
483   // If the given map contains the start of another mmap, they overlap.
484   auto I = MMaps.upper_bound(Map.Addr);
485   if (I != MMaps.end() && Map.contains(I->second.Addr))
486     return &I->second;
487
488   // If no element starts inside the given mmap, the only possible overlap would
489   // be if the preceding mmap contains the start point of the given mmap.
490   if (I != MMaps.begin()) {
491     --I;
492     if (I->second.contains(Map.Addr))
493       return &I->second;
494   }
495   return nullptr;
496 }
497
498 StringRef MarkupFilter::lineEnding() const {
499   return Line.endswith("\r\n") ? "\r\n" : "\n";
500 }
501
502 bool MarkupFilter::MMap::contains(uint64_t Addr) const {
503   return this->Addr <= Addr && Addr < this->Addr + Size;
504 }