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       if (llvm::Error e = repro::Finalize(r.GetReproducerPath())) {
196         SetError(result, std::move(e));
197         return result.Succeeded();
198       }
199     } else if (r.IsReplaying()) {
200       // Make this operation a NO-OP in replay mode.
201       result.SetStatus(eReturnStatusSuccessFinishNoResult);
202       return result.Succeeded();
203     } else {
204       result.AppendErrorWithFormat("Unable to get the reproducer generator");
205       return false;
206     }
207 
208     result.GetOutputStream()
209         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
210     result.GetOutputStream()
211         << "Please have a look at the directory to assess if you're willing to "
212            "share the contained information.\n";
213 
214     m_interpreter.BroadcastEvent(
215         CommandInterpreter::eBroadcastBitQuitCommandReceived);
216     result.SetStatus(eReturnStatusQuit);
217     return result.Succeeded();
218   }
219 };
220 
221 class CommandObjectReproducerXCrash : public CommandObjectParsed {
222 public:
223   CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
224       : CommandObjectParsed(interpreter, "reproducer xcrash",
225                             "Intentionally force  the debugger to crash in "
226                             "order to trigger and test reproducer generation.",
227                             nullptr) {}
228 
229   ~CommandObjectReproducerXCrash() override = default;
230 
231   Options *GetOptions() override { return &m_options; }
232 
233   class CommandOptions : public Options {
234   public:
235     CommandOptions() : Options() {}
236 
237     ~CommandOptions() override = default;
238 
239     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
240                           ExecutionContext *execution_context) override {
241       Status error;
242       const int short_option = m_getopt_table[option_idx].val;
243 
244       switch (short_option) {
245       case 's':
246         signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
247             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
248         if (!error.Success())
249           error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
250                                          option_arg.str().c_str());
251         break;
252       default:
253         llvm_unreachable("Unimplemented option");
254       }
255 
256       return error;
257     }
258 
259     void OptionParsingStarting(ExecutionContext *execution_context) override {
260       signal = eReproducerCrashSigsegv;
261     }
262 
263     ArrayRef<OptionDefinition> GetDefinitions() override {
264       return makeArrayRef(g_reproducer_xcrash_options);
265     }
266 
267     ReproducerCrashSignal signal = eReproducerCrashSigsegv;
268   };
269 
270 protected:
271   bool DoExecute(Args &command, CommandReturnObject &result) override {
272     if (!command.empty()) {
273       result.AppendErrorWithFormat("'%s' takes no arguments",
274                                    m_cmd_name.c_str());
275       return false;
276     }
277 
278     auto &r = Reproducer::Instance();
279 
280     if (!r.IsCapturing() && !r.IsReplaying()) {
281       result.SetError(
282           "forcing a crash is only supported when capturing a reproducer.");
283       result.SetStatus(eReturnStatusSuccessFinishNoResult);
284       return false;
285     }
286 
287     switch (m_options.signal) {
288     case eReproducerCrashSigill:
289       std::raise(SIGILL);
290       break;
291     case eReproducerCrashSigsegv:
292       std::raise(SIGSEGV);
293       break;
294     }
295 
296     result.SetStatus(eReturnStatusQuit);
297     return result.Succeeded();
298   }
299 
300 private:
301   CommandOptions m_options;
302 };
303 
304 class CommandObjectReproducerStatus : public CommandObjectParsed {
305 public:
306   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
307       : CommandObjectParsed(
308             interpreter, "reproducer status",
309             "Show the current reproducer status. In capture mode the "
310             "debugger "
311             "is collecting all the information it needs to create a "
312             "reproducer.  In replay mode the reproducer is replaying a "
313             "reproducer. When the reproducers are off, no data is collected "
314             "and no reproducer can be generated.",
315             nullptr) {}
316 
317   ~CommandObjectReproducerStatus() override = default;
318 
319 protected:
320   bool DoExecute(Args &command, CommandReturnObject &result) override {
321     if (!command.empty()) {
322       result.AppendErrorWithFormat("'%s' takes no arguments",
323                                    m_cmd_name.c_str());
324       return false;
325     }
326 
327     auto &r = Reproducer::Instance();
328     if (r.IsCapturing()) {
329       result.GetOutputStream() << "Reproducer is in capture mode.\n";
330     } else if (r.IsReplaying()) {
331       result.GetOutputStream() << "Reproducer is in replay mode.\n";
332     } else {
333       result.GetOutputStream() << "Reproducer is off.\n";
334     }
335 
336     if (r.IsCapturing() || r.IsReplaying()) {
337       result.GetOutputStream()
338           << "Path: " << r.GetReproducerPath().GetPath() << '\n';
339     }
340 
341     // Auto generate is hidden unless enabled because this is mostly for
342     // development and testing.
343     if (Generator *g = r.GetGenerator()) {
344       if (g->IsAutoGenerate())
345         result.GetOutputStream() << "Auto generate: on\n";
346     }
347 
348     result.SetStatus(eReturnStatusSuccessFinishResult);
349     return result.Succeeded();
350   }
351 };
352 
353 class CommandObjectReproducerDump : public CommandObjectParsed {
354 public:
355   CommandObjectReproducerDump(CommandInterpreter &interpreter)
356       : CommandObjectParsed(interpreter, "reproducer dump",
357                             "Dump the information contained in a reproducer. "
358                             "If no reproducer is specified during replay, it "
359                             "dumps the content of the current reproducer.",
360                             nullptr) {}
361 
362   ~CommandObjectReproducerDump() override = default;
363 
364   Options *GetOptions() override { return &m_options; }
365 
366   class CommandOptions : public Options {
367   public:
368     CommandOptions() : Options(), file() {}
369 
370     ~CommandOptions() override = default;
371 
372     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
373                           ExecutionContext *execution_context) override {
374       Status error;
375       const int short_option = m_getopt_table[option_idx].val;
376 
377       switch (short_option) {
378       case 'f':
379         file.SetFile(option_arg, FileSpec::Style::native);
380         FileSystem::Instance().Resolve(file);
381         break;
382       case 'p':
383         provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
384             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
385         if (!error.Success())
386           error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
387                                          option_arg.str().c_str());
388         break;
389       default:
390         llvm_unreachable("Unimplemented option");
391       }
392 
393       return error;
394     }
395 
396     void OptionParsingStarting(ExecutionContext *execution_context) override {
397       file.Clear();
398       provider = eReproducerProviderNone;
399     }
400 
401     ArrayRef<OptionDefinition> GetDefinitions() override {
402       return makeArrayRef(g_reproducer_dump_options);
403     }
404 
405     FileSpec file;
406     ReproducerProvider provider = eReproducerProviderNone;
407   };
408 
409 protected:
410   bool DoExecute(Args &command, CommandReturnObject &result) override {
411     if (!command.empty()) {
412       result.AppendErrorWithFormat("'%s' takes no arguments",
413                                    m_cmd_name.c_str());
414       return false;
415     }
416 
417     llvm::Optional<Loader> loader_storage;
418     Loader *loader =
419         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
420     if (!loader)
421       return false;
422 
423     switch (m_options.provider) {
424     case eReproducerProviderFiles: {
425       FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
426 
427       // Read the VFS mapping.
428       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
429           vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
430       if (!buffer) {
431         SetError(result, errorCodeToError(buffer.getError()));
432         return false;
433       }
434 
435       // Initialize a VFS from the given mapping.
436       IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
437           std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
438 
439       // Dump the VFS to a buffer.
440       std::string str;
441       raw_string_ostream os(str);
442       static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
443       os.flush();
444 
445       // Return the string.
446       result.AppendMessage(str);
447       result.SetStatus(eReturnStatusSuccessFinishResult);
448       return true;
449     }
450     case eReproducerProviderSymbolFiles: {
451       Expected<std::string> symbol_files =
452           loader->LoadBuffer<SymbolFileProvider>();
453       if (!symbol_files) {
454         SetError(result, symbol_files.takeError());
455         return false;
456       }
457 
458       std::vector<SymbolFileProvider::Entry> entries;
459       llvm::yaml::Input yin(*symbol_files);
460       yin >> entries;
461 
462       for (const auto &entry : entries) {
463         result.AppendMessageWithFormat("- uuid:        %s\n",
464                                        entry.uuid.c_str());
465         result.AppendMessageWithFormat("  module path: %s\n",
466                                        entry.module_path.c_str());
467         result.AppendMessageWithFormat("  symbol path: %s\n",
468                                        entry.symbol_path.c_str());
469       }
470       result.SetStatus(eReturnStatusSuccessFinishResult);
471       return true;
472     }
473     case eReproducerProviderVersion: {
474       Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
475       if (!version) {
476         SetError(result, version.takeError());
477         return false;
478       }
479       result.AppendMessage(*version);
480       result.SetStatus(eReturnStatusSuccessFinishResult);
481       return true;
482     }
483     case eReproducerProviderWorkingDirectory: {
484       Expected<std::string> cwd =
485           repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader);
486       if (!cwd) {
487         SetError(result, cwd.takeError());
488         return false;
489       }
490       result.AppendMessage(*cwd);
491       result.SetStatus(eReturnStatusSuccessFinishResult);
492       return true;
493     }
494     case eReproducerProviderHomeDirectory: {
495       Expected<std::string> home =
496           repro::GetDirectoryFrom<HomeDirectoryProvider>(loader);
497       if (!home) {
498         SetError(result, home.takeError());
499         return false;
500       }
501       result.AppendMessage(*home);
502       result.SetStatus(eReturnStatusSuccessFinishResult);
503       return true;
504     }
505     case eReproducerProviderCommands: {
506       std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
507           repro::MultiLoader<repro::CommandProvider>::Create(loader);
508       if (!multi_loader) {
509         SetError(result,
510                  make_error<StringError>("Unable to create command loader.",
511                                          llvm::inconvertibleErrorCode()));
512         return false;
513       }
514 
515       // Iterate over the command files and dump them.
516       llvm::Optional<std::string> command_file;
517       while ((command_file = multi_loader->GetNextFile())) {
518         if (!command_file)
519           break;
520 
521         auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
522         if (auto err = command_buffer.getError()) {
523           SetError(result, errorCodeToError(err));
524           return false;
525         }
526         result.AppendMessage((*command_buffer)->getBuffer());
527       }
528 
529       result.SetStatus(eReturnStatusSuccessFinishResult);
530       return true;
531     }
532     case eReproducerProviderGDB: {
533       std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
534           multi_loader =
535               repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
536 
537       if (!multi_loader) {
538         SetError(result,
539                  make_error<StringError>("Unable to create GDB loader.",
540                                          llvm::inconvertibleErrorCode()));
541         return false;
542       }
543 
544       llvm::Optional<std::string> gdb_file;
545       while ((gdb_file = multi_loader->GetNextFile())) {
546         if (llvm::Expected<std::vector<GDBRemotePacket>> packets =
547                 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) {
548           for (GDBRemotePacket &packet : *packets) {
549             packet.Dump(result.GetOutputStream());
550           }
551         } else {
552           SetError(result, packets.takeError());
553           return false;
554         }
555       }
556 
557       result.SetStatus(eReturnStatusSuccessFinishResult);
558       return true;
559     }
560     case eReproducerProviderProcessInfo: {
561       std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
562           multi_loader =
563               repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
564 
565       if (!multi_loader) {
566         SetError(result, make_error<StringError>(
567                              llvm::inconvertibleErrorCode(),
568                              "Unable to create process info loader."));
569         return false;
570       }
571 
572       llvm::Optional<std::string> process_file;
573       while ((process_file = multi_loader->GetNextFile())) {
574         if (llvm::Expected<ProcessInstanceInfoList> infos =
575                 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) {
576           for (ProcessInstanceInfo info : *infos)
577             info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
578         } else {
579           SetError(result, infos.takeError());
580           return false;
581         }
582       }
583 
584       result.SetStatus(eReturnStatusSuccessFinishResult);
585       return true;
586     }
587     case eReproducerProviderNone:
588       result.SetError("No valid provider specified.");
589       return false;
590     }
591 
592     result.SetStatus(eReturnStatusSuccessFinishNoResult);
593     return result.Succeeded();
594   }
595 
596 private:
597   CommandOptions m_options;
598 };
599 
600 class CommandObjectReproducerVerify : public CommandObjectParsed {
601 public:
602   CommandObjectReproducerVerify(CommandInterpreter &interpreter)
603       : CommandObjectParsed(interpreter, "reproducer verify",
604                             "Verify the contents of a reproducer. "
605                             "If no reproducer is specified during replay, it "
606                             "verifies the content of the current reproducer.",
607                             nullptr) {}
608 
609   ~CommandObjectReproducerVerify() override = default;
610 
611   Options *GetOptions() override { return &m_options; }
612 
613   class CommandOptions : public Options {
614   public:
615     CommandOptions() : Options(), file() {}
616 
617     ~CommandOptions() override = default;
618 
619     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
620                           ExecutionContext *execution_context) override {
621       Status error;
622       const int short_option = m_getopt_table[option_idx].val;
623 
624       switch (short_option) {
625       case 'f':
626         file.SetFile(option_arg, FileSpec::Style::native);
627         FileSystem::Instance().Resolve(file);
628         break;
629       default:
630         llvm_unreachable("Unimplemented option");
631       }
632 
633       return error;
634     }
635 
636     void OptionParsingStarting(ExecutionContext *execution_context) override {
637       file.Clear();
638     }
639 
640     ArrayRef<OptionDefinition> GetDefinitions() override {
641       return makeArrayRef(g_reproducer_verify_options);
642     }
643 
644     FileSpec file;
645   };
646 
647 protected:
648   bool DoExecute(Args &command, CommandReturnObject &result) override {
649     if (!command.empty()) {
650       result.AppendErrorWithFormat("'%s' takes no arguments",
651                                    m_cmd_name.c_str());
652       return false;
653     }
654 
655     llvm::Optional<Loader> loader_storage;
656     Loader *loader =
657         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
658     if (!loader)
659       return false;
660 
661     bool errors = false;
662     auto error_callback = [&](llvm::StringRef error) {
663       errors = true;
664       result.AppendError(error);
665     };
666 
667     bool warnings = false;
668     auto warning_callback = [&](llvm::StringRef warning) {
669       warnings = true;
670       result.AppendWarning(warning);
671     };
672 
673     auto note_callback = [&](llvm::StringRef warning) {
674       result.AppendMessage(warning);
675     };
676 
677     Verifier verifier(loader);
678     verifier.Verify(error_callback, warning_callback, note_callback);
679 
680     if (warnings || errors) {
681       result.AppendMessage("reproducer verification failed");
682       result.SetStatus(eReturnStatusFailed);
683     } else {
684       result.AppendMessage("reproducer verification succeeded");
685       result.SetStatus(eReturnStatusSuccessFinishResult);
686     }
687 
688     return result.Succeeded();
689   }
690 
691 private:
692   CommandOptions m_options;
693 };
694 
695 CommandObjectReproducer::CommandObjectReproducer(
696     CommandInterpreter &interpreter)
697     : CommandObjectMultiword(
698           interpreter, "reproducer",
699           "Commands for manipulating reproducers. Reproducers make it "
700           "possible "
701           "to capture full debug sessions with all its dependencies. The "
702           "resulting reproducer is used to replay the debug session while "
703           "debugging the debugger.\n"
704           "Because reproducers need the whole the debug session from "
705           "beginning to end, you need to launch the debugger in capture or "
706           "replay mode, commonly though the command line driver.\n"
707           "Reproducers are unrelated record-replay debugging, as you cannot "
708           "interact with the debugger during replay.\n",
709           "reproducer <subcommand> [<subcommand-options>]") {
710   LoadSubCommand(
711       "generate",
712       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
713   LoadSubCommand("status", CommandObjectSP(
714                                new CommandObjectReproducerStatus(interpreter)));
715   LoadSubCommand("dump",
716                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
717   LoadSubCommand("verify", CommandObjectSP(
718                                new CommandObjectReproducerVerify(interpreter)));
719   LoadSubCommand("xcrash", CommandObjectSP(
720                                new CommandObjectReproducerXCrash(interpreter)));
721 }
722 
723 CommandObjectReproducer::~CommandObjectReproducer() = default;
724