Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {}
Reproducer::~Reproducer() = default;
-ReproducerGenerate::ReproducerGenerate(std::error_code &EC)
- : Root(createReproducerDir(EC)) {
+ReproducerGenerate::ReproducerGenerate(std::error_code &EC, int Argc,
+ char **Argv, bool GenerateOnExit)
+ : Root(createReproducerDir(EC)), GenerateOnExit(GenerateOnExit) {
+ for (int I = 0; I < Argc; ++I)
+ Args.push_back(Argv[I]);
if (!Root.empty())
FC = std::make_shared<FileCollector>(Root, Root);
VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC);
}
ReproducerGenerate::~ReproducerGenerate() {
+ if (GenerateOnExit && !Generated)
+ generate();
+}
+
+void ReproducerGenerate::generate() {
if (!FC)
return;
+ Generated = true;
FC->copyFiles(false);
SmallString<128> Mapping(Root);
sys::path::append(Mapping, "mapping.yaml");
FC->writeMapping(Mapping.str());
- outs() << "reproducer written to " << Root << '\n';
+ errs() << "********************\n";
+ errs() << "Reproducer written to " << Root << '\n';
+ errs() << "Please include the reproducer and the following invocation in "
+ "your bug report:\n";
+ for (llvm::StringRef Arg : Args)
+ errs() << Arg << ' ';
+ errs() << "--use-reproducer " << Root << '\n';
+ errs() << "********************\n";
}
ReproducerUse::~ReproducerUse() = default;
}
llvm::Expected<std::unique_ptr<Reproducer>>
-Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) {
+Reproducer::createReproducer(ReproducerMode Mode, StringRef Root, int Argc,
+ char **Argv) {
+
+ std::error_code EC;
+ std::unique_ptr<Reproducer> Repro;
switch (Mode) {
- case ReproducerMode::Generate: {
- std::error_code EC;
- std::unique_ptr<Reproducer> Repro =
- std::make_unique<ReproducerGenerate>(EC);
- if (EC)
- return errorCodeToError(EC);
- return std::move(Repro);
- }
- case ReproducerMode::Use: {
- std::error_code EC;
- std::unique_ptr<Reproducer> Repro =
- std::make_unique<ReproducerUse>(Root, EC);
- if (EC)
- return errorCodeToError(EC);
- return std::move(Repro);
- }
+ case ReproducerMode::GenerateOnExit:
+ Repro = std::make_unique<ReproducerGenerate>(EC, Argc, Argv, true);
+ break;
+ case ReproducerMode::GenerateOnCrash:
+ Repro = std::make_unique<ReproducerGenerate>(EC, Argc, Argv, false);
+ break;
+ case ReproducerMode::Use:
+ Repro = std::make_unique<ReproducerUse>(Root, EC);
+ break;
case ReproducerMode::Off:
- return std::make_unique<Reproducer>();
+ Repro = std::make_unique<Reproducer>();
+ break;
}
- llvm_unreachable("All cases handled above.");
+ if (EC)
+ return errorCodeToError(EC);
+ return Repro;
}
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/FileCollector.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
std::vector<std::string> InputFiles;
unsigned NumThreads;
DWARFVerify Verify = DWARFVerify::None;
- ReproducerMode ReproMode = ReproducerMode::Off;
+ ReproducerMode ReproMode = ReproducerMode::GenerateOnCrash;
dsymutil::LinkOptions LinkOpts;
};
return DwarfLinkerAccelTableKind::Default;
}
+static Expected<ReproducerMode> getReproducerMode(opt::InputArgList &Args) {
+ if (Args.hasArg(OPT_gen_reproducer))
+ return ReproducerMode::GenerateOnExit;
+ if (opt::Arg *Reproducer = Args.getLastArg(OPT_reproducer)) {
+ StringRef S = Reproducer->getValue();
+ if (S == "GenerateOnExit")
+ return ReproducerMode::GenerateOnExit;
+ if (S == "GenerateOnCrash")
+ return ReproducerMode::GenerateOnCrash;
+ if (S == "Use")
+ return ReproducerMode::Use;
+ if (S == "Off")
+ return ReproducerMode::Off;
+ return make_error<StringError>(
+ "invalid reproducer mode: '" + S +
+ "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "
+ "'Use', 'Off'.",
+ inconvertibleErrorCode());
+ }
+ return ReproducerMode::GenerateOnCrash;
+}
+
static Expected<DWARFVerify> getVerifyKind(opt::InputArgList &Args) {
if (Args.hasArg(OPT_verify))
return DWARFVerify::Output;
if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {
Options.ReproMode = ReproducerMode::Use;
Options.ReproducerPath = ReproducerPath->getValue();
+ } else {
+ if (Expected<ReproducerMode> ReproMode = getReproducerMode(Args)) {
+ Options.ReproMode = *ReproMode;
+ } else {
+ return ReproMode.takeError();
+ }
}
- if (Args.hasArg(OPT_gen_reproducer))
- Options.ReproMode = ReproducerMode::Generate;
-
if (Expected<DwarfLinkerAccelTableKind> AccelKind = getAccelTableKind(Args)) {
Options.LinkOpts.TheAccelTableKind = *AccelKind;
} else {
InitializeAllTargets();
InitializeAllAsmPrinters();
- auto Repro =
- Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath);
+ auto Repro = Reproducer::createReproducer(Options.ReproMode,
+ Options.ReproducerPath, argc, argv);
if (!Repro) {
WithColor::error() << toString(Repro.takeError());
return EXIT_FAILURE;
VerifyOutput = false;
}
- SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
- std::atomic_char AllOK(1);
- for (auto &Map : *DebugMapPtrsOrErr) {
- if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
- Map->print(outs());
+ // Set up a crash recovery context.
+ CrashRecoveryContext::Enable();
+ CrashRecoveryContext CRC;
+ CRC.DumpStackAndCleanupOnFailure = true;
- if (Options.DumpDebugMap)
- continue;
-
- if (!Options.SymbolMap.empty())
- Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
-
- if (Map->begin() == Map->end())
- WithColor::warning()
- << "no debug symbols in executable (-arch "
- << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
-
- // Using a std::shared_ptr rather than std::unique_ptr because move-only
- // types don't work with std::bind in the ThreadPool implementation.
- std::shared_ptr<raw_fd_ostream> OS;
-
- std::string OutputFile = OutputLocationOrErr->DWARFFile;
- if (NeedsTempFiles) {
- TempFiles.emplace_back(Map->getTriple().getArchName().str());
+ std::atomic_char AllOK(1);
+ SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
- auto E = TempFiles.back().createTempFile();
- if (E) {
- WithColor::error() << toString(std::move(E));
- return EXIT_FAILURE;
+ const bool Crashed = !CRC.RunSafely([&]() {
+ for (auto &Map : *DebugMapPtrsOrErr) {
+ if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
+ Map->print(outs());
+
+ if (Options.DumpDebugMap)
+ continue;
+
+ if (!Options.SymbolMap.empty())
+ Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
+
+ if (Map->begin() == Map->end())
+ WithColor::warning()
+ << "no debug symbols in executable (-arch "
+ << MachOUtils::getArchName(Map->getTriple().getArchName())
+ << ")\n";
+
+ // Using a std::shared_ptr rather than std::unique_ptr because move-only
+ // types don't work with std::bind in the ThreadPool implementation.
+ std::shared_ptr<raw_fd_ostream> OS;
+
+ std::string OutputFile = OutputLocationOrErr->DWARFFile;
+ if (NeedsTempFiles) {
+ TempFiles.emplace_back(Map->getTriple().getArchName().str());
+
+ auto E = TempFiles.back().createTempFile();
+ if (E) {
+ WithColor::error() << toString(std::move(E));
+ AllOK.fetch_and(false);
+ return;
+ }
+
+ auto &TempFile = *(TempFiles.back().File);
+ OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
+ /*shouldClose*/ false);
+ OutputFile = TempFile.TmpName;
+ } else {
+ std::error_code EC;
+ OS = std::make_shared<raw_fd_ostream>(
+ Options.LinkOpts.NoOutput ? "-" : OutputFile, EC,
+ sys::fs::OF_None);
+ if (EC) {
+ WithColor::error() << OutputFile << ": " << EC.message();
+ AllOK.fetch_and(false);
+ return;
+ }
}
- auto &TempFile = *(TempFiles.back().File);
- OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
- /*shouldClose*/ false);
- OutputFile = TempFile.TmpName;
- } else {
- std::error_code EC;
- OS = std::make_shared<raw_fd_ostream>(
- Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None);
- if (EC) {
- WithColor::error() << OutputFile << ": " << EC.message();
- return EXIT_FAILURE;
- }
+ auto LinkLambda = [&,
+ OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
+ LinkOptions Options) {
+ AllOK.fetch_and(
+ linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
+ Stream->flush();
+ if (VerifyOutput) {
+ AllOK.fetch_and(verifyOutput(
+ OutputFile, Map->getTriple().getArchName(), Options.Verbose));
+ }
+ };
+
+ // FIXME: The DwarfLinker can have some very deep recursion that can max
+ // out the (significantly smaller) stack when using threads. We don't
+ // want this limitation when we only have a single thread.
+ if (S.ThreadsRequested == 1)
+ LinkLambda(OS, Options.LinkOpts);
+ else
+ Threads.async(LinkLambda, OS, Options.LinkOpts);
}
- auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
- LinkOptions Options) {
- AllOK.fetch_and(
- linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
- Stream->flush();
- if (VerifyOutput) {
- AllOK.fetch_and(verifyOutput(
- OutputFile, Map->getTriple().getArchName(), Options.Verbose));
- }
- };
-
- // FIXME: The DwarfLinker can have some very deep recursion that can max
- // out the (significantly smaller) stack when using threads. We don't
- // want this limitation when we only have a single thread.
- if (S.ThreadsRequested == 1)
- LinkLambda(OS, Options.LinkOpts);
- else
- Threads.async(LinkLambda, OS, Options.LinkOpts);
- }
+ Threads.wait();
+ });
- Threads.wait();
+ if (Crashed)
+ (*Repro)->generate();
if (!AllOK)
return EXIT_FAILURE;