[llvm] 034c462 - [Debugify] Add finer control over origin stacktrace collection (#206128)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 02:41:36 PDT 2026
Author: Stephen Tozer
Date: 2026-06-29T10:41:31+01:00
New Revision: 034c462848ebd6a3e5eeb9d6a519656b6ff8445e
URL: https://github.com/llvm/llvm-project/commit/034c462848ebd6a3e5eeb9d6a519656b6ff8445e
DIFF: https://github.com/llvm/llvm-project/commit/034c462848ebd6a3e5eeb9d6a519656b6ff8445e.diff
LOG: [Debugify] Add finer control over origin stacktrace collection (#206128)
As part of an attempt to streamline DebugLoc coverage/origin tracking
using debugify, this patch adds a command-line flag that is used to
explicitly enable origin-tracking, instead of being always-enabled if
built with the feature. This flag also allows us to specify a list of
passes to enable origin-tracking for, instead of having it enabled
across all passes.
Disabling origin-tracking still incurs the cost of storing a SmallVector
with small size=0 (i.e. 3*sizeof(void*)) in each DebugLoc, but avoids
the tremendous cost of collecting stacktraces at every empty DebugLoc
construction - which occurs very frequently even in builds without
missing coverage.
The motivation for adding this flag is that it allows a Clang built with
origin-tracking enabled to still be used in coverage-tracking mode with
a relatively low cost; since origin-tracking only adds details to the
errors that coverage-tracking detects, this allows a workflow where we
use Clang with coverage-tracking enabled for all compile commands in a
project, and for any compiles that detect missing coverage, the command
is re-run with origin-tracking enabled for all the passes where missing
locations were detected. This ensures we only pay for the very costly
origin-tracking step when it is actually used, making origin-tracking
more efficient in CI and practical in local builds.
Added:
Modified:
llvm/docs/HowToUpdateDebugInfo.rst
llvm/include/llvm/IR/DebugLoc.h
llvm/lib/IR/DebugLoc.cpp
llvm/lib/Transforms/Utils/Debugify.cpp
Removed:
################################################################################
diff --git a/llvm/docs/HowToUpdateDebugInfo.rst b/llvm/docs/HowToUpdateDebugInfo.rst
index 507d1de84c5d5..d062010ecdb36 100644
--- a/llvm/docs/HowToUpdateDebugInfo.rst
+++ b/llvm/docs/HowToUpdateDebugInfo.rst
@@ -445,12 +445,19 @@ source location.
For triaging source location bugs detected with ``debugify``, you may find it
helpful to instead set the CMake flag to enable "origin tracking",
-``-DLLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING=COVERAGE_AND_ORIGIN``. This flag adds
-more detail to ``debugify``'s output, by including one or more stacktraces with
-every missing source location, capturing the point at which the empty source
-location was created, and every point at which it was copied to an instruction,
-making it trivial in most cases to find the origin of the underlying bug. If
-using origin tracking, it is recommended to also build LLVM with debug info
+``-DLLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING=COVERAGE_AND_ORIGIN``. This flag
+allows more detail to be added to ``debugify``'s output, by including one or
+more stacktraces with every missing source location, capturing the point at
+which the empty source location was created, and every point at which it was
+copied to an instruction, making it trivial in most cases to find the origin of
+the underlying bug. When origin tracking is enabled, the
+``--enable-origin-stacktraces`` flag must be passed to actually trigger the
+collecting of stacktraces; this flag can be passed as-is to collect stacktraces
+all the time, or it can be passed with a comma-separated list of pass names (in
+their internal PascalCase form) to enable collecting stacktraces during only
+those passes.
+
+If using origin tracking, it is recommended to also build LLVM with debug info
enabled, so that the stacktrace can be accurately symbolized.
.. note::
diff --git a/llvm/include/llvm/IR/DebugLoc.h b/llvm/include/llvm/IR/DebugLoc.h
index 7fd7564357769..34e6aadd83cae 100644
--- a/llvm/include/llvm/IR/DebugLoc.h
+++ b/llvm/include/llvm/IR/DebugLoc.h
@@ -28,6 +28,8 @@ class Function;
#if LLVM_ENABLE_DEBUGLOC_TRACKING_COVERAGE
#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
+extern bool DebugLocOriginCollectionEnabled;
+
struct DbgLocOrigin {
static constexpr unsigned long MaxDepth = 16;
using StackTracesTy =
diff --git a/llvm/lib/IR/DebugLoc.cpp b/llvm/lib/IR/DebugLoc.cpp
index c5eda4e81b1ce..16a82c2a05208 100644
--- a/llvm/lib/IR/DebugLoc.cpp
+++ b/llvm/lib/IR/DebugLoc.cpp
@@ -14,9 +14,12 @@ using namespace llvm;
#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
#include "llvm/Support/Signals.h"
+namespace llvm {
+bool DebugLocOriginCollectionEnabled = false;
+} // namespace llvm
DbgLocOrigin::DbgLocOrigin(bool ShouldCollectTrace) {
- if (!ShouldCollectTrace)
+ if (!ShouldCollectTrace || !DebugLocOriginCollectionEnabled)
return;
auto &[Depth, StackTrace] = StackTraces.emplace_back();
Depth = sys::getStackTrace(StackTrace);
diff --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp
index a9f5716b5c396..e501b6f2a4e34 100644
--- a/llvm/lib/Transforms/Utils/Debugify.cpp
+++ b/llvm/lib/Transforms/Utils/Debugify.cpp
@@ -34,6 +34,8 @@
// We need the Signals header to operate on stacktraces if we're using DebugLoc
// origin-tracking.
#include "llvm/Support/Signals.h"
+#else
+#include "llvm/Support/WithColor.h"
#endif
#define DEBUG_TYPE "debugify"
@@ -67,6 +69,33 @@ cl::opt<Level> DebugifyLevel(
raw_ostream &dbg() { return Quiet ? nulls() : errs(); }
#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
+cl::list<std::string> EnableOriginStacktraces(
+ "enable-origin-stacktraces",
+ cl::desc("Collect DebugLoc origin stacktraces; a comma-separated list of "
+ "passes may be given, in which case stacktraces will be collected "
+ "in those passes only"),
+ cl::value_desc("Pass1,Pass2,Pass3,..."), cl::CommaSeparated,
+ cl::ValueOptional);
+
+// For a given pass, sets whether the collection of DebugLoc origin stacktraces
+// is enabled or not.
+static void setDebugLocOriginCollectionForPass(StringRef PassName) {
+ if (!EnableOriginStacktraces.getNumOccurrences()) {
+ llvm::DebugLocOriginCollectionEnabled = false;
+ return;
+ }
+ if (EnableOriginStacktraces.size() == 1 &&
+ EnableOriginStacktraces[0].empty()) {
+ llvm::DebugLocOriginCollectionEnabled = true;
+ return;
+ }
+ llvm::DebugLocOriginCollectionEnabled =
+ llvm::is_contained(EnableOriginStacktraces, PassName);
+}
+static void unsetDebugLocOriginCollection() {
+ llvm::DebugLocOriginCollectionEnabled = false;
+}
+
// These maps refer to addresses in the current LLVM process, so we can reuse
// them everywhere - therefore, we store them at file scope.
static SymbolizedAddressMap SymbolizedAddrs;
@@ -111,6 +140,22 @@ void collectStackAddresses(Instruction &I) {
}
}
#else
+// These functions are only used in origin-tracking builds; they are no-ops in
+// normal builds.
+static void setDebugLocOriginCollectionForPass(StringRef PassName) {}
+static void unsetDebugLocOriginCollection() {}
+
+cl::list<std::string> EnableOriginStacktraces(
+ "enable-origin-stacktraces",
+ cl::desc("Collect DebugLoc origin stacktraces; requires "
+ "LLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING=COVERAGE_AND_ORIGIN"),
+ cl::CommaSeparated, cl::ValueOptional, cl::Hidden,
+ cl::cb<void, std::string>([](std::string Pass) {
+ WithColor::warning() << "--enable-origin-stacktraces has no effect "
+ "without LLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING="
+ "COVERAGE_AND_ORIGIN\n";
+ }));
+
void collectStackAddresses(Instruction &I) {}
#endif // LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
@@ -280,6 +325,7 @@ bool llvm::applyDebugifyMetadata(
static bool applyDebugify(Function &F, enum DebugifyMode Mode,
DebugInfoPerPass *DebugInfoBeforePass,
StringRef NameOfWrappedPass = "") {
+ setDebugLocOriginCollectionForPass(NameOfWrappedPass);
Module &M = *F.getParent();
auto FuncIt = F.getIterator();
if (Mode == DebugifyMode::SyntheticDebugInfo)
@@ -294,6 +340,7 @@ static bool applyDebugify(Function &F, enum DebugifyMode Mode,
static bool applyDebugify(Module &M, enum DebugifyMode Mode,
DebugInfoPerPass *DebugInfoBeforePass,
StringRef NameOfWrappedPass = "") {
+ setDebugLocOriginCollectionForPass(NameOfWrappedPass);
if (Mode == DebugifyMode::SyntheticDebugInfo)
return applyDebugifyMetadata(M, M.functions(),
"ModuleDebugify: ", /*ApplyToMF*/ nullptr);
@@ -507,16 +554,18 @@ static bool checkInstructions(const DebugInstMap &DILocsBefore,
auto InstName = Instruction::getOpcodeName(Instr->getOpcode());
auto CreateJSONBugEntry = [&](const char *Action) {
- Bugs.push_back(llvm::json::Object({
+ auto BugEntry = llvm::json::Object({
{"metadata", "DILocation"},
{"fn-name", FnName.str()},
{"bb-name", BBName.str()},
{"instr", InstName},
{"action", Action},
+ });
#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
- {"origin", symbolizeStackTrace(Instr)},
+ if (!Instr->getDebugLoc().getOriginStackTraces().empty())
+ BugEntry.insert({"origin", symbolizeStackTrace(Instr)});
#endif
- }));
+ Bugs.push_back(std::move(BugEntry));
};
auto InstrIt = DILocsBefore.find(Instr);
@@ -614,6 +663,7 @@ bool llvm::checkDebugInfoMetadata(Module &M,
StringRef Banner, StringRef NameOfWrappedPass,
StringRef OrigDIVerifyBugsReportFilePath) {
LLVM_DEBUG(dbgs() << Banner << ": (after) " << NameOfWrappedPass << '\n');
+ unsetDebugLocOriginCollection();
if (!M.getNamedMetadata("llvm.dbg.cu")) {
dbg() << Banner << ": Skipping module without debug info\n";
@@ -788,6 +838,7 @@ bool checkDebugifyMetadata(Module &M,
bool Strip, DebugifyStatsMap *StatsMap) {
// Skip modules without debugify metadata.
NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");
+ unsetDebugLocOriginCollection();
if (!NMD) {
dbg() << Banner << ": Skipping module without debugify metadata\n";
return false;
More information about the llvm-commits
mailing list