[llvm-branch-commits] [llvm] [DLCov] Origin-Tracking: Enable collecting and symbolizing stack traces (PR #143591)
Stephen Tozer via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jun 11 04:32:20 PDT 2025
https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/143591
>From 12f5a10c1dc2ae6943947c85a5bd05a295ae1c7c Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at 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{};
More information about the llvm-branch-commits
mailing list