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 @@ -19,6 +19,7 @@ /// The reproducer mode. enum class ReproducerMode { Generate, + GenerateForCrash, Use, Off, }; @@ -34,12 +35,21 @@ IntrusiveRefCntPtr getVFS() const { return VFS; } + auto getRoot() const { return Root; } + /// Create a Reproducer instance based on the given mode. static llvm::Expected> createReproducer(ReproducerMode Mode, StringRef Root); protected: + explicit Reproducer(StringRef Root); + + SmallString<128> getMappingFileName() const; + IntrusiveRefCntPtr VFS; + + /// The path to the reproducer. + std::string Root; }; /// Reproducer instance used to generate a new reproducer. The VFS returned by @@ -47,15 +57,15 @@ /// dsymutil. class ReproducerGenerate : public Reproducer { public: - ReproducerGenerate(std::error_code &EC); + ReproducerGenerate(std::error_code &EC, bool IC); ~ReproducerGenerate() override; private: - /// The path to the reproducer. - std::string Root; - /// The FileCollector used by the FileCollectorFileSystem. std::shared_ptr FC; + + // For crash generation we want to suppress printing of debug map + bool IsCrash = false; }; /// Reproducer instance used to use an existing reproducer. The VFS returned by @@ -66,9 +76,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,11 +26,20 @@ return EC ? "" : std::string(Root); } +SmallString<128> Reproducer::getMappingFileName() const { + SmallString<128> Mapping(Root); + sys::path::append(Mapping, "mapping.yaml"); + return Mapping; +} + Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {} Reproducer::~Reproducer() = default; -ReproducerGenerate::ReproducerGenerate(std::error_code &EC) - : Root(createReproducerDir(EC)), FC() { +Reproducer::Reproducer(StringRef Rt) + : VFS{vfs::getRealFileSystem()}, Root{std::move(Rt)} {} + +ReproducerGenerate::ReproducerGenerate(std::error_code &EC, bool IC) + : Reproducer{createReproducerDir(EC)}, FC(), IsCrash{IC} { if (!Root.empty()) FC = std::make_shared(Root, Root); VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC); @@ -37,17 +49,17 @@ if (!FC) return; FC->copyFiles(false); - SmallString<128> Mapping(Root); - sys::path::append(Mapping, "mapping.yaml"); + auto Mapping = getMappingFileName(); FC->writeMapping(Mapping.str()); - outs() << "reproducer written to " << Root << '\n'; + if (!IsCrash) + outs() << "reproducer written to " << Root << '\n'; } 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} { + auto Mapping = getMappingFileName(); ErrorOr> Buffer = vfs::getRealFileSystem()->getBufferForFile(Mapping.str()); @@ -61,11 +73,15 @@ llvm::Expected> Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) { + bool IsCrash = false; switch (Mode) { + case ReproducerMode::GenerateForCrash: + IsCrash = true; + [[clang::fallthrough]]; case ReproducerMode::Generate: { std::error_code EC; std::unique_ptr Repro = - std::make_unique(EC); + std::make_unique(EC, IsCrash); if (EC) return errorCodeToError(EC); return std::move(Repro); 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" @@ -473,18 +476,23 @@ return OutputLocation(std::string(Path.str()), ResourceDir); } -int main(int argc, char **argv) { - InitLLVM X(argc, argv); - +static auto parseArguments(int Argc, char **Argv) { // Parse arguments. 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); + return std::make_pair(T, T.ParseArgs(ArgsArr, MAI, MAC)); +} + +static int dsymutilMain(int Argc, char **Argv) { + std::string ProgName{Argv[0]}; + auto ArgPair = parseArguments(Argc, Argv); + auto &T = ArgPair.first; + auto &Args = ArgPair.second; 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)) { @@ -494,7 +502,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" @@ -673,7 +681,6 @@ } Threads.wait(); - if (!AllOK) return 1; @@ -693,3 +700,56 @@ return 0; } + +static void generateDiagnosticFiles(DsymutilOptions &Options) { + dbgs() << "\n********************\n\n" + "PLEASE ATTACH THE FILES IN THE FOLLOWING DIRECTORY TO THE BUG " + "REPORT:\nBinar(ies|y), object file(s) and associated debug map " + "are located at:\n"; + auto Repro = Reproducer::createReproducer(ReproducerMode::GenerateForCrash, + StringRef{}); + if (!Repro) { + dbgs() << "Unable to create reproducer due to " + << toString(Repro.takeError()) << '\n'; + return; + } + auto VFS = (*Repro)->getVFS(); + for (auto &InputFile : Options.InputFiles) { + auto DebugMapPtrsOrErr = + parseDebugMap(VFS, InputFile, Options.Archs, {}, false, false, false); + if (auto EC = DebugMapPtrsOrErr.getError()) { + dbgs() << "cannot parse the debug map for '" << InputFile + << "': " << EC.message() << '\n'; + } + } + + // Once the reproducer exits then the mapping will be dumped + dbgs() << "Directory: " << (Repro.get())->getRoot() << '\n'; +} + +// 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; + if (!CRC.RunSafely( + [&Rval, argc, argv]() { Rval = dsymutilMain(argc, argv); })) { + auto ArgPair = parseArguments(argc, argv); + auto &Args = ArgPair.second; + // Now generate all the diagnostic info into the temp directory + auto OptionsOrErr = getOptions(Args); + if (OptionsOrErr) { + generateDiagnosticFiles(*OptionsOrErr); + } else { + dbgs() << "Failed to parse options in crash dump due to " + << toString(OptionsOrErr.takeError()) << '\n'; + } + return CRC.RetCode; + } + return Rval; +}