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/ManagedStatic.h"
23 #include "llvm/Support/Mutex.h"
24 #include "llvm/Support/Signals.h"
25 #include "llvm/Support/Threading.h"
26 #include "llvm/Support/ToolOutputFile.h"
47 void generate(std::string &description);
58 static void crashHandler(
void *);
61 static void registerSignalHandler();
64 std::string pipelineElements;
81 static llvm::ManagedStatic<llvm::sys::SmartMutex<true>> reproducerMutex;
82 static llvm::ManagedStatic<
83 llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
89 llvm::ManagedStatic<llvm::sys::SmartMutex<true>>
90 RecoveryReproducerContext::reproducerMutex;
91 llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
92 RecoveryReproducerContext::reproducerSet;
95 std::string passPipelineStr,
Operation *op,
97 : pipelineElements(std::move(passPipelineStr)),
98 preCrashOperation(op->
clone()), streamFactory(streamFactory),
99 disableThreads(!op->
getContext()->isMultithreadingEnabled()),
100 verifyPasses(verifyPasses) {
106 preCrashOperation->
erase();
112 const std::string &pipelineElements,
113 bool disableThreads,
bool verifyPasses) {
114 llvm::raw_string_ostream descOS(description);
118 std::unique_ptr<ReproducerStream> stream = factory(error);
120 descOS <<
"failed to create output stream: " << error;
123 descOS <<
"reproducer generated at `" << stream->description() <<
"`";
125 std::string pipeline =
128 state.attachResourcePrinter(
131 builder.
buildBool(
"disable_threading", disableThreads);
132 builder.
buildBool(
"verify_each", verifyPasses);
136 op->
print(stream->os(), state);
141 pipelineElements, disableThreads, verifyPasses);
145 llvm::sys::SmartScopedLock<true> lock(*reproducerMutex);
146 reproducerSet->remove(
this);
147 if (reproducerSet->empty())
148 llvm::CrashRecoveryContext::Disable();
152 llvm::sys::SmartScopedLock<true> lock(*reproducerMutex);
153 if (reproducerSet->empty())
154 llvm::CrashRecoveryContext::Enable();
155 registerSignalHandler();
156 reproducerSet->insert(
this);
159 void RecoveryReproducerContext::crashHandler(
void *) {
164 std::string description;
165 context->generate(description);
168 emitError(context->preCrashOperation->getLoc())
169 <<
"A signal was caught while processing the MLIR module:"
170 << description <<
"; marking pass as failed";
174 void RecoveryReproducerContext::registerSignalHandler() {
176 static bool registered =
177 (llvm::sys::AddSignalHandler(crashHandler,
nullptr),
false);
209 :
impl(std::make_unique<
Impl>(streamFactory, localReproducer)) {}
214 bool pmFlagVerifyPasses) {
215 assert((!
impl->localReproducer ||
217 "expected multi-threading to be disabled when generating a local "
220 llvm::CrashRecoveryContext::Enable();
221 impl->pmFlagVerifyPasses = pmFlagVerifyPasses;
225 if (!
impl->localReproducer)
231 std::pair<Pass *, Operation *> passOpPair) {
232 os <<
"`" << passOpPair.first->getName() <<
"` on "
233 <<
"'" << passOpPair.second->getName() <<
"' operation";
234 if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(passOpPair.second))
235 os <<
": @" << symbol.getName();
239 LogicalResult executionResult) {
241 if (
impl->activeContexts.empty())
245 if (succeeded(executionResult))
246 return impl->activeContexts.clear();
249 <<
"Failures have been detected while "
250 "processing an MLIR pass pipeline";
254 if (!
impl->localReproducer) {
255 assert(
impl->activeContexts.size() == 1 &&
"expected one active context");
258 std::string description;
259 impl->activeContexts.front()->generate(description);
262 Diagnostic ¬e =
diag.attachNote() <<
"Pipeline failed while executing [";
263 llvm::interleaveComma(
impl->runningPasses, note,
264 [&](
const std::pair<Pass *, Operation *> &value) {
265 formatPassOpReproducerMessage(note, value);
267 note <<
"]: " << description;
268 impl->runningPasses.clear();
269 impl->activeContexts.clear();
276 assert(
impl->activeContexts.size() ==
impl->runningPasses.size() &&
277 "expected running passes to match active contexts");
281 std::string description;
282 reproducerContext.
generate(description);
285 Diagnostic ¬e =
diag.attachNote() <<
"Pipeline failed while executing ";
287 note <<
": " << description;
289 impl->activeContexts.clear();
290 impl->runningPasses.clear();
297 impl->runningPasses.insert(std::make_pair(pass, op));
298 if (!
impl->localReproducer)
303 if (!
impl->activeContexts.empty())
304 impl->activeContexts.back()->disable();
309 scopes.push_back(op->
getName());
316 llvm::raw_string_ostream passOS(passStr);
318 passOS << scope <<
"(";
320 for (
unsigned i = 0, e = scopes.size(); i < e; ++i)
323 impl->activeContexts.push_back(std::make_unique<RecoveryReproducerContext>(
324 passStr, op,
impl->streamFactory,
impl->pmFlagVerifyPasses));
329 llvm::raw_string_ostream passOS(passStr);
330 llvm::interleaveComma(
333 impl->activeContexts.push_back(std::make_unique<RecoveryReproducerContext>(
334 passStr, op,
impl->streamFactory,
impl->pmFlagVerifyPasses));
340 impl->runningPasses.remove(std::make_pair(pass, op));
341 if (
impl->localReproducer) {
342 impl->activeContexts.pop_back();
346 if (!
impl->activeContexts.empty())
347 impl->activeContexts.back()->enable();
359 ~CrashReproducerInstrumentation()
override =
default;
362 if (!isa<OpToOpPassAdaptor>(pass))
363 generator.prepareReproducerFor(pass, op);
367 if (!isa<OpToOpPassAdaptor>(pass))
368 generator.removeLastReproducerFor(pass, op);
371 void runAfterPassFailed(
Pass *pass,
Operation *op)
override {
376 alreadyFailed =
true;
383 bool alreadyFailed =
false;
395 FileReproducerStream(std::unique_ptr<llvm::ToolOutputFile> outputFile)
396 : outputFile(std::move(outputFile)) {}
397 ~FileReproducerStream()
override { outputFile->keep(); }
400 StringRef description()
override {
return outputFile->getFilename(); }
403 raw_ostream &os()
override {
return outputFile->os(); }
407 std::unique_ptr<llvm::ToolOutputFile> outputFile =
nullptr;
415 LogicalResult PassManager::runWithCrashRecovery(
Operation *op,
417 crashReproGenerator->initialize(getPasses(), op, verifyPasses);
420 LogicalResult passManagerResult = failure();
421 llvm::CrashRecoveryContext recoveryContext;
422 recoveryContext.RunSafelyOnThread(
423 [&] { passManagerResult = runPasses(op, am); });
424 crashReproGenerator->finalize(op, passManagerResult);
425 return passManagerResult;
432 std::string filename = outputFile.str();
433 return [filename](std::string &error) -> std::unique_ptr<ReproducerStream> {
434 std::unique_ptr<llvm::ToolOutputFile> outputFile =
437 error =
"Failed to create reproducer stream: " + error;
440 return std::make_unique<FileReproducerStream>(std::move(outputFile));
445 raw_ostream &os, StringRef anchorName,
449 StringRef anchorName,
451 Operation *op, StringRef outputFile,
bool disableThreads,
454 std::string description;
455 std::string pipelineStr;
456 llvm::raw_string_ostream passOS(pipelineStr);
459 pipelineStr, disableThreads, verifyPasses);
464 bool genLocalReproducer) {
471 assert(!crashReproGenerator &&
472 "crash reproducer has already been initialized");
473 if (genLocalReproducer &&
getContext()->isMultithreadingEnabled())
474 llvm::report_fatal_error(
475 "Local crash reproduction can't be setup on a "
476 "pass-manager without disabling multi-threading first.");
478 crashReproGenerator = std::make_unique<PassCrashReproducerGenerator>(
479 factory, genLocalReproducer);
481 std::make_unique<CrashReproducerInstrumentation>(*crashReproGenerator));
490 if (entry.getKey() ==
"pipeline") {
491 FailureOr<std::string> value = entry.parseAsString();
492 if (succeeded(value))
493 this->pipeline = std::move(*value);
496 if (entry.getKey() ==
"disable_threading") {
497 FailureOr<bool> value = entry.parseAsBool();
498 if (succeeded(value))
499 this->disableThreading = *value;
502 if (entry.getKey() ==
"verify_each") {
503 FailureOr<bool> value = entry.parseAsBool();
504 if (succeeded(value))
505 this->verifyEach = *value;
508 return entry.emitError() <<
"unknown 'mlir_reproducer' resource key '"
509 << entry.getKey() <<
"'";
515 if (pipeline.has_value()) {
522 if (disableThreading.has_value())
525 if (verifyEach.has_value())
static MLIRContext * getContext(OpFoldResult val)
static const mlir::GenInfo * generator
static std::string diag(const llvm::Value &value)
static void appendReproducer(std::string &description, Operation *op, const ReproducerStreamFactory &factory, const std::string &pipelineElements, bool disableThreads, bool verifyPasses)
static void formatPassOpReproducerMessage(Diagnostic &os, std::pair< Pass *, Operation * > passOpPair)
static ReproducerStreamFactory makeReproducerStreamFactory(StringRef outputFile)
void printAsTextualPipeline(raw_ostream &os, StringRef anchorName, const llvm::iterator_range< OpPassManager::pass_iterator > &passes)
Prints out the passes of the pass manager as the textual representation of pipelines.
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 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.
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...
void prepareReproducerFor(Pass *pass, Operation *op)
Prepare a new reproducer for the given pass, operating on op.
~PassCrashReproducerGenerator()
PassCrashReproducerGenerator(ReproducerStreamFactory &streamFactory, bool localReproducer)
Include the generated interface declarations.
std::unique_ptr< llvm::ToolOutputFile > openOutputFile(llvm::StringRef outputFilename, std::string *errorMessage=nullptr)
Open the file specified by its name for writing.
std::string makeReproducer(StringRef anchorName, const llvm::iterator_range< OpPassManager::pass_iterator > &passes, Operation *op, StringRef outputFile, bool disableThreads=false, bool verifyPasses=false)
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
std::function< std::unique_ptr< ReproducerStream >(std::string &error)> ReproducerStreamFactory
Method type for constructing ReproducerStream.
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 pmFlagVerifyPasses
Various pass manager flags that get emitted when generating a reproducer.
ReproducerStreamFactory streamFactory
The factory to use when generating a crash reproducer.
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.
Impl(ReproducerStreamFactory &streamFactory, bool localReproducer)
SmallVector< std::unique_ptr< RecoveryReproducerContext > > activeContexts
A record of all of the currently active reproducer contexts.
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.
Streams on which to output crash reproducer.
This class contains all of the context for generating a recovery reproducer.
void disable()
Disable this reproducer context.
~RecoveryReproducerContext()
RecoveryReproducerContext(std::string passPipelineStr, Operation *op, ReproducerStreamFactory &streamFactory, bool verifyPasses)
void generate(std::string &description)
Generate a reproducer with the current context.
void enable()
Enable a previously disabled reproducer context.