[Mlir-commits] [mlir] e62ff42 - [mlir][Pass] Register a signal handler when generating crash reproducers.
River Riddle
llvmlistbot at llvm.org
Wed Apr 29 15:23:27 PDT 2020
Author: River Riddle
Date: 2020-04-29T15:23:10-07:00
New Revision: e62ff42f79e5b59e3d1331ab9988528aae031a48
URL: https://github.com/llvm/llvm-project/commit/e62ff42f79e5b59e3d1331ab9988528aae031a48
DIFF: https://github.com/llvm/llvm-project/commit/e62ff42f79e5b59e3d1331ab9988528aae031a48.diff
LOG: [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
Added:
Modified:
mlir/lib/Pass/Pass.cpp
Removed:
################################################################################
diff --git a/mlir/lib/Pass/Pass.cpp b/mlir/lib/Pass/Pass.cpp
index 8f08b8554e95..1d9a6c48c34c 100644
--- a/mlir/lib/Pass/Pass.cpp
+++ b/mlir/lib/Pass/Pass.cpp
@@ -18,10 +18,12 @@
#include "mlir/IR/Module.h"
#include "mlir/Support/FileUtilities.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -545,6 +547,115 @@ void OpToOpPassAdaptor::runOnOperationAsyncImpl() {
// PassCrashReproducer
//===----------------------------------------------------------------------===//
+namespace {
+/// This class contains all of the context for generating a recovery reproducer.
+/// Each recovery context is registered globally to allow for generating
+/// reproducers when a signal is raised, such as a segfault.
+struct RecoveryReproducerContext {
+ RecoveryReproducerContext(MutableArrayRef<std::unique_ptr<Pass>> passes,
+ ModuleOp module, StringRef filename,
+ bool disableThreads, bool verifyPasses);
+ ~RecoveryReproducerContext();
+
+ /// Generate a reproducer with the current context.
+ LogicalResult generate(std::string &error);
+
+private:
+ /// This function is invoked in the event of a crash.
+ static void crashHandler(void *);
+
+ /// Register a signal handler to run in the event of a crash.
+ static void registerSignalHandler();
+
+ /// The textual description of the currently executing pipeline.
+ std::string pipeline;
+
+ /// The MLIR module representing the IR before the crash.
+ OwningModuleRef module;
+
+ /// The filename to use when generating the reproducer.
+ StringRef filename;
+
+ /// Various pass manager flags.
+ bool disableThreads;
+ bool verifyPasses;
+
+ /// The current set of active reproducer contexts. This is used in the event
+ /// of a crash. This is not thread_local as the pass manager may produce any
+ /// number of child threads. This uses a set to allow for multiple MLIR pass
+ /// managers to be running at the same time.
+ static llvm::ManagedStatic<llvm::sys::SmartMutex<true>> reproducerMutex;
+ static llvm::ManagedStatic<
+ llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
+ reproducerSet;
+};
+} // end anonymous namespace
+
+llvm::ManagedStatic<llvm::sys::SmartMutex<true>>
+ RecoveryReproducerContext::reproducerMutex;
+llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
+ RecoveryReproducerContext::reproducerSet;
+
+RecoveryReproducerContext::RecoveryReproducerContext(
+ MutableArrayRef<std::unique_ptr<Pass>> passes, ModuleOp module,
+ StringRef filename, bool disableThreads, bool verifyPasses)
+ : module(module.clone()), filename(filename),
+ disableThreads(disableThreads), verifyPasses(verifyPasses) {
+ // Grab the textual pipeline being executed..
+ {
+ llvm::raw_string_ostream pipelineOS(pipeline);
+ ::printAsTextualPipeline(passes, pipelineOS);
+ }
+
+ // Make sure that the handler is registered, and update the current context.
+ llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex);
+ registerSignalHandler();
+ reproducerSet->insert(this);
+}
+
+RecoveryReproducerContext::~RecoveryReproducerContext() {
+ llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex);
+ reproducerSet->remove(this);
+}
+
+LogicalResult RecoveryReproducerContext::generate(std::string &error) {
+ std::unique_ptr<llvm::ToolOutputFile> outputFile =
+ mlir::openOutputFile(filename, &error);
+ if (!outputFile)
+ return failure();
+ auto &outputOS = outputFile->os();
+
+ // Output the current pass manager configuration.
+ outputOS << "// configuration: -pass-pipeline='" << pipeline << "'";
+ if (disableThreads)
+ outputOS << " -disable-pass-threading";
+
+ // TODO: Should this also be configured with a pass manager flag?
+ outputOS << "\n// note: verifyPasses=" << (verifyPasses ? "true" : "false")
+ << "\n";
+
+ // Output the .mlir module.
+ module->print(outputOS);
+ outputFile->keep();
+ return success();
+}
+
+void RecoveryReproducerContext::crashHandler(void *) {
+ // Walk the current stack of contexts and generate a reproducer for each one.
+ // We can't know for certain which one was the cause, so we need to generate
+ // a reproducer for all of them.
+ std::string ignored;
+ for (RecoveryReproducerContext *context : *reproducerSet)
+ context->generate(ignored);
+}
+
+void RecoveryReproducerContext::registerSignalHandler() {
+ // Ensure that the handler is only registered once.
+ static bool registered =
+ (llvm::sys::AddSignalHandler(crashHandler, nullptr), false);
+ (void)registered;
+}
+
/// Run the pass manager with crash recover enabled.
LogicalResult PassManager::runWithCrashRecovery(ModuleOp module,
AnalysisManager am) {
@@ -572,21 +683,11 @@ LogicalResult PassManager::runWithCrashRecovery(ModuleOp module,
LogicalResult
PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
ModuleOp module, AnalysisManager am) {
- /// Enable crash recovery.
- llvm::CrashRecoveryContext::Enable();
-
- // Grab the textual pipeline being executed first, just in case the passes
- // become compromised.
- std::string pipeline;
- {
- llvm::raw_string_ostream pipelineOS(pipeline);
- ::printAsTextualPipeline(passes, pipelineOS);
- }
-
- // Clone the initial module before running it through the pass pipeline.
- OwningModuleRef reproducerModule = module.clone();
+ RecoveryReproducerContext context(passes, module, *crashReproducerFileName,
+ impl->disableThreads, impl->verifyPasses);
// Safely invoke the passes within a recovery context.
+ llvm::CrashRecoveryContext::Enable();
LogicalResult passManagerResult = failure();
llvm::CrashRecoveryContext recoveryContext;
recoveryContext.RunSafelyOnThread([&] {
@@ -600,26 +701,9 @@ PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
return success();
std::string error;
- std::unique_ptr<llvm::ToolOutputFile> outputFile =
- mlir::openOutputFile(*crashReproducerFileName, &error);
- if (!outputFile)
+ if (failed(context.generate(error)))
return module.emitError("<MLIR-PassManager-Crash-Reproducer>: ") << error;
- auto &outputOS = outputFile->os();
-
- // Output the current pass manager configuration.
- outputOS << "// configuration: -pass-pipeline='" << pipeline << "'";
- if (impl->disableThreads)
- outputOS << " -disable-pass-threading";
-
- // TODO: Should this also be configured with a pass manager flag?
- outputOS << "\n// note: verifyPasses="
- << (impl->verifyPasses ? "true" : "false") << "\n";
-
- // Output the .mlir module.
- reproducerModule->print(outputOS);
- outputFile->keep();
-
- return reproducerModule->emitError()
+ return module.emitError()
<< "A failure has been detected while processing the MLIR module, a "
"reproducer has been generated in '"
<< *crashReproducerFileName << "'";
More information about the Mlir-commits
mailing list