Index: llvm/include/llvm/Passes/StandardInstrumentations.h =================================================================== --- llvm/include/llvm/Passes/StandardInstrumentations.h +++ llvm/include/llvm/Passes/StandardInstrumentations.h @@ -18,10 +18,12 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/PassInstrumentation.h" +#include "llvm/IR/PassManager.h" #include "llvm/IR/PassTimingInfo.h" #include #include +#include namespace llvm { @@ -73,6 +75,20 @@ bool DebugLogging; }; +// Print IR on crash. +class PrintCrashIRInstrumentation { +public: + PrintCrashIRInstrumentation() + : SavedIR("*** Dump of IR Before Last Pass Unknown ***"), Index(-1) {} + ~PrintCrashIRInstrumentation(); + void registerCallbacks(PassInstrumentationCallbacks &PIC); + void reportCrashIR(); + +protected: + std::string SavedIR; + int Index; +}; + /// This class provides an interface to register all the standard pass /// instrumentations and manages their state (if any). class StandardInstrumentations { @@ -80,6 +96,7 @@ PrintPassInstrumentation PrintPass; TimePassesHandler TimePasses; OptNoneInstrumentation OptNone; + PrintCrashIRInstrumentation PrintCrashIR; public: StandardInstrumentations(bool DebugLogging) : PrintPass(DebugLogging) {} @@ -88,6 +105,7 @@ TimePassesHandler &getTimePasses() { return TimePasses; } }; + } // namespace llvm #endif Index: llvm/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/lib/Passes/PassBuilder.cpp +++ llvm/lib/Passes/PassBuilder.cpp @@ -258,6 +258,18 @@ cl::Hidden, cl::desc("Enable inline deferral during PGO")); +// A pass for testing -print-on-crash. +// DO NOT USE THIS EXCEPT FOR TESTING! +class TriggerCrashPass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); +}; + +// DO NOT USE THIS EXCEPT FOR TESTING! +PreservedAnalyses TriggerCrashPass::run(Function &, FunctionAnalysisManager &) { + __builtin_trap(); +} + PipelineTuningOptions::PipelineTuningOptions() { LoopInterleaving = true; LoopVectorization = true; Index: llvm/lib/Passes/PassRegistry.def =================================================================== --- llvm/lib/Passes/PassRegistry.def +++ llvm/lib/Passes/PassRegistry.def @@ -260,6 +260,7 @@ FUNCTION_PASS("spec-phis", SpeculateAroundPHIsPass()) FUNCTION_PASS("sroa", SROA()) FUNCTION_PASS("tailcallelim", TailCallElimPass()) +FUNCTION_PASS("trigger-crash", TriggerCrashPass()) FUNCTION_PASS("vector-combine", VectorCombinePass()) FUNCTION_PASS("verify", VerifierPass()) FUNCTION_PASS("verify", DominatorTreeVerifierPass()) Index: llvm/lib/Passes/StandardInstrumentations.cpp =================================================================== --- llvm/lib/Passes/StandardInstrumentations.cpp +++ llvm/lib/Passes/StandardInstrumentations.cpp @@ -15,6 +15,7 @@ #include "llvm/Passes/StandardInstrumentations.h" #include "llvm/ADT/Any.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Analysis/CallGraphSCCPass.h" #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/LoopInfo.h" @@ -23,9 +24,12 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PassInstrumentation.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include #include using namespace llvm; @@ -43,6 +47,14 @@ cl::desc("Print all pass management debugging information. " "`-debug-pass-manager` must also be specified")); +// A hidden option to print the IR that was being processed when a pass +// crashes. Note that nothing will be output if there is no crash. This +// option is affected by the -print-module-scope option. +static cl::opt + PrintCrashIR("print-on-crash", + cl::desc("Print the last form of the IR before crash"), + cl::init(false), cl::Hidden); + namespace { /// Extracting Module out of \p IR unit. Also fills a textual description @@ -189,6 +201,18 @@ llvm_unreachable("Unknown wrapped IR type"); } +// Avoid races when creating/destroying the crash IR printers. +std::mutex mtx; +// All of the crash reporters that will report on a crash. +std::vector CrashReporters; +// Crash handler registered when print-on-crash is specified. +void reportCrashedIR(void *) { + // called by signal handlers so do not lock here + for (unsigned I = 0, E = CrashReporters.size(); I < E; ++I) + if (CrashReporters[I]) + CrashReporters[I]->reportCrashIR(); +} + } // namespace PrintIRInstrumentation::~PrintIRInstrumentation() { @@ -338,10 +362,49 @@ }); } +PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() { + // Was this registered? + if (Index == -1) + return; + + mtx.lock(); + CrashReporters[Index] = nullptr; + mtx.unlock(); +} + +inline void PrintCrashIRInstrumentation::reportCrashIR() { dbgs() << SavedIR; } + +void PrintCrashIRInstrumentation::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + if (!PrintCrashIR) + return; + + mtx.lock(); + static int SignalHandlerAdded = false; + if (!SignalHandlerAdded) { + SignalHandlerAdded = true; + sys::AddSignalHandler(reportCrashedIR, nullptr); + } + Index = CrashReporters.size(); + CrashReporters.emplace_back(this); + mtx.unlock(); + + PIC.registerBeforePassCallback([this](StringRef PassID, Any IR) { + SavedIR.clear(); + SmallString<80> Banner = + formatv("*** Dump of {0}IR Before Last Pass {1} Started ***", + llvm::forcePrintModuleIR() ? "Module " : "", PassID); + raw_string_ostream OS(SavedIR); + unwrapAndPrint(OS, IR, Banner, llvm::forcePrintModuleIR()); + return true; + }); +} + void StandardInstrumentations::registerCallbacks( PassInstrumentationCallbacks &PIC) { PrintIR.registerCallbacks(PIC); PrintPass.registerCallbacks(PIC); TimePasses.registerCallbacks(PIC); OptNone.registerCallbacks(PIC); + PrintCrashIR.registerCallbacks(PIC); } Index: llvm/test/Other/print-on-crash.ll =================================================================== --- /dev/null +++ llvm/test/Other/print-on-crash.ll @@ -0,0 +1,25 @@ +; 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 calls +; __builtin_trap. + +; RUN: not --crash opt -print-on-crash -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK1 + +; 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=CHECK2 + +; The input corresponds to "int main() { return 0; }" but is irrelevant. + +; CHECK1: *** Dump of IR Before Last Pass TriggerCrashPass Started *** +; CHECK1: @main +; CHECK1: entry: +; CHECK2-NOT: *** Dump of IR + +define dso_local signext i32 @main() #0 { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + ret i32 0 +} +