Skip to content

Commit 24239e2

Browse files
jamieschmeiseraeubanks
authored andcommitted
Add new hidden option -print-on-crash that prints out IR that caused opt pipeline to crash
A new hidden option -print-on-crash that prints the IR as it was upon entering the last pass when there is a crash. The IR is saved in its print form before each pass is started and a signal handler is registered. If the compilation crashes, the signal handler will print the saved IR to dbgs(). This option can be modified using -print-module-scope to get the IR for the complete module. Note that this option only works with the new pass manager. Reviewed By: yrouban Differential Revision: https://reviews.llvm.org/D86657
1 parent 21c060c commit 24239e2

File tree

5 files changed

+116
-0
lines changed

5 files changed

+116
-0
lines changed

llvm/include/llvm/Passes/StandardInstrumentations.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,25 @@ class DotCfgChangeReporter : public ChangeReporter<IRDataT<DCData>> {
480480
std::unique_ptr<raw_fd_ostream> HTML;
481481
};
482482

483+
// Print IR on crash.
484+
class PrintCrashIRInstrumentation {
485+
public:
486+
PrintCrashIRInstrumentation()
487+
: SavedIR("*** Dump of IR Before Last Pass Unknown ***") {}
488+
~PrintCrashIRInstrumentation();
489+
void registerCallbacks(PassInstrumentationCallbacks &PIC);
490+
void reportCrashIR();
491+
492+
protected:
493+
std::string SavedIR;
494+
495+
private:
496+
// The crash reporter that will report on a crash.
497+
static PrintCrashIRInstrumentation *CrashReporter;
498+
// Crash handler registered when print-on-crash is specified.
499+
static void SignalHandler(void *);
500+
};
501+
483502
/// This class provides an interface to register all the standard pass
484503
/// instrumentations and manages their state (if any).
485504
class StandardInstrumentations {
@@ -493,6 +512,7 @@ class StandardInstrumentations {
493512
PseudoProbeVerifier PseudoProbeVerification;
494513
InLineChangePrinter PrintChangedDiff;
495514
DotCfgChangeReporter WebsiteChangeReporter;
515+
PrintCrashIRInstrumentation PrintCrashIR;
496516
VerifyInstrumentation Verify;
497517

498518
bool VerifyEach;

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,17 @@ bool shouldPopulateClassToPassNames() {
375375
!printAfterPasses().empty();
376376
}
377377

378+
// A pass for testing -print-on-crash.
379+
// DO NOT USE THIS EXCEPT FOR TESTING!
380+
class TriggerCrashPass : public PassInfoMixin<TriggerCrashPass> {
381+
public:
382+
PreservedAnalyses run(Module &, ModuleAnalysisManager &) {
383+
abort();
384+
return PreservedAnalyses::all();
385+
}
386+
static StringRef name() { return "TriggerCrashPass"; }
387+
};
388+
378389
} // namespace
379390

380391
PassBuilder::PassBuilder(TargetMachine *TM, PipelineTuningOptions PTO,

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ MODULE_PASS("strip-debug-declare", StripDebugDeclarePass())
114114
MODULE_PASS("strip-nondebug", StripNonDebugSymbolsPass())
115115
MODULE_PASS("strip-nonlinetable-debuginfo", StripNonLineTableDebugInfoPass())
116116
MODULE_PASS("synthetic-counts-propagation", SyntheticCountsPropagation())
117+
MODULE_PASS("trigger-crash", TriggerCrashPass())
117118
MODULE_PASS("verify", VerifierPass())
118119
MODULE_PASS("view-callgraph", CallGraphViewerPass())
119120
MODULE_PASS("wholeprogramdevirt", WholeProgramDevirtPass())

llvm/lib/Passes/StandardInstrumentations.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
#include "llvm/IR/PrintPasses.h"
2929
#include "llvm/IR/Verifier.h"
3030
#include "llvm/Support/CommandLine.h"
31+
#include "llvm/Support/CrashRecoveryContext.h"
3132
#include "llvm/Support/Debug.h"
3233
#include "llvm/Support/FormatVariadic.h"
3334
#include "llvm/Support/GraphWriter.h"
3435
#include "llvm/Support/MemoryBuffer.h"
3536
#include "llvm/Support/Program.h"
3637
#include "llvm/Support/Regex.h"
38+
#include "llvm/Support/Signals.h"
3739
#include "llvm/Support/raw_ostream.h"
3840
#include <unordered_map>
3941
#include <unordered_set>
@@ -165,6 +167,12 @@ static cl::opt<std::string> DotCfgDir(
165167
cl::desc("Generate dot files into specified directory for changed IRs"),
166168
cl::Hidden, cl::init("./"));
167169

170+
// An option to print the IR that was being processed when a pass crashes.
171+
static cl::opt<bool>
172+
PrintCrashIR("print-on-crash",
173+
cl::desc("Print the last form of the IR before crash"),
174+
cl::init(false), cl::Hidden);
175+
168176
namespace {
169177

170178
// Perform a system based diff between \p Before and \p After, using
@@ -2115,6 +2123,51 @@ StandardInstrumentations::StandardInstrumentations(
21152123
ChangePrinter::PrintChangedDotCfgVerbose),
21162124
Verify(DebugLogging), VerifyEach(VerifyEach) {}
21172125

2126+
PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
2127+
nullptr;
2128+
2129+
void PrintCrashIRInstrumentation::reportCrashIR() { dbgs() << SavedIR; }
2130+
2131+
void PrintCrashIRInstrumentation::SignalHandler(void *) {
2132+
// Called by signal handlers so do not lock here
2133+
// Is the PrintCrashIRInstrumentation still alive?
2134+
if (!CrashReporter)
2135+
return;
2136+
2137+
assert(PrintCrashIR && "Did not expect to get here without option set.");
2138+
CrashReporter->reportCrashIR();
2139+
}
2140+
2141+
PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
2142+
if (!CrashReporter)
2143+
return;
2144+
2145+
assert(PrintCrashIR && "Did not expect to get here without option set.");
2146+
CrashReporter = nullptr;
2147+
}
2148+
2149+
void PrintCrashIRInstrumentation::registerCallbacks(
2150+
PassInstrumentationCallbacks &PIC) {
2151+
if (!PrintCrashIR || CrashReporter)
2152+
return;
2153+
2154+
sys::AddSignalHandler(SignalHandler, nullptr);
2155+
CrashReporter = this;
2156+
2157+
PIC.registerBeforeNonSkippedPassCallback([this](StringRef PassID, Any IR) {
2158+
SavedIR.clear();
2159+
raw_string_ostream OS(SavedIR);
2160+
OS << formatv("*** Dump of {0}IR Before Last Pass {1}",
2161+
llvm::forcePrintModuleIR() ? "Module " : "", PassID);
2162+
if (!isInteresting(IR, PassID)) {
2163+
OS << " Filtered Out ***\n";
2164+
return;
2165+
}
2166+
OS << " Started ***\n";
2167+
unwrapAndPrint(OS, IR);
2168+
});
2169+
}
2170+
21182171
void StandardInstrumentations::registerCallbacks(
21192172
PassInstrumentationCallbacks &PIC, FunctionAnalysisManager *FAM) {
21202173
PrintIR.registerCallbacks(PIC);
@@ -2130,6 +2183,7 @@ void StandardInstrumentations::registerCallbacks(
21302183
Verify.registerCallbacks(PIC);
21312184
PrintChangedDiff.registerCallbacks(PIC);
21322185
WebsiteChangeReporter.registerCallbacks(PIC);
2186+
PrintCrashIR.registerCallbacks(PIC);
21332187
}
21342188

21352189
template class ChangeReporter<std::string>;

llvm/test/Other/print-on-crash.ll

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; A test that the hidden option -print-on-crash properly sets a signal handler
2+
; which gets called when a pass crashes. The trigger-crash pass asserts.
3+
4+
; RUN: not --crash opt -print-on-crash -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_SIMPLE
5+
6+
; A test that the signal handler set by the hidden option -print-on-crash
7+
; is not called when no pass crashes.
8+
9+
; RUN: opt -print-on-crash -passes="default<O2>" < %s 2>&1 | FileCheck %s --check-prefix=CHECK_NO_CRASH
10+
11+
; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
12+
13+
; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash -filter-passes=TriggerCrashPass < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
14+
15+
; 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
16+
17+
; CHECK_SIMPLE: *** Dump of IR Before Last Pass {{.*}} Started ***
18+
; CHECK_SIMPLE: @main
19+
; CHECK_SIMPLE: entry:
20+
; CHECK_NO_CRASH-NOT: *** Dump of IR
21+
; CHECK_MODULE: *** Dump of Module IR Before Last Pass {{.*}} Started ***
22+
; CHECK_MODULE: ; ModuleID = {{.*}}
23+
; CHECK_FILTERED: *** Dump of Module IR Before Last Pass {{.*}} Filtered Out ***
24+
25+
define i32 @main() {
26+
entry:
27+
%retval = alloca i32, align 4
28+
store i32 0, i32* %retval, align 4
29+
ret i32 0
30+
}

0 commit comments

Comments
 (0)