Lang Hames dcf8ae8028
Reapply "[ORC] Add utilities for limited symbolication of JIT backtra… (#175469)
…ces"

This reapplies 906b48616c03948a4df62a5a144f7108f3c455e8, which was
reverted in c11df52f9b847170b766fb71defd2a9222d95a8d due to bot
failures.

The testcase has been dropped from this recommit as it failed on several
bots (possbly due to differing backtrace formats or failure modes). I'll
re-introduce the testcase in a follow-up commit so that it cane be
iterated on (and re-reverted if necessary) without affecting the options
introduced by this commit. (Since these options are best-effort
debugging tools it's ok if they live in-tree without a test for now).
2026-01-12 12:23:07 +11:00

151 lines
4.7 KiB
C++

//===------- BacktraceTools.cpp - Backtrace symbolication tools ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/BacktraceTools.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MemoryBuffer.h"
namespace llvm::orc {
Expected<std::shared_ptr<SymbolTableDumpPlugin>>
SymbolTableDumpPlugin::Create(StringRef Path) {
std::error_code EC;
auto P = std::make_shared<SymbolTableDumpPlugin>(Path, EC);
if (EC)
return createFileError(Path, EC);
return P;
}
SymbolTableDumpPlugin::SymbolTableDumpPlugin(StringRef Path,
std::error_code &EC)
: OutputStream(Path, EC) {}
void SymbolTableDumpPlugin::modifyPassConfig(
MaterializationResponsibility &MR, jitlink::LinkGraph &G,
jitlink::PassConfiguration &Config) {
Config.PostAllocationPasses.push_back([this](jitlink::LinkGraph &G) -> Error {
std::scoped_lock<std::mutex> Lock(DumpMutex);
OutputStream << "\"" << G.getName() << "\"\n";
for (auto &Sec : G.sections()) {
// NoAlloc symbols don't exist in the executing process, so can't
// contribute to symbolication. (Note: We leave Finalize-liftime symbols
// in for now in case of crashes during finalization, but we should
// probably make this optional).
if (Sec.getMemLifetime() == MemLifetime::NoAlloc)
continue;
// Write out named symbols. Anonymous symbols are skipped, since they
// don't add any information for symbolication purposes.
for (auto *Sym : Sec.symbols()) {
if (Sym->hasName())
OutputStream << formatv("{0:x}", Sym->getAddress().getValue()) << " "
<< Sym->getName() << "\n";
}
}
OutputStream.flush();
return Error::success();
});
}
Expected<DumpedSymbolTable> DumpedSymbolTable::Create(StringRef Path) {
auto MB = MemoryBuffer::getFile(Path);
if (!MB)
return createFileError(Path, MB.getError());
return DumpedSymbolTable(std::move(*MB));
}
DumpedSymbolTable::DumpedSymbolTable(std::unique_ptr<MemoryBuffer> SymtabBuffer)
: SymtabBuffer(std::move(SymtabBuffer)) {
parseBuffer();
}
void DumpedSymbolTable::parseBuffer() {
// Read the symbol table file
SmallVector<StringRef, 0> Rows;
SymtabBuffer->getBuffer().split(Rows, '\n');
StringRef CurGraph = "<unidentified>";
for (auto Row : Rows) {
Row = Row.trim();
if (Row.empty())
continue;
// Check for graph name line (enclosed in quotes)
if (Row.starts_with("\"") && Row.ends_with("\"")) {
CurGraph = Row.trim('"');
continue;
}
// Parse "address symbol_name" lines, ignoring malformed lines.
size_t SpacePos = Row.find(' ');
if (SpacePos == StringRef::npos)
continue;
StringRef AddrStr = Row.substr(0, SpacePos);
StringRef SymName = Row.substr(SpacePos + 1);
uint64_t Addr;
if (AddrStr.starts_with("0x"))
AddrStr = AddrStr.drop_front(2);
if (AddrStr.getAsInteger(16, Addr))
continue; // Skip malformed lines
SymbolInfos[Addr] = {SymName, CurGraph};
}
}
std::string DumpedSymbolTable::symbolicate(StringRef Backtrace) {
// Symbolicate the backtrace by replacing rows with empty symbol names
SmallVector<StringRef, 0> BacktraceRows;
Backtrace.split(BacktraceRows, '\n');
std::string Result;
raw_string_ostream Out(Result);
for (auto Row : BacktraceRows) {
// Look for a row ending with a hex number. If there's only one column, or
// if the last column is not a hex number, then just reproduce the input
// row.
auto [RowStart, AddrCol] = Row.rtrim().rsplit(' ');
auto AddrStr = AddrCol.starts_with("0x") ? AddrCol.drop_front(2) : AddrCol;
uint64_t Addr;
if (AddrStr.empty() || AddrStr.getAsInteger(16, Addr)) {
Out << Row << "\n";
continue;
}
// Search for the address
auto I = SymbolInfos.upper_bound(Addr);
// If no JIT symbol entry within 2Gb then skip.
if (I == SymbolInfos.begin() || (Addr - std::prev(I)->first >= 1U << 31)) {
Out << Row << "\n";
continue;
}
// Found a symbol. Output modified line.
auto &[SymAddr, SymInfo] = *std::prev(I);
Out << RowStart << " " << AddrCol << " " << SymInfo.SymName;
if (auto Delta = Addr - SymAddr)
Out << " + " << formatv("{0}", Delta);
Out << " (" << SymInfo.GraphName << ")\n";
}
return Result;
}
} // namespace llvm::orc