Skip to content

Commit

Permalink
[Symbolizer] Implement pc element in symbolizing filter.
Browse files Browse the repository at this point in the history
Implements the pc element for the symbolizing filter, including it's
"ra" and "pc" modes. Return addresses ("ra") are adjusted by
decrementing one. By default, {{{pc}}} elements are assumed to point to
precise code ("pc") locations. Backtrace elements will adopt the
opposite convention.

Along the way, some minor refactors of value printing and colorization.

Reviewed By: peter.smith

Differential Revision: https://reviews.llvm.org/D131115

(cherry picked from commit bf48b12)
  • Loading branch information
mysterymath authored and tru committed Sep 7, 2022
1 parent 8f820dd commit 4cc81e3
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 28 deletions.
1 change: 0 additions & 1 deletion llvm/docs/CommandGuide/llvm-symbolizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ OPTIONS
and prints the results to standard output. The following markup elements are
not yet supported:

* ``{{pc}}``
* ``{{bt}}``
* ``{{hexdict}}``
* ``{{dumpfile}}``
Expand Down
2 changes: 1 addition & 1 deletion llvm/docs/SymbolizerMarkupFormat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ human-readable symbolic form.
{{{symbol:_ZN7Mangled4NameEv}}}
{{{symbol:foobar}}}

``{{{pc:%p}}}``, ``{{{pc:%p:ra}}}``, ``{{{pc:%p:pc}}}`` [#not_yet_implemented]_
``{{{pc:%p}}}``, ``{{{pc:%p:ra}}}``, ``{{{pc:%p:pc}}}``

Here ``%p`` is the memory address of a code location. It might be presented as a
function name and source location. The second two forms distinguish the kind of
Expand Down
17 changes: 17 additions & 0 deletions llvm/include/llvm/DebugInfo/Symbolize/MarkupFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ class MarkupFilter {
SmallVector<const MMap *> MMaps = {};
};

// The semantics of a possible program counter value.
enum class PCType {
// The address is a return address and must be adjusted to point to the call
// itself.
ReturnAddress,
// The address is the precise location in the code and needs no adjustment.
PreciseCode,
};

bool tryContextualElement(const MarkupNode &Node,
const SmallVector<MarkupNode> &DeferredNodes);
bool tryMMap(const MarkupNode &Element,
Expand All @@ -87,6 +96,7 @@ class MarkupFilter {

bool tryPresentation(const MarkupNode &Node);
bool trySymbol(const MarkupNode &Node);
bool tryPC(const MarkupNode &Node);
bool tryData(const MarkupNode &Node);

bool trySGR(const MarkupNode &Node);
Expand All @@ -96,6 +106,9 @@ class MarkupFilter {
void restoreColor();
void resetColor();

void printRawElement(const MarkupNode &Element);
void printValue(Twine Value);

Optional<Module> parseModule(const MarkupNode &Element) const;
Optional<MMap> parseMMap(const MarkupNode &Element) const;

Expand All @@ -104,17 +117,21 @@ class MarkupFilter {
Optional<uint64_t> parseSize(StringRef Str) const;
Optional<SmallVector<uint8_t>> parseBuildID(StringRef Str) const;
Optional<std::string> parseMode(StringRef Str) const;
Optional<PCType> parsePCType(StringRef Str) const;

bool checkTag(const MarkupNode &Node) const;
bool checkNumFields(const MarkupNode &Element, size_t Size) const;
bool checkNumFieldsAtLeast(const MarkupNode &Element, size_t Size) const;
bool checkNumFieldsAtMost(const MarkupNode &Element, size_t Size) const;

void reportTypeError(StringRef Str, StringRef TypeName) const;
void reportLocation(StringRef::iterator Loc) const;

const MMap *getOverlappingMMap(const MMap &Map) const;
const MMap *getContainingMMap(uint64_t Addr) const;

uint64_t adjustAddr(uint64_t Addr, PCType Type) const;

StringRef lineEnding() const;

raw_ostream &OS;
Expand Down
145 changes: 120 additions & 25 deletions llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/Symbolize/Markup.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Debuginfod/Debuginfod.h"
Expand Down Expand Up @@ -163,18 +164,17 @@ bool MarkupFilter::tryModule(const MarkupNode &Node,
filterNode(Node);
beginModuleInfoLine(&Module);
OS << "; BuildID=";
highlightValue();
OS << toHex(Module.BuildID, /*LowerCase=*/true);
highlight();
printValue(toHex(Module.BuildID, /*LowerCase=*/true));
return true;
}

void MarkupFilter::beginModuleInfoLine(const Module *M) {
highlight();
OS << "[[[ELF module";
highlightValue();
OS << formatv(" #{0:x} \"{1}\"", M->ID, M->Name);
highlight();
printValue(formatv(" #{0:x} ", M->ID));
OS << '"';
printValue(M->Name);
OS << '"';
MIL = ModuleInfoLine{M};
}

Expand All @@ -186,13 +186,12 @@ void MarkupFilter::endAnyModuleInfoLine() {
});
for (const MMap *M : MIL->MMaps) {
OS << (M == MIL->MMaps.front() ? ' ' : ',');
highlightValue();
OS << formatv("[{0:x}-{1:x}]", M->Addr, M->Addr + M->Size - 1);
highlight();
OS << '(';
highlightValue();
OS << M->Mode;
highlight();
OS << '[';
printValue(formatv("{0:x}", M->Addr));
OS << '-';
printValue(formatv("{0:x}", M->Addr + M->Size - 1));
OS << "](";
printValue(M->Mode);
OS << ')';
}
OS << "]]]" << lineEnding();
Expand All @@ -215,6 +214,8 @@ void MarkupFilter::filterNode(const MarkupNode &Node) {
bool MarkupFilter::tryPresentation(const MarkupNode &Node) {
if (trySymbol(Node))
return true;
if (tryPC(Node))
return true;
return tryData(Node);
}

Expand All @@ -230,6 +231,61 @@ bool MarkupFilter::trySymbol(const MarkupNode &Node) {
return true;
}

bool MarkupFilter::tryPC(const MarkupNode &Node) {
if (Node.Tag != "pc")
return false;
if (!checkNumFieldsAtLeast(Node, 1))
return true;
if (!checkNumFieldsAtMost(Node, 2))
return true;

Optional<uint64_t> Addr = parseAddr(Node.Fields[0]);
if (!Addr)
return true;

// PC addresses that aren't part of a backtrace are assumed to be precise code
// locations.
PCType Type = PCType::PreciseCode;
if (Node.Fields.size() == 2) {
Optional<PCType> ParsedType = parsePCType(Node.Fields[1]);
if (!ParsedType)
return true;
Type = *ParsedType;
}
*Addr = adjustAddr(*Addr, Type);

const MMap *MMap = getContainingMMap(*Addr);
if (!MMap) {
WithColor::error() << "no mmap covers address\n";
reportLocation(Node.Fields[0].begin());
printRawElement(Node);
return true;
}

Expected<DILineInfo> LI = Symbolizer.symbolizeCode(
MMap->Mod->BuildID, {MMap->getModuleRelativeAddr(*Addr)});
if (!LI) {
WithColor::defaultErrorHandler(LI.takeError());
printRawElement(Node);
return true;
}
if (LI->FileName == DILineInfo::BadString &&
LI->FunctionName == DILineInfo::BadString && LI->Line == 0) {
printRawElement(Node);
return true;
}

highlight();
printValue(LI->FunctionName);
OS << '[';
printValue(LI->FileName);
OS << ':';
printValue(Twine(LI->Line));
OS << ']';
restoreColor();
return true;
}

bool MarkupFilter::tryData(const MarkupNode &Node) {
if (Node.Tag != "data")
return false;
Expand All @@ -239,29 +295,19 @@ bool MarkupFilter::tryData(const MarkupNode &Node) {
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();
printRawElement(Node);
return true;
}

Expected<DIGlobal> Symbol = Symbolizer.symbolizeData(
MMap->Mod->BuildID, {MMap->getModuleRelativeAddr(*Addr)});
if (!Symbol) {
WithColor::defaultErrorHandler(Symbol.takeError());
PrintRaw();
printRawElement(Node);
return true;
}

Expand Down Expand Up @@ -343,6 +389,24 @@ void MarkupFilter::resetColor() {
OS.resetColor();
}

void MarkupFilter::printRawElement(const MarkupNode &Element) {
highlight();
OS << "[[[";
printValue(Element.Tag);
for (StringRef Field : Element.Fields) {
OS << ':';
printValue(Field);
}
OS << "]]]";
restoreColor();
}

void MarkupFilter::printValue(Twine Value) {
highlightValue();
OS << Value;
highlight();
}

// This macro helps reduce the amount of indirection done through Optional
// below, since the usual case upon returning a None Optional is to return None.
#define ASSIGN_OR_RETURN_NONE(TYPE, NAME, EXPR) \
Expand Down Expand Up @@ -476,6 +540,17 @@ Optional<std::string> MarkupFilter::parseMode(StringRef Str) const {
return Str.lower();
}

Optional<MarkupFilter::PCType> MarkupFilter::parsePCType(StringRef Str) const {
Optional<MarkupFilter::PCType> Type =
StringSwitch<Optional<MarkupFilter::PCType>>(Str)
.Case("ra", MarkupFilter::PCType::ReturnAddress)
.Case("pc", MarkupFilter::PCType::PreciseCode)
.Default(None);
if (!Type)
reportTypeError(Str, "PC type");
return Type;
}

bool MarkupFilter::checkTag(const MarkupNode &Node) const {
if (any_of(Node.Tag, [](char C) { return C < 'a' || C > 'z'; })) {
WithColor::error(errs()) << "tags must be all lowercase characters\n";
Expand Down Expand Up @@ -508,6 +583,18 @@ bool MarkupFilter::checkNumFieldsAtLeast(const MarkupNode &Element,
return true;
}

bool MarkupFilter::checkNumFieldsAtMost(const MarkupNode &Element,
size_t Size) const {
if (Element.Fields.size() > Size) {
WithColor::error(errs())
<< "expected at most " << Size << " field(s); found "
<< Element.Fields.size() << "\n";
reportLocation(Element.Tag.end());
return false;
}
return true;
}

void MarkupFilter::reportTypeError(StringRef Str, StringRef TypeName) const {
WithColor::error(errs()) << "expected " << TypeName << "; found '" << Str
<< "'\n";
Expand Down Expand Up @@ -556,6 +643,14 @@ const MarkupFilter::MMap *MarkupFilter::getContainingMMap(uint64_t Addr) const {
return I->second.contains(Addr) ? &I->second : nullptr;
}

uint64_t MarkupFilter::adjustAddr(uint64_t Addr, PCType Type) const {
// Decrementing return addresses by one moves them into the call instruction.
// The address doesn't have to be the start of the call instruction, just some
// byte on the inside. Subtracting one avoids needing detailed instruction
// length information here.
return Type == MarkupFilter::PCType::ReturnAddress ? Addr - 1 : Addr;
}

StringRef MarkupFilter::lineEnding() const {
return Line.endswith("\r\n") ? "\r\n" : "\n";
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/DebugInfo/symbolize-filter-markup-data.test
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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]]
CHECK: [[BEGIN]]data:0x5[[END]]

ERR: error: expected 1 field(s); found 0
ERR: error: no mmap covers address
Expand Down
Loading

0 comments on commit 4cc81e3

Please sign in to comment.