…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).
151 lines
4.7 KiB
C++
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
|