1 //===-- CommandObjectReproducer.cpp ---------------------------------------===//
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 //===----------------------------------------------------------------------===//
8 
9 #include "CommandObjectReproducer.h"
10 
11 #include "lldb/Host/HostInfo.h"
12 #include "lldb/Host/OptionParser.h"
13 #include "lldb/Interpreter/CommandInterpreter.h"
14 #include "lldb/Interpreter/CommandReturnObject.h"
15 #include "lldb/Interpreter/OptionArgParser.h"
16 #include "lldb/Utility/GDBRemote.h"
17 #include "lldb/Utility/ProcessInfo.h"
18 #include "lldb/Utility/Reproducer.h"
19 
20 #include <csignal>
21 
22 using namespace lldb;
23 using namespace llvm;
24 using namespace lldb_private;
25 using namespace lldb_private::repro;
26 
27 enum ReproducerProvider {
28   eReproducerProviderCommands,
29   eReproducerProviderFiles,
30   eReproducerProviderSymbolFiles,
31   eReproducerProviderGDB,
32   eReproducerProviderProcessInfo,
33   eReproducerProviderVersion,
34   eReproducerProviderWorkingDirectory,
35   eReproducerProviderHomeDirectory,
36   eReproducerProviderNone
37 };
38 
39 static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
40     {
41         eReproducerProviderCommands,
42         "commands",
43         "Command Interpreter Commands",
44     },
45     {
46         eReproducerProviderFiles,
47         "files",
48         "Files",
49     },
50     {
51         eReproducerProviderSymbolFiles,
52         "symbol-files",
53         "Symbol Files",
54     },
55     {
56         eReproducerProviderGDB,
57         "gdb",
58         "GDB Remote Packets",
59     },
60     {
61         eReproducerProviderProcessInfo,
62         "processes",
63         "Process Info",
64     },
65     {
66         eReproducerProviderVersion,
67         "version",
68         "Version",
69     },
70     {
71         eReproducerProviderWorkingDirectory,
72         "cwd",
73         "Working Directory",
74     },
75     {
76         eReproducerProviderHomeDirectory,
77         "home",
78         "Home Directory",
79     },
80     {
81         eReproducerProviderNone,
82         "none",
83         "None",
84     },
85 };
86 
87 static constexpr OptionEnumValues ReproducerProviderType() {
88   return OptionEnumValues(g_reproducer_provider_type);
89 }
90 
91 #define LLDB_OPTIONS_reproducer_dump
92 #include "CommandOptions.inc"
93 
94 enum ReproducerCrashSignal {
95   eReproducerCrashSigill,
96   eReproducerCrashSigsegv,
97 };
98 
99 static constexpr OptionEnumValueElement g_reproducer_signaltype[] = {
100     {
101         eReproducerCrashSigill,
102         "SIGILL",
103         "Illegal instruction",
104     },
105     {
106         eReproducerCrashSigsegv,
107         "SIGSEGV",
108         "Segmentation fault",
109     },
110 };
111 
112 static constexpr OptionEnumValues ReproducerSignalType() {
113   return OptionEnumValues(g_reproducer_signaltype);
114 }
115 
116 #define LLDB_OPTIONS_reproducer_xcrash
117 #include "CommandOptions.inc"
118 
119 #define LLDB_OPTIONS_reproducer_verify
120 #include "CommandOptions.inc"
121 
122 template <typename T>
123 llvm::Expected<T> static ReadFromYAML(StringRef filename) {
124   auto error_or_file = MemoryBuffer::getFile(filename);
125   if (auto err = error_or_file.getError()) {
126     return errorCodeToError(err);
127   }
128 
129   T t;
130   yaml::Input yin((*error_or_file)->getBuffer());
131   yin >> t;
132 
133   if (auto err = yin.error()) {
134     return errorCodeToError(err);
135   }
136 
137   return t;
138 }
139 
140 static void SetError(CommandReturnObject &result, Error err) {
141   result.GetErrorStream().Printf("error: %s\n",
142                                  toString(std::move(err)).c_str());
143   result.SetStatus(eReturnStatusFailed);
144 }
145 
146 /// Create a loader from the given path if specified. Otherwise use the current
147 /// loader used for replay.
148 static Loader *
149 GetLoaderFromPathOrCurrent(llvm::Optional<Loader> &loader_storage,
150                            CommandReturnObject &result,
151                            FileSpec reproducer_path) {
152   if (reproducer_path) {
153     loader_storage.emplace(reproducer_path);
154     Loader *loader = &(*loader_storage);
155     if (Error err = loader->LoadIndex()) {
156       // This is a hard error and will set the result to eReturnStatusFailed.
157       SetError(result, std::move(err));
158       return nullptr;
159     }
160     return loader;
161   }
162 
163   if (Loader *loader = Reproducer::Instance().GetLoader())
164     return loader;
165 
166   // This is a soft error because this is expected to fail during capture.
167   result.SetError("Not specifying a reproducer is only support during replay.");
168   result.SetStatus(eReturnStatusSuccessFinishNoResult);
169   return nullptr;
170 }
171 
172 class CommandObjectReproducerGenerate : public CommandObjectParsed {
173 public:
174   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
175       : CommandObjectParsed(
176             interpreter, "reproducer generate",
177             "Generate reproducer on disk. When the debugger is in capture "
178             "mode, this command will output the reproducer to a directory on "
179             "disk and quit. In replay mode this command in a no-op.",
180             nullptr) {}
181 
182   ~CommandObjectReproducerGenerate() override = default;
183 
184 protected:
185   bool DoExecute(Args &command, CommandReturnObject &result) override {
186     if (!command.empty()) {
187       result.AppendErrorWithFormat("'%s' takes no arguments",
188                                    m_cmd_name.c_str());
189       return false;
190     }
191 
192     auto &r = Reproducer::Instance();
193     if (auto generator = r.GetGenerator()) {
194       generator->Keep();
195     } else if (r.IsReplaying()) {
196       // Make this operation a NO-OP in replay mode.
197       result.SetStatus(eReturnStatusSuccessFinishNoResult);
198       return result.Succeeded();
199     } else {
200       result.AppendErrorWithFormat("Unable to get the reproducer generator");
201       result.SetStatus(eReturnStatusFailed);
202       return false;
203     }
204 
205     result.GetOutputStream()
206         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
207     result.GetOutputStream()
208         << "Please have a look at the directory to assess if you're willing to "
209            "share the contained information.\n";
210 
211     m_interpreter.BroadcastEvent(
212         CommandInterpreter::eBroadcastBitQuitCommandReceived);
213     result.SetStatus(eReturnStatusQuit);
214     return result.Succeeded();
215   }
216 };
217 
218 class CommandObjectReproducerXCrash : public CommandObjectParsed {
219 public:
220   CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
221       : CommandObjectParsed(interpreter, "reproducer xcrash",
222                             "Intentionally force  the debugger to crash in "
223                             "order to trigger and test reproducer generation.",
224                             nullptr) {}
225 
226   ~CommandObjectReproducerXCrash() override = default;
227 
228   Options *GetOptions() override { return &m_options; }
229 
230   class CommandOptions : public Options {
231   public:
232     CommandOptions() : Options() {}
233 
234     ~CommandOptions() override = default;
235 
236     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
237                           ExecutionContext *execution_context) override {
238       Status error;
239       const int short_option = m_getopt_table[option_idx].val;
240 
241       switch (short_option) {
242       case 's':
243         signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
244             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
245         if (!error.Success())
246           error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
247                                          option_arg.str().c_str());
248         break;
249       default:
250         llvm_unreachable("Unimplemented option");
251       }
252 
253       return error;
254     }
255 
256     void OptionParsingStarting(ExecutionContext *execution_context) override {
257       signal = eReproducerCrashSigsegv;
258     }
259 
260     ArrayRef<OptionDefinition> GetDefinitions() override {
261       return makeArrayRef(g_reproducer_xcrash_options);
262     }
263 
264     ReproducerCrashSignal signal = eReproducerCrashSigsegv;
265   };
266 
267 protected:
268   bool DoExecute(Args &command, CommandReturnObject &result) override {
269     if (!command.empty()) {
270       result.AppendErrorWithFormat("'%s' takes no arguments",
271                                    m_cmd_name.c_str());
272       return false;
273     }
274 
275     auto &r = Reproducer::Instance();
276 
277     if (!r.IsCapturing() && !r.IsReplaying()) {
278       result.SetError(
279           "forcing a crash is only supported when capturing a reproducer.");
280       result.SetStatus(eReturnStatusSuccessFinishNoResult);
281       return false;
282     }
283 
284     switch (m_options.signal) {
285     case eReproducerCrashSigill:
286       std::raise(SIGILL);
287       break;
288     case eReproducerCrashSigsegv:
289       std::raise(SIGSEGV);
290       break;
291     }
292 
293     result.SetStatus(eReturnStatusQuit);
294     return result.Succeeded();
295   }
296 
297 private:
298   CommandOptions m_options;
299 };
300 
301 class CommandObjectReproducerStatus : public CommandObjectParsed {
302 public:
303   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
304       : CommandObjectParsed(
305             interpreter, "reproducer status",
306             "Show the current reproducer status. In capture mode the "
307             "debugger "
308             "is collecting all the information it needs to create a "
309             "reproducer.  In replay mode the reproducer is replaying a "
310             "reproducer. When the reproducers are off, no data is collected "
311             "and no reproducer can be generated.",
312             nullptr) {}
313 
314   ~CommandObjectReproducerStatus() override = default;
315 
316 protected:
317   bool DoExecute(Args &command, CommandReturnObject &result) override {
318     if (!command.empty()) {
319       result.AppendErrorWithFormat("'%s' takes no arguments",
320                                    m_cmd_name.c_str());
321       return false;
322     }
323 
324     auto &r = Reproducer::Instance();
325     if (r.IsCapturing()) {
326       result.GetOutputStream() << "Reproducer is in capture mode.\n";
327     } else if (r.IsReplaying()) {
328       result.GetOutputStream() << "Reproducer is in replay mode.\n";
329     } else {
330       result.GetOutputStream() << "Reproducer is off.\n";
331     }
332 
333     if (r.IsCapturing() || r.IsReplaying()) {
334       result.GetOutputStream()
335           << "Path: " << r.GetReproducerPath().GetPath() << '\n';
336     }
337 
338     // Auto generate is hidden unless enabled because this is mostly for
339     // development and testing.
340     if (Generator *g = r.GetGenerator()) {
341       if (g->IsAutoGenerate())
342         result.GetOutputStream() << "Auto generate: on\n";
343     }
344 
345     result.SetStatus(eReturnStatusSuccessFinishResult);
346     return result.Succeeded();
347   }
348 };
349 
350 class CommandObjectReproducerDump : public CommandObjectParsed {
351 public:
352   CommandObjectReproducerDump(CommandInterpreter &interpreter)
353       : CommandObjectParsed(interpreter, "reproducer dump",
354                             "Dump the information contained in a reproducer. "
355                             "If no reproducer is specified during replay, it "
356                             "dumps the content of the current reproducer.",
357                             nullptr) {}
358 
359   ~CommandObjectReproducerDump() override = default;
360 
361   Options *GetOptions() override { return &m_options; }
362 
363   class CommandOptions : public Options {
364   public:
365     CommandOptions() : Options(), file() {}
366 
367     ~CommandOptions() override = default;
368 
369     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
370                           ExecutionContext *execution_context) override {
371       Status error;
372       const int short_option = m_getopt_table[option_idx].val;
373 
374       switch (short_option) {
375       case 'f':
376         file.SetFile(option_arg, FileSpec::Style::native);
377         FileSystem::Instance().Resolve(file);
378         break;
379       case 'p':
380         provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
381             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
382         if (!error.Success())
383           error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
384                                          option_arg.str().c_str());
385         break;
386       default:
387         llvm_unreachable("Unimplemented option");
388       }
389 
390       return error;
391     }
392 
393     void OptionParsingStarting(ExecutionContext *execution_context) override {
394       file.Clear();
395       provider = eReproducerProviderNone;
396     }
397 
398     ArrayRef<OptionDefinition> GetDefinitions() override {
399       return makeArrayRef(g_reproducer_dump_options);
400     }
401 
402     FileSpec file;
403     ReproducerProvider provider = eReproducerProviderNone;
404   };
405 
406 protected:
407   bool DoExecute(Args &command, CommandReturnObject &result) override {
408     if (!command.empty()) {
409       result.AppendErrorWithFormat("'%s' takes no arguments",
410                                    m_cmd_name.c_str());
411       return false;
412     }
413 
414     llvm::Optional<Loader> loader_storage;
415     Loader *loader =
416         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
417     if (!loader)
418       return false;
419 
420     switch (m_options.provider) {
421     case eReproducerProviderFiles: {
422       FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
423 
424       // Read the VFS mapping.
425       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
426           vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
427       if (!buffer) {
428         SetError(result, errorCodeToError(buffer.getError()));
429         return false;
430       }
431 
432       // Initialize a VFS from the given mapping.
433       IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
434           std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
435 
436       // Dump the VFS to a buffer.
437       std::string str;
438       raw_string_ostream os(str);
439       static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
440       os.flush();
441 
442       // Return the string.
443       result.AppendMessage(str);
444       result.SetStatus(eReturnStatusSuccessFinishResult);
445       return true;
446     }
447     case eReproducerProviderSymbolFiles: {
448       Expected<std::string> symbol_files =
449           loader->LoadBuffer<SymbolFileProvider>();
450       if (!symbol_files) {
451         SetError(result, symbol_files.takeError());
452         return false;
453       }
454 
455       std::vector<SymbolFileProvider::Entry> entries;
456       llvm::yaml::Input yin(*symbol_files);
457       yin >> entries;
458 
459       for (const auto &entry : entries) {
460         result.AppendMessageWithFormat("- uuid:        %s\n",
461                                        entry.uuid.c_str());
462         result.AppendMessageWithFormat("  module path: %s\n",
463                                        entry.module_path.c_str());
464         result.AppendMessageWithFormat("  symbol path: %s\n",
465                                        entry.symbol_path.c_str());
466       }
467       result.SetStatus(eReturnStatusSuccessFinishResult);
468       return true;
469     }
470     case eReproducerProviderVersion: {
471       Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
472       if (!version) {
473         SetError(result, version.takeError());
474         return false;
475       }
476       result.AppendMessage(*version);
477       result.SetStatus(eReturnStatusSuccessFinishResult);
478       return true;
479     }
480     case eReproducerProviderWorkingDirectory: {
481       Expected<std::string> cwd =
482           repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader);
483       if (!cwd) {
484         SetError(result, cwd.takeError());
485         return false;
486       }
487       result.AppendMessage(*cwd);
488       result.SetStatus(eReturnStatusSuccessFinishResult);
489       return true;
490     }
491     case eReproducerProviderHomeDirectory: {
492       Expected<std::string> home =
493           repro::GetDirectoryFrom<HomeDirectoryProvider>(loader);
494       if (!home) {
495         SetError(result, home.takeError());
496         return false;
497       }
498       result.AppendMessage(*home);
499       result.SetStatus(eReturnStatusSuccessFinishResult);
500       return true;
501     }
502     case eReproducerProviderCommands: {
503       std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
504           repro::MultiLoader<repro::CommandProvider>::Create(loader);
505       if (!multi_loader) {
506         SetError(result,
507                  make_error<StringError>("Unable to create command loader.",
508                                          llvm::inconvertibleErrorCode()));
509         return false;
510       }
511 
512       // Iterate over the command files and dump them.
513       llvm::Optional<std::string> command_file;
514       while ((command_file = multi_loader->GetNextFile())) {
515         if (!command_file)
516           break;
517 
518         auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
519         if (auto err = command_buffer.getError()) {
520           SetError(result, errorCodeToError(err));
521           return false;
522         }
523         result.AppendMessage((*command_buffer)->getBuffer());
524       }
525 
526       result.SetStatus(eReturnStatusSuccessFinishResult);
527       return true;
528     }
529     case eReproducerProviderGDB: {
530       std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
531           multi_loader =
532               repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
533 
534       if (!multi_loader) {
535         SetError(result,
536                  make_error<StringError>("Unable to create GDB loader.",
537                                          llvm::inconvertibleErrorCode()));
538         return false;
539       }
540 
541       llvm::Optional<std::string> gdb_file;
542       while ((gdb_file = multi_loader->GetNextFile())) {
543         if (llvm::Expected<std::vector<GDBRemotePacket>> packets =
544                 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) {
545           for (GDBRemotePacket &packet : *packets) {
546             packet.Dump(result.GetOutputStream());
547           }
548         } else {
549           SetError(result, packets.takeError());
550           return false;
551         }
552       }
553 
554       result.SetStatus(eReturnStatusSuccessFinishResult);
555       return true;
556     }
557     case eReproducerProviderProcessInfo: {
558       std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
559           multi_loader =
560               repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
561 
562       if (!multi_loader) {
563         SetError(result, make_error<StringError>(
564                              llvm::inconvertibleErrorCode(),
565                              "Unable to create process info loader."));
566         return false;
567       }
568 
569       llvm::Optional<std::string> process_file;
570       while ((process_file = multi_loader->GetNextFile())) {
571         if (llvm::Expected<ProcessInstanceInfoList> infos =
572                 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) {
573           for (ProcessInstanceInfo info : *infos)
574             info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
575         } else {
576           SetError(result, infos.takeError());
577           return false;
578         }
579       }
580 
581       result.SetStatus(eReturnStatusSuccessFinishResult);
582       return true;
583     }
584     case eReproducerProviderNone:
585       result.SetError("No valid provider specified.");
586       return false;
587     }
588 
589     result.SetStatus(eReturnStatusSuccessFinishNoResult);
590     return result.Succeeded();
591   }
592 
593 private:
594   CommandOptions m_options;
595 };
596 
597 class CommandObjectReproducerVerify : public CommandObjectParsed {
598 public:
599   CommandObjectReproducerVerify(CommandInterpreter &interpreter)
600       : CommandObjectParsed(interpreter, "reproducer verify",
601                             "Verify the contents of a reproducer. "
602                             "If no reproducer is specified during replay, it "
603                             "verifies the content of the current reproducer.",
604                             nullptr) {}
605 
606   ~CommandObjectReproducerVerify() override = default;
607 
608   Options *GetOptions() override { return &m_options; }
609 
610   class CommandOptions : public Options {
611   public:
612     CommandOptions() : Options(), file() {}
613 
614     ~CommandOptions() override = default;
615 
616     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
617                           ExecutionContext *execution_context) override {
618       Status error;
619       const int short_option = m_getopt_table[option_idx].val;
620 
621       switch (short_option) {
622       case 'f':
623         file.SetFile(option_arg, FileSpec::Style::native);
624         FileSystem::Instance().Resolve(file);
625         break;
626       default:
627         llvm_unreachable("Unimplemented option");
628       }
629 
630       return error;
631     }
632 
633     void OptionParsingStarting(ExecutionContext *execution_context) override {
634       file.Clear();
635     }
636 
637     ArrayRef<OptionDefinition> GetDefinitions() override {
638       return makeArrayRef(g_reproducer_verify_options);
639     }
640 
641     FileSpec file;
642   };
643 
644 protected:
645   bool DoExecute(Args &command, CommandReturnObject &result) override {
646     if (!command.empty()) {
647       result.AppendErrorWithFormat("'%s' takes no arguments",
648                                    m_cmd_name.c_str());
649       return false;
650     }
651 
652     llvm::Optional<Loader> loader_storage;
653     Loader *loader =
654         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
655     if (!loader)
656       return false;
657 
658     bool errors = false;
659     auto error_callback = [&](llvm::StringRef error) {
660       errors = true;
661       result.AppendError(error);
662     };
663 
664     bool warnings = false;
665     auto warning_callback = [&](llvm::StringRef warning) {
666       warnings = true;
667       result.AppendWarning(warning);
668     };
669 
670     auto note_callback = [&](llvm::StringRef warning) {
671       result.AppendMessage(warning);
672     };
673 
674     Verifier verifier(loader);
675     verifier.Verify(error_callback, warning_callback, note_callback);
676 
677     if (warnings || errors) {
678       result.AppendMessage("reproducer verification failed");
679       result.SetStatus(eReturnStatusFailed);
680     } else {
681       result.AppendMessage("reproducer verification succeeded");
682       result.SetStatus(eReturnStatusSuccessFinishResult);
683     }
684 
685     return result.Succeeded();
686   }
687 
688 private:
689   CommandOptions m_options;
690 };
691 
692 CommandObjectReproducer::CommandObjectReproducer(
693     CommandInterpreter &interpreter)
694     : CommandObjectMultiword(
695           interpreter, "reproducer",
696           "Commands for manipulating reproducers. Reproducers make it "
697           "possible "
698           "to capture full debug sessions with all its dependencies. The "
699           "resulting reproducer is used to replay the debug session while "
700           "debugging the debugger.\n"
701           "Because reproducers need the whole the debug session from "
702           "beginning to end, you need to launch the debugger in capture or "
703           "replay mode, commonly though the command line driver.\n"
704           "Reproducers are unrelated record-replay debugging, as you cannot "
705           "interact with the debugger during replay.\n",
706           "reproducer <subcommand> [<subcommand-options>]") {
707   LoadSubCommand(
708       "generate",
709       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
710   LoadSubCommand("status", CommandObjectSP(
711                                new CommandObjectReproducerStatus(interpreter)));
712   LoadSubCommand("dump",
713                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
714   LoadSubCommand("verify", CommandObjectSP(
715                                new CommandObjectReproducerVerify(interpreter)));
716   LoadSubCommand("xcrash", CommandObjectSP(
717                                new CommandObjectReproducerXCrash(interpreter)));
718 }
719 
720 CommandObjectReproducer::~CommandObjectReproducer() = default;
721