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