This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rGc8dfe907299e: [Reproducer] Generate LLDB reproducer on crash 
(authored by JDevlieghere).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D70474/new/

https://reviews.llvm.org/D70474

Files:
  lldb/include/lldb/API/SBReproducer.h
  lldb/source/API/SBReproducer.cpp
  lldb/source/Commands/CommandObjectReproducer.cpp
  lldb/source/Commands/Options.td
  lldb/test/Shell/Reproducer/Inputs/GDBRemoteCrashCapture.in
  lldb/test/Shell/Reproducer/TestCrash.test
  lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test
  lldb/tools/driver/Driver.cpp

Index: lldb/tools/driver/Driver.cpp
===================================================================
--- lldb/tools/driver/Driver.cpp
+++ lldb/tools/driver/Driver.cpp
@@ -732,6 +732,20 @@
   signal(signo, sigcont_handler);
 }
 
+void reproducer_handler(void *) {
+  if (SBReproducer::Generate()) {
+    llvm::outs() << "********************\n";
+    llvm::outs() << "Crash reproducer for ";
+    llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';
+    llvm::outs() << "Reproducer written to '" << SBReproducer::GetPath()
+                 << "'\n";
+    llvm::outs()
+        << "Please have a look at the directory to assess if you're willing to "
+           "share the contained information.\n";
+    llvm::outs() << "********************\n";
+  }
+}
+
 static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
   std::string usage_str = tool_name.str() + "options";
   table.PrintHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
@@ -832,6 +846,9 @@
     return *exit_code;
   }
 
+  // Register the reproducer signal handler.
+  llvm::sys::AddSignalHandler(reproducer_handler, nullptr);
+
   SBError error = SBDebugger::InitializeWithErrorHandling();
   if (error.Fail()) {
     WithColor::error() << "initialization failed: " << error.GetCString()
Index: lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test
===================================================================
--- lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test
+++ lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test
@@ -6,11 +6,18 @@
 # process. To ensure we're not actually running the original binary we check
 # that the string "testing" is not printed.
 
-# RUN: rm -rf %t.repro
 # RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
+
+# Test reproducer generate command.
+# RUN: rm -rf %t.repro
 # RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture --capture-path %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
 # RUN: env FOO=BAR %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
 
+# Test crash reproducer.
+# RUN: rm -rf %t.repro
+# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCrashCapture.in --capture --capture-path %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
+# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
+
 # CHECK: Breakpoint 1
 # CHECK: Process {{.*}} stopped
 # CHECK: Process {{.*}} launched
Index: lldb/test/Shell/Reproducer/TestCrash.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Reproducer/TestCrash.test
@@ -0,0 +1,14 @@
+# UNSUPPORTED: system-windows
+# This tests that a reproducer is generated when LLDB crashes.
+
+# Start clean.
+# RUN: rm -rf %t.repro
+
+# RUN: %lldb -b --capture --capture-path %t.repro -o 'reproducer xcrash -s SIGSEGV' | FileCheck %s
+# RUN: %lldb -b --capture --capture-path %t.repro -o 'reproducer xcrash -s SIGBUS' | FileCheck %s
+# RUN: %lldb -b --capture --capture-path %t.repro -o 'reproducer xcrash -s SIGILL' | FileCheck %s
+
+# CHECK: ********************
+# CHECK: Crash reproducer for
+# CHECK: Reproducer written to
+# CHECK: ********************
Index: lldb/test/Shell/Reproducer/Inputs/GDBRemoteCrashCapture.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/Reproducer/Inputs/GDBRemoteCrashCapture.in
@@ -0,0 +1,6 @@
+breakpoint set -f simple.c -l 12
+run
+bt
+cont
+reproducer status
+reproducer xcrash -s SIGSEGV
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -438,6 +438,12 @@
     "provided, that reproducer is dumped.">;
 }
 
+let Command = "reproducer xcrash" in {
+  def reproducer_signal : Option<"signal", "s">, Group<1>,
+    EnumArg<"None", "ReproducerSignalType()">,
+    Required, Desc<"The signal to crash the debugger.">;
+}
+
 let Command = "memory read" in {
   def memory_read_num_per_line : Option<"num-per-line", "l">, Group<1>,
     Arg<"NumberPerLine">, Desc<"The number of items per line to display.">;
Index: lldb/source/Commands/CommandObjectReproducer.cpp
===================================================================
--- lldb/source/Commands/CommandObjectReproducer.cpp
+++ lldb/source/Commands/CommandObjectReproducer.cpp
@@ -17,6 +17,8 @@
 #include "lldb/Interpreter/OptionArgParser.h"
 #include "lldb/Interpreter/OptionGroupBoolean.h"
 
+#include <csignal>
+
 using namespace lldb;
 using namespace llvm;
 using namespace lldb_private;
@@ -71,6 +73,37 @@
 #define LLDB_OPTIONS_reproducer_dump
 #include "CommandOptions.inc"
 
+enum ReproducerCrashSignal {
+  eReproducerCrashSigbus,
+  eReproducerCrashSigill,
+  eReproducerCrashSigsegv,
+};
+
+static constexpr OptionEnumValueElement g_reproducer_signaltype[] = {
+    {
+        eReproducerCrashSigbus,
+        "SIGBUS",
+        "Bus error",
+    },
+    {
+        eReproducerCrashSigill,
+        "SIGILL",
+        "Illegal instruction",
+    },
+    {
+        eReproducerCrashSigsegv,
+        "SIGSEGV",
+        "Segmentation fault",
+    },
+};
+
+static constexpr OptionEnumValues ReproducerSignalType() {
+  return OptionEnumValues(g_reproducer_signaltype);
+}
+
+#define LLDB_OPTIONS_reproducer_xcrash
+#include "CommandOptions.inc"
+
 class CommandObjectReproducerGenerate : public CommandObjectParsed {
 public:
   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
@@ -117,12 +150,98 @@
   }
 };
 
+class CommandObjectReproducerXCrash : public CommandObjectParsed {
+public:
+  CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "reproducer xcrash",
+                            "Intentionally force  the debugger to crash in "
+                            "order to trigger and test reproducer generation.",
+                            nullptr) {}
+
+  ~CommandObjectReproducerXCrash() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options() {}
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 's':
+        signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
+            option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+        if (!error.Success())
+          error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
+                                         option_arg.str().c_str());
+        break;
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      signal = eReproducerCrashSigsegv;
+    }
+
+    ArrayRef<OptionDefinition> GetDefinitions() override {
+      return makeArrayRef(g_reproducer_xcrash_options);
+    }
+
+    ReproducerCrashSignal signal = eReproducerCrashSigsegv;
+  };
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (!command.empty()) {
+      result.AppendErrorWithFormat("'%s' takes no arguments",
+                                   m_cmd_name.c_str());
+      return false;
+    }
+
+    auto &r = Reproducer::Instance();
+    if (!r.IsCapturing()) {
+      result.SetError(
+          "forcing a crash is only supported when capturing a reproducer.");
+      result.SetStatus(eReturnStatusSuccessFinishNoResult);
+      return false;
+    }
+
+    switch (m_options.signal) {
+    case eReproducerCrashSigill:
+      std::raise(SIGILL);
+      break;
+    case eReproducerCrashSigbus:
+      std::raise(SIGBUS);
+      break;
+    case eReproducerCrashSigsegv:
+      std::raise(SIGSEGV);
+      break;
+    }
+
+    result.SetStatus(eReturnStatusQuit);
+    return result.Succeeded();
+  }
+
+private:
+  CommandOptions m_options;
+};
+
 class CommandObjectReproducerStatus : public CommandObjectParsed {
 public:
   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
       : CommandObjectParsed(
             interpreter, "reproducer status",
-            "Show the current reproducer status. In capture mode the debugger "
+            "Show the current reproducer status. In capture mode the "
+            "debugger "
             "is collecting all the information it needs to create a "
             "reproducer.  In replay mode the reproducer is replaying a "
             "reproducer. When the reproducers are off, no data is collected "
@@ -365,7 +484,8 @@
     CommandInterpreter &interpreter)
     : CommandObjectMultiword(
           interpreter, "reproducer",
-          "Commands for manipulating reproducers. Reproducers make it possible "
+          "Commands for manipulating reproducers. Reproducers make it "
+          "possible "
           "to capture full debug sessions with all its dependencies. The "
           "resulting reproducer is used to replay the debug session while "
           "debugging the debugger.\n"
@@ -382,6 +502,8 @@
                                new CommandObjectReproducerStatus(interpreter)));
   LoadSubCommand("dump",
                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
+  LoadSubCommand("xcrash", CommandObjectSP(
+                               new CommandObjectReproducerXCrash(interpreter)));
 }
 
 CommandObjectReproducer::~CommandObjectReproducer() = default;
Index: lldb/source/API/SBReproducer.cpp
===================================================================
--- lldb/source/API/SBReproducer.cpp
+++ lldb/source/API/SBReproducer.cpp
@@ -30,7 +30,7 @@
 using namespace lldb_private::repro;
 
 SBRegistry::SBRegistry() {
-  Registry& R = *this;
+  Registry &R = *this;
 
   RegisterMethods<SBAddress>(R);
   RegisterMethods<SBAttachInfo>(R);
@@ -149,6 +149,22 @@
   return nullptr;
 }
 
+bool SBReproducer::Generate() {
+  auto &r = Reproducer::Instance();
+  if (auto generator = r.GetGenerator()) {
+    generator->Keep();
+    return true;
+  }
+  return false;
+}
+
+const char *SBReproducer::GetPath() {
+  static std::string path;
+  auto &r = Reproducer::Instance();
+  path = r.GetReproducerPath().GetCString();
+  return path.c_str();
+}
+
 char lldb_private::repro::SBProvider::ID = 0;
 const char *SBProvider::Info::name = "sbapi";
 const char *SBProvider::Info::file = "sbapi.bin";
Index: lldb/include/lldb/API/SBReproducer.h
===================================================================
--- lldb/include/lldb/API/SBReproducer.h
+++ lldb/include/lldb/API/SBReproducer.h
@@ -21,6 +21,8 @@
   static const char *Capture();
   static const char *Capture(const char *path);
   static const char *Replay(const char *path);
+  static const char *GetPath();
+  static bool Generate();
 };
 
 } // namespace lldb
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to