[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