[lld] r268169 - ELF: Make --reproduce to produce a response file.

Rui Ueyama via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 30 14:40:04 PDT 2016


Author: ruiu
Date: Sat Apr 30 16:40:04 2016
New Revision: 268169

URL: http://llvm.org/viewvc/llvm-project?rev=268169&view=rev
Log:
ELF: Make --reproduce to produce a response file.

The aim of this patch is to make it easy to re-run the command without
updating paths in the command line. Here is a use case.

Assume that Alice is having an issue with lld and is reporting the issue
to developer Bob. Alice's current directly is /home/alice/work and her
command line is "ld.lld -o foo foo.o ../bar.o". She adds "--reproduce repro"
to the command line and re-run. Then the following text will be produced as
response.txt (notice that the paths are rewritten so that they are
relative to /home/alice/work/repro.)

  -o home/alice/work/foo home/alice/work/foo.o home/alice/bar.o

The command also produces the following files by copying inputs.

  /home/alice/repro/home/alice/work/foo.o
  /home/alice/repro/home/alice/bar.o

Alice zips the directory and send it to Bob. Bob get an archive from Alice
and extract it to his home directory as /home/bob/repro. Now his directory
have the following files.

  /home/bob/repro/response.txt
  /home/bob/repro/home/alice/work/foo.o
  /home/bob/repro/home/alice/bar.o

Bob then re-run the command with these files by the following commands.

  cd /home/bob/repro
  ld.lld @response.txt

This command will run the linker with the same command line options and
the same input files as Alice's, so it is very likely that Bob will see
the same issue as Alice saw.

Differential Revision: http://reviews.llvm.org/D19737

Modified:
    lld/trunk/ELF/Driver.cpp
    lld/trunk/ELF/Driver.h
    lld/trunk/ELF/DriverUtils.cpp
    lld/trunk/test/ELF/reproduce.s

Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=268169&r1=268168&r2=268169&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Sat Apr 30 16:40:04 2016
@@ -109,8 +109,6 @@ void LinkerDriver::addFile(StringRef Pat
   using namespace llvm::sys::fs;
   if (Config->Verbose)
     llvm::outs() << Path << "\n";
-  if (!Config->Reproduce.empty())
-    copyFile(Path, concat_paths(Config->Reproduce, Path));
 
   Optional<MemoryBufferRef> Buffer = readFile(Path);
   if (!Buffer.hasValue())
@@ -238,25 +236,6 @@ static bool hasZOption(opt::InputArgList
   return false;
 }
 
-static void logCommandline(ArrayRef<const char *> Args) {
-  if (std::error_code EC = sys::fs::create_directories(
-        Config->Reproduce, /*IgnoreExisting=*/false)) {
-    error(EC, Config->Reproduce + ": can't create directory");
-    return;
-  }
-
-  SmallString<128> Path;
-  path::append(Path, Config->Reproduce, "invocation.txt");
-  std::error_code EC;
-  raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
-  check(EC);
-
-  OS << Args[0];
-  for (size_t I = 1, E = Args.size(); I < E; ++I)
-    OS << " " << Args[I];
-  OS << "\n";
-}
-
 void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
   ELFOptTable Parser;
   opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
@@ -273,7 +252,7 @@ void LinkerDriver::main(ArrayRef<const c
   initLLVM(Args);
 
   if (!Config->Reproduce.empty())
-    logCommandline(ArgsArr);
+    saveLinkerInputs(Args);
 
   createFiles(Args);
   checkOptions(Args);

Modified: lld/trunk/ELF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.h?rev=268169&r1=268168&r2=268169&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.h (original)
+++ lld/trunk/ELF/Driver.h Sat Apr 30 16:40:04 2016
@@ -68,9 +68,7 @@ enum {
 void printHelp(const char *Argv0);
 void printVersion();
 
-std::string concat_paths(StringRef S, StringRef T);
-void copyFile(StringRef Src, StringRef Dest);
-
+void saveLinkerInputs(const llvm::opt::InputArgList &Args);
 std::string findFromSearchPaths(StringRef Path);
 std::string searchLibrary(StringRef Path);
 std::string buildSysrootedPath(llvm::StringRef Dir, llvm::StringRef File);

Modified: lld/trunk/ELF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/DriverUtils.cpp?rev=268169&r1=268168&r2=268169&view=diff
==============================================================================
--- lld/trunk/ELF/DriverUtils.cpp (original)
+++ lld/trunk/ELF/DriverUtils.cpp Sat Apr 30 16:40:04 2016
@@ -87,31 +87,93 @@ void elf::printVersion() {
   outs() << "\n";
 }
 
-// Concatenates S and T so that the resulting path becomes S/T.
-// There are a few exceptions:
-//
-//  1. The result will never escape from S. Therefore, all ".."
-//     are removed from T before concatenatig them.
-//  2. Windows drive letters are removed from T before concatenation.
-std::string elf::concat_paths(StringRef S, StringRef T) {
-  // Remove leading '/' or a drive letter, and then remove "..".
-  SmallString<128> T2(path::relative_path(T));
-  path::remove_dots(T2, /*remove_dot_dot=*/true);
+// Makes a given pathname an absolute path first, and then remove
+// beginning /. For example, "../foo.o" is converted to "home/john/foo.o",
+// assuming that the current directory is "/home/john/bar".
+static std::string relative_to_root(StringRef Path) {
+  SmallString<128> Abs = Path;
+  if (std::error_code EC = fs::make_absolute(Abs))
+    fatal("make_absolute failed: " + EC.message());
+  path::remove_dots(Abs, /*remove_dot_dot=*/true);
 
+  // This is Windows specific. root_name() returns a drive letter
+  // (e.g. "c:") or a UNC name (//net). We want to keep it as part
+  // of the result.
   SmallString<128> Res;
-  path::append(Res, S, T2);
+  StringRef Root = path::root_name(Path);
+  if (Path.endswith(":"))
+    Res = Root.drop_back();
+  else if (Path.startswith("//"))
+    Res = Root.substr(2);
+
+  path::append(Res, path::relative_path(Abs));
   return Res.str();
 }
 
-void elf::copyFile(StringRef Src, StringRef Dest) {
+// Copies file Src to {Config->Reproduce}/Src.
+// Returns the new path relative to Config->Reproduce.
+static std::string copyFile(StringRef Src) {
+  std::string Relpath = relative_to_root(Src);
+  SmallString<128> Dest;
+  path::append(Dest, Config->Reproduce, Relpath);
+
   SmallString<128> Dir(Dest);
   path::remove_filename(Dir);
-  if (std::error_code EC = sys::fs::create_directories(Dir)) {
+  if (std::error_code EC = sys::fs::create_directories(Dir))
     error(EC, Dir + ": can't create directory");
-    return;
-  }
   if (std::error_code EC = sys::fs::copy_file(Src, Dest))
     error(EC, "failed to copy file: " + Dest);
+  return Relpath;
+}
+
+// Quote a given string if it contains a space character.
+static std::string quote(StringRef S) {
+  if (S.find(' ') == StringRef::npos)
+    return S;
+  return ("\"" + S + "\"").str();
+}
+
+// Copies all input files to Config->Reproduce directory and
+// create a response file as "response.txt", so that you can re-run
+// the same command with the same inputs just by executing
+// "ld.lld @response.txt". Used by --reproduce. This feature is
+// supposed to be used by users to report an issue to LLD developers.
+void elf::saveLinkerInputs(const llvm::opt::InputArgList &Args) {
+  // Create the output directory.
+  if (std::error_code EC = sys::fs::create_directories(
+        Config->Reproduce, /*IgnoreExisting=*/false)) {
+    error(EC, Config->Reproduce + ": can't create directory");
+    return;
+  }
+
+  // Open "response.txt".
+  SmallString<128> Path;
+  path::append(Path, Config->Reproduce, "response.txt");
+  std::error_code EC;
+  raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
+  check(EC);
+
+  // Dump the command line to response.txt while copying files
+  // and rewriting paths.
+  for (auto *Arg : Args) {
+    switch (Arg->getOption().getID()) {
+    case OPT_reproduce:
+      break;
+    case OPT_script:
+      OS << "--script ";
+      // fallthrough
+    case OPT_INPUT: {
+      StringRef Path = Arg->getValue();
+      if (fs::exists(Path))
+        OS << quote(copyFile(Path)) << "\n";
+      else
+        OS << quote(Path) << "\n";
+      break;
+    }
+    default:
+      OS << Arg->getAsString(Args) << "\n";
+    }
+  }
 }
 
 std::string elf::findFromSearchPaths(StringRef Path) {

Modified: lld/trunk/test/ELF/reproduce.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/reproduce.s?rev=268169&r1=268168&r2=268169&view=diff
==============================================================================
--- lld/trunk/test/ELF/reproduce.s (original)
+++ lld/trunk/test/ELF/reproduce.s Sat Apr 30 16:40:04 2016
@@ -5,16 +5,24 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.dir/build1/foo.o
 # RUN: cd %t.dir
 # RUN: ld.lld build1/foo.o -o bar -shared --as-needed --reproduce repro
-# RUN: diff build1/foo.o repro/build1/foo.o
+# RUN: diff build1/foo.o repro/%:t.dir/build1/foo.o
 
-# RUN: FileCheck %s --check-prefix=INVOCATION < repro/invocation.txt
-# INVOCATION: lld{{[^\s]*}} build1/foo.o -o bar -shared --as-needed --reproduce repro
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+# RSP:      {{.*}}foo.o
+# RSP-NEXT: -o bar
+# RSP-NEXT: -shared
+# RSP-NEXT: --as-needed
 
 # RUN: mkdir -p %t.dir/build2/a/b/c
 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.dir/build2/foo.o
 # RUN: cd %t.dir/build2/a/b/c
 # RUN: ld.lld ./../../../foo.o -o bar -shared --as-needed --reproduce repro
-# RUN: diff %t.dir/build2/foo.o repro/foo.o
+# RUN: diff %t.dir/build2/foo.o repro/%:t.dir/build2/foo.o
+
+# RUN: not ld.lld build1/foo.o --reproduce repro2 'foo bar' -soname=foo
+# RUN: FileCheck %s --check-prefix=RSP2 < repro2/response.txt
+# RSP2:      "foo bar"
+# RSP2-NEXT: -soname=foo
 
 # RUN: not ld.lld build1/foo.o -o bar -shared --as-needed --reproduce . 2>&1 \
 # RUN:   | FileCheck --check-prefix=ERROR %s




More information about the llvm-commits mailing list