[llvm] 92fd397 - [dsymutil] Add reproducers to dsymutil

Jonas Devlieghere via llvm-commits llvm-commits at lists.llvm.org
Thu May 21 10:59:56 PDT 2020


Author: Jonas Devlieghere
Date: 2020-05-21T10:59:49-07:00
New Revision: 92fd3971e0db1d9faf2dbbcceb2b79b3192fd79d

URL: https://github.com/llvm/llvm-project/commit/92fd3971e0db1d9faf2dbbcceb2b79b3192fd79d
DIFF: https://github.com/llvm/llvm-project/commit/92fd3971e0db1d9faf2dbbcceb2b79b3192fd79d.diff

LOG: [dsymutil] Add reproducers to dsymutil

Add support for generating a dsymutil reproducer. The result is a folder
containing all the object files for linking.

When --gen-reproducer is passed, dsymutil uses a FileCollectorFileSystem
which keeps track of all the files used by dsymutil. These files are
copied into a temporary directory when dsymutil exists.

When this path is passed to --use-reproducer, dsymutil uses a
RedirectingFileSystem that will use the files from the reproducer
directory instead of the actual paths. This means you don't need to mess
with the OSO path prefix.

Differential revision: https://reviews.llvm.org/D79398

Added: 
    llvm/test/tools/dsymutil/X86/reproducer.test
    llvm/tools/dsymutil/Reproducer.cpp
    llvm/tools/dsymutil/Reproducer.h

Modified: 
    llvm/docs/CommandGuide/dsymutil.rst
    llvm/test/tools/dsymutil/cmdline.test
    llvm/tools/dsymutil/CMakeLists.txt
    llvm/tools/dsymutil/Options.td
    llvm/tools/dsymutil/dsymutil.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/dsymutil.rst b/llvm/docs/CommandGuide/dsymutil.rst
index 5b7016ced2f5..78954fcc8d87 100644
--- a/llvm/docs/CommandGuide/dsymutil.rst
+++ b/llvm/docs/CommandGuide/dsymutil.rst
@@ -42,6 +42,10 @@ OPTIONS
  Produce a flat dSYM file. A ``.dwarf`` extension will be appended to the
  executable name unless the output file is specified using the ``-o`` option.
 
+.. option:: --gen-reproducer
+
+ Generate a reproducer consisting of the input object files.
+
 .. option:: --help, -h
 
  Print this help output.
@@ -131,6 +135,10 @@ OPTIONS
  other DWARF optimizations. This option will rebuild the '.apple_names' and
  '.apple_types' hashed accelerator tables.
 
+.. option:: --use-reproducer <path>
+
+ Use the object files from the given reproducer path.
+
 .. option:: --verbose
 
  Display verbose information when linking.

diff  --git a/llvm/test/tools/dsymutil/X86/reproducer.test b/llvm/test/tools/dsymutil/X86/reproducer.test
new file mode 100644
index 000000000000..02d73b91e399
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/reproducer.test
@@ -0,0 +1,76 @@
+# 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: .debug_info
+CHECK:  DW_TAG_compile_unit
+CHECK-NEXT:    DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK-NEXT:    DW_AT_language (DW_LANG_C99)
+CHECK-NEXT:    DW_AT_name ("basic1.c")
+CHECK-NEXT:    DW_AT_stmt_list (0x00000000)
+CHECK-NEXT:    DW_AT_comp_dir ("/Inputs")
+CHECK-NEXT:    DW_AT_low_pc (0x0000000100000ea0)
+CHECK:    DW_TAG_subprogram
+CHECK-NEXT:    DW_AT_name ("main")
+CHECK-NEXT:      DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK-NEXT:      DW_AT_decl_line (23)
+CHECK-NEXT:      DW_AT_prototyped (0x01)
+CHECK-NEXT:      DW_AT_type (0x00000063
+CHECK-NEXT:      DW_AT_external (0x01)
+CHECK-NEXT:      DW_AT_accessibility (DW_ACCESS_public)
+CHECK-NEXT:      DW_AT_low_pc (0x0000000100000ea0)
+CHECK-NEXT:      DW_AT_high_pc (0x0000000100000ec4)
+CHECK-NEXT:      DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK:      DW_TAG_formal_parameter
+CHECK-NEXT:        DW_AT_name ("argc")
+CHECK-NEXT:        DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK-NEXT:        DW_AT_decl_line (23)
+CHECK-NEXT:        DW_AT_type (0x00000063
+CHECK-NEXT:        DW_AT_location (DW_OP_fbreg -8)
+CHECK:      DW_TAG_formal_parameter
+CHECK-NEXT:        DW_AT_name ("argv")
+CHECK-NEXT:        DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK-NEXT:        DW_AT_decl_line (23)
+CHECK-NEXT:        DW_AT_type (0x0000006a
+CHECK-NEXT:        DW_AT_location (DW_OP_fbreg -16)
+CHECK:      NULL
+CHECK:    DW_TAG_base_type
+CHECK-NEXT:      DW_AT_name ("int")
+CHECK-NEXT:      DW_AT_encoding (DW_ATE_signed)
+CHECK-NEXT:      DW_AT_byte_size (0x04)
+CHECK:    DW_TAG_pointer_type
+CHECK-NEXT:      DW_AT_type (0x0000006f
+CHECK:    DW_TAG_pointer_type
+CHECK-NEXT:      DW_AT_type (0x00000074
+CHECK:    DW_TAG_const_type
+CHECK-NEXT:      DW_AT_type (0x00000079
+CHECK:    DW_TAG_base_type
+CHECK-NEXT:      DW_AT_name ("char")
+CHECK-NEXT:      DW_AT_encoding (DW_ATE_signed_char)
+CHECK-NEXT:      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

diff  --git a/llvm/test/tools/dsymutil/cmdline.test b/llvm/test/tools/dsymutil/cmdline.test
index 29f6e70f0130..e3f8bbdf7503 100644
--- a/llvm/test/tools/dsymutil/cmdline.test
+++ b/llvm/test/tools/dsymutil/cmdline.test
@@ -9,6 +9,7 @@ CHECK: -accelerator
 CHECK: -arch <arch>
 CHECK: -dump-debug-map
 CHECK: -flat
+CHECK: -gen-reproducer
 CHECK: -help
 CHECK: -minimize
 CHECK: -no-odr
@@ -28,6 +29,7 @@ CHECK: -symtab
 CHECK: {{-S}}
 CHECK: -toolchain
 CHECK: -update
+CHECK: -use-reproducer <path>
 CHECK: -verbose
 CHECK: -verify
 CHECK: {{-y}}

diff  --git a/llvm/tools/dsymutil/CMakeLists.txt b/llvm/tools/dsymutil/CMakeLists.txt
index a42e1a98080a..1efc58d19e92 100644
--- a/llvm/tools/dsymutil/CMakeLists.txt
+++ b/llvm/tools/dsymutil/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_tool(dsymutil
   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
index bdd11b5c4c1e..7bd21b0d4346 100644
--- a/llvm/tools/dsymutil/Options.td
+++ b/llvm/tools/dsymutil/Options.td
@@ -164,6 +164,16 @@ def: Separate<["-"], "j">,
   HelpText<"Alias for --num-threads">,
   Group<grp_general>;
 
+def gen_reproducer: F<"gen-reproducer">,
+  HelpText<"Generate a reproducer consisting of the input object files.">,
+  Group<grp_general>;
+
+def use_reproducer: Separate<["--", "-"], "use-reproducer">,
+  MetaVarName<"<path>">,
+  HelpText<"Use the object files from the given reproducer path.">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "use-reproducer=">, Alias<use_reproducer>;
+
 def remarks_prepend_path: Separate<["--", "-"], "remarks-prepend-path">,
   MetaVarName<"<path>">,
   HelpText<"Specify a directory to prepend to the paths of the external remark files.">,

diff  --git a/llvm/tools/dsymutil/Reproducer.cpp b/llvm/tools/dsymutil/Reproducer.cpp
new file mode 100644
index 000000000000..baa6bc6f11e0
--- /dev/null
+++ b/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 <Reproducer.h>
+
+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<FileCollector>(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<std::unique_ptr<MemoryBuffer>> Buffer =
+      vfs::getRealFileSystem()->getBufferForFile(Mapping.str());
+
+  if (!Buffer) {
+    EC = Buffer.getError();
+    return;
+  }
+
+  VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, Mapping);
+}
+
+llvm::Expected<std::unique_ptr<Reproducer>>
+Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) {
+  switch (Mode) {
+  case ReproducerMode::Generate: {
+    std::error_code EC;
+    auto Repro = std::make_unique<ReproducerGenerate>(EC);
+    if (EC)
+      return errorCodeToError(EC);
+    return Repro;
+  }
+  case ReproducerMode::Use: {
+    std::error_code EC;
+    auto Repro = std::make_unique<ReproducerUse>(Root, EC);
+    if (EC)
+      return errorCodeToError(EC);
+    return Repro;
+  }
+  case ReproducerMode::Off:
+    return std::make_unique<Reproducer>();
+  }
+}

diff  --git a/llvm/tools/dsymutil/Reproducer.h b/llvm/tools/dsymutil/Reproducer.h
new file mode 100644
index 000000000000..e965e1ceda24
--- /dev/null
+++ b/llvm/tools/dsymutil/Reproducer.h
@@ -0,0 +1,77 @@
+//===- 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 {
+
+/// The reproducer mode.
+enum class ReproducerMode {
+  Generate,
+  Use,
+  Off,
+};
+
+/// The reproducer class manages the sate related to reproducers in dsymutil.
+/// Instances should be created with Reproducer::createReproducer. An instance
+/// of this class is returned when reproducers are off. The VFS returned by
+/// this instance is the real file system.
+class Reproducer {
+public:
+  Reproducer();
+  virtual ~Reproducer();
+
+  IntrusiveRefCntPtr<vfs::FileSystem> getVFS() const { return VFS; }
+
+  /// Create a Reproducer instance based on the given mode.
+  static llvm::Expected<std::unique_ptr<Reproducer>>
+  createReproducer(ReproducerMode Mode, StringRef Root);
+
+protected:
+  IntrusiveRefCntPtr<vfs::FileSystem> VFS;
+};
+
+/// Reproducer instance used to generate a new reproducer. The VFS returned by
+/// this instance is a FileCollectorFileSystem that tracks every file used by
+/// dsymutil.
+class ReproducerGenerate : public Reproducer {
+public:
+  ReproducerGenerate(std::error_code &EC);
+  ~ReproducerGenerate() override;
+
+private:
+  /// The path to the reproducer.
+  std::string Root;
+
+  /// The FileCollector used by the FileCollectorFileSystem.
+  std::shared_ptr<FileCollector> FC;
+};
+
+/// 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.
+class ReproducerUse : public Reproducer {
+public:
+  ReproducerUse(StringRef Root, std::error_code &EC);
+  ~ReproducerUse() override;
+
+private:
+  /// The path to the reproducer.
+  std::string Root;
+};
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_REPRODUCER_H

diff  --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp
index 8ea89933e362..32df55611f07 100644
--- 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 @@ struct DsymutilOptions {
   std::string SymbolMap;
   std::string OutputFile;
   std::string Toolchain;
+  std::string ReproducerPath;
   std::vector<std::string> Archs;
   std::vector<std::string> InputFiles;
   unsigned NumThreads;
+  ReproducerMode ReproMode = ReproducerMode::Off;
   dsymutil::LinkOptions LinkOpts;
 };
 
@@ -182,6 +186,12 @@ static Error verifyOptions(const DsymutilOptions &Options) {
         "paper trail warnings are not supported for YAML input.",
         errc::invalid_argument);
 
+  if (!Options.ReproducerPath.empty() &&
+      Options.ReproMode != ReproducerMode::Use)
+    return make_error<StringError>(
+        "cannot combine --gen-reproducer and --use-reproducer.",
+        errc::invalid_argument);
+
   return Error::success();
 }
 
@@ -222,6 +232,14 @@ static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
   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<AccelTableKind> AccelKind = getAccelTableKind(Args)) {
     Options.LinkOpts.TheAccelTableKind = *AccelKind;
   } else {
@@ -499,6 +517,15 @@ int main(int argc, char **argv) {
   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)) {


        


More information about the llvm-commits mailing list