summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/include/lldb/API/SBReproducer.h2
-rw-r--r--lldb/source/API/SBReproducer.cpp18
-rw-r--r--lldb/source/Commands/CommandObjectReproducer.cpp126
-rw-r--r--lldb/source/Commands/Options.td6
-rw-r--r--lldb/test/Shell/Reproducer/Inputs/GDBRemoteCrashCapture.in6
-rw-r--r--lldb/test/Shell/Reproducer/TestCrash.test14
-rw-r--r--lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test9
-rw-r--r--lldb/tools/driver/Driver.cpp17
8 files changed, 194 insertions, 4 deletions
diff --git a/lldb/include/lldb/API/SBReproducer.h b/lldb/include/lldb/API/SBReproducer.h
index 0f1739d0c5b..93e567607aa 100644
--- a/lldb/include/lldb/API/SBReproducer.h
+++ b/lldb/include/lldb/API/SBReproducer.h
@@ -21,6 +21,8 @@ public:
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
diff --git a/lldb/source/API/SBReproducer.cpp b/lldb/source/API/SBReproducer.cpp
index 6e11b2c6366..d50d95ebb54 100644
--- a/lldb/source/API/SBReproducer.cpp
+++ b/lldb/source/API/SBReproducer.cpp
@@ -30,7 +30,7 @@ using namespace lldb_private;
using namespace lldb_private::repro;
SBRegistry::SBRegistry() {
- Registry& R = *this;
+ Registry &R = *this;
RegisterMethods<SBAddress>(R);
RegisterMethods<SBAttachInfo>(R);
@@ -149,6 +149,22 @@ const char *SBReproducer::Replay(const char *path) {
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";
diff --git a/lldb/source/Commands/CommandObjectReproducer.cpp b/lldb/source/Commands/CommandObjectReproducer.cpp
index a22c704ebd0..7f97ba2875c 100644
--- a/lldb/source/Commands/CommandObjectReproducer.cpp
+++ b/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 @@ static constexpr OptionEnumValues ReproducerProviderType() {
#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 @@ protected:
}
};
+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 @@ CommandObjectReproducer::CommandObjectReproducer(
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 @@ CommandObjectReproducer::CommandObjectReproducer(
new CommandObjectReproducerStatus(interpreter)));
LoadSubCommand("dump",
CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
+ LoadSubCommand("xcrash", CommandObjectSP(
+ new CommandObjectReproducerXCrash(interpreter)));
}
CommandObjectReproducer::~CommandObjectReproducer() = default;
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 501f81470a2..f53d1481f4e 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -438,6 +438,12 @@ let Command = "reproducer dump" in {
"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.">;
diff --git a/lldb/test/Shell/Reproducer/Inputs/GDBRemoteCrashCapture.in b/lldb/test/Shell/Reproducer/Inputs/GDBRemoteCrashCapture.in
new file mode 100644
index 00000000000..1a733f4dd94
--- /dev/null
+++ b/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
diff --git a/lldb/test/Shell/Reproducer/TestCrash.test b/lldb/test/Shell/Reproducer/TestCrash.test
new file mode 100644
index 00000000000..1f26a4ebf03
--- /dev/null
+++ b/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: ********************
diff --git a/lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test b/lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test
index 04a3e5465bb..609c8392929 100644
--- a/lldb/test/Shell/Reproducer/TestGDBRemoteRepro.test
+++ b/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
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index 3a22d7aabc1..9d685f2e35b 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -732,6 +732,20 @@ void sigcont_handler(int signo) {
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 @@ int main(int argc, char const *argv[]) {
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()
OpenPOWER on IntegriCloud