Skip to content

Commit e62ff42

Browse files
committed
[mlir][Pass] Register a signal handler when generating crash reproducers.
The current implementation uses CrashRecoveryContext, but this only supports recovering in a certain number of cases. This revision adds a signal handler to support even more situations. This revision was able to properly generate a reproducer for a segfault in the Inliner, that the current recovery couldn't. Differential Revision: https://reviews.llvm.org/D78315
1 parent 983382f commit e62ff42

File tree

1 file changed

+116
-32
lines changed

1 file changed

+116
-32
lines changed

mlir/lib/Pass/Pass.cpp

Lines changed: 116 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
#include "mlir/IR/Module.h"
1919
#include "mlir/Support/FileUtilities.h"
2020
#include "llvm/ADT/STLExtras.h"
21+
#include "llvm/ADT/SetVector.h"
2122
#include "llvm/Support/CommandLine.h"
2223
#include "llvm/Support/CrashRecoveryContext.h"
2324
#include "llvm/Support/Mutex.h"
2425
#include "llvm/Support/Parallel.h"
26+
#include "llvm/Support/Signals.h"
2527
#include "llvm/Support/Threading.h"
2628
#include "llvm/Support/ToolOutputFile.h"
2729

@@ -545,6 +547,115 @@ void OpToOpPassAdaptor::runOnOperationAsyncImpl() {
545547
// PassCrashReproducer
546548
//===----------------------------------------------------------------------===//
547549

550+
namespace {
551+
/// This class contains all of the context for generating a recovery reproducer.
552+
/// Each recovery context is registered globally to allow for generating
553+
/// reproducers when a signal is raised, such as a segfault.
554+
struct RecoveryReproducerContext {
555+
RecoveryReproducerContext(MutableArrayRef<std::unique_ptr<Pass>> passes,
556+
ModuleOp module, StringRef filename,
557+
bool disableThreads, bool verifyPasses);
558+
~RecoveryReproducerContext();
559+
560+
/// Generate a reproducer with the current context.
561+
LogicalResult generate(std::string &error);
562+
563+
private:
564+
/// This function is invoked in the event of a crash.
565+
static void crashHandler(void *);
566+
567+
/// Register a signal handler to run in the event of a crash.
568+
static void registerSignalHandler();
569+
570+
/// The textual description of the currently executing pipeline.
571+
std::string pipeline;
572+
573+
/// The MLIR module representing the IR before the crash.
574+
OwningModuleRef module;
575+
576+
/// The filename to use when generating the reproducer.
577+
StringRef filename;
578+
579+
/// Various pass manager flags.
580+
bool disableThreads;
581+
bool verifyPasses;
582+
583+
/// The current set of active reproducer contexts. This is used in the event
584+
/// of a crash. This is not thread_local as the pass manager may produce any
585+
/// number of child threads. This uses a set to allow for multiple MLIR pass
586+
/// managers to be running at the same time.
587+
static llvm::ManagedStatic<llvm::sys::SmartMutex<true>> reproducerMutex;
588+
static llvm::ManagedStatic<
589+
llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
590+
reproducerSet;
591+
};
592+
} // end anonymous namespace
593+
594+
llvm::ManagedStatic<llvm::sys::SmartMutex<true>>
595+
RecoveryReproducerContext::reproducerMutex;
596+
llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
597+
RecoveryReproducerContext::reproducerSet;
598+
599+
RecoveryReproducerContext::RecoveryReproducerContext(
600+
MutableArrayRef<std::unique_ptr<Pass>> passes, ModuleOp module,
601+
StringRef filename, bool disableThreads, bool verifyPasses)
602+
: module(module.clone()), filename(filename),
603+
disableThreads(disableThreads), verifyPasses(verifyPasses) {
604+
// Grab the textual pipeline being executed..
605+
{
606+
llvm::raw_string_ostream pipelineOS(pipeline);
607+
::printAsTextualPipeline(passes, pipelineOS);
608+
}
609+
610+
// Make sure that the handler is registered, and update the current context.
611+
llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex);
612+
registerSignalHandler();
613+
reproducerSet->insert(this);
614+
}
615+
616+
RecoveryReproducerContext::~RecoveryReproducerContext() {
617+
llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex);
618+
reproducerSet->remove(this);
619+
}
620+
621+
LogicalResult RecoveryReproducerContext::generate(std::string &error) {
622+
std::unique_ptr<llvm::ToolOutputFile> outputFile =
623+
mlir::openOutputFile(filename, &error);
624+
if (!outputFile)
625+
return failure();
626+
auto &outputOS = outputFile->os();
627+
628+
// Output the current pass manager configuration.
629+
outputOS << "// configuration: -pass-pipeline='" << pipeline << "'";
630+
if (disableThreads)
631+
outputOS << " -disable-pass-threading";
632+
633+
// TODO: Should this also be configured with a pass manager flag?
634+
outputOS << "\n// note: verifyPasses=" << (verifyPasses ? "true" : "false")
635+
<< "\n";
636+
637+
// Output the .mlir module.
638+
module->print(outputOS);
639+
outputFile->keep();
640+
return success();
641+
}
642+
643+
void RecoveryReproducerContext::crashHandler(void *) {
644+
// Walk the current stack of contexts and generate a reproducer for each one.
645+
// We can't know for certain which one was the cause, so we need to generate
646+
// a reproducer for all of them.
647+
std::string ignored;
648+
for (RecoveryReproducerContext *context : *reproducerSet)
649+
context->generate(ignored);
650+
}
651+
652+
void RecoveryReproducerContext::registerSignalHandler() {
653+
// Ensure that the handler is only registered once.
654+
static bool registered =
655+
(llvm::sys::AddSignalHandler(crashHandler, nullptr), false);
656+
(void)registered;
657+
}
658+
548659
/// Run the pass manager with crash recover enabled.
549660
LogicalResult PassManager::runWithCrashRecovery(ModuleOp module,
550661
AnalysisManager am) {
@@ -572,21 +683,11 @@ LogicalResult PassManager::runWithCrashRecovery(ModuleOp module,
572683
LogicalResult
573684
PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
574685
ModuleOp module, AnalysisManager am) {
575-
/// Enable crash recovery.
576-
llvm::CrashRecoveryContext::Enable();
577-
578-
// Grab the textual pipeline being executed first, just in case the passes
579-
// become compromised.
580-
std::string pipeline;
581-
{
582-
llvm::raw_string_ostream pipelineOS(pipeline);
583-
::printAsTextualPipeline(passes, pipelineOS);
584-
}
585-
586-
// Clone the initial module before running it through the pass pipeline.
587-
OwningModuleRef reproducerModule = module.clone();
686+
RecoveryReproducerContext context(passes, module, *crashReproducerFileName,
687+
impl->disableThreads, impl->verifyPasses);
588688

589689
// Safely invoke the passes within a recovery context.
690+
llvm::CrashRecoveryContext::Enable();
590691
LogicalResult passManagerResult = failure();
591692
llvm::CrashRecoveryContext recoveryContext;
592693
recoveryContext.RunSafelyOnThread([&] {
@@ -600,26 +701,9 @@ PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
600701
return success();
601702

602703
std::string error;
603-
std::unique_ptr<llvm::ToolOutputFile> outputFile =
604-
mlir::openOutputFile(*crashReproducerFileName, &error);
605-
if (!outputFile)
704+
if (failed(context.generate(error)))
606705
return module.emitError("<MLIR-PassManager-Crash-Reproducer>: ") << error;
607-
auto &outputOS = outputFile->os();
608-
609-
// Output the current pass manager configuration.
610-
outputOS << "// configuration: -pass-pipeline='" << pipeline << "'";
611-
if (impl->disableThreads)
612-
outputOS << " -disable-pass-threading";
613-
614-
// TODO: Should this also be configured with a pass manager flag?
615-
outputOS << "\n// note: verifyPasses="
616-
<< (impl->verifyPasses ? "true" : "false") << "\n";
617-
618-
// Output the .mlir module.
619-
reproducerModule->print(outputOS);
620-
outputFile->keep();
621-
622-
return reproducerModule->emitError()
706+
return module.emitError()
623707
<< "A failure has been detected while processing the MLIR module, a "
624708
"reproducer has been generated in '"
625709
<< *crashReproducerFileName << "'";

0 commit comments

Comments
 (0)