Index: llvm/test/tools/dsymutil/X86/reproducer.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/X86/reproducer.test @@ -0,0 +1,75 @@ +# Recreate the folder structure in a temp directory we can remove later. +RUN: rm -rf %t +RUN: mkdir -p %t/Inputs +RUN: cp %p/../Inputs/basic.macho.x86_64 %t/Inputs +RUN: cp %p/../Inputs/basic1.macho.x86_64.o %t/Inputs +RUN: cp %p/../Inputs/basic2.macho.x86_64.o %t/Inputs +RUN: cp %p/../Inputs/basic3.macho.x86_64.o %t/Inputs + +# Sanity check all the files are present. +RUN: dsymutil -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s + +# Create a reproducer. +RUN: env DSYMUTIL_REPRODUCER_PATH=%t.repro dsymutil -gen-reproducer -f -o %t.generate -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | FileCheck %s --check-prefixes=REPRODUCER +RUN: llvm-dwarfdump -a %t.generate | FileCheck %s + +# Remove the input files and sanity check that was successful. +RUN: rm -rf %t +RUN: not dsymutil -f -o %t.error -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 2>&1 | FileCheck %s --check-prefix=ERROR + +# Use the reproducer. +RUN: dsymutil -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s + +# Conflicting options. +RUN: not dsymutil -gen-reproducer -use-reproducer %t.repro -f -o %t.error -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 2>&1 | FileCheck %s --check-prefix=CONFLICT + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_language (DW_LANG_C99) +CHECK: DW_AT_name ("basic1.c") +CHECK: DW_AT_stmt_list (0x00000000) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000ea0) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("main") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_prototyped (0x01) +CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_external (0x01) +CHECK: DW_AT_accessibility (DW_ACCESS_public) +CHECK: DW_AT_low_pc (0x0000000100000ea0) +CHECK: DW_AT_high_pc (0x0000000100000ec4) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("argc") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_location (DW_OP_fbreg -8) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("argv") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_type (0x0000006a +CHECK: DW_AT_location (DW_OP_fbreg -16) +CHECK: NULL +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("int") +CHECK: DW_AT_encoding (DW_ATE_signed) +CHECK: DW_AT_byte_size (0x04) +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x0000006f +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x00000074 +CHECK: DW_TAG_const_type +CHECK: DW_AT_type (0x00000079 +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("char") +CHECK: DW_AT_encoding (DW_ATE_signed_char) +CHECK: DW_AT_byte_size (0x01) +CHECK: NULL + +REPRODUCER: reproducer written +ERROR: error: cannot parse the debug map +CONFLICT: cannot combine --gen-reproducer and --use-reproducer Index: llvm/test/tools/dsymutil/cmdline.test =================================================================== --- llvm/test/tools/dsymutil/cmdline.test +++ llvm/test/tools/dsymutil/cmdline.test @@ -7,6 +7,7 @@ HELP: -arch HELP: -dump-debug-map HELP: -flat +HELP: -gen-reproducer HELP: -minimize HELP: -no-odr HELP: -no-output @@ -24,6 +25,7 @@ HELP: {{ -S }} HELP: -toolchain HELP: -update +HELP: -use-reproducer HELP: -verbose HELP: -verify HELP: {{ -y }} Index: llvm/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/tools/dsymutil/CMakeLists.txt +++ llvm/tools/dsymutil/CMakeLists.txt @@ -26,6 +26,7 @@ DwarfLinkerForBinary.cpp MachODebugMapParser.cpp MachOUtils.cpp + Reproducer.cpp SymbolMap.cpp DEPENDS Index: llvm/tools/dsymutil/Options.td =================================================================== --- llvm/tools/dsymutil/Options.td +++ llvm/tools/dsymutil/Options.td @@ -163,6 +163,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.">, Index: llvm/tools/dsymutil/Reproducer.h =================================================================== --- /dev/null +++ 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(); + IntrusiveRefCntPtr getVFS() const { return VFS; } + + static llvm::Expected> + createReproducer(ReproducerMode Mode, StringRef Root); + +protected: + IntrusiveRefCntPtr VFS; +}; + +class ReproducerGenerate : public Reproducer { +public: + ReproducerGenerate(std::error_code &EC); + ~ReproducerGenerate() override; + +private: + std::string Root; + std::shared_ptr FC; +}; + +class ReproducerUse : public Reproducer { +public: + ReproducerUse(StringRef Root, std::error_code &EC); + ~ReproducerUse() override; + +private: + std::string Root; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_REPRODUCER_H Index: llvm/tools/dsymutil/Reproducer.cpp =================================================================== --- /dev/null +++ llvm/tools/dsymutil/Reproducer.cpp @@ -0,0 +1,82 @@ +//===- 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; + if (const char *Path = getenv("DSYMUTIL_REPRODUCER_PATH")) { + Root.assign(Path); + EC = sys::fs::create_directory(Root); + } else { + EC = sys::fs::createUniqueDirectory("dsymutil", Root); + } + return EC ? "" : std::string(Root); +} + +Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {} +Reproducer::~Reproducer() = default; + +ReproducerGenerate::ReproducerGenerate(std::error_code &EC) + : Root(createReproducerDir(EC)), FC() { + if (!Root.empty()) + FC = std::make_shared(Root, Root); + VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC); +} + +ReproducerGenerate::~ReproducerGenerate() { + if (!FC) + return; + FC->copyFiles(false); + SmallString<128> Mapping(Root); + sys::path::append(Mapping, "mapping.yaml"); + FC->writeMapping(Mapping.str()); + 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"); + ErrorOr> Buffer = + vfs::getRealFileSystem()->getBufferForFile(Mapping.str()); + + if (!Buffer) { + EC = Buffer.getError(); + return; + } + + VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, Mapping); +} + +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(); + } +} Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ 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 = ReproducerMode::Off; 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(); } @@ -222,6 +232,14 @@ Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics); + 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 { @@ -499,6 +517,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)) {