1//===------- BacktraceTools.cpp - Backtrace symbolication tools ----------===//
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#include "llvm/ExecutionEngine/Orc/BacktraceTools.h"
10#include "llvm/ExecutionEngine/JITLink/JITLink.h"
11#include "llvm/Support/FormatVariadic.h"
12#include "llvm/Support/MemoryBuffer.h"
13
14namespace llvm::orc {
15
16Expected<std::shared_ptr<SymbolTableDumpPlugin>>
17SymbolTableDumpPlugin::Create(StringRef Path) {
18 std::error_code EC;
19 auto P = std::make_shared<SymbolTableDumpPlugin>(args&: Path, args&: EC);
20 if (EC)
21 return createFileError(F: Path, EC);
22 return P;
23}
24
25SymbolTableDumpPlugin::SymbolTableDumpPlugin(StringRef Path,
26 std::error_code &EC)
27 : OutputStream(Path, EC) {}
28
29void SymbolTableDumpPlugin::modifyPassConfig(
30 MaterializationResponsibility &MR, jitlink::LinkGraph &G,
31 jitlink::PassConfiguration &Config) {
32
33 Config.PostAllocationPasses.push_back(x: [this](jitlink::LinkGraph &G) -> Error {
34 std::scoped_lock<std::mutex> Lock(DumpMutex);
35
36 OutputStream << "\"" << G.getName() << "\"\n";
37 for (auto &Sec : G.sections()) {
38 // NoAlloc symbols don't exist in the executing process, so can't
39 // contribute to symbolication. (Note: We leave Finalize-liftime symbols
40 // in for now in case of crashes during finalization, but we should
41 // probably make this optional).
42 if (Sec.getMemLifetime() == MemLifetime::NoAlloc)
43 continue;
44
45 // Write out named symbols. Anonymous symbols are skipped, since they
46 // don't add any information for symbolication purposes.
47 for (auto *Sym : Sec.symbols()) {
48 if (Sym->hasName())
49 OutputStream << formatv(Fmt: "{0:x}", Vals: Sym->getAddress().getValue()) << " "
50 << Sym->getName() << "\n";
51 }
52 }
53
54 OutputStream.flush();
55 return Error::success();
56 });
57}
58
59Expected<DumpedSymbolTable> DumpedSymbolTable::Create(StringRef Path) {
60 auto MB = MemoryBuffer::getFile(Filename: Path);
61 if (!MB)
62 return createFileError(F: Path, EC: MB.getError());
63
64 return DumpedSymbolTable(std::move(*MB));
65}
66
67DumpedSymbolTable::DumpedSymbolTable(std::unique_ptr<MemoryBuffer> SymtabBuffer)
68 : SymtabBuffer(std::move(SymtabBuffer)) {
69 parseBuffer();
70}
71
72void DumpedSymbolTable::parseBuffer() {
73 // Read the symbol table file
74 SmallVector<StringRef, 0> Rows;
75 SymtabBuffer->getBuffer().split(A&: Rows, Separator: '\n');
76
77 StringRef CurGraph = "<unidentified>";
78 for (auto Row : Rows) {
79 Row = Row.trim();
80 if (Row.empty())
81 continue;
82
83 // Check for graph name line (enclosed in quotes)
84 if (Row.starts_with(Prefix: "\"") && Row.ends_with(Suffix: "\"")) {
85 CurGraph = Row.trim(Char: '"');
86 continue;
87 }
88
89 // Parse "address symbol_name" lines, ignoring malformed lines.
90 size_t SpacePos = Row.find(C: ' ');
91 if (SpacePos == StringRef::npos)
92 continue;
93
94 StringRef AddrStr = Row.substr(Start: 0, N: SpacePos);
95 StringRef SymName = Row.substr(Start: SpacePos + 1);
96
97 uint64_t Addr;
98 if (AddrStr.starts_with(Prefix: "0x"))
99 AddrStr = AddrStr.drop_front(N: 2);
100 if (AddrStr.getAsInteger(Radix: 16, Result&: Addr))
101 continue; // Skip malformed lines
102
103 SymbolInfos[Addr] = {.SymName: SymName, .GraphName: CurGraph};
104 }
105}
106
107std::string DumpedSymbolTable::symbolicate(StringRef Backtrace) {
108 // Symbolicate the backtrace by replacing rows with empty symbol names
109 SmallVector<StringRef, 0> BacktraceRows;
110 Backtrace.split(A&: BacktraceRows, Separator: '\n');
111
112 std::string Result;
113 raw_string_ostream Out(Result);
114 for (auto Row : BacktraceRows) {
115 // Look for a row ending with a hex number. If there's only one column, or
116 // if the last column is not a hex number, then just reproduce the input
117 // row.
118 auto [RowStart, AddrCol] = Row.rtrim().rsplit(Separator: ' ');
119 auto AddrStr = AddrCol.starts_with(Prefix: "0x") ? AddrCol.drop_front(N: 2) : AddrCol;
120
121 uint64_t Addr;
122 if (AddrStr.empty() || AddrStr.getAsInteger(Radix: 16, Result&: Addr)) {
123 Out << Row << "\n";
124 continue;
125 }
126
127 // Search for the address
128 auto I = SymbolInfos.upper_bound(x: Addr);
129
130 // If no JIT symbol entry within 2Gb then skip.
131 if (I == SymbolInfos.begin() || (Addr - std::prev(x: I)->first >= 1U << 31)) {
132 Out << Row << "\n";
133 continue;
134 }
135
136 // Found a symbol. Output modified line.
137 auto &[SymAddr, SymInfo] = *std::prev(x: I);
138 Out << RowStart << " " << AddrCol << " " << SymInfo.SymName;
139 if (auto Delta = Addr - SymAddr)
140 Out << " + " << formatv(Fmt: "{0}", Vals&: Delta);
141 Out << " (" << SymInfo.GraphName << ")\n";
142 }
143
144 return Result;
145}
146
147} // namespace llvm::orc
148