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.AppendError(
166       "Not specifying a reproducer is only support during replay.");
167   result.SetStatus(eReturnStatusSuccessFinishNoResult);
168   return nullptr;
169 }
170 
171 class CommandObjectReproducerGenerate : public CommandObjectParsed {
172 public:
173   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
174       : CommandObjectParsed(
175             interpreter, "reproducer generate",
176             "Generate reproducer on disk. When the debugger is in capture "
177             "mode, this command will output the reproducer to a directory on "
178             "disk and quit. In replay mode this command in a no-op.",
179             nullptr) {}
180 
181   ~CommandObjectReproducerGenerate() override = default;
182 
183 protected:
184   bool DoExecute(Args &command, CommandReturnObject &result) override {
185     auto &r = Reproducer::Instance();
186     if (auto generator = r.GetGenerator()) {
187       generator->Keep();
188     } else {
189       result.AppendErrorWithFormat("Unable to get the reproducer generator");
190       return false;
191     }
192 
193     result.GetOutputStream()
194         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
195     result.GetOutputStream()
196         << "Please have a look at the directory to assess if you're willing to "
197            "share the contained information.\n";
198 
199     m_interpreter.BroadcastEvent(
200         CommandInterpreter::eBroadcastBitQuitCommandReceived);
201     result.SetStatus(eReturnStatusQuit);
202     return result.Succeeded();
203   }
204 };
205 
206 class CommandObjectReproducerXCrash : public CommandObjectParsed {
207 public:
208   CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
209       : CommandObjectParsed(interpreter, "reproducer xcrash",
210                             "Intentionally force  the debugger to crash in "
211                             "order to trigger and test reproducer generation.",
212                             nullptr) {}
213 
214   ~CommandObjectReproducerXCrash() override = default;
215 
216   Options *GetOptions() override { return &m_options; }
217 
218   class CommandOptions : public Options {
219   public:
220     CommandOptions() = default;
221 
222     ~CommandOptions() override = default;
223 
224     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
225                           ExecutionContext *execution_context) override {
226       Status error;
227       const int short_option = m_getopt_table[option_idx].val;
228 
229       switch (short_option) {
230       case 's':
231         signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
232             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
233         if (!error.Success())
234           error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
235                                          option_arg.str().c_str());
236         break;
237       default:
238         llvm_unreachable("Unimplemented option");
239       }
240 
241       return error;
242     }
243 
244     void OptionParsingStarting(ExecutionContext *execution_context) override {
245       signal = eReproducerCrashSigsegv;
246     }
247 
248     ArrayRef<OptionDefinition> GetDefinitions() override {
249       return makeArrayRef(g_reproducer_xcrash_options);
250     }
251 
252     ReproducerCrashSignal signal = eReproducerCrashSigsegv;
253   };
254 
255 protected:
256   bool DoExecute(Args &command, CommandReturnObject &result) override {
257     auto &r = Reproducer::Instance();
258 
259     if (!r.IsCapturing()) {
260       result.AppendError(
261           "forcing a crash is only supported when capturing a reproducer.");
262       result.SetStatus(eReturnStatusSuccessFinishNoResult);
263       return false;
264     }
265 
266     switch (m_options.signal) {
267     case eReproducerCrashSigill:
268       std::raise(SIGILL);
269       break;
270     case eReproducerCrashSigsegv:
271       std::raise(SIGSEGV);
272       break;
273     }
274 
275     result.SetStatus(eReturnStatusQuit);
276     return result.Succeeded();
277   }
278 
279 private:
280   CommandOptions m_options;
281 };
282 
283 class CommandObjectReproducerStatus : public CommandObjectParsed {
284 public:
285   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
286       : CommandObjectParsed(
287             interpreter, "reproducer status",
288             "Show the current reproducer status. In capture mode the "
289             "debugger "
290             "is collecting all the information it needs to create a "
291             "reproducer.  In replay mode the reproducer is replaying a "
292             "reproducer. When the reproducers are off, no data is collected "
293             "and no reproducer can be generated.",
294             nullptr) {}
295 
296   ~CommandObjectReproducerStatus() override = default;
297 
298 protected:
299   bool DoExecute(Args &command, CommandReturnObject &result) override {
300     auto &r = Reproducer::Instance();
301     if (r.IsCapturing()) {
302       result.GetOutputStream() << "Reproducer is in capture mode.\n";
303       result.GetOutputStream()
304           << "Path: " << r.GetReproducerPath().GetPath() << '\n';
305     } else {
306       result.GetOutputStream() << "Reproducer is off.\n";
307     }
308 
309     // Auto generate is hidden unless enabled because this is mostly for
310     // development and testing.
311     if (Generator *g = r.GetGenerator()) {
312       if (g->IsAutoGenerate())
313         result.GetOutputStream() << "Auto generate: on\n";
314     }
315 
316     result.SetStatus(eReturnStatusSuccessFinishResult);
317     return result.Succeeded();
318   }
319 };
320 
321 class CommandObjectReproducerDump : public CommandObjectParsed {
322 public:
323   CommandObjectReproducerDump(CommandInterpreter &interpreter)
324       : CommandObjectParsed(interpreter, "reproducer dump",
325                             "Dump the information contained in a reproducer. "
326                             "If no reproducer is specified during replay, it "
327                             "dumps the content of the current reproducer.",
328                             nullptr) {}
329 
330   ~CommandObjectReproducerDump() override = default;
331 
332   Options *GetOptions() override { return &m_options; }
333 
334   class CommandOptions : public Options {
335   public:
336     CommandOptions() = default;
337 
338     ~CommandOptions() override = default;
339 
340     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
341                           ExecutionContext *execution_context) override {
342       Status error;
343       const int short_option = m_getopt_table[option_idx].val;
344 
345       switch (short_option) {
346       case 'f':
347         file.SetFile(option_arg, FileSpec::Style::native);
348         FileSystem::Instance().Resolve(file);
349         break;
350       case 'p':
351         provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
352             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
353         if (!error.Success())
354           error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
355                                          option_arg.str().c_str());
356         break;
357       default:
358         llvm_unreachable("Unimplemented option");
359       }
360 
361       return error;
362     }
363 
364     void OptionParsingStarting(ExecutionContext *execution_context) override {
365       file.Clear();
366       provider = eReproducerProviderNone;
367     }
368 
369     ArrayRef<OptionDefinition> GetDefinitions() override {
370       return makeArrayRef(g_reproducer_dump_options);
371     }
372 
373     FileSpec file;
374     ReproducerProvider provider = eReproducerProviderNone;
375   };
376 
377 protected:
378   bool DoExecute(Args &command, CommandReturnObject &result) override {
379     llvm::Optional<Loader> loader_storage;
380     Loader *loader =
381         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
382     if (!loader)
383       return false;
384 
385     switch (m_options.provider) {
386     case eReproducerProviderFiles: {
387       FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
388 
389       // Read the VFS mapping.
390       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
391           vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
392       if (!buffer) {
393         SetError(result, errorCodeToError(buffer.getError()));
394         return false;
395       }
396 
397       // Initialize a VFS from the given mapping.
398       IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
399           std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
400 
401       // Dump the VFS to a buffer.
402       std::string str;
403       raw_string_ostream os(str);
404       static_cast<vfs::RedirectingFileSystem &>(*vfs).print(os);
405       os.flush();
406 
407       // Return the string.
408       result.AppendMessage(str);
409       result.SetStatus(eReturnStatusSuccessFinishResult);
410       return true;
411     }
412     case eReproducerProviderSymbolFiles: {
413       Expected<std::string> symbol_files =
414           loader->LoadBuffer<SymbolFileProvider>();
415       if (!symbol_files) {
416         SetError(result, symbol_files.takeError());
417         return false;
418       }
419 
420       std::vector<SymbolFileProvider::Entry> entries;
421       llvm::yaml::Input yin(*symbol_files);
422       yin >> entries;
423 
424       for (const auto &entry : entries) {
425         result.AppendMessageWithFormat("- uuid:        %s\n",
426                                        entry.uuid.c_str());
427         result.AppendMessageWithFormat("  module path: %s\n",
428                                        entry.module_path.c_str());
429         result.AppendMessageWithFormat("  symbol path: %s\n",
430                                        entry.symbol_path.c_str());
431       }
432       result.SetStatus(eReturnStatusSuccessFinishResult);
433       return true;
434     }
435     case eReproducerProviderVersion: {
436       Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
437       if (!version) {
438         SetError(result, version.takeError());
439         return false;
440       }
441       result.AppendMessage(*version);
442       result.SetStatus(eReturnStatusSuccessFinishResult);
443       return true;
444     }
445     case eReproducerProviderWorkingDirectory: {
446       Expected<std::string> cwd =
447           repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader);
448       if (!cwd) {
449         SetError(result, cwd.takeError());
450         return false;
451       }
452       result.AppendMessage(*cwd);
453       result.SetStatus(eReturnStatusSuccessFinishResult);
454       return true;
455     }
456     case eReproducerProviderHomeDirectory: {
457       Expected<std::string> home =
458           repro::GetDirectoryFrom<HomeDirectoryProvider>(loader);
459       if (!home) {
460         SetError(result, home.takeError());
461         return false;
462       }
463       result.AppendMessage(*home);
464       result.SetStatus(eReturnStatusSuccessFinishResult);
465       return true;
466     }
467     case eReproducerProviderCommands: {
468       std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
469           repro::MultiLoader<repro::CommandProvider>::Create(loader);
470       if (!multi_loader) {
471         SetError(result,
472                  make_error<StringError>("Unable to create command loader.",
473                                          llvm::inconvertibleErrorCode()));
474         return false;
475       }
476 
477       // Iterate over the command files and dump them.
478       llvm::Optional<std::string> command_file;
479       while ((command_file = multi_loader->GetNextFile())) {
480         if (!command_file)
481           break;
482 
483         auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
484         if (auto err = command_buffer.getError()) {
485           SetError(result, errorCodeToError(err));
486           return false;
487         }
488         result.AppendMessage((*command_buffer)->getBuffer());
489       }
490 
491       result.SetStatus(eReturnStatusSuccessFinishResult);
492       return true;
493     }
494     case eReproducerProviderGDB: {
495       std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
496           multi_loader =
497               repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
498 
499       if (!multi_loader) {
500         SetError(result,
501                  make_error<StringError>("Unable to create GDB loader.",
502                                          llvm::inconvertibleErrorCode()));
503         return false;
504       }
505 
506       llvm::Optional<std::string> gdb_file;
507       while ((gdb_file = multi_loader->GetNextFile())) {
508         if (llvm::Expected<std::vector<GDBRemotePacket>> packets =
509                 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) {
510           for (GDBRemotePacket &packet : *packets) {
511             packet.Dump(result.GetOutputStream());
512           }
513         } else {
514           SetError(result, packets.takeError());
515           return false;
516         }
517       }
518 
519       result.SetStatus(eReturnStatusSuccessFinishResult);
520       return true;
521     }
522     case eReproducerProviderProcessInfo: {
523       std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
524           multi_loader =
525               repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
526 
527       if (!multi_loader) {
528         SetError(result, make_error<StringError>(
529                              llvm::inconvertibleErrorCode(),
530                              "Unable to create process info loader."));
531         return false;
532       }
533 
534       llvm::Optional<std::string> process_file;
535       while ((process_file = multi_loader->GetNextFile())) {
536         if (llvm::Expected<ProcessInstanceInfoList> infos =
537                 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) {
538           for (ProcessInstanceInfo info : *infos)
539             info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
540         } else {
541           SetError(result, infos.takeError());
542           return false;
543         }
544       }
545 
546       result.SetStatus(eReturnStatusSuccessFinishResult);
547       return true;
548     }
549     case eReproducerProviderNone:
550       result.AppendError("No valid provider specified.");
551       return false;
552     }
553 
554     result.SetStatus(eReturnStatusSuccessFinishNoResult);
555     return result.Succeeded();
556   }
557 
558 private:
559   CommandOptions m_options;
560 };
561 
562 CommandObjectReproducer::CommandObjectReproducer(
563     CommandInterpreter &interpreter)
564     : CommandObjectMultiword(
565           interpreter, "reproducer",
566           "Commands for manipulating reproducers. Reproducers make it "
567           "possible "
568           "to capture full debug sessions with all its dependencies. The "
569           "resulting reproducer is used to replay the debug session while "
570           "debugging the debugger.\n"
571           "Because reproducers need the whole the debug session from "
572           "beginning to end, you need to launch the debugger in capture or "
573           "replay mode, commonly though the command line driver.\n"
574           "Reproducers are unrelated record-replay debugging, as you cannot "
575           "interact with the debugger during replay.\n",
576           "reproducer <subcommand> [<subcommand-options>]") {
577   LoadSubCommand(
578       "generate",
579       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
580   LoadSubCommand("status", CommandObjectSP(
581                                new CommandObjectReproducerStatus(interpreter)));
582   LoadSubCommand("dump",
583                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
584   LoadSubCommand("xcrash", CommandObjectSP(
585                                new CommandObjectReproducerXCrash(interpreter)));
586 }
587 
588 CommandObjectReproducer::~CommandObjectReproducer() = default;
589