| //===------- 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 |