[llvm] [PassManager] Add pretty stack frames (PR #96078)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 24 00:50:05 PDT 2024


https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/96078

>From b218927b09ec2303af704da559b9f5280fcae6f6 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Wed, 19 Jun 2024 15:57:35 +0200
Subject: [PATCH] [PassManager] Add pretty stack frames

In NewPM pass managers, add a "pretty stack frame" that tells you
which pass crashed while running which function:

```
Stack dump:
0.	Program arguments: build/bin/opt -S -passes=instcombine llvm/test/Transforms/InstCombine/vec_shuffle.ll
1.	Running pass "ModuleToFunctionPassAdaptor" on module "llvm/test/Transforms/InstCombine/vec_shuffle.ll"
2.	Running pass "InstCombinePass" on function "test16a"
```

While the crashing pass is usually evident from the stack trace,
knowing which function caused it is quite handy.

Similar functionality existed in the LegacyPM.
---
 llvm/include/llvm/IR/PassInstrumentation.h |  7 ++++++
 llvm/include/llvm/IR/PassManager.h         | 10 +++++++++
 llvm/include/llvm/IR/PassManagerImpl.h     | 26 +++++++++++++++++++++-
 llvm/lib/IR/PassManager.cpp                | 13 +++++++++++
 llvm/lib/Passes/PassBuilder.cpp            | 16 ++++++++++---
 llvm/lib/Passes/PassRegistry.def           |  3 ++-
 llvm/test/Other/crash-stack-trace.ll       | 20 +++++++++++++++++
 llvm/test/Other/print-on-crash.ll          | 12 +++++-----
 8 files changed, 96 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/Other/crash-stack-trace.ll

diff --git a/llvm/include/llvm/IR/PassInstrumentation.h b/llvm/include/llvm/IR/PassInstrumentation.h
index f2eb8a95b7881..9fcc2d5957a30 100644
--- a/llvm/include/llvm/IR/PassInstrumentation.h
+++ b/llvm/include/llvm/IR/PassInstrumentation.h
@@ -332,6 +332,13 @@ class PassInstrumentation {
     if (Callbacks)
       Callbacks->BeforeNonSkippedPassCallbacks.pop_back();
   }
+
+  /// Get the pass name for a given pass class name.
+  StringRef getPassNameForClassName(StringRef ClassName) const {
+    if (Callbacks)
+      return Callbacks->getPassNameForClassName(ClassName);
+    return {};
+  }
 };
 
 bool isSpecialPass(StringRef PassID, const std::vector<StringRef> &Specials);
diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h
index 23a05c056dfaf..226eba7d8027a 100644
--- a/llvm/include/llvm/IR/PassManager.h
+++ b/llvm/include/llvm/IR/PassManager.h
@@ -224,11 +224,21 @@ class PassManager : public PassInfoMixin<
   std::vector<std::unique_ptr<PassConceptT>> Passes;
 };
 
+template <typename IRUnitT>
+void printIRUnitNameForStackTrace(raw_ostream &OS, const IRUnitT &IR);
+
+template <>
+void printIRUnitNameForStackTrace<Module>(raw_ostream &OS, const Module &IR);
+
 extern template class PassManager<Module>;
 
 /// Convenience typedef for a pass manager over modules.
 using ModulePassManager = PassManager<Module>;
 
+template <>
+void printIRUnitNameForStackTrace<Function>(raw_ostream &OS,
+                                            const Function &IR);
+
 extern template class PassManager<Function>;
 
 /// Convenience typedef for a pass manager over functions.
diff --git a/llvm/include/llvm/IR/PassManagerImpl.h b/llvm/include/llvm/IR/PassManagerImpl.h
index e8a95761c3494..91f5774d2feb9 100644
--- a/llvm/include/llvm/IR/PassManagerImpl.h
+++ b/llvm/include/llvm/IR/PassManagerImpl.h
@@ -15,9 +15,10 @@
 #ifndef LLVM_IR_PASSMANAGERIMPL_H
 #define LLVM_IR_PASSMANAGERIMPL_H
 
-#include "llvm/Support/CommandLine.h"
 #include "llvm/IR/PassInstrumentation.h"
 #include "llvm/IR/PassManager.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/PrettyStackTrace.h"
 
 extern llvm::cl::opt<bool> UseNewDbgInfoFormat;
 
@@ -26,6 +27,28 @@ namespace llvm {
 template <typename IRUnitT, typename AnalysisManagerT, typename... ExtraArgTs>
 PreservedAnalyses PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>::run(
     IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) {
+  class StackTraceEntry : public PrettyStackTraceEntry {
+    const PassInstrumentation &PI;
+    PassConceptT &Pass;
+    IRUnitT &IR;
+
+  public:
+    explicit StackTraceEntry(const PassInstrumentation &PI, PassConceptT &Pass,
+                             IRUnitT &IR)
+        : PI(PI), Pass(Pass), IR(IR) {}
+
+    void print(raw_ostream &OS) const override {
+      OS << "Running pass \"";
+      Pass.printPipeline(OS, [this](StringRef ClassName) {
+        auto PassName = PI.getPassNameForClassName(ClassName);
+        return PassName.empty() ? ClassName : PassName;
+      });
+      OS << "\" on ";
+      printIRUnitNameForStackTrace(OS, IR);
+      OS << "\n";
+    }
+  };
+
   PreservedAnalyses PA = PreservedAnalyses::all();
 
   // Request PassInstrumentation from analysis manager, will use it to run
@@ -47,6 +70,7 @@ PreservedAnalyses PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>::run(
     if (!PI.runBeforePass<IRUnitT>(*Pass, IR))
       continue;
 
+    StackTraceEntry Entry(PI, *Pass, IR);
     PreservedAnalyses PassPA = Pass->run(IR, AM, ExtraArgs...);
 
     // Update the analysis manager as each pass runs and potentially
diff --git a/llvm/lib/IR/PassManager.cpp b/llvm/lib/IR/PassManager.cpp
index cbddf3dfb056c..59d27d4d5eb65 100644
--- a/llvm/lib/IR/PassManager.cpp
+++ b/llvm/lib/IR/PassManager.cpp
@@ -13,6 +13,7 @@
 using namespace llvm;
 
 namespace llvm {
+
 // Explicit template instantiations and specialization defininitions for core
 // template typedefs.
 template class AllAnalysesOn<Module>;
@@ -144,6 +145,18 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M,
   return PA;
 }
 
+template <>
+void llvm::printIRUnitNameForStackTrace<Module>(raw_ostream &OS,
+                                                const Module &IR) {
+  OS << "module \"" << IR.getName() << "\"";
+}
+
+template <>
+void llvm::printIRUnitNameForStackTrace<Function>(raw_ostream &OS,
+                                                  const Function &IR) {
+  OS << "function \"" << IR.getName() << "\"";
+}
+
 AnalysisSetKey CFGAnalyses::SetKey;
 
 AnalysisSetKey PreservedAnalyses::AllAnalysesKey;
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 12ef995073651..456b94075e383 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -325,15 +325,25 @@ AnalysisKey NoOpLoopAnalysis::Key;
 
 namespace {
 
-// A pass for testing -print-on-crash.
+// Passes for testing crashes.
 // DO NOT USE THIS EXCEPT FOR TESTING!
-class TriggerCrashPass : public PassInfoMixin<TriggerCrashPass> {
+class TriggerCrashModulePass : public PassInfoMixin<TriggerCrashModulePass> {
 public:
   PreservedAnalyses run(Module &, ModuleAnalysisManager &) {
     abort();
     return PreservedAnalyses::all();
   }
-  static StringRef name() { return "TriggerCrashPass"; }
+  static StringRef name() { return "TriggerCrashModulePass"; }
+};
+
+class TriggerCrashFunctionPass
+    : public PassInfoMixin<TriggerCrashFunctionPass> {
+public:
+  PreservedAnalyses run(Function &, FunctionAnalysisManager &) {
+    abort();
+    return PreservedAnalyses::all();
+  }
+  static StringRef name() { return "TriggerCrashFunctionPass"; }
 };
 
 // A pass for testing message reporting of -verify-each failures.
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 60c517790bcab..6c1b04e49fedd 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -137,7 +137,7 @@ MODULE_PASS("strip-debug-declare", StripDebugDeclarePass())
 MODULE_PASS("strip-nondebug", StripNonDebugSymbolsPass())
 MODULE_PASS("strip-nonlinetable-debuginfo", StripNonLineTableDebugInfoPass())
 MODULE_PASS("synthetic-counts-propagation", SyntheticCountsPropagation())
-MODULE_PASS("trigger-crash", TriggerCrashPass())
+MODULE_PASS("trigger-crash-module", TriggerCrashModulePass())
 MODULE_PASS("trigger-verifier-error", TriggerVerifierErrorPass())
 MODULE_PASS("tsan-module", ModuleThreadSanitizerPass())
 MODULE_PASS("verify", VerifierPass())
@@ -459,6 +459,7 @@ FUNCTION_PASS("structurizecfg", StructurizeCFGPass())
 FUNCTION_PASS("tailcallelim", TailCallElimPass())
 FUNCTION_PASS("tlshoist", TLSVariableHoistPass())
 FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
+FUNCTION_PASS("trigger-crash-function", TriggerCrashFunctionPass())
 FUNCTION_PASS("trigger-verifier-error", TriggerVerifierErrorPass())
 FUNCTION_PASS("tsan", ThreadSanitizerPass())
 FUNCTION_PASS("typepromotion", TypePromotionPass(TM))
diff --git a/llvm/test/Other/crash-stack-trace.ll b/llvm/test/Other/crash-stack-trace.ll
new file mode 100644
index 0000000000000..29e43fe8197c2
--- /dev/null
+++ b/llvm/test/Other/crash-stack-trace.ll
@@ -0,0 +1,20 @@
+; REQUIRES: asserts
+
+; RUN: not --crash opt -passes=trigger-crash-module %s -disable-output 2>&1 | \
+; RUN: FileCheck %s --check-prefix=CHECK-MODULE
+
+; CHECK-MODULE:      Stack dump:
+; CHECK-MODULE-NEXT: 0. Program arguments:
+; CHECK-MODULE-NEXT: 1. Running pass "trigger-crash-module" on module "{{.*}}crash-stack-trace.ll"
+
+; RUN: not --crash opt -passes='sroa,trigger-crash-function' %s -disable-output 2>&1 | \
+; RUN: FileCheck %s --check-prefix=CHECK-FUNCTION
+
+; CHECK-FUNCTION:      Stack dump:
+; CHECK-FUNCTION-NEXT: 0. Program arguments:
+; CHECK-FUNCTION-NEXT: 1. Running pass "function(sroa<modify-cfg>,trigger-crash-function)" on module "{{.*}}crash-stack-trace.ll"
+; CHECK-FUNCTION-NEXT: 2. Running pass "trigger-crash-function" on function "foo"
+
+define void @foo() {
+  ret void
+}
diff --git a/llvm/test/Other/print-on-crash.ll b/llvm/test/Other/print-on-crash.ll
index f9bf8f039c1db..565da0c389c16 100644
--- a/llvm/test/Other/print-on-crash.ll
+++ b/llvm/test/Other/print-on-crash.ll
@@ -1,9 +1,9 @@
 ; A test that the hidden option -print-on-crash properly sets a signal handler
-; which gets called when a pass crashes.  The trigger-crash pass asserts.
+; which gets called when a pass crashes.  The trigger-crash-module pass asserts.
 
-; RUN: not --crash opt -print-on-crash -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_SIMPLE
+; RUN: not --crash opt -print-on-crash -passes=trigger-crash-module < %s 2>&1 | FileCheck %s --check-prefix=CHECK_SIMPLE
 
-; RUN: not --crash opt -print-on-crash-path=%t -passes=trigger-crash < %s
+; RUN: not --crash opt -print-on-crash-path=%t -passes=trigger-crash-module < %s
 ; RUN: FileCheck %s --check-prefix=CHECK_SIMPLE --input-file=%t
 
 ; A test that the signal handler set by the  hidden option -print-on-crash
@@ -11,11 +11,11 @@
 
 ; RUN: opt -disable-output -print-on-crash -passes="default<O2>" < %s 2>&1 | FileCheck %s --check-prefix=CHECK_NO_CRASH --allow-empty
 
-; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
+; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
 
-; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash -filter-passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
+; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module -filter-passes=trigger-crash-module < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
 
-; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash -filter-passes=blah < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FILTERED
+; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module -filter-passes=blah < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FILTERED
 
 ; CHECK_SIMPLE: *** Dump of IR Before Last Pass {{.*}} Started ***
 ; CHECK_SIMPLE: @main



More information about the llvm-commits mailing list