18
18
#include " mlir/IR/Module.h"
19
19
#include " mlir/Support/FileUtilities.h"
20
20
#include " llvm/ADT/STLExtras.h"
21
+ #include " llvm/ADT/SetVector.h"
21
22
#include " llvm/Support/CommandLine.h"
22
23
#include " llvm/Support/CrashRecoveryContext.h"
23
24
#include " llvm/Support/Mutex.h"
24
25
#include " llvm/Support/Parallel.h"
26
+ #include " llvm/Support/Signals.h"
25
27
#include " llvm/Support/Threading.h"
26
28
#include " llvm/Support/ToolOutputFile.h"
27
29
@@ -545,6 +547,115 @@ void OpToOpPassAdaptor::runOnOperationAsyncImpl() {
545
547
// PassCrashReproducer
546
548
// ===----------------------------------------------------------------------===//
547
549
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
+
548
659
// / Run the pass manager with crash recover enabled.
549
660
LogicalResult PassManager::runWithCrashRecovery (ModuleOp module ,
550
661
AnalysisManager am) {
@@ -572,21 +683,11 @@ LogicalResult PassManager::runWithCrashRecovery(ModuleOp module,
572
683
LogicalResult
573
684
PassManager::runWithCrashRecovery (MutableArrayRef<std::unique_ptr<Pass>> passes,
574
685
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 );
588
688
589
689
// Safely invoke the passes within a recovery context.
690
+ llvm::CrashRecoveryContext::Enable ();
590
691
LogicalResult passManagerResult = failure ();
591
692
llvm::CrashRecoveryContext recoveryContext;
592
693
recoveryContext.RunSafelyOnThread ([&] {
@@ -600,26 +701,9 @@ PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
600
701
return success ();
601
702
602
703
std::string error;
603
- std::unique_ptr<llvm::ToolOutputFile> outputFile =
604
- mlir::openOutputFile (*crashReproducerFileName, &error);
605
- if (!outputFile)
704
+ if (failed (context.generate (error)))
606
705
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 ()
623
707
<< " A failure has been detected while processing the MLIR module, a "
624
708
" reproducer has been generated in '"
625
709
<< *crashReproducerFileName << " '" ;
0 commit comments