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