Skip to content

Commit aee622f

Browse files
committed
[mlir] Enable passing crash reproducer stream factory method
Add factory to create streams for logging the reproducer. Allows for more general logging (beyond file) and logging the configuration/module separately (logged in order, configuration before module). Also enable querying filename of ToolOutputFile. Differential Revision: https://reviews.llvm.org/D94868
1 parent 551aaa2 commit aee622f

File tree

4 files changed

+115
-31
lines changed

4 files changed

+115
-31
lines changed

llvm/include/llvm/Support/ToolOutputFile.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class ToolOutputFile {
3535
/// The flag which indicates whether we should not delete the file.
3636
bool Keep;
3737

38+
StringRef getFilename() { return Filename; }
3839
explicit CleanupInstaller(StringRef Filename);
3940
~CleanupInstaller();
4041
} Installer;
@@ -57,6 +58,9 @@ class ToolOutputFile {
5758
/// Return the contained raw_fd_ostream.
5859
raw_fd_ostream &os() { return *OS; }
5960

61+
/// Return the filename initialized with.
62+
StringRef getFilename() { return Installer.getFilename(); }
63+
6064
/// Indicate that the tool's job wrt this output file has been successful and
6165
/// the file should not be deleted.
6266
void keep() { Installer.Keep = true; }

mlir/docs/PassManagement.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,8 +1145,7 @@ was executing, as well as the initial IR before any passes were run. A potential
11451145
reproducible may have the form:
11461146

11471147
```mlir
1148-
// configuration: -pass-pipeline='func(cse,canonicalize),inline'
1149-
// note: verifyPasses=false
1148+
// configuration: -pass-pipeline='func(cse,canonicalize),inline' -verify-each
11501149

11511150
module {
11521151
func @foo() {
@@ -1159,6 +1158,10 @@ The configuration dumped can be passed to `mlir-opt` by specifying
11591158
`-run-reproducer` flag. This will result in parsing the first line configuration
11601159
of the reproducer and adding those to the command line options.
11611160

1161+
Beyond specifying a filename, one can also register a `ReproducerStreamFactory`
1162+
function that would be invoked in the case of a crash and the reproducer written
1163+
to its stream.
1164+
11621165
### Local Reproducer Generation
11631166

11641167
An additional flag may be passed to

mlir/include/mlir/Pass/PassManager.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,29 @@ class PassManager : public OpPassManager {
192192
void enableCrashReproducerGeneration(StringRef outputFile,
193193
bool genLocalReproducer = false);
194194

195+
/// Streams on which to output crash reproducer.
196+
struct ReproducerStream {
197+
virtual ~ReproducerStream() = default;
198+
199+
/// Description of the reproducer stream.
200+
virtual StringRef description() = 0;
201+
202+
/// Stream on which to output reproducer.
203+
virtual raw_ostream &os() = 0;
204+
};
205+
206+
/// Method type for constructing ReproducerStream.
207+
using ReproducerStreamFactory =
208+
std::function<std::unique_ptr<ReproducerStream>(std::string &error)>;
209+
210+
/// Enable support for the pass manager to generate a reproducer on the event
211+
/// of a crash or a pass failure. `factory` is used to construct the streams
212+
/// to write the generated reproducer to. If `genLocalReproducer` is true, the
213+
/// pass manager will attempt to generate a local reproducer that contains the
214+
/// smallest pipeline.
215+
void enableCrashReproducerGeneration(ReproducerStreamFactory factory,
216+
bool genLocalReproducer = false);
217+
195218
/// Runs the verifier after each individual pass.
196219
void enableVerifier(bool enabled = true);
197220

@@ -349,8 +372,8 @@ class PassManager : public OpPassManager {
349372
/// A manager for pass instrumentations.
350373
std::unique_ptr<PassInstrumentor> instrumentor;
351374

352-
/// An optional filename to use when generating a crash reproducer if valid.
353-
Optional<std::string> crashReproducerFileName;
375+
/// An optional factory to use when generating a crash reproducer if valid.
376+
ReproducerStreamFactory crashReproducerStreamFactory;
354377

355378
/// Flag that specifies if pass timing is enabled.
356379
bool passTiming : 1;

mlir/lib/Pass/Pass.cpp

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,8 @@ namespace {
645645
/// reproducers when a signal is raised, such as a segfault.
646646
struct RecoveryReproducerContext {
647647
RecoveryReproducerContext(MutableArrayRef<std::unique_ptr<Pass>> passes,
648-
Operation *op, StringRef filename,
648+
Operation *op,
649+
PassManager::ReproducerStreamFactory &crashStream,
649650
bool disableThreads, bool verifyPasses);
650651
~RecoveryReproducerContext();
651652

@@ -665,8 +666,9 @@ struct RecoveryReproducerContext {
665666
/// The MLIR operation representing the IR before the crash.
666667
Operation *preCrashOperation;
667668

668-
/// The filename to use when generating the reproducer.
669-
StringRef filename;
669+
/// The factory for the reproducer output stream to use when generating the
670+
/// reproducer.
671+
PassManager::ReproducerStreamFactory &crashStreamFactory;
670672

671673
/// Various pass manager and context flags.
672674
bool disableThreads;
@@ -681,6 +683,24 @@ struct RecoveryReproducerContext {
681683
llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
682684
reproducerSet;
683685
};
686+
687+
/// Instance of ReproducerStream backed by file.
688+
struct FileReproducerStream : public PassManager::ReproducerStream {
689+
FileReproducerStream(std::unique_ptr<llvm::ToolOutputFile> outputFile)
690+
: outputFile(std::move(outputFile)) {}
691+
~FileReproducerStream() override;
692+
693+
/// Description of the reproducer stream.
694+
StringRef description() override;
695+
696+
/// Stream on which to output reprooducer.
697+
raw_ostream &os() override;
698+
699+
private:
700+
/// ToolOutputFile corresponding to opened `filename`.
701+
std::unique_ptr<llvm::ToolOutputFile> outputFile = nullptr;
702+
};
703+
684704
} // end anonymous namespace
685705

686706
llvm::ManagedStatic<llvm::sys::SmartMutex<true>>
@@ -690,8 +710,9 @@ llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
690710

691711
RecoveryReproducerContext::RecoveryReproducerContext(
692712
MutableArrayRef<std::unique_ptr<Pass>> passes, Operation *op,
693-
StringRef filename, bool disableThreads, bool verifyPasses)
694-
: preCrashOperation(op->clone()), filename(filename),
713+
PassManager::ReproducerStreamFactory &crashStreamFactory,
714+
bool disableThreads, bool verifyPasses)
715+
: preCrashOperation(op->clone()), crashStreamFactory(crashStreamFactory),
695716
disableThreads(disableThreads), verifyPasses(verifyPasses) {
696717
// Grab the textual pipeline being executed..
697718
{
@@ -717,25 +738,42 @@ RecoveryReproducerContext::~RecoveryReproducerContext() {
717738
llvm::CrashRecoveryContext::Disable();
718739
}
719740

741+
/// Description of the reproducer stream.
742+
StringRef FileReproducerStream::description() {
743+
return outputFile->getFilename();
744+
}
745+
746+
/// Stream on which to output reproducer.
747+
raw_ostream &FileReproducerStream::os() { return outputFile->os(); }
748+
749+
FileReproducerStream::~FileReproducerStream() { outputFile->keep(); }
750+
720751
LogicalResult RecoveryReproducerContext::generate(std::string &error) {
721-
std::unique_ptr<llvm::ToolOutputFile> outputFile =
722-
mlir::openOutputFile(filename, &error);
723-
if (!outputFile)
752+
std::unique_ptr<PassManager::ReproducerStream> crashStream =
753+
crashStreamFactory(error);
754+
if (!crashStream)
724755
return failure();
725-
auto &outputOS = outputFile->os();
726756

727757
// Output the current pass manager configuration.
728-
outputOS << "// configuration: -pass-pipeline='" << pipeline << "'";
758+
auto &os = crashStream->os();
759+
os << "// configuration: -pass-pipeline='" << pipeline << "'";
729760
if (disableThreads)
730-
outputOS << " -mlir-disable-threading";
731-
732-
// TODO: Should this also be configured with a pass manager flag?
733-
outputOS << "\n// note: verifyPasses=" << (verifyPasses ? "true" : "false")
734-
<< "\n";
761+
os << " -mlir-disable-threading";
762+
if (verifyPasses)
763+
os << " -verify-each";
764+
os << '\n';
735765

736766
// Output the .mlir module.
737-
preCrashOperation->print(outputOS);
738-
outputFile->keep();
767+
preCrashOperation->print(os);
768+
769+
bool shouldPrintOnOp =
770+
preCrashOperation->getContext()->shouldPrintOpOnDiagnostic();
771+
preCrashOperation->getContext()->printOpOnDiagnostic(false);
772+
preCrashOperation->emitError()
773+
<< "A failure has been detected while processing the MLIR module, a "
774+
"reproducer has been generated in '"
775+
<< crashStream->description() << "'";
776+
preCrashOperation->getContext()->printOpOnDiagnostic(shouldPrintOnOp);
739777
return success();
740778
}
741779

@@ -778,7 +816,7 @@ LogicalResult PassManager::runWithCrashRecovery(Operation *op,
778816
LogicalResult
779817
PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
780818
Operation *op, AnalysisManager am) {
781-
RecoveryReproducerContext context(passes, op, *crashReproducerFileName,
819+
RecoveryReproducerContext context(passes, op, crashReproducerStreamFactory,
782820
!getContext()->isMultithreadingEnabled(),
783821
verifyPasses);
784822

@@ -798,13 +836,6 @@ PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
798836
std::string error;
799837
if (failed(context.generate(error)))
800838
return op->emitError("<MLIR-PassManager-Crash-Reproducer>: ") << error;
801-
bool shouldPrintOnOp = op->getContext()->shouldPrintOpOnDiagnostic();
802-
op->getContext()->printOpOnDiagnostic(false);
803-
op->emitError()
804-
<< "A failure has been detected while processing the MLIR module, a "
805-
"reproducer has been generated in '"
806-
<< *crashReproducerFileName << "'";
807-
op->getContext()->printOpOnDiagnostic(shouldPrintOnOp);
808839
return failure();
809840
}
810841

@@ -848,7 +879,7 @@ LogicalResult PassManager::run(Operation *op) {
848879
// If reproducer generation is enabled, run the pass manager with crash
849880
// handling enabled.
850881
LogicalResult result =
851-
crashReproducerFileName
882+
crashReproducerStreamFactory
852883
? runWithCrashRecovery(op, am)
853884
: OpToOpPassAdaptor::runPipeline(getPasses(), op, am, verifyPasses,
854885
impl->initializationGeneration);
@@ -869,7 +900,30 @@ LogicalResult PassManager::run(Operation *op) {
869900
/// pipeline.
870901
void PassManager::enableCrashReproducerGeneration(StringRef outputFile,
871902
bool genLocalReproducer) {
872-
crashReproducerFileName = std::string(outputFile);
903+
// Capture the filename by value in case outputFile is out of scope when
904+
// invoked.
905+
std::string filename = outputFile.str();
906+
enableCrashReproducerGeneration(
907+
[filename](std::string &error) -> std::unique_ptr<ReproducerStream> {
908+
std::unique_ptr<llvm::ToolOutputFile> outputFile =
909+
mlir::openOutputFile(filename, &error);
910+
if (!outputFile) {
911+
error = "Failed to create reproducer stream: " + error;
912+
return nullptr;
913+
}
914+
return std::make_unique<FileReproducerStream>(std::move(outputFile));
915+
},
916+
genLocalReproducer);
917+
}
918+
919+
/// Enable support for the pass manager to generate a reproducer on the event
920+
/// of a crash or a pass failure. `factory` is used to construct the streams
921+
/// to write the generated reproducer to. If `genLocalReproducer` is true, the
922+
/// pass manager will attempt to generate a local reproducer that contains the
923+
/// smallest pipeline.
924+
void PassManager::enableCrashReproducerGeneration(
925+
ReproducerStreamFactory factory, bool genLocalReproducer) {
926+
crashReproducerStreamFactory = factory;
873927
localReproducer = genLocalReproducer;
874928
}
875929

0 commit comments

Comments
 (0)