#ifndef LLVM_PASSES_STANDARDINSTRUMENTATIONS_H
#define LLVM_PASSES_STANDARDINSTRUMENTATIONS_H
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/IPO/SampleProfileProbe.h"
+#include <mutex>
#include <string>
#include <utility>
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:
+ // All of the crash reporters that will report on a crash.
+ static DenseSet<PrintCrashIRInstrumentation *> *CrashReporters;
+ // Crash handler registered when print-on-crash is specified.
+ static void SignalHandler(void *);
+ // Exception-safe locking
+ class MtxLock {
+ public:
+ MtxLock() { Mtx.lock(); }
+ ~MtxLock() { Mtx.unlock(); }
+
+ protected:
+ // Avoid races when creating/destroying the crash IR printers.
+ static std::mutex Mtx;
+ };
+};
+
/// This class provides an interface to register all the standard pass
/// instrumentations and manages their state (if any).
class StandardInstrumentations {
IRChangedPrinter PrintChangedIR;
PseudoProbeVerifier PseudoProbeVerification;
InLineChangePrinter PrintChangedDiff;
+ PrintCrashIRInstrumentation PrintCrashIR;
VerifyInstrumentation Verify;
bool VerifyEach;
extern template class ChangeReporter<ChangedIRData>;
extern template class TextChangeReporter<ChangedIRData>;
-
} // namespace llvm
#endif
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/ADT/Any.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/CallGraphSCCPass.h"
#include "llvm/Analysis/LazyCallGraph.h"
#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 <unordered_set>
#include <vector>
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<bool>
+ 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
PrintChangedDiff(PrintChanged == ChangePrinter::PrintChangedDiffVerbose),
Verify(DebugLogging), VerifyEach(VerifyEach) {}
+std::mutex PrintCrashIRInstrumentation::MtxLock::Mtx;
+DenseSet<PrintCrashIRInstrumentation *>
+ *PrintCrashIRInstrumentation::CrashReporters = nullptr;
+
+void PrintCrashIRInstrumentation::reportCrashIR() { dbgs() << SavedIR; }
+
+void PrintCrashIRInstrumentation::SignalHandler(void *) {
+ // Called by signal handlers so do not lock here
+ // Are any of PrintCrashIRInstrumentation objects still alive?
+ if (!CrashReporters)
+ return;
+
+ assert(PrintCrashIR && "Did not expect to get here without option set.");
+ for (auto I : *CrashReporters)
+ I->reportCrashIR();
+}
+
+PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
+ if (!PrintCrashIR)
+ return;
+
+ MtxLock Lock;
+ assert(CrashReporters && "Expected CrashReporters to be set");
+
+ // Was this registered?
+ DenseSet<PrintCrashIRInstrumentation *>::iterator I =
+ CrashReporters->find(this);
+ if (I == CrashReporters->end())
+ return;
+ CrashReporters->erase(I);
+ if (!CrashReporters->empty())
+ return;
+ delete CrashReporters;
+ CrashReporters = nullptr;
+}
+
+void PrintCrashIRInstrumentation::registerCallbacks(
+ PassInstrumentationCallbacks &PIC) {
+ if (!PrintCrashIR)
+ return;
+
+ {
+ MtxLock Lock;
+ if (!CrashReporters) {
+ CrashReporters = new DenseSet<PrintCrashIRInstrumentation *>();
+ sys::AddSignalHandler(SignalHandler, nullptr);
+ }
+ CrashReporters->insert(this);
+ }
+ PIC.registerBeforeNonSkippedPassCallback([this](StringRef PassID, Any IR) {
+ assert((MtxLock(), CrashReporters && CrashReporters->find(this) !=
+ CrashReporters->end()) &&
+ "Expected CrashReporters to be set and containing this");
+ 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());
+ });
+}
+
void StandardInstrumentations::registerCallbacks(
PassInstrumentationCallbacks &PIC) {
PrintIR.registerCallbacks(PIC);
if (VerifyEach)
Verify.registerCallbacks(PIC);
PrintChangedDiff.registerCallbacks(PIC);
+ PrintCrashIR.registerCallbacks(PIC);
}
namespace llvm {
--- /dev/null
+; 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=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<O2>" < %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
+
+; The input corresponds to "int main() { return 0; }" but is irrelevant.
+
+; 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 = {{.*}}
+
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4
+ store i32 0, i32* %retval, align 4
+ ret i32 0
+}