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