[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