Index: llvm/include/llvm/Passes/StandardInstrumentations.h =================================================================== --- llvm/include/llvm/Passes/StandardInstrumentations.h +++ llvm/include/llvm/Passes/StandardInstrumentations.h @@ -187,17 +187,6 @@ // Register required callbacks. void registerRequiredCallbacks(PassInstrumentationCallbacks &PIC); - // Return true when this is a defined function for which printing - // of changes is desired. - bool isInterestingFunction(const Function &F); - - // Return true when this is a pass for which printing of changes is desired. - bool isInterestingPass(StringRef PassID); - - // Return true when this is a pass on IR for which printing - // of changes is desired. - bool isInteresting(Any IR, StringRef PassID); - // Called on the first IR processed. virtual void handleInitialIR(Any IR) = 0; // Called before and after a pass to get the representation of the IR. @@ -409,6 +398,25 @@ void registerCallbacks(PassInstrumentationCallbacks &PIC); }; +// Print IR on crash. +class PrintCrashIRInstrumentation { +public: + PrintCrashIRInstrumentation() + : SavedIR("*** Dump of IR Before Last Pass Unknown ***") {} + ~PrintCrashIRInstrumentation(); + void registerCallbacks(PassInstrumentationCallbacks &PIC); + void reportCrashIR(); + +protected: + std::string SavedIR; + +private: + // The crash reporter that will report on a crash. + static PrintCrashIRInstrumentation *CrashReporter; + // Crash handler registered when print-on-crash is specified. + static void SignalHandler(void *); +}; + /// This class provides an interface to register all the standard pass /// instrumentations and manages their state (if any). class StandardInstrumentations { @@ -421,6 +429,7 @@ IRChangedPrinter PrintChangedIR; PseudoProbeVerifier PseudoProbeVerification; InLineChangePrinter PrintChangedDiff; + PrintCrashIRInstrumentation PrintCrashIR; VerifyInstrumentation Verify; bool VerifyEach; Index: llvm/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/lib/Passes/PassBuilder.cpp +++ llvm/lib/Passes/PassBuilder.cpp @@ -442,6 +442,13 @@ return !printBeforePasses().empty() || !printAfterPasses().empty(); } +// A pass for testing -print-on-crash. +// DO NOT USE THIS EXCEPT FOR TESTING! +class TriggerCrashPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &, ModuleAnalysisManager &) { assert(false); } +}; + } // namespace PassBuilder::PassBuilder(TargetMachine *TM, PipelineTuningOptions PTO, Index: llvm/lib/Passes/PassRegistry.def =================================================================== --- llvm/lib/Passes/PassRegistry.def +++ llvm/lib/Passes/PassRegistry.def @@ -110,6 +110,7 @@ MODULE_PASS("strip-nondebug", StripNonDebugSymbolsPass()) MODULE_PASS("strip-nonlinetable-debuginfo", StripNonLineTableDebugInfoPass()) MODULE_PASS("synthetic-counts-propagation", SyntheticCountsPropagation()) +MODULE_PASS("trigger-crash", TriggerCrashPass()) MODULE_PASS("verify", VerifierPass()) MODULE_PASS("wholeprogramdevirt", WholeProgramDevirtPass()) MODULE_PASS("dfsan", DataFlowSanitizerPass()) Index: llvm/lib/Passes/StandardInstrumentations.cpp =================================================================== --- llvm/lib/Passes/StandardInstrumentations.cpp +++ llvm/lib/Passes/StandardInstrumentations.cpp @@ -27,10 +27,12 @@ #include "llvm/IR/PrintPasses.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -119,6 +121,12 @@ DiffBinary("print-changed-diff-path", cl::Hidden, cl::init("diff"), cl::desc("system diff used by change reporters")); +// An option to print the IR that was being processed when a pass crashes. +static cl::opt + PrintCrashIR("print-on-crash", + cl::desc("Print the last form of the IR before crash"), + cl::init(false), cl::Hidden); + namespace { // Perform a system based diff between \p Before and \p After, using @@ -368,20 +376,11 @@ "DevirtSCCRepeatedPass", "ModuleInlinerWrapperPass"}); } -} // namespace - -template -ChangeReporter::~ChangeReporter() { - assert(BeforeStack.empty() && "Problem with Change Printer stack."); -} - -template -bool ChangeReporter::isInterestingFunction(const Function &F) { +bool isInterestingFunction(const Function &F) { return isFunctionInPrintList(F.getName()); } -template -bool ChangeReporter::isInterestingPass(StringRef PassID) { +bool isInterestingPass(StringRef PassID) { if (isIgnored(PassID)) return false; @@ -392,8 +391,7 @@ // Return true when this is a pass on IR for which printing // of changes is desired. -template -bool ChangeReporter::isInteresting(Any IR, StringRef PassID) { +bool isInteresting(Any IR, StringRef PassID) { if (!isInterestingPass(PassID)) return false; if (any_isa(IR)) @@ -401,6 +399,13 @@ return true; } +} // namespace + +template +ChangeReporter::~ChangeReporter() { + assert(BeforeStack.empty() && "Problem with Change Printer stack."); +} + template void ChangeReporter::saveIRBeforePass(Any IR, StringRef PassID) { // Always need to place something on the stack because invalidated passes @@ -1224,6 +1229,51 @@ PrintChanged == ChangePrinter::PrintChangedColourDiffQuiet), Verify(DebugLogging), VerifyEach(VerifyEach) {} +PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter = + nullptr; + +void PrintCrashIRInstrumentation::reportCrashIR() { dbgs() << SavedIR; } + +void PrintCrashIRInstrumentation::SignalHandler(void *) { + // Called by signal handlers so do not lock here + // Is the PrintCrashIRInstrumentation still alive? + if (!CrashReporter) + return; + + assert(PrintCrashIR && "Did not expect to get here without option set."); + CrashReporter->reportCrashIR(); +} + +PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() { + if (!CrashReporter) + return; + + assert(PrintCrashIR && "Did not expect to get here without option set."); + CrashReporter = nullptr; +} + +void PrintCrashIRInstrumentation::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + if (!PrintCrashIR || CrashReporter) + return; + + sys::AddSignalHandler(SignalHandler, nullptr); + CrashReporter = this; + + PIC.registerBeforeNonSkippedPassCallback([this](StringRef PassID, Any IR) { + SavedIR.clear(); + raw_string_ostream OS(SavedIR); + OS << formatv("*** Dump of {0}IR Before Last Pass {1}", + llvm::forcePrintModuleIR() ? "Module " : "", PassID); + if (!isInteresting(IR, PassID)) { + OS << " Filtered Out ***\n"; + return; + } + OS << " Started ***\n"; + unwrapAndPrint(OS, IR); + }); +} + void StandardInstrumentations::registerCallbacks( PassInstrumentationCallbacks &PIC, FunctionAnalysisManager *FAM) { PrintIR.registerCallbacks(PIC); @@ -1238,6 +1288,7 @@ if (VerifyEach) Verify.registerCallbacks(PIC); PrintChangedDiff.registerCallbacks(PIC); + PrintCrashIR.registerCallbacks(PIC); } namespace llvm { Index: llvm/test/Other/print-on-crash.ll =================================================================== --- /dev/null +++ llvm/test/Other/print-on-crash.ll @@ -0,0 +1,32 @@ +; 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. + +; REQUIRES: asserts + +; RUN: not --crash opt -print-on-crash -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_SIMPLE + +; A test that the signal handler set by the hidden option -print-on-crash +; is not called when no pass crashes. + +; RUN: opt -print-on-crash -passes="default" < %s 2>&1 | FileCheck %s --check-prefix=CHECK_NO_CRASH + +; 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 -filter-passes="(anonymous namespace)::TriggerCrashPass" < %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 + +; CHECK_SIMPLE: *** Dump of IR Before Last Pass {{.*}} Started *** +; CHECK_SIMPLE: @main +; CHECK_SIMPLE: entry: +; CHECK_NO_CRASH-NOT: *** Dump of IR +; CHECK_MODULE: *** Dump of Module IR Before Last Pass {{.*}} Started *** +; CHECK_MODULE: ; ModuleID = {{.*}} +; CHECK_FILTERED: *** Dump of Module IR Before Last Pass {{.*}} Filtered Out *** + +define i32 @main() { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + ret i32 0 +}