diff --git a/llvm/test/tools/dsymutil/X86/reproducer.test b/llvm/test/tools/dsymutil/X86/reproducer.test --- a/llvm/test/tools/dsymutil/X86/reproducer.test +++ b/llvm/test/tools/dsymutil/X86/reproducer.test @@ -75,6 +75,6 @@ CHECK-NEXT: DW_AT_byte_size (0x01) CHECK: NULL -REPRODUCER: reproducer written +REPRODUCER: Reproducer Directory ERROR: error: cannot parse the debug map CONFLICT: cannot combine --gen-reproducer and --use-reproducer diff --git a/llvm/tools/dsymutil/Reproducer.h b/llvm/tools/dsymutil/Reproducer.h --- a/llvm/tools/dsymutil/Reproducer.h +++ b/llvm/tools/dsymutil/Reproducer.h @@ -13,12 +13,15 @@ #include "llvm/Support/FileCollector.h" #include "llvm/Support/VirtualFileSystem.h" +#include + namespace llvm { namespace dsymutil { /// The reproducer mode. enum class ReproducerMode { Generate, + GenerateOnCrash, Use, Off, }; @@ -34,12 +37,25 @@ IntrusiveRefCntPtr getVFS() const { return VFS; } + const std::string &getRoot() const { return Root; } + + /// Get the mode of this reproducer + ReproducerMode getMode() const { return Mode; } + /// Create a Reproducer instance based on the given mode. static llvm::Expected> createReproducer(ReproducerMode Mode, StringRef Root); protected: + Reproducer(std::string Root, ReproducerMode Mode); + + void getMappingFileName(SmallVectorImpl &Mapping) const; + IntrusiveRefCntPtr VFS; + + /// The path to the reproducer. + std::string Root; + ReproducerMode Mode = ReproducerMode::Off; }; /// Reproducer instance used to generate a new reproducer. The VFS returned by @@ -47,17 +63,38 @@ /// dsymutil. class ReproducerGenerate : public Reproducer { public: - ReproducerGenerate(std::error_code &EC); + ReproducerGenerate(std::error_code &EC, + ReproducerMode Mode = ReproducerMode::Generate); ~ReproducerGenerate() override; -private: - /// The path to the reproducer. - std::string Root; + /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc + static bool classof(const Reproducer *R) { + return R->getMode() == ReproducerMode::Generate; + } + ReproducerGenerate(ReproducerGenerate &&); + +protected: /// The FileCollector used by the FileCollectorFileSystem. std::shared_ptr FC; }; +class ReproducerGenerateForCrash : public ReproducerGenerate { +public: + ReproducerGenerateForCrash(std::error_code &EC); + ReproducerGenerateForCrash(ReproducerGenerate &&); + void dodump(bool DoDataDump); + ~ReproducerGenerateForCrash() override; + + /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc + static bool classof(const Reproducer *R) { + return R->getMode() == ReproducerMode::GenerateOnCrash; + } + +private: + bool DumpFiles = true; +}; + /// Reproducer instance used to use an existing reproducer. The VFS returned by /// this instance is a RedirectingFileSystem that remaps paths to their /// counterpart in the reproducer. @@ -66,9 +103,6 @@ ReproducerUse(StringRef Root, std::error_code &EC); ~ReproducerUse() override; -private: - /// The path to the reproducer. - std::string Root; }; } // end namespace dsymutil diff --git a/llvm/tools/dsymutil/Reproducer.cpp b/llvm/tools/dsymutil/Reproducer.cpp --- a/llvm/tools/dsymutil/Reproducer.cpp +++ b/llvm/tools/dsymutil/Reproducer.cpp @@ -8,6 +8,9 @@ #include "Reproducer.h" #include "llvm/Support/Path.h" +#include +#include +#include using namespace llvm; using namespace llvm::dsymutil; @@ -23,31 +26,67 @@ return EC ? "" : std::string(Root); } +void Reproducer::getMappingFileName(SmallVectorImpl &Mapping) const { + Mapping.assign(Root.begin(), Root.end()); + sys::path::append(Mapping, "mapping.yaml"); +} + Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {} Reproducer::~Reproducer() = default; -ReproducerGenerate::ReproducerGenerate(std::error_code &EC) - : Root(createReproducerDir(EC)), FC() { +Reproducer::Reproducer(std::string Root, ReproducerMode Mode) + : VFS(vfs::getRealFileSystem()), Root(std::move(Root)), Mode(Mode) {} + +ReproducerGenerate::ReproducerGenerate(std::error_code &EC, ReproducerMode Mode) + : Reproducer(createReproducerDir(EC), Mode), FC() { if (!Root.empty()) FC = std::make_shared(Root, Root); VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC); } +ReproducerGenerate::ReproducerGenerate(ReproducerGenerate &&Other) + : Reproducer(static_cast(Other)), FC(std::move(Other.FC)) {} + ReproducerGenerate::~ReproducerGenerate() { if (!FC) return; FC->copyFiles(false); - SmallString<128> Mapping(Root); - sys::path::append(Mapping, "mapping.yaml"); + SmallString<128> Mapping; + getMappingFileName(Mapping); FC->writeMapping(Mapping.str()); - outs() << "reproducer written to " << Root << '\n'; + outs() << "Reproducer Directory: " << getRoot() << '\n'; +} + +ReproducerGenerateForCrash::ReproducerGenerateForCrash(std::error_code &EC) + : ReproducerGenerate(EC, ReproducerMode::GenerateOnCrash) {} + +void ReproducerGenerateForCrash::dodump(bool DoDataDump) { + DumpFiles = DoDataDump; +} + +ReproducerGenerateForCrash::ReproducerGenerateForCrash( + ReproducerGenerate &&Other) + : ReproducerGenerate(std::move(Other)) {} + +ReproducerGenerateForCrash::~ReproducerGenerateForCrash() { + if (!DumpFiles) { + FC.reset(); + return; + } + dbgs() << "\n********************\n\n" + "PLEASE ATTACH THE FOLLOWING DIRECTORY TO THE BUG " + "REPORT:\nBinar(ies|y), object file(s) and associated debug map " + "are located at:\n"; + // Once the reproducer exits then the mapping will be dumped + // by the parent class destructor } ReproducerUse::~ReproducerUse() = default; -ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) { - SmallString<128> Mapping(Root); - sys::path::append(Mapping, "mapping.yaml"); +ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) + : Reproducer{Root.str(), ReproducerMode::Use} { + SmallString<128> Mapping; + getMappingFileName(Mapping); ErrorOr> Buffer = vfs::getRealFileSystem()->getBufferForFile(Mapping.str()); @@ -61,23 +100,24 @@ llvm::Expected> Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) { + std::unique_ptr Repro; + std::error_code EC; switch (Mode) { - case ReproducerMode::Generate: { - std::error_code EC; - std::unique_ptr Repro = - std::make_unique(EC); + case ReproducerMode::GenerateOnCrash: + Repro = std::make_unique(EC); + LLVM_FALLTHROUGH; + case ReproducerMode::Generate: + if (Repro == nullptr) + Repro = std::make_unique(EC); + if (EC) return errorCodeToError(EC); return std::move(Repro); - } - case ReproducerMode::Use: { - std::error_code EC; - std::unique_ptr Repro = - std::make_unique(Root, EC); + case ReproducerMode::Use: + Repro = std::make_unique(Root, EC); if (EC) return errorCodeToError(EC); return std::move(Repro); - } case ReproducerMode::Off: return std::make_unique(); } diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" +#include "llvm/Config/config.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" @@ -32,11 +33,13 @@ #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" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/WithColor.h" @@ -99,7 +102,7 @@ std::vector Archs; std::vector InputFiles; unsigned NumThreads; - ReproducerMode ReproMode = ReproducerMode::Off; + ReproducerMode ReproMode = ReproducerMode::GenerateOnCrash; dsymutil::LinkOptions LinkOpts; }; @@ -478,18 +481,16 @@ return OutputLocation(std::string(Path.str()), ResourceDir); } -int main(int argc, char **argv) { - InitLLVM X(argc, argv); - - // Parse arguments. +static int dsymutilMain(std::unique_ptr &Repro, int Argc, + char **Argv) { + std::string ProgName(Argv[0]); DsymutilOptTable T; unsigned MAI; unsigned MAC; - ArrayRef ArgsArr = makeArrayRef(argv + 1, argc - 1); - opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); - + ArrayRef ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + auto Args = T.ParseArgs(ArgsArr, MAI, MAC); void *P = (void *)(intptr_t)getOutputFileName; - std::string SDKPath = sys::fs::getMainExecutable(argv[0], P); + std::string SDKPath = sys::fs::getMainExecutable(Argv[0], P); SDKPath = std::string(sys::path::parent_path(SDKPath)); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) { @@ -499,7 +500,7 @@ if (Args.hasArg(OPT_help)) { T.PrintHelp( - outs(), (std::string(argv[0]) + " [options] ").c_str(), + outs(), (ProgName + " [options] ").c_str(), "manipulate archived DWARF debug symbol files.\n\n" "dsymutil links the DWARF debug information found in the object files\n" "for the executable by using debug symbols information\n" @@ -526,14 +527,16 @@ InitializeAllTargets(); InitializeAllAsmPrinters(); - auto Repro = + auto IsRepro = Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath); - if (!Repro) { - WithColor::error() << toString(Repro.takeError()); + if (!IsRepro) { + WithColor::error() << toString(IsRepro.takeError()); return EXIT_FAILURE; } - Options.LinkOpts.VFS = (*Repro)->getVFS(); + Repro = std::move(*IsRepro); + + Options.LinkOpts.VFS = Repro->getVFS(); for (const auto &Arch : Options.Archs) if (Arch != "*" && Arch != "all" && @@ -706,3 +709,38 @@ return EXIT_SUCCESS; } + +// NOLINTNEXTLINE(readability-identifier-naming) +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + setBugReportMsg( + "PLEASE submit a bug report to " BUG_REPORT_URL + " and include the crash backtrace, object files and debug map.\n"); + CrashRecoveryContext::Enable(); + CrashRecoveryContext CRC; + CRC.DumpStackAndCleanupOnFailure = true; + int Rval = 0; + std::unique_ptr Repro; + if (!CRC.RunSafely([&Rval, &Repro, argc, argv]() { + Rval = dsymutilMain(Repro, argc, argv); + })) { + if (auto *RPtr = llvm::dyn_cast(Repro.get())) { + Repro.reset(); // Generate reproducer on teardown + } else if (auto *RPtr = llvm::dyn_cast(Repro.get())) { + ReproducerGenerateForCrash CrashRepro(std::move(*RPtr)); + } else { + WithColor::error() << "Unable to create crash reproducer.\n"; + } + dbgs() << "Command: dsymutil "; + for (auto i = 1; i < argc; i++) { + dbgs() << argv[i] << " "; + } + dbgs() << "\n"; + return CRC.RetCode; + } + + if (auto *RPtr = llvm::dyn_cast(Repro.get())) { + RPtr->dodump(false); + } + return Rval; +}