From d27ddbc7baf841ac966aa8c27486f3914e387636 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 19 Jun 2024 15:57:35 +0200 Subject: [PATCH 1/2] [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 &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> Passes; }; +template +void printIRUnitNameForStackTrace(raw_ostream &OS, const IRUnitT &IR); + +template <> +void printIRUnitNameForStackTrace(raw_ostream &OS, const Module &IR); + extern template class PassManager; /// Convenience typedef for a pass manager over modules. using ModulePassManager = PassManager; +template <> +void printIRUnitNameForStackTrace(raw_ostream &OS, + const Function &IR); + extern template class PassManager; /// 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 UseNewDbgInfoFormat; @@ -26,6 +27,28 @@ namespace llvm { template PreservedAnalyses PassManager::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::run( if (!PI.runBeforePass(*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; @@ -144,6 +145,18 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M, return PA; } +template <> +void llvm::printIRUnitNameForStackTrace(raw_ostream &OS, + const Module &IR) { + OS << "module \"" << IR.getName() << "\""; +} + +template <> +void llvm::printIRUnitNameForStackTrace(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 c19d5a79ccd6d..1d926df8d04f2 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 { +class TriggerCrashModulePass : public PassInfoMixin { public: PreservedAnalyses run(Module &, ModuleAnalysisManager &) { abort(); return PreservedAnalyses::all(); } - static StringRef name() { return "TriggerCrashPass"; } + static StringRef name() { return "TriggerCrashModulePass"; } +}; + +class TriggerCrashFunctionPass + : public PassInfoMixin { +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,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" < %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 From 164f203d57889c49d49d705390d614550189f2ad Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 24 Jun 2024 10:06:33 +0200 Subject: [PATCH 2/2] drop unrelated whitespace change --- llvm/lib/IR/PassManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/llvm/lib/IR/PassManager.cpp b/llvm/lib/IR/PassManager.cpp index 59d27d4d5eb65..64a6effe5c0db 100644 --- a/llvm/lib/IR/PassManager.cpp +++ b/llvm/lib/IR/PassManager.cpp @@ -13,7 +13,6 @@ using namespace llvm; namespace llvm { - // Explicit template instantiations and specialization defininitions for core // template typedefs. template class AllAnalysesOn;