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