diff --git a/llvm/tools/dsymutil/CMakeLists.txt b/llvm/tools/dsymutil/CMakeLists.txt --- a/llvm/tools/dsymutil/CMakeLists.txt +++ b/llvm/tools/dsymutil/CMakeLists.txt @@ -26,6 +26,7 @@ DwarfLinkerForBinary.cpp MachODebugMapParser.cpp MachOUtils.cpp + Reproducer.cpp SymbolMap.cpp DEPENDS diff --git a/llvm/tools/dsymutil/Options.td b/llvm/tools/dsymutil/Options.td --- a/llvm/tools/dsymutil/Options.td +++ b/llvm/tools/dsymutil/Options.td @@ -154,6 +154,16 @@ HelpText<"Alias for --num-threads">, Group; +def gen_reproducer: F<"gen-reproducer">, + HelpText<"Generate a reproducer consisting of the input object files.">, + Group; + +def use_reproducer: Separate<["--", "-"], "use-reproducer">, + MetaVarName<"">, + HelpText<"Use the object files from the given reproducer path.">, + Group; +def: Joined<["--", "-"], "use-reproducer=">, Alias; + def remarks_prepend_path: Separate<["--", "-"], "remarks-prepend-path">, MetaVarName<"">, HelpText<"Specify a directory to prepend to the paths of the external remark files.">, diff --git a/llvm/tools/dsymutil/Reproducer.h b/llvm/tools/dsymutil/Reproducer.h new file mode 100644 --- /dev/null +++ b/llvm/tools/dsymutil/Reproducer.h @@ -0,0 +1,60 @@ +//===- tools/dsymutil/Reproducer.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_REPRODUCER_H +#define LLVM_TOOLS_DSYMUTIL_REPRODUCER_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/FileCollector.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace llvm { +namespace dsymutil { + +enum class ReproducerMode { + Generate, + Use, + Off, +}; + +class Reproducer { +public: + Reproducer(); + virtual ~Reproducer(); + virtual IntrusiveRefCntPtr getVFS() const; + + static llvm::Expected> + createReproducer(ReproducerMode Mode, StringRef Root); +}; + +class ReproducerGenerate : public Reproducer { +public: + ReproducerGenerate(std::error_code &EC); + ~ReproducerGenerate() override; + IntrusiveRefCntPtr getVFS() const override; + +private: + std::string Root; + std::shared_ptr FC; +}; + +class ReproducerUse : public Reproducer { +public: + ReproducerUse(StringRef Root, std::error_code &EC); + ~ReproducerUse() override; + IntrusiveRefCntPtr getVFS() const override; + +private: + IntrusiveRefCntPtr VFS; + std::string Root; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_REPRODUCER_H diff --git a/llvm/tools/dsymutil/Reproducer.cpp b/llvm/tools/dsymutil/Reproducer.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/dsymutil/Reproducer.cpp @@ -0,0 +1,88 @@ +//===- Reproducer.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Path.h" +#include + +using namespace llvm; +using namespace llvm::dsymutil; + +static std::string createReproducerDir(std::error_code &EC) { + SmallString<128> Root; + EC = sys::fs::createUniqueDirectory("dsymutil", Root); + return EC ? "" : std::string(Root); +} + +Reproducer::Reproducer() = default; +Reproducer::~Reproducer() = default; + +IntrusiveRefCntPtr Reproducer::getVFS() const { + return vfs::getRealFileSystem(); +} + +ReproducerGenerate::ReproducerGenerate(std::error_code &EC) + : Root(createReproducerDir(EC)), FC() { + if (!Root.empty()) + FC = std::make_shared(Root, Root); +} + +ReproducerGenerate::~ReproducerGenerate() { + if (!FC) + return; + FC->copyFiles(false); + SmallString<128> Mapping(Root); + sys::path::append(Mapping, "mapping.yaml"); + FC->writeMapping(Mapping.str()); + errs() << "Reproducer written to " << Root << '\n'; +} + +IntrusiveRefCntPtr ReproducerGenerate::getVFS() const { + return FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC); +} + +ReproducerUse::~ReproducerUse() = default; + +ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) { + SmallString<128> Mapping(Root); + sys::path::append(Mapping, "mapping.yaml"); + ErrorOr> Buffer = + vfs::getRealFileSystem()->getBufferForFile(Mapping.str()); + + if (!Buffer) { + EC = Buffer.getError(); + return; + } + + VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, Mapping); +} + +IntrusiveRefCntPtr ReproducerUse::getVFS() const { + return VFS; +} + +llvm::Expected> +Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) { + switch (Mode) { + case ReproducerMode::Generate: { + std::error_code EC; + auto Repro = std::make_unique(EC); + if (EC) + return errorCodeToError(EC); + return Repro; + } + case ReproducerMode::Use: { + std::error_code EC; + auto Repro = std::make_unique(Root, EC); + if (EC) + return errorCodeToError(EC); + return 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 @@ -16,6 +16,7 @@ #include "DebugMap.h" #include "LinkUtils.h" #include "MachOUtils.h" +#include "Reproducer.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -31,6 +32,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileCollector.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" @@ -92,9 +94,11 @@ std::string SymbolMap; std::string OutputFile; std::string Toolchain; + std::string ReproducerPath; std::vector Archs; std::vector InputFiles; unsigned NumThreads; + ReproducerMode ReproMode; dsymutil::LinkOptions LinkOpts; }; @@ -182,6 +186,12 @@ "paper trail warnings are not supported for YAML input.", errc::invalid_argument); + if (!Options.ReproducerPath.empty() && + Options.ReproMode != ReproducerMode::Use) + return make_error( + "cannot combine --gen-reproducer and --use-reproducer.", + errc::invalid_argument); + return Error::success(); } @@ -221,6 +231,14 @@ Options.LinkOpts.Update = Args.hasArg(OPT_update); Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); + if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) { + Options.ReproMode = ReproducerMode::Use; + Options.ReproducerPath = ReproducerPath->getValue(); + } + + if (Args.hasArg(OPT_gen_reproducer)) + Options.ReproMode = ReproducerMode::Generate; + if (Expected AccelKind = getAccelTableKind(Args)) { Options.LinkOpts.TheAccelTableKind = *AccelKind; } else { @@ -498,6 +516,15 @@ InitializeAllTargets(); InitializeAllAsmPrinters(); + auto Repro = + Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath); + if (!Repro) { + WithColor::error() << toString(Repro.takeError()); + return 1; + } + + Options.LinkOpts.VFS = (*Repro)->getVFS(); + for (const auto &Arch : Options.Archs) if (Arch != "*" && Arch != "all" && !object::MachOObjectFile::isValidArch(Arch)) {