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