https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/143591
>From b2ecf5ed0da6fd3e03192ae921680b7576c12365 Mon Sep 17 00:00:00 2001 From: Stephen Tozer <stephen.to...@sony.com> Date: Tue, 10 Jun 2025 19:58:09 +0100 Subject: [PATCH] [DLCov] Origin-Tracking: SymbolizeAddresses --- llvm/include/llvm/Support/Signals.h | 40 +++++++++ llvm/lib/Support/Signals.cpp | 116 +++++++++++++++++++++++++++ llvm/lib/Support/Unix/Signals.inc | 15 ++++ llvm/lib/Support/Windows/Signals.inc | 5 ++ 4 files changed, 176 insertions(+) diff --git a/llvm/include/llvm/Support/Signals.h b/llvm/include/llvm/Support/Signals.h index 6ce26acdd458e..a6f99d8bbdc95 100644 --- a/llvm/include/llvm/Support/Signals.h +++ b/llvm/include/llvm/Support/Signals.h @@ -14,7 +14,9 @@ #ifndef LLVM_SUPPORT_SIGNALS_H #define LLVM_SUPPORT_SIGNALS_H +#include "llvm/Config/llvm-config.h" #include "llvm/Support/Compiler.h" +#include <array> #include <cstdint> #include <string> @@ -22,6 +24,22 @@ namespace llvm { class StringRef; class raw_ostream; +#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING +// Typedefs that are convenient but only used by the stack-trace-collection code +// added if DebugLoc origin-tracking is enabled. +template <typename T, typename Enable> struct DenseMapInfo; +template <typename ValueT, typename ValueInfoT> class DenseSet; +namespace detail { +template <typename KeyT, typename ValueT> struct DenseMapPair; +} +template <typename KeyT, typename ValueT, typename KeyInfoT, typename BucketT> +class DenseMap; +using AddressSet = DenseSet<void *, DenseMapInfo<void *, void>>; +using SymbolizedAddressMap = + DenseMap<void *, std::string, DenseMapInfo<void *, void>, + detail::DenseMapPair<void *, std::string>>; +#endif + namespace sys { /// This function runs all the registered interrupt handlers, including the @@ -57,6 +75,28 @@ LLVM_ABI void DisableSystemDialogsOnCrash(); /// specified, the entire frame is printed. LLVM_ABI void PrintStackTrace(raw_ostream &OS, int Depth = 0); +#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING +#ifdef NDEBUG +#error DebugLoc origin-tracking should not be enabled in Release builds. +#endif +/// Populates the given array with a stack trace of the current program, up to +/// MaxDepth frames. Returns the number of frames returned, which will be +/// inserted into \p StackTrace from index 0. All entries after the returned +/// depth will be unmodified. NB: This is only intended to be used for +/// introspection of LLVM by Debugify, will not be enabled in release builds, +/// and should not be relied on for other purposes. +template <unsigned long MaxDepth> +int getStackTrace(std::array<void *, MaxDepth> &StackTrace); + +/// Takes a set of \p Addresses, symbolizes them and stores the result in the +/// provided \p SymbolizedAddresses map. +/// NB: This is only intended to be used for introspection of LLVM by +/// Debugify, will not be enabled in release builds, and should not be relied +/// on for other purposes. +void symbolizeAddresses(AddressSet &Addresses, + SymbolizedAddressMap &SymbolizedAddresses); +#endif + // Run all registered signal handlers. LLVM_ABI void RunSignalHandlers(); diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index 9f9030e79d104..50b0d6e78ddd1 100644 --- a/llvm/lib/Support/Signals.cpp +++ b/llvm/lib/Support/Signals.cpp @@ -253,6 +253,122 @@ static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, return true; } +#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING +void sys::symbolizeAddresses(AddressSet &Addresses, + SymbolizedAddressMap &SymbolizedAddresses) { + assert(!DisableSymbolicationFlag && !getenv(DisableSymbolizationEnv) && + "Debugify origin stacktraces require symbolization to be enabled."); + + // Convert Set of Addresses to ordered list. + SmallVector<void *, 0> AddressList(Addresses.begin(), Addresses.end()); + if (AddressList.empty()) + return; + int NumAddresses = AddressList.size(); + llvm::sort(AddressList); + + // Use llvm-symbolizer tool to symbolize the stack traces. First look for it + // alongside our binary, then in $PATH. + ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code(); + if (const char *Path = getenv(LLVMSymbolizerPathEnv)) { + LLVMSymbolizerPathOrErr = sys::findProgramByName(Path); + } + if (!LLVMSymbolizerPathOrErr) + LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer"); + assert(!!LLVMSymbolizerPathOrErr && + "Debugify origin stacktraces require llvm-symbolizer."); + const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr; + + // Try to guess the main executable name, since we don't have argv0 available + // here. + std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr); + + BumpPtrAllocator Allocator; + StringSaver StrPool(Allocator); + std::vector<const char *> Modules(NumAddresses, nullptr); + std::vector<intptr_t> Offsets(NumAddresses, 0); + if (!findModulesAndOffsets(AddressList.data(), NumAddresses, Modules.data(), + Offsets.data(), MainExecutableName.c_str(), + StrPool)) + return; + int InputFD; + SmallString<32> InputFile, OutputFile; + sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile); + sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile); + FileRemover InputRemover(InputFile.c_str()); + FileRemover OutputRemover(OutputFile.c_str()); + + { + raw_fd_ostream Input(InputFD, true); + for (int i = 0; i < NumAddresses; i++) { + if (Modules[i]) + Input << Modules[i] << " " << (void *)Offsets[i] << "\n"; + } + } + + std::optional<StringRef> Redirects[] = {InputFile.str(), OutputFile.str(), + StringRef("")}; + StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining", +#ifdef _WIN32 + // Pass --relative-address on Windows so that we don't + // have to add ImageBase from PE file. + // FIXME: Make this the default for llvm-symbolizer. + "--relative-address", +#endif + "--demangle"}; + int RunResult = + sys::ExecuteAndWait(LLVMSymbolizerPath, Args, std::nullopt, Redirects); + if (RunResult != 0) + return; + + // This report format is based on the sanitizer stack trace printer. See + // sanitizer_stacktrace_printer.cc in compiler-rt. + auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str()); + if (!OutputBuf) + return; + StringRef Output = OutputBuf.get()->getBuffer(); + SmallVector<StringRef, 32> Lines; + Output.split(Lines, "\n"); + auto CurLine = Lines.begin(); + for (int i = 0; i < NumAddresses; i++) { + assert(!SymbolizedAddresses.contains(AddressList[i])); + std::string &SymbolizedAddr = SymbolizedAddresses[AddressList[i]]; + raw_string_ostream OS(SymbolizedAddr); + if (!Modules[i]) { + OS << format_ptr(AddressList[i]) << '\n'; + continue; + } + // Read pairs of lines (function name and file/line info) until we + // encounter empty line. + for (bool IsFirst = true;; IsFirst = false) { + if (CurLine == Lines.end()) + return; + StringRef FunctionName = *CurLine++; + if (FunctionName.empty()) + break; + // Add indentation for lines after the first; we use 3 spaces, because + // currently that aligns with the expected indentation that will be added + // to the first line by Debugify. + if (!IsFirst) + OS << " "; + OS << format_ptr(AddressList[i]) << ' '; + if (!FunctionName.starts_with("??")) + OS << FunctionName << ' '; + if (CurLine == Lines.end()) { + OS << '\n'; + return; + } + StringRef FileLineInfo = *CurLine++; + if (!FileLineInfo.starts_with("??")) + OS << FileLineInfo; + else + OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")"; + OS << '\n'; + } + } + return; +} +#endif + static bool printMarkupContext(raw_ostream &OS, const char *MainExecutableName); LLVM_ATTRIBUTE_USED diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc index 6668a2953b3b2..70b2cf7c756a9 100644 --- a/llvm/lib/Support/Unix/Signals.inc +++ b/llvm/lib/Support/Unix/Signals.inc @@ -507,6 +507,21 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { return 0; } +#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING +#if !defined(HAVE_BACKTRACE) +#error DebugLoc origin-tracking currently requires `backtrace()`. +#endif +namespace llvm { +namespace sys { +template <unsigned long MaxDepth> +int getStackTrace(std::array<void *, MaxDepth> &StackTrace) { + return backtrace(StackTrace.data(), MaxDepth); +} +template int getStackTrace<16ul>(std::array<void *, 16ul> &); +} // namespace sys +} // namespace llvm +#endif + /// If this is an ELF platform, we can find all loaded modules and their virtual /// addresses with dl_iterate_phdr. static bool findModulesAndOffsets(void **StackTrace, int Depth, diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc index f11ad09f37139..0fe6967b291da 100644 --- a/llvm/lib/Support/Windows/Signals.inc +++ b/llvm/lib/Support/Windows/Signals.inc @@ -9,6 +9,7 @@ // This file provides the Win32 specific implementation of the Signals class. // //===----------------------------------------------------------------------===// +#include "llvm/Config/llvm-config.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ExitCodes.h" #include "llvm/Support/FileSystem.h" @@ -542,6 +543,10 @@ void sys::PrintStackTraceOnErrorSignal(StringRef Argv0, extern "C" VOID WINAPI RtlCaptureContext(PCONTEXT ContextRecord); #endif +#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING +#error DebugLoc origin-tracking currently unimplemented for Windows. +#endif + static void LocalPrintStackTrace(raw_ostream &OS, PCONTEXT C) { STACKFRAME64 StackFrame{}; CONTEXT Context{}; _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits