[PATCH] D102304: [WIP][lld] Implement crash reproducer

Haowei Wu via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Tue May 11 20:47:29 PDT 2021


haowei created this revision.
haowei added reviewers: phosek, mcgrathr.
Herald added subscribers: dang, arichardson, emaste.
Herald added a reviewer: MaskRay.
haowei requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

This is still work in progress to test my ideas. Not ready for review yet.

In this patch, I tried to use CrashRecoveryContext to re-run lldMain with '--reproduce' when the first run crashes. Flag 'reproduce-on-crash' is added to optionally turn on this feature.  Flag '--debug-crash' to allow lld crash on purpose to test crash reproducer.

The reason I choose this approach is that it has a few advantages:

- It is quite simple.
- It works with every lld driver which already support '--reproduce'.

However, during testing, I saw  a few issues:

- If the crash happens before the driver initialize the TarWriter for reproducer, this implementation will not catch it.
- lldMain is not designed to run twice internally. It will write unexpected errors to stderr. (e.g. I saw 'lld/ELF/InputSection.cpp:1402: void lld::elf::MergeInputSection::splitIntoPieces(): Assertion `pieces.empty()' failed.' error in tests)

Therefore, I am wondering if I should consider another approach: reimplement the reproducer each driver (start with ELF driver) to make it independent from the lld driver. In this case the reproducer can be directly invoked from CrashRecoveryContext in the lld/tools/lld/lld.cpp instead of being invoked from the rerun lld driver.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D102304

Files:
  lld/ELF/Driver.cpp
  lld/ELF/Options.td
  lld/tools/lld/lld.cpp


Index: lld/tools/lld/lld.cpp
===================================================================
--- lld/tools/lld/lld.cpp
+++ lld/tools/lld/lld.cpp
@@ -173,8 +173,40 @@
     llvm::CrashRecoveryContext crc;
     if (!crc.RunSafely([&]() {
           r = lldMain(argc, argv, stdoutOS, stderrOS, /*exitEarly=*/false);
-        }))
-      return {crc.RetCode, /*canRunAgain=*/false};
+        })) {
+      // lld crashed, save return code.
+      SafeReturn ret{crc.RetCode, /*canRunAgain=*/false};
+      // Generate reproducer if related flag is specified.
+      std::vector<const char *> args(argv, argv + argc);
+      bool hasReproducer = false;
+      bool hasReproduceOnCrash = false;
+      for (const char *arg : args) {
+        std::string arg_str(arg);
+        if (arg_str == "--reproduce-on-crash")
+          hasReproduceOnCrash = true;
+        if (StringRef(arg_str).startswith("--reproduce="))
+          hasReproducer = true;
+      }
+      if (hasReproduceOnCrash && !hasReproducer) {
+        // Creat temp file for reproducer.
+        SmallString<128> reproducerPath;
+        std::error_code EC =
+            llvm::sys::fs::createTemporaryFile("lld", "tar", reproducerPath);
+        if (EC) {
+          stderrOS << "failed to create temporary file for reproducer\n";
+          return ret;
+        }
+        stdoutOS << "reproducer writes to \"" << reproducerPath << "\"\n";
+        std::string reproduceArg = "--reproduce=" + std::string(reproducerPath);
+        args.push_back(reproduceArg.c_str());
+        llvm::CrashRecoveryContext crcReproducer;
+        crcReproducer.RunSafely([&]() {
+          lldMain(args.size(), args.data(), stdoutOS, stderrOS,
+                  /*exitEarly=*/false);
+        });
+      }
+      return ret;
+    }
   }
 
   // Cleanup memory and reset everything back in pristine condition. This path
Index: lld/ELF/Options.td
===================================================================
--- lld/ELF/Options.td
+++ lld/ELF/Options.td
@@ -351,6 +351,12 @@
   Eq<"reproduce",
      "Write tar file containing inputs and command to reproduce link">;
 
+def reproduce_on_crash: F<"reproduce-on-crash">,
+     HelpText<"Generate tar file containing inputs and command when program crashes">;
+
+def debug_crash: F<"debug-crash">,
+    HelpText<"Debug flag to crash lld on purpose">;
+
 defm rosegment: BB<"rosegment",
   "Put read-only non-executable sections in their own segment (default)",
   "Do not put read-only non-executable sections in their own segment">;
Index: lld/ELF/Driver.cpp
===================================================================
--- lld/ELF/Driver.cpp
+++ lld/ELF/Driver.cpp
@@ -582,6 +582,10 @@
 
     timeTraceProfilerCleanup();
   }
+
+  if (args.getLastArg(OPT_debug_crash)) {
+    LLVM_BUILTIN_TRAP;
+  }
 }
 
 static std::string getRpath(opt::InputArgList &args) {


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D102304.344639.patch
Type: text/x-patch
Size: 2868 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20210512/399c6be5/attachment.bin>


More information about the llvm-commits mailing list