[PowerPC] Collect some CallLowering arguments into a struct. [NFC]
[llvm-project.git] / lldb / source / Commands / CommandObjectReproducer.cpp
blob52c42a7336a485068e660d8c521529868d692b5c
1 //===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "CommandObjectReproducer.h"
11 #include "lldb/Host/OptionParser.h"
12 #include "lldb/Utility/GDBRemote.h"
13 #include "lldb/Utility/Reproducer.h"
15 #include "lldb/Interpreter/CommandInterpreter.h"
16 #include "lldb/Interpreter/CommandReturnObject.h"
17 #include "lldb/Interpreter/OptionArgParser.h"
19 #include <csignal>
21 using namespace lldb;
22 using namespace llvm;
23 using namespace lldb_private;
24 using namespace lldb_private::repro;
26 enum ReproducerProvider {
27 eReproducerProviderCommands,
28 eReproducerProviderFiles,
29 eReproducerProviderGDB,
30 eReproducerProviderVersion,
31 eReproducerProviderWorkingDirectory,
32 eReproducerProviderNone
35 static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
37 eReproducerProviderCommands,
38 "commands",
39 "Command Interpreter Commands",
42 eReproducerProviderFiles,
43 "files",
44 "Files",
47 eReproducerProviderGDB,
48 "gdb",
49 "GDB Remote Packets",
52 eReproducerProviderVersion,
53 "version",
54 "Version",
57 eReproducerProviderWorkingDirectory,
58 "cwd",
59 "Working Directory",
62 eReproducerProviderNone,
63 "none",
64 "None",
68 static constexpr OptionEnumValues ReproducerProviderType() {
69 return OptionEnumValues(g_reproducer_provider_type);
72 #define LLDB_OPTIONS_reproducer_dump
73 #include "CommandOptions.inc"
75 enum ReproducerCrashSignal {
76 eReproducerCrashSigill,
77 eReproducerCrashSigsegv,
80 static constexpr OptionEnumValueElement g_reproducer_signaltype[] = {
82 eReproducerCrashSigill,
83 "SIGILL",
84 "Illegal instruction",
87 eReproducerCrashSigsegv,
88 "SIGSEGV",
89 "Segmentation fault",
93 static constexpr OptionEnumValues ReproducerSignalType() {
94 return OptionEnumValues(g_reproducer_signaltype);
97 #define LLDB_OPTIONS_reproducer_xcrash
98 #include "CommandOptions.inc"
100 class CommandObjectReproducerGenerate : public CommandObjectParsed {
101 public:
102 CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
103 : CommandObjectParsed(
104 interpreter, "reproducer generate",
105 "Generate reproducer on disk. When the debugger is in capture "
106 "mode, this command will output the reproducer to a directory on "
107 "disk and quit. In replay mode this command in a no-op.",
108 nullptr) {}
110 ~CommandObjectReproducerGenerate() override = default;
112 protected:
113 bool DoExecute(Args &command, CommandReturnObject &result) override {
114 if (!command.empty()) {
115 result.AppendErrorWithFormat("'%s' takes no arguments",
116 m_cmd_name.c_str());
117 return false;
120 auto &r = Reproducer::Instance();
121 if (auto generator = r.GetGenerator()) {
122 generator->Keep();
123 } else if (r.IsReplaying()) {
124 // Make this operation a NO-OP in replay mode.
125 result.SetStatus(eReturnStatusSuccessFinishNoResult);
126 return result.Succeeded();
127 } else {
128 result.AppendErrorWithFormat("Unable to get the reproducer generator");
129 result.SetStatus(eReturnStatusFailed);
130 return false;
133 result.GetOutputStream()
134 << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
135 result.GetOutputStream()
136 << "Please have a look at the directory to assess if you're willing to "
137 "share the contained information.\n";
139 m_interpreter.BroadcastEvent(
140 CommandInterpreter::eBroadcastBitQuitCommandReceived);
141 result.SetStatus(eReturnStatusQuit);
142 return result.Succeeded();
146 class CommandObjectReproducerXCrash : public CommandObjectParsed {
147 public:
148 CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
149 : CommandObjectParsed(interpreter, "reproducer xcrash",
150 "Intentionally force the debugger to crash in "
151 "order to trigger and test reproducer generation.",
152 nullptr) {}
154 ~CommandObjectReproducerXCrash() override = default;
156 Options *GetOptions() override { return &m_options; }
158 class CommandOptions : public Options {
159 public:
160 CommandOptions() : Options() {}
162 ~CommandOptions() override = default;
164 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
165 ExecutionContext *execution_context) override {
166 Status error;
167 const int short_option = m_getopt_table[option_idx].val;
169 switch (short_option) {
170 case 's':
171 signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
172 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
173 if (!error.Success())
174 error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
175 option_arg.str().c_str());
176 break;
177 default:
178 llvm_unreachable("Unimplemented option");
181 return error;
184 void OptionParsingStarting(ExecutionContext *execution_context) override {
185 signal = eReproducerCrashSigsegv;
188 ArrayRef<OptionDefinition> GetDefinitions() override {
189 return makeArrayRef(g_reproducer_xcrash_options);
192 ReproducerCrashSignal signal = eReproducerCrashSigsegv;
195 protected:
196 bool DoExecute(Args &command, CommandReturnObject &result) override {
197 if (!command.empty()) {
198 result.AppendErrorWithFormat("'%s' takes no arguments",
199 m_cmd_name.c_str());
200 return false;
203 auto &r = Reproducer::Instance();
205 if (!r.IsCapturing() && !r.IsReplaying()) {
206 result.SetError(
207 "forcing a crash is only supported when capturing a reproducer.");
208 result.SetStatus(eReturnStatusSuccessFinishNoResult);
209 return false;
212 switch (m_options.signal) {
213 case eReproducerCrashSigill:
214 std::raise(SIGILL);
215 break;
216 case eReproducerCrashSigsegv:
217 std::raise(SIGSEGV);
218 break;
221 result.SetStatus(eReturnStatusQuit);
222 return result.Succeeded();
225 private:
226 CommandOptions m_options;
229 class CommandObjectReproducerStatus : public CommandObjectParsed {
230 public:
231 CommandObjectReproducerStatus(CommandInterpreter &interpreter)
232 : CommandObjectParsed(
233 interpreter, "reproducer status",
234 "Show the current reproducer status. In capture mode the "
235 "debugger "
236 "is collecting all the information it needs to create a "
237 "reproducer. In replay mode the reproducer is replaying a "
238 "reproducer. When the reproducers are off, no data is collected "
239 "and no reproducer can be generated.",
240 nullptr) {}
242 ~CommandObjectReproducerStatus() override = default;
244 protected:
245 bool DoExecute(Args &command, CommandReturnObject &result) override {
246 if (!command.empty()) {
247 result.AppendErrorWithFormat("'%s' takes no arguments",
248 m_cmd_name.c_str());
249 return false;
252 auto &r = Reproducer::Instance();
253 if (r.IsCapturing()) {
254 result.GetOutputStream() << "Reproducer is in capture mode.\n";
255 } else if (r.IsReplaying()) {
256 result.GetOutputStream() << "Reproducer is in replay mode.\n";
257 } else {
258 result.GetOutputStream() << "Reproducer is off.\n";
261 if (r.IsCapturing() || r.IsReplaying()) {
262 result.GetOutputStream()
263 << "Path: " << r.GetReproducerPath().GetPath() << '\n';
266 // Auto generate is hidden unless enabled because this is mostly for
267 // development and testing.
268 if (Generator *g = r.GetGenerator()) {
269 if (g->IsAutoGenerate())
270 result.GetOutputStream() << "Auto generate: on\n";
273 result.SetStatus(eReturnStatusSuccessFinishResult);
274 return result.Succeeded();
278 static void SetError(CommandReturnObject &result, Error err) {
279 result.GetErrorStream().Printf("error: %s\n",
280 toString(std::move(err)).c_str());
281 result.SetStatus(eReturnStatusFailed);
284 class CommandObjectReproducerDump : public CommandObjectParsed {
285 public:
286 CommandObjectReproducerDump(CommandInterpreter &interpreter)
287 : CommandObjectParsed(interpreter, "reproducer dump",
288 "Dump the information contained in a reproducer. "
289 "If no reproducer is specified during replay, it "
290 "dumps the content of the current reproducer.",
291 nullptr) {}
293 ~CommandObjectReproducerDump() override = default;
295 Options *GetOptions() override { return &m_options; }
297 class CommandOptions : public Options {
298 public:
299 CommandOptions() : Options(), file() {}
301 ~CommandOptions() override = default;
303 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
304 ExecutionContext *execution_context) override {
305 Status error;
306 const int short_option = m_getopt_table[option_idx].val;
308 switch (short_option) {
309 case 'f':
310 file.SetFile(option_arg, FileSpec::Style::native);
311 FileSystem::Instance().Resolve(file);
312 break;
313 case 'p':
314 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
315 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
316 if (!error.Success())
317 error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
318 option_arg.str().c_str());
319 break;
320 default:
321 llvm_unreachable("Unimplemented option");
324 return error;
327 void OptionParsingStarting(ExecutionContext *execution_context) override {
328 file.Clear();
329 provider = eReproducerProviderNone;
332 ArrayRef<OptionDefinition> GetDefinitions() override {
333 return makeArrayRef(g_reproducer_dump_options);
336 FileSpec file;
337 ReproducerProvider provider = eReproducerProviderNone;
340 protected:
341 bool DoExecute(Args &command, CommandReturnObject &result) override {
342 if (!command.empty()) {
343 result.AppendErrorWithFormat("'%s' takes no arguments",
344 m_cmd_name.c_str());
345 return false;
348 // If no reproducer path is specified, use the loader currently used for
349 // replay. Otherwise create a new loader just for dumping.
350 llvm::Optional<Loader> loader_storage;
351 Loader *loader = nullptr;
352 if (!m_options.file) {
353 loader = Reproducer::Instance().GetLoader();
354 if (loader == nullptr) {
355 result.SetError(
356 "Not specifying a reproducer is only support during replay.");
357 result.SetStatus(eReturnStatusSuccessFinishNoResult);
358 return false;
360 } else {
361 loader_storage.emplace(m_options.file);
362 loader = &(*loader_storage);
363 if (Error err = loader->LoadIndex()) {
364 SetError(result, std::move(err));
365 return false;
369 // If we get here we should have a valid loader.
370 assert(loader);
372 switch (m_options.provider) {
373 case eReproducerProviderFiles: {
374 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
376 // Read the VFS mapping.
377 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
378 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
379 if (!buffer) {
380 SetError(result, errorCodeToError(buffer.getError()));
381 return false;
384 // Initialize a VFS from the given mapping.
385 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
386 std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
388 // Dump the VFS to a buffer.
389 std::string str;
390 raw_string_ostream os(str);
391 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
392 os.flush();
394 // Return the string.
395 result.AppendMessage(str);
396 result.SetStatus(eReturnStatusSuccessFinishResult);
397 return true;
399 case eReproducerProviderVersion: {
400 Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
401 if (!version) {
402 SetError(result, version.takeError());
403 return false;
405 result.AppendMessage(*version);
406 result.SetStatus(eReturnStatusSuccessFinishResult);
407 return true;
409 case eReproducerProviderWorkingDirectory: {
410 Expected<std::string> cwd =
411 loader->LoadBuffer<WorkingDirectoryProvider>();
412 if (!cwd) {
413 SetError(result, cwd.takeError());
414 return false;
416 result.AppendMessage(*cwd);
417 result.SetStatus(eReturnStatusSuccessFinishResult);
418 return true;
420 case eReproducerProviderCommands: {
421 std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
422 repro::MultiLoader<repro::CommandProvider>::Create(loader);
423 if (!multi_loader) {
424 SetError(result,
425 make_error<StringError>(llvm::inconvertibleErrorCode(),
426 "Unable to create command loader."));
427 return false;
430 // Iterate over the command files and dump them.
431 llvm::Optional<std::string> command_file;
432 while ((command_file = multi_loader->GetNextFile())) {
433 if (!command_file)
434 break;
436 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
437 if (auto err = command_buffer.getError()) {
438 SetError(result, errorCodeToError(err));
439 return false;
441 result.AppendMessage((*command_buffer)->getBuffer());
444 result.SetStatus(eReturnStatusSuccessFinishResult);
445 return true;
447 case eReproducerProviderGDB: {
448 std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
449 multi_loader =
450 repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
451 llvm::Optional<std::string> gdb_file;
452 while ((gdb_file = multi_loader->GetNextFile())) {
453 auto error_or_file = MemoryBuffer::getFile(*gdb_file);
454 if (auto err = error_or_file.getError()) {
455 SetError(result, errorCodeToError(err));
456 return false;
459 std::vector<GDBRemotePacket> packets;
460 yaml::Input yin((*error_or_file)->getBuffer());
461 yin >> packets;
463 if (auto err = yin.error()) {
464 SetError(result, errorCodeToError(err));
465 return false;
468 for (GDBRemotePacket &packet : packets) {
469 packet.Dump(result.GetOutputStream());
473 result.SetStatus(eReturnStatusSuccessFinishResult);
474 return true;
476 case eReproducerProviderNone:
477 result.SetError("No valid provider specified.");
478 return false;
481 result.SetStatus(eReturnStatusSuccessFinishNoResult);
482 return result.Succeeded();
485 private:
486 CommandOptions m_options;
489 CommandObjectReproducer::CommandObjectReproducer(
490 CommandInterpreter &interpreter)
491 : CommandObjectMultiword(
492 interpreter, "reproducer",
493 "Commands for manipulating reproducers. Reproducers make it "
494 "possible "
495 "to capture full debug sessions with all its dependencies. The "
496 "resulting reproducer is used to replay the debug session while "
497 "debugging the debugger.\n"
498 "Because reproducers need the whole the debug session from "
499 "beginning to end, you need to launch the debugger in capture or "
500 "replay mode, commonly though the command line driver.\n"
501 "Reproducers are unrelated record-replay debugging, as you cannot "
502 "interact with the debugger during replay.\n",
503 "reproducer <subcommand> [<subcommand-options>]") {
504 LoadSubCommand(
505 "generate",
506 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
507 LoadSubCommand("status", CommandObjectSP(
508 new CommandObjectReproducerStatus(interpreter)));
509 LoadSubCommand("dump",
510 CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
511 LoadSubCommand("xcrash", CommandObjectSP(
512 new CommandObjectReproducerXCrash(interpreter)));
515 CommandObjectReproducer::~CommandObjectReproducer() = default;