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();
111 const std::string &pipelineElements,
112 bool disableThreads,
bool verifyPasses) {
113 llvm::raw_string_ostream descOS(description);
117 std::unique_ptr<ReproducerStream> stream = factory(error);
119 descOS <<
"failed to create output stream: " << error;
122 descOS <<
"reproducer generated at `" << stream->description() <<
"`";
124 std::string pipeline =
127 state.attachResourcePrinter(
130 builder.
buildBool(
"disable_threading", disableThreads);
131 builder.
buildBool(
"verify_each", verifyPasses);
135 op->
print(stream->os(), state);
140 pipelineElements, disableThreads, verifyPasses);
144 llvm::sys::SmartScopedLock<true> lock(*reproducerMutex);
145 reproducerSet->remove(
this);
146 if (reproducerSet->empty())
147 llvm::CrashRecoveryContext::Disable();
151 llvm::sys::SmartScopedLock<true> lock(*reproducerMutex);
152 if (reproducerSet->empty())
153 llvm::CrashRecoveryContext::Enable();
154 registerSignalHandler();
155 reproducerSet->insert(
this);
158 void RecoveryReproducerContext::crashHandler(
void *) {
163 std::string description;
164 context->generate(description);
167 emitError(context->preCrashOperation->getLoc())
168 <<
"A signal was caught while processing the MLIR module:"
169 << description <<
"; marking pass as failed";
173 void RecoveryReproducerContext::registerSignalHandler() {
175 static bool registered =
176 (llvm::sys::AddSignalHandler(crashHandler,
nullptr),
false);
208 :
impl(std::make_unique<
Impl>(streamFactory, localReproducer)) {}
213 bool pmFlagVerifyPasses) {
214 assert((!
impl->localReproducer ||
216 "expected multi-threading to be disabled when generating a local "
219 llvm::CrashRecoveryContext::Enable();
220 impl->pmFlagVerifyPasses = pmFlagVerifyPasses;
224 if (!
impl->localReproducer)
230 std::pair<Pass *, Operation *> passOpPair) {
231 os <<
"`" << passOpPair.first->getName() <<
"` on "
232 <<
"'" << passOpPair.second->getName() <<
"' operation";
233 if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(passOpPair.second))
234 os <<
": @" << symbol.getName();
240 if (
impl->activeContexts.empty())
245 return impl->activeContexts.clear();
248 <<
"Failures have been detected while "
249 "processing an MLIR pass pipeline";
253 if (!
impl->localReproducer) {
254 assert(
impl->activeContexts.size() == 1 &&
"expected one active context");
257 std::string description;
258 impl->activeContexts.front()->generate(description);
261 Diagnostic ¬e =
diag.attachNote() <<
"Pipeline failed while executing [";
262 llvm::interleaveComma(
impl->runningPasses, note,
263 [&](
const std::pair<Pass *, Operation *> &value) {
264 formatPassOpReproducerMessage(note, value);
266 note <<
"]: " << description;
267 impl->runningPasses.clear();
268 impl->activeContexts.clear();
275 assert(
impl->activeContexts.size() ==
impl->runningPasses.size() &&
276 "expected running passes to match active contexts");
280 std::string description;
281 reproducerContext.
generate(description);
284 Diagnostic ¬e =
diag.attachNote() <<
"Pipeline failed while executing ";
286 note <<
": " << description;
288 impl->activeContexts.clear();
289 impl->runningPasses.clear();
296 impl->runningPasses.insert(std::make_pair(pass, op));
297 if (!
impl->localReproducer)
302 if (!
impl->activeContexts.empty())
303 impl->activeContexts.back()->disable();
308 scopes.push_back(op->
getName());
315 llvm::raw_string_ostream passOS(passStr);
317 passOS << scope <<
"(";
319 for (
unsigned i = 0, e = scopes.size(); i < e; ++i)
322 impl->activeContexts.push_back(std::make_unique<RecoveryReproducerContext>(
323 passOS.str(), op,
impl->streamFactory,
impl->pmFlagVerifyPasses));
328 llvm::raw_string_ostream passOS(passStr);
329 llvm::interleaveComma(
332 impl->activeContexts.push_back(std::make_unique<RecoveryReproducerContext>(
333 passOS.str(), op,
impl->streamFactory,
impl->pmFlagVerifyPasses));
339 impl->runningPasses.remove(std::make_pair(pass, op));
340 if (
impl->localReproducer) {
341 impl->activeContexts.pop_back();
345 if (!
impl->activeContexts.empty())
346 impl->activeContexts.back()->enable();
358 ~CrashReproducerInstrumentation()
override =
default;
361 if (!isa<OpToOpPassAdaptor>(pass))
362 generator.prepareReproducerFor(pass, op);
366 if (!isa<OpToOpPassAdaptor>(pass))
367 generator.removeLastReproducerFor(pass, op);
370 void runAfterPassFailed(
Pass *pass,
Operation *op)
override {
375 alreadyFailed =
true;
382 bool alreadyFailed =
false;
394 FileReproducerStream(std::unique_ptr<llvm::ToolOutputFile> outputFile)
395 : outputFile(std::move(outputFile)) {}
396 ~FileReproducerStream()
override { outputFile->keep(); }
399 StringRef description()
override {
return outputFile->getFilename(); }
402 raw_ostream &os()
override {
return outputFile->os(); }
406 std::unique_ptr<llvm::ToolOutputFile> outputFile =
nullptr;
416 crashReproGenerator->initialize(getPasses(), op, verifyPasses);
420 llvm::CrashRecoveryContext recoveryContext;
421 recoveryContext.RunSafelyOnThread(
422 [&] { passManagerResult = runPasses(op, am); });
423 crashReproGenerator->finalize(op, passManagerResult);
424 return passManagerResult;
431 std::string filename = outputFile.str();
432 return [filename](std::string &error) -> std::unique_ptr<ReproducerStream> {
433 std::unique_ptr<llvm::ToolOutputFile> outputFile =
436 error =
"Failed to create reproducer stream: " + error;
439 return std::make_unique<FileReproducerStream>(std::move(outputFile));
444 raw_ostream &os, StringRef anchorName,
448 StringRef anchorName,
450 Operation *op, StringRef outputFile,
bool disableThreads,
453 std::string description;
454 std::string pipelineStr;
455 llvm::raw_string_ostream passOS(pipelineStr);
458 pipelineStr, disableThreads, verifyPasses);
463 bool genLocalReproducer) {
470 assert(!crashReproGenerator &&
471 "crash reproducer has already been initialized");
472 if (genLocalReproducer &&
getContext()->isMultithreadingEnabled())
473 llvm::report_fatal_error(
474 "Local crash reproduction can't be setup on a "
475 "pass-manager without disabling multi-threading first.");
477 crashReproGenerator = std::make_unique<PassCrashReproducerGenerator>(
478 factory, genLocalReproducer);
480 std::make_unique<CrashReproducerInstrumentation>(*crashReproGenerator));
489 if (entry.getKey() ==
"pipeline") {
492 this->pipeline = std::move(*value);
495 if (entry.getKey() ==
"disable_threading") {
498 this->disableThreading = *value;
501 if (entry.getKey() ==
"verify_each") {
504 this->verifyEach = *value;
507 return entry.emitError() <<
"unknown 'mlir_reproducer' resource key '"
508 << entry.getKey() <<
"'";
514 if (pipeline.has_value()) {
521 if (disableThreading.has_value())
524 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 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.
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)
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.
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.
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.
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.
This class represents an efficient way to signal success or failure.
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.