17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/ScopeExit.h"
19 #include "llvm/ADT/SetVector.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/CrashRecoveryContext.h"
22 #include "llvm/Support/Mutex.h"
23 #include "llvm/Support/Signals.h"
24 #include "llvm/Support/Threading.h"
25 #include "llvm/Support/ToolOutputFile.h"
46 void generate(std::string &description);
57 static void crashHandler(
void *);
60 static void registerSignalHandler();
63 std::string pipelineElements;
80 static llvm::ManagedStatic<llvm::sys::SmartMutex<true>> reproducerMutex;
81 static llvm::ManagedStatic<
82 llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
88 llvm::ManagedStatic<llvm::sys::SmartMutex<true>>
89 RecoveryReproducerContext::reproducerMutex;
90 llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
91 RecoveryReproducerContext::reproducerSet;
94 std::string passPipelineStr,
Operation *op,
96 : pipelineElements(std::move(passPipelineStr)),
97 preCrashOperation(op->
clone()), streamFactory(streamFactory),
98 disableThreads(!op->
getContext()->isMultithreadingEnabled()),
99 verifyPasses(verifyPasses) {
105 preCrashOperation->
erase();
110 llvm::raw_string_ostream descOS(description);
114 std::unique_ptr<PassManager::ReproducerStream> stream = streamFactory(error);
116 descOS <<
"failed to create output stream: " << error;
119 descOS <<
"reproducer generated at `" << stream->description() <<
"`";
122 pipelineElements +
")")
125 state.attachResourcePrinter(
128 builder.
buildBool(
"disable_threading", disableThreads);
129 builder.
buildBool(
"verify_each", verifyPasses);
133 preCrashOperation->
print(stream->os(), state);
137 llvm::sys::SmartScopedLock<true> lock(*reproducerMutex);
138 reproducerSet->remove(
this);
139 if (reproducerSet->empty())
140 llvm::CrashRecoveryContext::Disable();
144 llvm::sys::SmartScopedLock<true> lock(*reproducerMutex);
145 if (reproducerSet->empty())
146 llvm::CrashRecoveryContext::Enable();
147 registerSignalHandler();
148 reproducerSet->insert(
this);
151 void RecoveryReproducerContext::crashHandler(
void *) {
156 std::string description;
157 context->generate(description);
160 emitError(context->preCrashOperation->getLoc())
161 <<
"A signal was caught while processing the MLIR module:"
162 << description <<
"; marking pass as failed";
166 void RecoveryReproducerContext::registerSignalHandler() {
168 static bool registered =
169 (llvm::sys::AddSignalHandler(crashHandler,
nullptr),
false);
202 :
impl(std::make_unique<
Impl>(streamFactory, localReproducer)) {}
207 bool pmFlagVerifyPasses) {
208 assert((!
impl->localReproducer ||
210 "expected multi-threading to be disabled when generating a local "
213 llvm::CrashRecoveryContext::Enable();
214 impl->pmFlagVerifyPasses = pmFlagVerifyPasses;
218 if (!
impl->localReproducer)
224 std::pair<Pass *, Operation *> passOpPair) {
225 os <<
"`" << passOpPair.first->getName() <<
"` on "
226 <<
"'" << passOpPair.second->getName() <<
"' operation";
227 if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(passOpPair.second))
228 os <<
": @" << symbol.getName();
234 if (
impl->activeContexts.empty())
239 return impl->activeContexts.clear();
242 <<
"Failures have been detected while "
243 "processing an MLIR pass pipeline";
247 if (!
impl->localReproducer) {
248 assert(
impl->activeContexts.size() == 1 &&
"expected one active context");
251 std::string description;
252 impl->activeContexts.front()->generate(description);
255 Diagnostic ¬e =
diag.attachNote() <<
"Pipeline failed while executing [";
256 llvm::interleaveComma(
impl->runningPasses, note,
257 [&](
const std::pair<Pass *, Operation *> &value) {
258 formatPassOpReproducerMessage(note, value);
260 note <<
"]: " << description;
261 impl->runningPasses.clear();
262 impl->activeContexts.clear();
269 assert(
impl->activeContexts.size() ==
impl->runningPasses.size() &&
270 "expected running passes to match active contexts");
274 std::string description;
275 reproducerContext.
generate(description);
278 Diagnostic ¬e =
diag.attachNote() <<
"Pipeline failed while executing ";
280 note <<
": " << description;
282 impl->activeContexts.clear();
283 impl->runningPasses.clear();
290 impl->runningPasses.insert(std::make_pair(pass, op));
291 if (!
impl->localReproducer)
296 if (!
impl->activeContexts.empty())
297 impl->activeContexts.back()->disable();
302 scopes.push_back(op->
getName());
309 llvm::raw_string_ostream passOS(passStr);
311 passOS << scope <<
"(";
313 for (
unsigned i = 0, e = scopes.size(); i < e; ++i)
316 impl->activeContexts.push_back(std::make_unique<RecoveryReproducerContext>(
317 passOS.str(), op,
impl->streamFactory,
impl->pmFlagVerifyPasses));
322 llvm::raw_string_ostream passOS(passStr);
323 llvm::interleaveComma(
326 impl->activeContexts.push_back(std::make_unique<RecoveryReproducerContext>(
327 passOS.str(), op,
impl->streamFactory,
impl->pmFlagVerifyPasses));
333 impl->runningPasses.remove(std::make_pair(pass, op));
334 if (
impl->localReproducer) {
335 impl->activeContexts.pop_back();
339 if (!
impl->activeContexts.empty())
340 impl->activeContexts.back()->enable();
352 ~CrashReproducerInstrumentation()
override =
default;
355 if (!isa<OpToOpPassAdaptor>(pass))
356 generator.prepareReproducerFor(pass, op);
360 if (!isa<OpToOpPassAdaptor>(pass))
361 generator.removeLastReproducerFor(pass, op);
364 void runAfterPassFailed(
Pass *pass,
Operation *op)
override {
369 alreadyFailed =
true;
376 bool alreadyFailed =
false;
388 FileReproducerStream(std::unique_ptr<llvm::ToolOutputFile> outputFile)
389 : outputFile(std::move(outputFile)) {}
390 ~FileReproducerStream()
override { outputFile->keep(); }
393 StringRef description()
override {
return outputFile->getFilename(); }
396 raw_ostream &os()
override {
return outputFile->os(); }
400 std::unique_ptr<llvm::ToolOutputFile> outputFile =
nullptr;
410 crashReproGenerator->initialize(getPasses(), op, verifyPasses);
414 llvm::CrashRecoveryContext recoveryContext;
415 recoveryContext.RunSafelyOnThread(
416 [&] { passManagerResult = runPasses(op, am); });
417 crashReproGenerator->finalize(op, passManagerResult);
418 return passManagerResult;
422 bool genLocalReproducer) {
425 std::string filename = outputFile.str();
426 enableCrashReproducerGeneration(
427 [filename](std::string &error) -> std::unique_ptr<ReproducerStream> {
428 std::unique_ptr<llvm::ToolOutputFile> outputFile =
431 error =
"Failed to create reproducer stream: " + error;
434 return std::make_unique<FileReproducerStream>(std::move(outputFile));
441 assert(!crashReproGenerator &&
442 "crash reproducer has already been initialized");
443 if (genLocalReproducer &&
getContext()->isMultithreadingEnabled())
444 llvm::report_fatal_error(
445 "Local crash reproduction can't be setup on a "
446 "pass-manager without disabling multi-threading first.");
448 crashReproGenerator = std::make_unique<PassCrashReproducerGenerator>(
449 factory, genLocalReproducer);
451 std::make_unique<CrashReproducerInstrumentation>(*crashReproGenerator));
460 if (entry.getKey() ==
"pipeline") {
463 this->pipeline = std::move(*value);
466 if (entry.getKey() ==
"disable_threading") {
469 this->disableThreading = *value;
472 if (entry.getKey() ==
"verify_each") {
475 this->verifyEach = *value;
478 return entry.emitError() <<
"unknown 'mlir_reproducer' resource key '"
479 << entry.getKey() <<
"'";
485 if (pipeline.has_value()) {
492 if (disableThreading.has_value())
495 if (verifyEach.has_value())
static MLIRContext * getContext(OpFoldResult val)
static const mlir::GenInfo * generator
static std::string diag(const llvm::Value &value)
static void formatPassOpReproducerMessage(Diagnostic &os, std::pair< Pass *, Operation * > passOpPair)
This class represents an analysis manager for a particular operation instance.
This class represents a single parsed resource entry.
This class is used to build resource entries for use by the printer.
virtual void buildString(StringRef key, StringRef data)=0
Build a resource entry represented by the given human-readable string value.
virtual void buildBool(StringRef key, bool data)=0
Build a resource entry represented by the given bool.
This class provides management for the lifetime of the state used when printing the IR.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
This class provides support for representing a failure result, or a valid value of type T.
This class represents a diagnostic that is inflight and set to be reported.
void disableMultithreading(bool disable=true)
Set the flag specifying if multi-threading is disabled by the context.
bool isMultithreadingEnabled()
Return true if multi-threading is enabled by the context.
This class represents a pass manager that runs passes on either a specific operation type,...
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
void print(raw_ostream &os, const OpPrintingFlags &flags=std::nullopt)
MLIRContext * getContext()
Return the context this operation is associated with.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
OperationName getName()
The name of an operation is the key identifier for it.
void erase()
Remove this operation from its parent block and delete it.
This class represents a configuration for the MLIR assembly parser.
void attachResourceParser(std::unique_ptr< AsmResourceParser > parser)
Attach the given resource parser.
PassInstrumentation provides several entry points into the pass manager infrastructure.
The main pass manager and pipeline builder.
MLIRContext * getContext() const
Return an instance of the context.
std::function< std::unique_ptr< ReproducerStream >(std::string &error)> ReproducerStreamFactory
Method type for constructing ReproducerStream.
void enableCrashReproducerGeneration(StringRef outputFile, bool genLocalReproducer=false)
Enable support for the pass manager to generate a reproducer on the event of a crash or a pass failur...
void enableVerifier(bool enabled=true)
Runs the verifier after each individual pass.
The abstract base pass class.
void printAsTextualPipeline(raw_ostream &os)
Prints out the pass in the textual representation of pipelines.
void initialize(iterator_range< PassManager::pass_iterator > passes, Operation *op, bool pmFlagVerifyPasses)
Initialize the generator in preparation for reproducer generation.
void removeLastReproducerFor(Pass *pass, Operation *op)
Remove the last recorded reproducer anchored at the given pass and operation.
void finalize(Operation *rootOp, LogicalResult executionResult)
Finalize the current run of the generator, generating any necessary reproducers if the provided execu...
PassCrashReproducerGenerator(PassManager::ReproducerStreamFactory &streamFactory, bool localReproducer)
void prepareReproducerFor(Pass *pass, Operation *op)
Prepare a new reproducer for the given pass, operating on op.
~PassCrashReproducerGenerator()
Detect if any of the given parameter types has a sub-element handler.
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
std::unique_ptr< llvm::ToolOutputFile > openOutputFile(llvm::StringRef outputFilename, std::string *errorMessage=nullptr)
Open the file specified by its name for writing.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
LogicalResult parsePassPipeline(StringRef pipeline, OpPassManager &pm, raw_ostream &errorStream=llvm::errs())
Parse the textual representation of a pass pipeline, adding the result to 'pm' on success.
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
bool pmFlagVerifyPasses
Various pass manager flags that get emitted when generating a reproducer.
Impl(PassManager::ReproducerStreamFactory &streamFactory, bool localReproducer)
SetVector< std::pair< Pass *, Operation * > > runningPasses
The set of all currently running passes.
bool localReproducer
Flag indicating if reproducer generation should be localized to the failing pass.
PassManager::ReproducerStreamFactory streamFactory
The factory to use when generating a crash reproducer.
SmallVector< std::unique_ptr< RecoveryReproducerContext > > activeContexts
A record of all of the currently active reproducer contexts.
This class represents an efficient way to signal success or failure.
Streams on which to output crash reproducer.
void attachResourceParser(ParserConfig &config)
Attach an assembly resource parser to 'config' that collects the MLIR reproducer configuration into t...
LogicalResult apply(PassManager &pm) const
Apply the reproducer options to 'pm' and its context.
This class contains all of the context for generating a recovery reproducer.
void disable()
Disable this reproducer context.
~RecoveryReproducerContext()
void generate(std::string &description)
Generate a reproducer with the current context.
RecoveryReproducerContext(std::string passPipelineStr, Operation *op, PassManager::ReproducerStreamFactory &streamFactory, bool verifyPasses)
void enable()
Enable a previously disabled reproducer context.