[llvm] [Debugify] Add finer control over origin stacktrace collection (PR #206128)

Stephen Tozer via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 29 01:51:20 PDT 2026


https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/206128

>From 622aaac1930012de937d7c40e6a1ba0424213de3 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Fri, 26 Jun 2026 14:13:09 +0100
Subject: [PATCH 1/4] [Debugify] Add option to disable origin stacktrace
 collection

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. 2*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.
---
 llvm/docs/HowToUpdateDebugInfo.rst     | 19 ++++++++----
 llvm/include/llvm/IR/DebugLoc.h        |  2 ++
 llvm/lib/IR/DebugLoc.cpp               | 31 ++++++++++++++++++-
 llvm/lib/Transforms/Utils/Debugify.cpp | 42 ++++++++++++++++++++++++--
 4 files changed, 84 insertions(+), 10 deletions(-)

diff --git a/llvm/docs/HowToUpdateDebugInfo.rst b/llvm/docs/HowToUpdateDebugInfo.rst
index 507d1de84c5d5..8ecc58a90ee9b 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 stacktrace 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..00d4bdd522011 100644
--- a/llvm/lib/IR/DebugLoc.cpp
+++ b/llvm/lib/IR/DebugLoc.cpp
@@ -14,9 +14,20 @@ using namespace llvm;
 
 #if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
 #include "llvm/Support/Signals.h"
+namespace llvm {
+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);
+
+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);
@@ -31,6 +42,24 @@ void DbgLocOrigin::addTrace() {
   auto &[Depth, StackTrace] = StackTraces.emplace_back();
   Depth = sys::getStackTrace(StackTrace);
 }
+#else
+#include "llvm/Support/WithColor.h"
+
+namespace llvm {
+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, bool>([](bool IsEnabled) {
+      if (IsEnabled) {
+        WithColor::warning()
+            << "--enable-origin-stacktraces has no effect "
+               "without LLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING="
+               "COVERAGE_AND_ORIGIN\n";
+      }
+    }));
+} // namespace llvm
 #endif // LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp
index a9f5716b5c396..5a192103f79d5 100644
--- a/llvm/lib/Transforms/Utils/Debugify.cpp
+++ b/llvm/lib/Transforms/Utils/Debugify.cpp
@@ -40,6 +40,10 @@
 
 using namespace llvm;
 
+namespace llvm {
+extern cl::list<std::string> EnableOriginStacktraces;
+} // namespace llvm
+
 namespace {
 
 cl::opt<bool> ApplyAtomGroups("debugify-atoms", cl::init(false));
@@ -277,9 +281,36 @@ bool llvm::applyDebugifyMetadata(
   return true;
 }
 
+// For a given pass, sets whether the collection of DebugLoc origin stacktraces
+// is enabled or not.
+#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
+static void setDebugLocOriginCollectionForPass(StringRef PassName) {
+  if (!llvm::EnableOriginStacktraces.getNumOccurrences()) {
+    llvm::DebugLocOriginCollectionEnabled = false;
+    return;
+  }
+  if (llvm::EnableOriginStacktraces.size() == 1 &&
+      llvm::EnableOriginStacktraces[0].empty()) {
+    llvm::DebugLocOriginCollectionEnabled = true;
+    return;
+  }
+  llvm::DebugLocOriginCollectionEnabled =
+      llvm::is_contained(llvm::EnableOriginStacktraces, PassName);
+}
+static void unsetDebugLocOriginCollection() {
+  llvm::DebugLocOriginCollectionEnabled = false;
+}
+#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() {}
+#endif
+
 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 +325,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 +539,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 +648,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 +823,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;

>From b8b701627c6ef7794efc12e1c92660cca902a978 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Fri, 26 Jun 2026 18:11:34 +0100
Subject: [PATCH 2/4] Move cmd line flag to debugify.cpp, small fixups

---
 llvm/lib/IR/DebugLoc.cpp               | 26 ---------
 llvm/lib/Transforms/Utils/Debugify.cpp | 75 +++++++++++++++-----------
 2 files changed, 45 insertions(+), 56 deletions(-)

diff --git a/llvm/lib/IR/DebugLoc.cpp b/llvm/lib/IR/DebugLoc.cpp
index 00d4bdd522011..16a82c2a05208 100644
--- a/llvm/lib/IR/DebugLoc.cpp
+++ b/llvm/lib/IR/DebugLoc.cpp
@@ -15,14 +15,6 @@ using namespace llvm;
 #if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
 #include "llvm/Support/Signals.h"
 namespace llvm {
-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);
-
 bool DebugLocOriginCollectionEnabled = false;
 } // namespace llvm
 
@@ -42,24 +34,6 @@ void DbgLocOrigin::addTrace() {
   auto &[Depth, StackTrace] = StackTraces.emplace_back();
   Depth = sys::getStackTrace(StackTrace);
 }
-#else
-#include "llvm/Support/WithColor.h"
-
-namespace llvm {
-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, bool>([](bool IsEnabled) {
-      if (IsEnabled) {
-        WithColor::warning()
-            << "--enable-origin-stacktraces has no effect "
-               "without LLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING="
-               "COVERAGE_AND_ORIGIN\n";
-      }
-    }));
-} // namespace llvm
 #endif // LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp
index 5a192103f79d5..4d335877b068d 100644
--- a/llvm/lib/Transforms/Utils/Debugify.cpp
+++ b/llvm/lib/Transforms/Utils/Debugify.cpp
@@ -34,16 +34,14 @@
 // 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"
 
 using namespace llvm;
 
-namespace llvm {
-extern cl::list<std::string> EnableOriginStacktraces;
-} // namespace llvm
-
 namespace {
 
 cl::opt<bool> ApplyAtomGroups("debugify-atoms", cl::init(false));
@@ -71,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 (!llvm::EnableOriginStacktraces.getNumOccurrences()) {
+    llvm::DebugLocOriginCollectionEnabled = false;
+    return;
+  }
+  if (llvm::EnableOriginStacktraces.size() == 1 &&
+      llvm::EnableOriginStacktraces[0].empty()) {
+    llvm::DebugLocOriginCollectionEnabled = true;
+    return;
+  }
+  llvm::DebugLocOriginCollectionEnabled =
+      llvm::is_contained(llvm::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;
@@ -115,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
 
@@ -281,32 +322,6 @@ bool llvm::applyDebugifyMetadata(
   return true;
 }
 
-// For a given pass, sets whether the collection of DebugLoc origin stacktraces
-// is enabled or not.
-#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN
-static void setDebugLocOriginCollectionForPass(StringRef PassName) {
-  if (!llvm::EnableOriginStacktraces.getNumOccurrences()) {
-    llvm::DebugLocOriginCollectionEnabled = false;
-    return;
-  }
-  if (llvm::EnableOriginStacktraces.size() == 1 &&
-      llvm::EnableOriginStacktraces[0].empty()) {
-    llvm::DebugLocOriginCollectionEnabled = true;
-    return;
-  }
-  llvm::DebugLocOriginCollectionEnabled =
-      llvm::is_contained(llvm::EnableOriginStacktraces, PassName);
-}
-static void unsetDebugLocOriginCollection() {
-  llvm::DebugLocOriginCollectionEnabled = false;
-}
-#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() {}
-#endif
-
 static bool applyDebugify(Function &F, enum DebugifyMode Mode,
                           DebugInfoPerPass *DebugInfoBeforePass,
                           StringRef NameOfWrappedPass = "") {

>From 9f5a73979262ace27665129e98bced970ee293d2 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Fri, 26 Jun 2026 18:29:45 +0100
Subject: [PATCH 3/4] Remove excess namespace

---
 llvm/lib/Transforms/Utils/Debugify.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp
index 4d335877b068d..e501b6f2a4e34 100644
--- a/llvm/lib/Transforms/Utils/Debugify.cpp
+++ b/llvm/lib/Transforms/Utils/Debugify.cpp
@@ -80,17 +80,17 @@ cl::list<std::string> EnableOriginStacktraces(
 // For a given pass, sets whether the collection of DebugLoc origin stacktraces
 // is enabled or not.
 static void setDebugLocOriginCollectionForPass(StringRef PassName) {
-  if (!llvm::EnableOriginStacktraces.getNumOccurrences()) {
+  if (!EnableOriginStacktraces.getNumOccurrences()) {
     llvm::DebugLocOriginCollectionEnabled = false;
     return;
   }
-  if (llvm::EnableOriginStacktraces.size() == 1 &&
-      llvm::EnableOriginStacktraces[0].empty()) {
+  if (EnableOriginStacktraces.size() == 1 &&
+      EnableOriginStacktraces[0].empty()) {
     llvm::DebugLocOriginCollectionEnabled = true;
     return;
   }
   llvm::DebugLocOriginCollectionEnabled =
-      llvm::is_contained(llvm::EnableOriginStacktraces, PassName);
+      llvm::is_contained(EnableOriginStacktraces, PassName);
 }
 static void unsetDebugLocOriginCollection() {
   llvm::DebugLocOriginCollectionEnabled = false;

>From 823fa1b64da9a077c2d454441820b15683f42744 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Mon, 29 Jun 2026 09:51:01 +0100
Subject: [PATCH 4/4] Plural

---
 llvm/docs/HowToUpdateDebugInfo.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/docs/HowToUpdateDebugInfo.rst b/llvm/docs/HowToUpdateDebugInfo.rst
index 8ecc58a90ee9b..d062010ecdb36 100644
--- a/llvm/docs/HowToUpdateDebugInfo.rst
+++ b/llvm/docs/HowToUpdateDebugInfo.rst
@@ -454,7 +454,7 @@ 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 stacktrace during only
+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



More information about the llvm-commits mailing list