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