diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectReproducer.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectReproducer.cpp | 178 |
1 files changed, 148 insertions, 30 deletions
diff --git a/lldb/source/Commands/CommandObjectReproducer.cpp b/lldb/source/Commands/CommandObjectReproducer.cpp index dc4579c20fc2..d15f622314d9 100644 --- a/lldb/source/Commands/CommandObjectReproducer.cpp +++ b/lldb/source/Commands/CommandObjectReproducer.cpp @@ -15,7 +15,8 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" -#include "lldb/Interpreter/OptionGroupBoolean.h" + +#include <csignal> using namespace lldb; using namespace llvm; @@ -68,7 +69,32 @@ static constexpr OptionEnumValues ReproducerProviderType() { return OptionEnumValues(g_reproducer_provider_type); } -#define LLDB_OPTIONS_reproducer +#define LLDB_OPTIONS_reproducer_dump +#include "CommandOptions.inc" + +enum ReproducerCrashSignal { + eReproducerCrashSigill, + eReproducerCrashSigsegv, +}; + +static constexpr OptionEnumValueElement g_reproducer_signaltype[] = { + { + 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 { @@ -78,7 +104,7 @@ public: interpreter, "reproducer generate", "Generate reproducer on disk. When the debugger is in capture " "mode, this command will output the reproducer to a directory on " - "disk. In replay mode this command in a no-op.", + "disk and quit. In replay mode this command in a no-op.", nullptr) {} ~CommandObjectReproducerGenerate() override = default; @@ -95,7 +121,7 @@ protected: if (auto generator = r.GetGenerator()) { generator->Keep(); } else if (r.IsReplaying()) { - // Make this operation a NOP in replay mode. + // Make this operation a NO-OP in replay mode. result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } else { @@ -110,9 +136,94 @@ protected: << "Please have a look at the directory to assess if you're willing to " "share the contained information.\n"; - result.SetStatus(eReturnStatusSuccessFinishResult); + m_interpreter.BroadcastEvent( + CommandInterpreter::eBroadcastBitQuitCommandReceived); + result.SetStatus(eReturnStatusQuit); + return result.Succeeded(); + } +}; + +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() && !r.IsReplaying()) { + 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 eReproducerCrashSigsegv: + std::raise(SIGSEGV); + break; + } + + result.SetStatus(eReturnStatusQuit); return result.Succeeded(); } + +private: + CommandOptions m_options; }; class CommandObjectReproducerStatus : public CommandObjectParsed { @@ -120,7 +231,8 @@ 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 " @@ -206,7 +318,7 @@ public: } ArrayRef<OptionDefinition> GetDefinitions() override { - return makeArrayRef(g_reproducer_options); + return makeArrayRef(g_reproducer_dump_options); } FileSpec file; @@ -294,10 +406,9 @@ protected: return true; } case eReproducerProviderCommands: { - // Create a new command loader. - std::unique_ptr<repro::CommandLoader> command_loader = - repro::CommandLoader::Create(loader); - if (!command_loader) { + std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader = + repro::MultiLoader<repro::CommandProvider>::Create(loader); + if (!multi_loader) { SetError(result, make_error<StringError>(llvm::inconvertibleErrorCode(), "Unable to create command loader.")); @@ -305,9 +416,8 @@ protected: } // Iterate over the command files and dump them. - while (true) { - llvm::Optional<std::string> command_file = - command_loader->GetNextFile(); + llvm::Optional<std::string> command_file; + while ((command_file = multi_loader->GetNextFile())) { if (!command_file) break; @@ -323,24 +433,29 @@ protected: return true; } case eReproducerProviderGDB: { - FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>(); - auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath()); - if (auto err = error_or_file.getError()) { - SetError(result, errorCodeToError(err)); - return false; - } + std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>> + multi_loader = + repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader); + llvm::Optional<std::string> gdb_file; + while ((gdb_file = multi_loader->GetNextFile())) { + auto error_or_file = MemoryBuffer::getFile(*gdb_file); + if (auto err = error_or_file.getError()) { + SetError(result, errorCodeToError(err)); + return false; + } - std::vector<GDBRemotePacket> packets; - yaml::Input yin((*error_or_file)->getBuffer()); - yin >> packets; + std::vector<GDBRemotePacket> packets; + yaml::Input yin((*error_or_file)->getBuffer()); + yin >> packets; - if (auto err = yin.error()) { - SetError(result, errorCodeToError(err)); - return false; - } + if (auto err = yin.error()) { + SetError(result, errorCodeToError(err)); + return false; + } - for (GDBRemotePacket &packet : packets) { - packet.Dump(result.GetOutputStream()); + for (GDBRemotePacket &packet : packets) { + packet.Dump(result.GetOutputStream()); + } } result.SetStatus(eReturnStatusSuccessFinishResult); @@ -363,7 +478,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" @@ -380,6 +496,8 @@ CommandObjectReproducer::CommandObjectReproducer( new CommandObjectReproducerStatus(interpreter))); LoadSubCommand("dump", CommandObjectSP(new CommandObjectReproducerDump(interpreter))); + LoadSubCommand("xcrash", CommandObjectSP( + new CommandObjectReproducerXCrash(interpreter))); } CommandObjectReproducer::~CommandObjectReproducer() = default; |