xref: /llvm-project/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp (revision 0060c54e0da6d1429875da2d30895faa7562b706)
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/STLExtras.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/StringSwitch.h"
22 #include "llvm/DebugInfo/DIContext.h"
23 #include "llvm/DebugInfo/Symbolize/Markup.h"
24 #include "llvm/DebugInfo/Symbolize/Symbolize.h"
25 #include "llvm/Demangle/Demangle.h"
26 #include "llvm/Support/Error.h"
27 #include "llvm/Support/FormatVariadic.h"
28 #include "llvm/Support/WithColor.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include <optional>
31 
32 using namespace llvm;
33 using namespace llvm::symbolize;
34 
35 MarkupFilter::MarkupFilter(raw_ostream &OS, LLVMSymbolizer &Symbolizer,
36                            std::optional<bool> ColorsEnabled)
37     : OS(OS), Symbolizer(Symbolizer),
38       ColorsEnabled(
39           ColorsEnabled.value_or(WithColor::defaultAutoDetectFunction()(OS))) {}
40 
41 void MarkupFilter::filter(std::string &&InputLine) {
42   Line = std::move(InputLine);
43   resetColor();
44 
45   Parser.parseLine(Line);
46   SmallVector<MarkupNode> DeferredNodes;
47   // See if the line is a contextual (i.e. contains a contextual element).
48   // In this case, anything after the contextual element is elided, or the whole
49   // line may be elided.
50   while (std::optional<MarkupNode> Node = Parser.nextNode()) {
51     // If this was a contextual line, then summarily stop processing.
52     if (tryContextualElement(*Node, DeferredNodes))
53       return;
54     // This node may yet be part of an elided contextual line.
55     DeferredNodes.push_back(*Node);
56   }
57 
58   // This was not a contextual line, so nothing in it should be elided.
59   endAnyModuleInfoLine();
60   for (const MarkupNode &Node : DeferredNodes)
61     filterNode(Node);
62 }
63 
64 void MarkupFilter::finish() {
65   Parser.flush();
66   while (std::optional<MarkupNode> Node = Parser.nextNode())
67     filterNode(*Node);
68   endAnyModuleInfoLine();
69   resetColor();
70   Modules.clear();
71   MMaps.clear();
72 }
73 
74 // See if the given node is a contextual element and handle it if so. This may
75 // either output or defer the element; in the former case, it will first emit
76 // any DeferredNodes.
77 //
78 // Returns true if the given element was a contextual element. In this case,
79 // DeferredNodes should be considered handled and should not be emitted. The
80 // rest of the containing line must also be ignored in case the element was
81 // deferred to a following line.
82 bool MarkupFilter::tryContextualElement(
83     const MarkupNode &Node, const SmallVector<MarkupNode> &DeferredNodes) {
84   if (tryMMap(Node, DeferredNodes))
85     return true;
86   if (tryReset(Node, DeferredNodes))
87     return true;
88   return tryModule(Node, DeferredNodes);
89 }
90 
91 bool MarkupFilter::tryMMap(const MarkupNode &Node,
92                            const SmallVector<MarkupNode> &DeferredNodes) {
93   if (Node.Tag != "mmap")
94     return false;
95   std::optional<MMap> ParsedMMap = parseMMap(Node);
96   if (!ParsedMMap)
97     return true;
98 
99   if (const MMap *M = getOverlappingMMap(*ParsedMMap)) {
100     WithColor::error(errs())
101         << formatv("overlapping mmap: #{0:x} [{1:x}-{2:x}]\n", M->Mod->ID,
102                    M->Addr, M->Addr + M->Size - 1);
103     reportLocation(Node.Fields[0].begin());
104     return true;
105   }
106 
107   auto Res = MMaps.emplace(ParsedMMap->Addr, std::move(*ParsedMMap));
108   assert(Res.second && "Overlap check should ensure emplace succeeds.");
109   MMap &MMap = Res.first->second;
110 
111   if (!MIL || MIL->Mod != MMap.Mod) {
112     endAnyModuleInfoLine();
113     for (const MarkupNode &Node : DeferredNodes)
114       filterNode(Node);
115     beginModuleInfoLine(MMap.Mod);
116     OS << "; adds";
117   }
118   MIL->MMaps.push_back(&MMap);
119   return true;
120 }
121 
122 bool MarkupFilter::tryReset(const MarkupNode &Node,
123                             const SmallVector<MarkupNode> &DeferredNodes) {
124   if (Node.Tag != "reset")
125     return false;
126   if (!checkNumFields(Node, 0))
127     return true;
128 
129   if (!Modules.empty() || !MMaps.empty()) {
130     endAnyModuleInfoLine();
131     for (const MarkupNode &Node : DeferredNodes)
132       filterNode(Node);
133     printRawElement(Node);
134     OS << lineEnding();
135 
136     Modules.clear();
137     MMaps.clear();
138   }
139   return true;
140 }
141 
142 bool MarkupFilter::tryModule(const MarkupNode &Node,
143                              const SmallVector<MarkupNode> &DeferredNodes) {
144   if (Node.Tag != "module")
145     return false;
146   std::optional<Module> ParsedModule = parseModule(Node);
147   if (!ParsedModule)
148     return true;
149 
150   auto Res = Modules.try_emplace(
151       ParsedModule->ID, std::make_unique<Module>(std::move(*ParsedModule)));
152   if (!Res.second) {
153     WithColor::error(errs()) << "duplicate module ID\n";
154     reportLocation(Node.Fields[0].begin());
155     return true;
156   }
157   Module &Module = *Res.first->second;
158 
159   endAnyModuleInfoLine();
160   for (const MarkupNode &Node : DeferredNodes)
161     filterNode(Node);
162   beginModuleInfoLine(&Module);
163   OS << "; BuildID=";
164   printValue(toHex(Module.BuildID, /*LowerCase=*/true));
165   return true;
166 }
167 
168 void MarkupFilter::beginModuleInfoLine(const Module *M) {
169   highlight();
170   OS << "[[[ELF module";
171   printValue(formatv(" #{0:x} ", M->ID));
172   OS << '"';
173   printValue(M->Name);
174   OS << '"';
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     OS << '[';
187     printValue(formatv("{0:x}", M->Addr));
188     OS << '-';
189     printValue(formatv("{0:x}", M->Addr + M->Size - 1));
190     OS << "](";
191     printValue(M->Mode);
192     OS << ')';
193   }
194   OS << "]]]" << lineEnding();
195   restoreColor();
196   MIL.reset();
197 }
198 
199 // Handle a node that is known not to be a contextual element.
200 void MarkupFilter::filterNode(const MarkupNode &Node) {
201   if (!checkTag(Node))
202     return;
203   if (tryPresentation(Node))
204     return;
205   if (trySGR(Node))
206     return;
207 
208   OS << Node.Text;
209 }
210 
211 bool MarkupFilter::tryPresentation(const MarkupNode &Node) {
212   if (trySymbol(Node))
213     return true;
214   if (tryPC(Node))
215     return true;
216   if (tryBackTrace(Node))
217     return true;
218   return tryData(Node);
219 }
220 
221 bool MarkupFilter::trySymbol(const MarkupNode &Node) {
222   if (Node.Tag != "symbol")
223     return false;
224   if (!checkNumFields(Node, 1))
225     return true;
226 
227   highlight();
228   OS << llvm::demangle(Node.Fields.front().str());
229   restoreColor();
230   return true;
231 }
232 
233 bool MarkupFilter::tryPC(const MarkupNode &Node) {
234   if (Node.Tag != "pc")
235     return false;
236   if (!checkNumFieldsAtLeast(Node, 1))
237     return true;
238   warnNumFieldsAtMost(Node, 2);
239 
240   std::optional<uint64_t> Addr = parseAddr(Node.Fields[0]);
241   if (!Addr)
242     return true;
243 
244   // PC addresses that aren't part of a backtrace are assumed to be precise code
245   // locations.
246   PCType Type = PCType::PreciseCode;
247   if (Node.Fields.size() == 2) {
248     std::optional<PCType> ParsedType = parsePCType(Node.Fields[1]);
249     if (!ParsedType)
250       return true;
251     Type = *ParsedType;
252   }
253   *Addr = adjustAddr(*Addr, Type);
254 
255   const MMap *MMap = getContainingMMap(*Addr);
256   if (!MMap) {
257     WithColor::error() << "no mmap covers address\n";
258     reportLocation(Node.Fields[0].begin());
259     printRawElement(Node);
260     return true;
261   }
262 
263   Expected<DILineInfo> LI = Symbolizer.symbolizeCode(
264       MMap->Mod->BuildID, {MMap->getModuleRelativeAddr(*Addr)});
265   if (!LI) {
266     WithColor::defaultErrorHandler(LI.takeError());
267     printRawElement(Node);
268     return true;
269   }
270   if (!*LI) {
271     printRawElement(Node);
272     return true;
273   }
274 
275   highlight();
276   printValue(LI->FunctionName);
277   OS << '[';
278   printValue(LI->FileName);
279   OS << ':';
280   printValue(Twine(LI->Line));
281   OS << ']';
282   restoreColor();
283   return true;
284 }
285 
286 bool MarkupFilter::tryBackTrace(const MarkupNode &Node) {
287   if (Node.Tag != "bt")
288     return false;
289   if (!checkNumFieldsAtLeast(Node, 2))
290     return true;
291   warnNumFieldsAtMost(Node, 3);
292 
293   std::optional<uint64_t> FrameNumber = parseFrameNumber(Node.Fields[0]);
294   if (!FrameNumber)
295     return true;
296 
297   std::optional<uint64_t> Addr = parseAddr(Node.Fields[1]);
298   if (!Addr)
299     return true;
300 
301   // Backtrace addresses are assumed to be return addresses by default.
302   PCType Type = PCType::ReturnAddress;
303   if (Node.Fields.size() == 3) {
304     std::optional<PCType> ParsedType = parsePCType(Node.Fields[2]);
305     if (!ParsedType)
306       return true;
307     Type = *ParsedType;
308   }
309   *Addr = adjustAddr(*Addr, Type);
310 
311   const MMap *MMap = getContainingMMap(*Addr);
312   if (!MMap) {
313     WithColor::error() << "no mmap covers address\n";
314     reportLocation(Node.Fields[0].begin());
315     printRawElement(Node);
316     return true;
317   }
318   uint64_t MRA = MMap->getModuleRelativeAddr(*Addr);
319 
320   Expected<DIInliningInfo> II =
321       Symbolizer.symbolizeInlinedCode(MMap->Mod->BuildID, {MRA});
322   if (!II) {
323     WithColor::defaultErrorHandler(II.takeError());
324     printRawElement(Node);
325     return true;
326   }
327 
328   highlight();
329   for (unsigned I = 0, E = II->getNumberOfFrames(); I != E; ++I) {
330     auto Header = formatv("{0, +6}", formatv("#{0}", FrameNumber)).sstr<16>();
331     // Don't highlight the # sign as a value.
332     size_t NumberIdx = Header.find("#") + 1;
333     OS << Header.substr(0, NumberIdx);
334     printValue(Header.substr(NumberIdx));
335     if (I == E - 1) {
336       OS << "   ";
337     } else {
338       OS << '.';
339       printValue(formatv("{0, -2}", I + 1));
340     }
341     printValue(formatv(" {0:x16} ", *Addr));
342 
343     DILineInfo LI = II->getFrame(I);
344     if (LI) {
345       printValue(LI.FunctionName);
346       OS << ' ';
347       printValue(LI.FileName);
348       OS << ':';
349       printValue(Twine(LI.Line));
350       OS << ':';
351       printValue(Twine(LI.Column));
352       OS << ' ';
353     }
354     OS << '(';
355     printValue(MMap->Mod->Name);
356     OS << "+";
357     printValue(formatv("{0:x}", MRA));
358     OS << ')';
359     if (I != E - 1)
360       OS << lineEnding();
361   }
362   restoreColor();
363   return true;
364 }
365 
366 bool MarkupFilter::tryData(const MarkupNode &Node) {
367   if (Node.Tag != "data")
368     return false;
369   if (!checkNumFields(Node, 1))
370     return true;
371   std::optional<uint64_t> Addr = parseAddr(Node.Fields[0]);
372   if (!Addr)
373     return true;
374 
375   const MMap *MMap = getContainingMMap(*Addr);
376   if (!MMap) {
377     WithColor::error() << "no mmap covers address\n";
378     reportLocation(Node.Fields[0].begin());
379     printRawElement(Node);
380     return true;
381   }
382 
383   Expected<DIGlobal> Symbol = Symbolizer.symbolizeData(
384       MMap->Mod->BuildID, {MMap->getModuleRelativeAddr(*Addr)});
385   if (!Symbol) {
386     WithColor::defaultErrorHandler(Symbol.takeError());
387     printRawElement(Node);
388     return true;
389   }
390 
391   highlight();
392   OS << Symbol->Name;
393   restoreColor();
394   return true;
395 }
396 
397 bool MarkupFilter::trySGR(const MarkupNode &Node) {
398   if (Node.Text == "\033[0m") {
399     resetColor();
400     return true;
401   }
402   if (Node.Text == "\033[1m") {
403     Bold = true;
404     if (ColorsEnabled)
405       OS.changeColor(raw_ostream::Colors::SAVEDCOLOR, Bold);
406     return true;
407   }
408   auto SGRColor = StringSwitch<std::optional<raw_ostream::Colors>>(Node.Text)
409                       .Case("\033[30m", raw_ostream::Colors::BLACK)
410                       .Case("\033[31m", raw_ostream::Colors::RED)
411                       .Case("\033[32m", raw_ostream::Colors::GREEN)
412                       .Case("\033[33m", raw_ostream::Colors::YELLOW)
413                       .Case("\033[34m", raw_ostream::Colors::BLUE)
414                       .Case("\033[35m", raw_ostream::Colors::MAGENTA)
415                       .Case("\033[36m", raw_ostream::Colors::CYAN)
416                       .Case("\033[37m", raw_ostream::Colors::WHITE)
417                       .Default(std::nullopt);
418   if (SGRColor) {
419     Color = *SGRColor;
420     if (ColorsEnabled)
421       OS.changeColor(*Color);
422     return true;
423   }
424 
425   return false;
426 }
427 
428 // Begin highlighting text by picking a different color than the current color
429 // state.
430 void MarkupFilter::highlight() {
431   if (!ColorsEnabled)
432     return;
433   OS.changeColor(Color == raw_ostream::Colors::BLUE ? raw_ostream::Colors::CYAN
434                                                     : raw_ostream::Colors::BLUE,
435                  Bold);
436 }
437 
438 // Begin highlighting a field within a highlighted markup string.
439 void MarkupFilter::highlightValue() {
440   if (!ColorsEnabled)
441     return;
442   OS.changeColor(raw_ostream::Colors::GREEN, Bold);
443 }
444 
445 // Set the output stream's color to the current color and bold state of the SGR
446 // abstract machine.
447 void MarkupFilter::restoreColor() {
448   if (!ColorsEnabled)
449     return;
450   if (Color) {
451     OS.changeColor(*Color, Bold);
452   } else {
453     OS.resetColor();
454     if (Bold)
455       OS.changeColor(raw_ostream::Colors::SAVEDCOLOR, Bold);
456   }
457 }
458 
459 // Set the SGR and output stream's color and bold states back to the default.
460 void MarkupFilter::resetColor() {
461   if (!Color && !Bold)
462     return;
463   Color.reset();
464   Bold = false;
465   if (ColorsEnabled)
466     OS.resetColor();
467 }
468 
469 void MarkupFilter::printRawElement(const MarkupNode &Element) {
470   highlight();
471   OS << "[[[";
472   printValue(Element.Tag);
473   for (StringRef Field : Element.Fields) {
474     OS << ':';
475     printValue(Field);
476   }
477   OS << "]]]";
478   restoreColor();
479 }
480 
481 void MarkupFilter::printValue(Twine Value) {
482   highlightValue();
483   OS << Value;
484   highlight();
485 }
486 
487 // This macro helps reduce the amount of indirection done through Optional
488 // below, since the usual case upon returning a std::nullopt Optional is to
489 // return std::nullopt.
490 #define ASSIGN_OR_RETURN_NONE(TYPE, NAME, EXPR)                                \
491   auto NAME##Opt = (EXPR);                                                     \
492   if (!NAME##Opt)                                                              \
493     return std::nullopt;                                                       \
494   TYPE NAME = std::move(*NAME##Opt)
495 
496 std::optional<MarkupFilter::Module>
497 MarkupFilter::parseModule(const MarkupNode &Element) const {
498   if (!checkNumFieldsAtLeast(Element, 3))
499     return std::nullopt;
500   ASSIGN_OR_RETURN_NONE(uint64_t, ID, parseModuleID(Element.Fields[0]));
501   StringRef Name = Element.Fields[1];
502   StringRef Type = Element.Fields[2];
503   if (Type != "elf") {
504     WithColor::error() << "unknown module type\n";
505     reportLocation(Type.begin());
506     return std::nullopt;
507   }
508   if (!checkNumFields(Element, 4))
509     return std::nullopt;
510   SmallVector<uint8_t> BuildID = parseBuildID(Element.Fields[3]);
511   if (BuildID.empty())
512     return std::nullopt;
513   return Module{ID, Name.str(), std::move(BuildID)};
514 }
515 
516 std::optional<MarkupFilter::MMap>
517 MarkupFilter::parseMMap(const MarkupNode &Element) const {
518   if (!checkNumFieldsAtLeast(Element, 3))
519     return std::nullopt;
520   ASSIGN_OR_RETURN_NONE(uint64_t, Addr, parseAddr(Element.Fields[0]));
521   ASSIGN_OR_RETURN_NONE(uint64_t, Size, parseSize(Element.Fields[1]));
522   StringRef Type = Element.Fields[2];
523   if (Type != "load") {
524     WithColor::error() << "unknown mmap type\n";
525     reportLocation(Type.begin());
526     return std::nullopt;
527   }
528   if (!checkNumFields(Element, 6))
529     return std::nullopt;
530   ASSIGN_OR_RETURN_NONE(uint64_t, ID, parseModuleID(Element.Fields[3]));
531   ASSIGN_OR_RETURN_NONE(std::string, Mode, parseMode(Element.Fields[4]));
532   auto It = Modules.find(ID);
533   if (It == Modules.end()) {
534     WithColor::error() << "unknown module ID\n";
535     reportLocation(Element.Fields[3].begin());
536     return std::nullopt;
537   }
538   ASSIGN_OR_RETURN_NONE(uint64_t, ModuleRelativeAddr,
539                         parseAddr(Element.Fields[5]));
540   return MMap{Addr, Size, It->second.get(), std::move(Mode),
541               ModuleRelativeAddr};
542 }
543 
544 // Parse an address (%p in the spec).
545 std::optional<uint64_t> MarkupFilter::parseAddr(StringRef Str) const {
546   if (Str.empty()) {
547     reportTypeError(Str, "address");
548     return std::nullopt;
549   }
550   if (all_of(Str, [](char C) { return C == '0'; }))
551     return 0;
552   if (!Str.starts_with("0x")) {
553     reportTypeError(Str, "address");
554     return std::nullopt;
555   }
556   uint64_t Addr;
557   if (Str.drop_front(2).getAsInteger(16, Addr)) {
558     reportTypeError(Str, "address");
559     return std::nullopt;
560   }
561   return Addr;
562 }
563 
564 // Parse a module ID (%i in the spec).
565 std::optional<uint64_t> MarkupFilter::parseModuleID(StringRef Str) const {
566   uint64_t ID;
567   if (Str.getAsInteger(0, ID)) {
568     reportTypeError(Str, "module ID");
569     return std::nullopt;
570   }
571   return ID;
572 }
573 
574 // Parse a size (%i in the spec).
575 std::optional<uint64_t> MarkupFilter::parseSize(StringRef Str) const {
576   uint64_t ID;
577   if (Str.getAsInteger(0, ID)) {
578     reportTypeError(Str, "size");
579     return std::nullopt;
580   }
581   return ID;
582 }
583 
584 // Parse a frame number (%i in the spec).
585 std::optional<uint64_t> MarkupFilter::parseFrameNumber(StringRef Str) const {
586   uint64_t ID;
587   if (Str.getAsInteger(10, ID)) {
588     reportTypeError(Str, "frame number");
589     return std::nullopt;
590   }
591   return ID;
592 }
593 
594 // Parse a build ID (%x in the spec).
595 object::BuildID MarkupFilter::parseBuildID(StringRef Str) const {
596   object::BuildID BID = llvm::object::parseBuildID(Str);
597   if (BID.empty())
598     reportTypeError(Str, "build ID");
599   return BID;
600 }
601 
602 // Parses the mode string for an mmap element.
603 std::optional<std::string> MarkupFilter::parseMode(StringRef Str) const {
604   if (Str.empty()) {
605     reportTypeError(Str, "mode");
606     return std::nullopt;
607   }
608 
609   // Pop off each of r/R, w/W, and x/X from the front, in that order.
610   StringRef Remainder = Str;
611   Remainder.consume_front_insensitive("r");
612   Remainder.consume_front_insensitive("w");
613   Remainder.consume_front_insensitive("x");
614 
615   // If anything remains, then the string wasn't a mode.
616   if (!Remainder.empty()) {
617     reportTypeError(Str, "mode");
618     return std::nullopt;
619   }
620 
621   // Normalize the mode.
622   return Str.lower();
623 }
624 
625 std::optional<MarkupFilter::PCType>
626 MarkupFilter::parsePCType(StringRef Str) const {
627   std::optional<MarkupFilter::PCType> Type =
628       StringSwitch<std::optional<MarkupFilter::PCType>>(Str)
629           .Case("ra", MarkupFilter::PCType::ReturnAddress)
630           .Case("pc", MarkupFilter::PCType::PreciseCode)
631           .Default(std::nullopt);
632   if (!Type)
633     reportTypeError(Str, "PC type");
634   return Type;
635 }
636 
637 bool MarkupFilter::checkTag(const MarkupNode &Node) const {
638   if (any_of(Node.Tag, [](char C) { return C < 'a' || C > 'z'; })) {
639     WithColor::error(errs()) << "tags must be all lowercase characters\n";
640     reportLocation(Node.Tag.begin());
641     return false;
642   }
643   return true;
644 }
645 
646 bool MarkupFilter::checkNumFields(const MarkupNode &Element,
647                                   size_t Size) const {
648   if (Element.Fields.size() != Size) {
649     bool Warn = Element.Fields.size() > Size;
650     WithColor(errs(), Warn ? HighlightColor::Warning : HighlightColor::Error)
651         << (Warn ? "warning: " : "error: ") << "expected " << Size
652         << " field(s); found " << Element.Fields.size() << "\n";
653     reportLocation(Element.Tag.end());
654     return Warn;
655   }
656   return true;
657 }
658 
659 bool MarkupFilter::checkNumFieldsAtLeast(const MarkupNode &Element,
660                                          size_t Size) const {
661   if (Element.Fields.size() < Size) {
662     WithColor::error(errs())
663         << "expected at least " << Size << " field(s); found "
664         << Element.Fields.size() << "\n";
665     reportLocation(Element.Tag.end());
666     return false;
667   }
668   return true;
669 }
670 
671 void MarkupFilter::warnNumFieldsAtMost(const MarkupNode &Element,
672                                        size_t Size) const {
673   if (Element.Fields.size() <= Size)
674     return;
675   WithColor::warning(errs())
676       << "expected at most " << Size << " field(s); found "
677       << Element.Fields.size() << "\n";
678   reportLocation(Element.Tag.end());
679 }
680 
681 void MarkupFilter::reportTypeError(StringRef Str, StringRef TypeName) const {
682   WithColor::error(errs()) << "expected " << TypeName << "; found '" << Str
683                            << "'\n";
684   reportLocation(Str.begin());
685 }
686 
687 // Prints two lines that point out the given location in the current Line using
688 // a caret. The iterator must be within the bounds of the most recent line
689 // passed to beginLine().
690 void MarkupFilter::reportLocation(StringRef::iterator Loc) const {
691   errs() << Line;
692   WithColor(errs().indent(Loc - StringRef(Line).begin()),
693             HighlightColor::String)
694       << '^';
695   errs() << '\n';
696 }
697 
698 // Checks for an existing mmap that overlaps the given one and returns a
699 // pointer to one of them.
700 const MarkupFilter::MMap *
701 MarkupFilter::getOverlappingMMap(const MMap &Map) const {
702   // If the given map contains the start of another mmap, they overlap.
703   auto I = MMaps.upper_bound(Map.Addr);
704   if (I != MMaps.end() && Map.contains(I->second.Addr))
705     return &I->second;
706 
707   // If no element starts inside the given mmap, the only possible overlap would
708   // be if the preceding mmap contains the start point of the given mmap.
709   if (I != MMaps.begin()) {
710     --I;
711     if (I->second.contains(Map.Addr))
712       return &I->second;
713   }
714   return nullptr;
715 }
716 
717 // Returns the MMap that contains the given address or nullptr if none.
718 const MarkupFilter::MMap *MarkupFilter::getContainingMMap(uint64_t Addr) const {
719   // Find the first mmap starting >= Addr.
720   auto I = MMaps.lower_bound(Addr);
721   if (I != MMaps.end() && I->second.contains(Addr))
722     return &I->second;
723 
724   // The previous mmap is the last one starting < Addr.
725   if (I == MMaps.begin())
726     return nullptr;
727   --I;
728   return I->second.contains(Addr) ? &I->second : nullptr;
729 }
730 
731 uint64_t MarkupFilter::adjustAddr(uint64_t Addr, PCType Type) const {
732   // Decrementing return addresses by one moves them into the call instruction.
733   // The address doesn't have to be the start of the call instruction, just some
734   // byte on the inside. Subtracting one avoids needing detailed instruction
735   // length information here.
736   return Type == MarkupFilter::PCType::ReturnAddress ? Addr - 1 : Addr;
737 }
738 
739 StringRef MarkupFilter::lineEnding() const {
740   return StringRef(Line).ends_with("\r\n") ? "\r\n" : "\n";
741 }
742 
743 bool MarkupFilter::MMap::contains(uint64_t Addr) const {
744   return this->Addr <= Addr && Addr < this->Addr + Size;
745 }
746 
747 // Returns the module-relative address for a given virtual address.
748 uint64_t MarkupFilter::MMap::getModuleRelativeAddr(uint64_t Addr) const {
749   return Addr - this->Addr + ModuleRelativeAddr;
750 }
751