15ffd83dbSDimitry Andric //===-- CommandObjectReproducer.cpp ---------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "CommandObjectReproducer.h"
100b57cec5SDimitry Andric 
115ffd83dbSDimitry Andric #include "lldb/Host/HostInfo.h"
129dba64beSDimitry Andric #include "lldb/Host/OptionParser.h"
130b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
140b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
150b57cec5SDimitry Andric #include "lldb/Interpreter/OptionArgParser.h"
165ffd83dbSDimitry Andric #include "lldb/Utility/GDBRemote.h"
175ffd83dbSDimitry Andric #include "lldb/Utility/ProcessInfo.h"
185ffd83dbSDimitry Andric #include "lldb/Utility/Reproducer.h"
19480093f4SDimitry Andric 
20480093f4SDimitry Andric #include <csignal>
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace lldb;
239dba64beSDimitry Andric using namespace llvm;
240b57cec5SDimitry Andric using namespace lldb_private;
259dba64beSDimitry Andric using namespace lldb_private::repro;
269dba64beSDimitry Andric 
279dba64beSDimitry Andric enum ReproducerProvider {
289dba64beSDimitry Andric   eReproducerProviderCommands,
299dba64beSDimitry Andric   eReproducerProviderFiles,
30af732203SDimitry Andric   eReproducerProviderSymbolFiles,
319dba64beSDimitry Andric   eReproducerProviderGDB,
325ffd83dbSDimitry Andric   eReproducerProviderProcessInfo,
339dba64beSDimitry Andric   eReproducerProviderVersion,
349dba64beSDimitry Andric   eReproducerProviderWorkingDirectory,
35af732203SDimitry Andric   eReproducerProviderHomeDirectory,
369dba64beSDimitry Andric   eReproducerProviderNone
379dba64beSDimitry Andric };
389dba64beSDimitry Andric 
399dba64beSDimitry Andric static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
409dba64beSDimitry Andric     {
419dba64beSDimitry Andric         eReproducerProviderCommands,
429dba64beSDimitry Andric         "commands",
439dba64beSDimitry Andric         "Command Interpreter Commands",
449dba64beSDimitry Andric     },
459dba64beSDimitry Andric     {
469dba64beSDimitry Andric         eReproducerProviderFiles,
479dba64beSDimitry Andric         "files",
489dba64beSDimitry Andric         "Files",
499dba64beSDimitry Andric     },
509dba64beSDimitry Andric     {
51af732203SDimitry Andric         eReproducerProviderSymbolFiles,
52af732203SDimitry Andric         "symbol-files",
53af732203SDimitry Andric         "Symbol Files",
54af732203SDimitry Andric     },
55af732203SDimitry Andric     {
569dba64beSDimitry Andric         eReproducerProviderGDB,
579dba64beSDimitry Andric         "gdb",
589dba64beSDimitry Andric         "GDB Remote Packets",
599dba64beSDimitry Andric     },
609dba64beSDimitry Andric     {
615ffd83dbSDimitry Andric         eReproducerProviderProcessInfo,
625ffd83dbSDimitry Andric         "processes",
635ffd83dbSDimitry Andric         "Process Info",
645ffd83dbSDimitry Andric     },
655ffd83dbSDimitry Andric     {
669dba64beSDimitry Andric         eReproducerProviderVersion,
679dba64beSDimitry Andric         "version",
689dba64beSDimitry Andric         "Version",
699dba64beSDimitry Andric     },
709dba64beSDimitry Andric     {
719dba64beSDimitry Andric         eReproducerProviderWorkingDirectory,
729dba64beSDimitry Andric         "cwd",
739dba64beSDimitry Andric         "Working Directory",
749dba64beSDimitry Andric     },
759dba64beSDimitry Andric     {
76af732203SDimitry Andric         eReproducerProviderHomeDirectory,
77af732203SDimitry Andric         "home",
78af732203SDimitry Andric         "Home Directory",
79af732203SDimitry Andric     },
80af732203SDimitry Andric     {
819dba64beSDimitry Andric         eReproducerProviderNone,
829dba64beSDimitry Andric         "none",
839dba64beSDimitry Andric         "None",
849dba64beSDimitry Andric     },
859dba64beSDimitry Andric };
869dba64beSDimitry Andric 
ReproducerProviderType()879dba64beSDimitry Andric static constexpr OptionEnumValues ReproducerProviderType() {
889dba64beSDimitry Andric   return OptionEnumValues(g_reproducer_provider_type);
899dba64beSDimitry Andric }
909dba64beSDimitry Andric 
91480093f4SDimitry Andric #define LLDB_OPTIONS_reproducer_dump
92480093f4SDimitry Andric #include "CommandOptions.inc"
93480093f4SDimitry Andric 
94480093f4SDimitry Andric enum ReproducerCrashSignal {
95480093f4SDimitry Andric   eReproducerCrashSigill,
96480093f4SDimitry Andric   eReproducerCrashSigsegv,
97480093f4SDimitry Andric };
98480093f4SDimitry Andric 
99480093f4SDimitry Andric static constexpr OptionEnumValueElement g_reproducer_signaltype[] = {
100480093f4SDimitry Andric     {
101480093f4SDimitry Andric         eReproducerCrashSigill,
102480093f4SDimitry Andric         "SIGILL",
103480093f4SDimitry Andric         "Illegal instruction",
104480093f4SDimitry Andric     },
105480093f4SDimitry Andric     {
106480093f4SDimitry Andric         eReproducerCrashSigsegv,
107480093f4SDimitry Andric         "SIGSEGV",
108480093f4SDimitry Andric         "Segmentation fault",
109480093f4SDimitry Andric     },
110480093f4SDimitry Andric };
111480093f4SDimitry Andric 
ReproducerSignalType()112480093f4SDimitry Andric static constexpr OptionEnumValues ReproducerSignalType() {
113480093f4SDimitry Andric   return OptionEnumValues(g_reproducer_signaltype);
114480093f4SDimitry Andric }
115480093f4SDimitry Andric 
116480093f4SDimitry Andric #define LLDB_OPTIONS_reproducer_xcrash
1179dba64beSDimitry Andric #include "CommandOptions.inc"
1180b57cec5SDimitry Andric 
119af732203SDimitry Andric #define LLDB_OPTIONS_reproducer_verify
120af732203SDimitry Andric #include "CommandOptions.inc"
121af732203SDimitry Andric 
1225ffd83dbSDimitry Andric template <typename T>
ReadFromYAML(StringRef filename)1235ffd83dbSDimitry Andric llvm::Expected<T> static ReadFromYAML(StringRef filename) {
1245ffd83dbSDimitry Andric   auto error_or_file = MemoryBuffer::getFile(filename);
1255ffd83dbSDimitry Andric   if (auto err = error_or_file.getError()) {
1265ffd83dbSDimitry Andric     return errorCodeToError(err);
1275ffd83dbSDimitry Andric   }
1285ffd83dbSDimitry Andric 
1295ffd83dbSDimitry Andric   T t;
1305ffd83dbSDimitry Andric   yaml::Input yin((*error_or_file)->getBuffer());
1315ffd83dbSDimitry Andric   yin >> t;
1325ffd83dbSDimitry Andric 
1335ffd83dbSDimitry Andric   if (auto err = yin.error()) {
1345ffd83dbSDimitry Andric     return errorCodeToError(err);
1355ffd83dbSDimitry Andric   }
1365ffd83dbSDimitry Andric 
1375ffd83dbSDimitry Andric   return t;
1385ffd83dbSDimitry Andric }
1395ffd83dbSDimitry Andric 
SetError(CommandReturnObject & result,Error err)140af732203SDimitry Andric static void SetError(CommandReturnObject &result, Error err) {
141*5f7ddb14SDimitry Andric   result.AppendError(toString(std::move(err)));
142af732203SDimitry Andric }
143af732203SDimitry Andric 
144af732203SDimitry Andric /// Create a loader from the given path if specified. Otherwise use the current
145af732203SDimitry Andric /// loader used for replay.
146af732203SDimitry Andric static Loader *
GetLoaderFromPathOrCurrent(llvm::Optional<Loader> & loader_storage,CommandReturnObject & result,FileSpec reproducer_path)147af732203SDimitry Andric GetLoaderFromPathOrCurrent(llvm::Optional<Loader> &loader_storage,
148af732203SDimitry Andric                            CommandReturnObject &result,
149af732203SDimitry Andric                            FileSpec reproducer_path) {
150af732203SDimitry Andric   if (reproducer_path) {
151af732203SDimitry Andric     loader_storage.emplace(reproducer_path);
152af732203SDimitry Andric     Loader *loader = &(*loader_storage);
153af732203SDimitry Andric     if (Error err = loader->LoadIndex()) {
154af732203SDimitry Andric       // This is a hard error and will set the result to eReturnStatusFailed.
155af732203SDimitry Andric       SetError(result, std::move(err));
156af732203SDimitry Andric       return nullptr;
157af732203SDimitry Andric     }
158af732203SDimitry Andric     return loader;
159af732203SDimitry Andric   }
160af732203SDimitry Andric 
161af732203SDimitry Andric   if (Loader *loader = Reproducer::Instance().GetLoader())
162af732203SDimitry Andric     return loader;
163af732203SDimitry Andric 
164af732203SDimitry Andric   // This is a soft error because this is expected to fail during capture.
165*5f7ddb14SDimitry Andric   result.AppendError(
166*5f7ddb14SDimitry Andric       "Not specifying a reproducer is only support during replay.");
167af732203SDimitry Andric   result.SetStatus(eReturnStatusSuccessFinishNoResult);
168af732203SDimitry Andric   return nullptr;
169af732203SDimitry Andric }
170af732203SDimitry Andric 
1710b57cec5SDimitry Andric class CommandObjectReproducerGenerate : public CommandObjectParsed {
1720b57cec5SDimitry Andric public:
CommandObjectReproducerGenerate(CommandInterpreter & interpreter)1730b57cec5SDimitry Andric   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
1740b57cec5SDimitry Andric       : CommandObjectParsed(
1750b57cec5SDimitry Andric             interpreter, "reproducer generate",
1760b57cec5SDimitry Andric             "Generate reproducer on disk. When the debugger is in capture "
1770b57cec5SDimitry Andric             "mode, this command will output the reproducer to a directory on "
178480093f4SDimitry Andric             "disk and quit. In replay mode this command in a no-op.",
1790b57cec5SDimitry Andric             nullptr) {}
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric   ~CommandObjectReproducerGenerate() override = default;
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric protected:
DoExecute(Args & command,CommandReturnObject & result)1840b57cec5SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
1850b57cec5SDimitry Andric     if (!command.empty()) {
1860b57cec5SDimitry Andric       result.AppendErrorWithFormat("'%s' takes no arguments",
1870b57cec5SDimitry Andric                                    m_cmd_name.c_str());
1880b57cec5SDimitry Andric       return false;
1890b57cec5SDimitry Andric     }
1900b57cec5SDimitry Andric 
1919dba64beSDimitry Andric     auto &r = Reproducer::Instance();
1920b57cec5SDimitry Andric     if (auto generator = r.GetGenerator()) {
1930b57cec5SDimitry Andric       generator->Keep();
194af732203SDimitry Andric       if (llvm::Error e = repro::Finalize(r.GetReproducerPath())) {
195af732203SDimitry Andric         SetError(result, std::move(e));
196af732203SDimitry Andric         return result.Succeeded();
197af732203SDimitry Andric       }
1989dba64beSDimitry Andric     } else if (r.IsReplaying()) {
199480093f4SDimitry Andric       // Make this operation a NO-OP in replay mode.
2000b57cec5SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishNoResult);
2010b57cec5SDimitry Andric       return result.Succeeded();
2020b57cec5SDimitry Andric     } else {
2030b57cec5SDimitry Andric       result.AppendErrorWithFormat("Unable to get the reproducer generator");
2040b57cec5SDimitry Andric       return false;
2050b57cec5SDimitry Andric     }
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric     result.GetOutputStream()
2080b57cec5SDimitry Andric         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
2090b57cec5SDimitry Andric     result.GetOutputStream()
2100b57cec5SDimitry Andric         << "Please have a look at the directory to assess if you're willing to "
2110b57cec5SDimitry Andric            "share the contained information.\n";
2120b57cec5SDimitry Andric 
213480093f4SDimitry Andric     m_interpreter.BroadcastEvent(
214480093f4SDimitry Andric         CommandInterpreter::eBroadcastBitQuitCommandReceived);
215480093f4SDimitry Andric     result.SetStatus(eReturnStatusQuit);
2160b57cec5SDimitry Andric     return result.Succeeded();
2170b57cec5SDimitry Andric   }
2180b57cec5SDimitry Andric };
2190b57cec5SDimitry Andric 
220480093f4SDimitry Andric class CommandObjectReproducerXCrash : public CommandObjectParsed {
221480093f4SDimitry Andric public:
CommandObjectReproducerXCrash(CommandInterpreter & interpreter)222480093f4SDimitry Andric   CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
223480093f4SDimitry Andric       : CommandObjectParsed(interpreter, "reproducer xcrash",
224480093f4SDimitry Andric                             "Intentionally force  the debugger to crash in "
225480093f4SDimitry Andric                             "order to trigger and test reproducer generation.",
226480093f4SDimitry Andric                             nullptr) {}
227480093f4SDimitry Andric 
228480093f4SDimitry Andric   ~CommandObjectReproducerXCrash() override = default;
229480093f4SDimitry Andric 
GetOptions()230480093f4SDimitry Andric   Options *GetOptions() override { return &m_options; }
231480093f4SDimitry Andric 
232480093f4SDimitry Andric   class CommandOptions : public Options {
233480093f4SDimitry Andric   public:
CommandOptions()234480093f4SDimitry Andric     CommandOptions() : Options() {}
235480093f4SDimitry Andric 
236480093f4SDimitry Andric     ~CommandOptions() override = default;
237480093f4SDimitry Andric 
SetOptionValue(uint32_t option_idx,StringRef option_arg,ExecutionContext * execution_context)238480093f4SDimitry Andric     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
239480093f4SDimitry Andric                           ExecutionContext *execution_context) override {
240480093f4SDimitry Andric       Status error;
241480093f4SDimitry Andric       const int short_option = m_getopt_table[option_idx].val;
242480093f4SDimitry Andric 
243480093f4SDimitry Andric       switch (short_option) {
244480093f4SDimitry Andric       case 's':
245480093f4SDimitry Andric         signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
246480093f4SDimitry Andric             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
247480093f4SDimitry Andric         if (!error.Success())
248480093f4SDimitry Andric           error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
249480093f4SDimitry Andric                                          option_arg.str().c_str());
250480093f4SDimitry Andric         break;
251480093f4SDimitry Andric       default:
252480093f4SDimitry Andric         llvm_unreachable("Unimplemented option");
253480093f4SDimitry Andric       }
254480093f4SDimitry Andric 
255480093f4SDimitry Andric       return error;
256480093f4SDimitry Andric     }
257480093f4SDimitry Andric 
OptionParsingStarting(ExecutionContext * execution_context)258480093f4SDimitry Andric     void OptionParsingStarting(ExecutionContext *execution_context) override {
259480093f4SDimitry Andric       signal = eReproducerCrashSigsegv;
260480093f4SDimitry Andric     }
261480093f4SDimitry Andric 
GetDefinitions()262480093f4SDimitry Andric     ArrayRef<OptionDefinition> GetDefinitions() override {
263480093f4SDimitry Andric       return makeArrayRef(g_reproducer_xcrash_options);
264480093f4SDimitry Andric     }
265480093f4SDimitry Andric 
266480093f4SDimitry Andric     ReproducerCrashSignal signal = eReproducerCrashSigsegv;
267480093f4SDimitry Andric   };
268480093f4SDimitry Andric 
269480093f4SDimitry Andric protected:
DoExecute(Args & command,CommandReturnObject & result)270480093f4SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
271480093f4SDimitry Andric     if (!command.empty()) {
272480093f4SDimitry Andric       result.AppendErrorWithFormat("'%s' takes no arguments",
273480093f4SDimitry Andric                                    m_cmd_name.c_str());
274480093f4SDimitry Andric       return false;
275480093f4SDimitry Andric     }
276480093f4SDimitry Andric 
277480093f4SDimitry Andric     auto &r = Reproducer::Instance();
278480093f4SDimitry Andric 
279480093f4SDimitry Andric     if (!r.IsCapturing() && !r.IsReplaying()) {
280*5f7ddb14SDimitry Andric       result.AppendError(
281480093f4SDimitry Andric           "forcing a crash is only supported when capturing a reproducer.");
282480093f4SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishNoResult);
283480093f4SDimitry Andric       return false;
284480093f4SDimitry Andric     }
285480093f4SDimitry Andric 
286480093f4SDimitry Andric     switch (m_options.signal) {
287480093f4SDimitry Andric     case eReproducerCrashSigill:
288480093f4SDimitry Andric       std::raise(SIGILL);
289480093f4SDimitry Andric       break;
290480093f4SDimitry Andric     case eReproducerCrashSigsegv:
291480093f4SDimitry Andric       std::raise(SIGSEGV);
292480093f4SDimitry Andric       break;
293480093f4SDimitry Andric     }
294480093f4SDimitry Andric 
295480093f4SDimitry Andric     result.SetStatus(eReturnStatusQuit);
296480093f4SDimitry Andric     return result.Succeeded();
297480093f4SDimitry Andric   }
298480093f4SDimitry Andric 
299480093f4SDimitry Andric private:
300480093f4SDimitry Andric   CommandOptions m_options;
301480093f4SDimitry Andric };
302480093f4SDimitry Andric 
3030b57cec5SDimitry Andric class CommandObjectReproducerStatus : public CommandObjectParsed {
3040b57cec5SDimitry Andric public:
CommandObjectReproducerStatus(CommandInterpreter & interpreter)3050b57cec5SDimitry Andric   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
3060b57cec5SDimitry Andric       : CommandObjectParsed(
3070b57cec5SDimitry Andric             interpreter, "reproducer status",
308480093f4SDimitry Andric             "Show the current reproducer status. In capture mode the "
309480093f4SDimitry Andric             "debugger "
3100b57cec5SDimitry Andric             "is collecting all the information it needs to create a "
3110b57cec5SDimitry Andric             "reproducer.  In replay mode the reproducer is replaying a "
3120b57cec5SDimitry Andric             "reproducer. When the reproducers are off, no data is collected "
3130b57cec5SDimitry Andric             "and no reproducer can be generated.",
3140b57cec5SDimitry Andric             nullptr) {}
3150b57cec5SDimitry Andric 
3160b57cec5SDimitry Andric   ~CommandObjectReproducerStatus() override = default;
3170b57cec5SDimitry Andric 
3180b57cec5SDimitry Andric protected:
DoExecute(Args & command,CommandReturnObject & result)3190b57cec5SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
3200b57cec5SDimitry Andric     if (!command.empty()) {
3210b57cec5SDimitry Andric       result.AppendErrorWithFormat("'%s' takes no arguments",
3220b57cec5SDimitry Andric                                    m_cmd_name.c_str());
3230b57cec5SDimitry Andric       return false;
3240b57cec5SDimitry Andric     }
3250b57cec5SDimitry Andric 
3269dba64beSDimitry Andric     auto &r = Reproducer::Instance();
3279dba64beSDimitry Andric     if (r.IsCapturing()) {
3280b57cec5SDimitry Andric       result.GetOutputStream() << "Reproducer is in capture mode.\n";
3299dba64beSDimitry Andric     } else if (r.IsReplaying()) {
3300b57cec5SDimitry Andric       result.GetOutputStream() << "Reproducer is in replay mode.\n";
3310b57cec5SDimitry Andric     } else {
3320b57cec5SDimitry Andric       result.GetOutputStream() << "Reproducer is off.\n";
3330b57cec5SDimitry Andric     }
3340b57cec5SDimitry Andric 
3355ffd83dbSDimitry Andric     if (r.IsCapturing() || r.IsReplaying()) {
3365ffd83dbSDimitry Andric       result.GetOutputStream()
3375ffd83dbSDimitry Andric           << "Path: " << r.GetReproducerPath().GetPath() << '\n';
3385ffd83dbSDimitry Andric     }
3395ffd83dbSDimitry Andric 
3405ffd83dbSDimitry Andric     // Auto generate is hidden unless enabled because this is mostly for
3415ffd83dbSDimitry Andric     // development and testing.
3425ffd83dbSDimitry Andric     if (Generator *g = r.GetGenerator()) {
3435ffd83dbSDimitry Andric       if (g->IsAutoGenerate())
3445ffd83dbSDimitry Andric         result.GetOutputStream() << "Auto generate: on\n";
3455ffd83dbSDimitry Andric     }
3465ffd83dbSDimitry Andric 
3470b57cec5SDimitry Andric     result.SetStatus(eReturnStatusSuccessFinishResult);
3480b57cec5SDimitry Andric     return result.Succeeded();
3490b57cec5SDimitry Andric   }
3500b57cec5SDimitry Andric };
3510b57cec5SDimitry Andric 
3529dba64beSDimitry Andric class CommandObjectReproducerDump : public CommandObjectParsed {
3539dba64beSDimitry Andric public:
CommandObjectReproducerDump(CommandInterpreter & interpreter)3549dba64beSDimitry Andric   CommandObjectReproducerDump(CommandInterpreter &interpreter)
3559dba64beSDimitry Andric       : CommandObjectParsed(interpreter, "reproducer dump",
3569dba64beSDimitry Andric                             "Dump the information contained in a reproducer. "
3579dba64beSDimitry Andric                             "If no reproducer is specified during replay, it "
3589dba64beSDimitry Andric                             "dumps the content of the current reproducer.",
3599dba64beSDimitry Andric                             nullptr) {}
3609dba64beSDimitry Andric 
3619dba64beSDimitry Andric   ~CommandObjectReproducerDump() override = default;
3629dba64beSDimitry Andric 
GetOptions()3639dba64beSDimitry Andric   Options *GetOptions() override { return &m_options; }
3649dba64beSDimitry Andric 
3659dba64beSDimitry Andric   class CommandOptions : public Options {
3669dba64beSDimitry Andric   public:
CommandOptions()3679dba64beSDimitry Andric     CommandOptions() : Options(), file() {}
3689dba64beSDimitry Andric 
3699dba64beSDimitry Andric     ~CommandOptions() override = default;
3709dba64beSDimitry Andric 
SetOptionValue(uint32_t option_idx,StringRef option_arg,ExecutionContext * execution_context)3719dba64beSDimitry Andric     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
3729dba64beSDimitry Andric                           ExecutionContext *execution_context) override {
3739dba64beSDimitry Andric       Status error;
3749dba64beSDimitry Andric       const int short_option = m_getopt_table[option_idx].val;
3759dba64beSDimitry Andric 
3769dba64beSDimitry Andric       switch (short_option) {
3779dba64beSDimitry Andric       case 'f':
3789dba64beSDimitry Andric         file.SetFile(option_arg, FileSpec::Style::native);
3799dba64beSDimitry Andric         FileSystem::Instance().Resolve(file);
3809dba64beSDimitry Andric         break;
3819dba64beSDimitry Andric       case 'p':
3829dba64beSDimitry Andric         provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
3839dba64beSDimitry Andric             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
3849dba64beSDimitry Andric         if (!error.Success())
3859dba64beSDimitry Andric           error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
3869dba64beSDimitry Andric                                          option_arg.str().c_str());
3879dba64beSDimitry Andric         break;
3889dba64beSDimitry Andric       default:
3899dba64beSDimitry Andric         llvm_unreachable("Unimplemented option");
3909dba64beSDimitry Andric       }
3919dba64beSDimitry Andric 
3929dba64beSDimitry Andric       return error;
3939dba64beSDimitry Andric     }
3949dba64beSDimitry Andric 
OptionParsingStarting(ExecutionContext * execution_context)3959dba64beSDimitry Andric     void OptionParsingStarting(ExecutionContext *execution_context) override {
3969dba64beSDimitry Andric       file.Clear();
3979dba64beSDimitry Andric       provider = eReproducerProviderNone;
3989dba64beSDimitry Andric     }
3999dba64beSDimitry Andric 
GetDefinitions()4009dba64beSDimitry Andric     ArrayRef<OptionDefinition> GetDefinitions() override {
401480093f4SDimitry Andric       return makeArrayRef(g_reproducer_dump_options);
4029dba64beSDimitry Andric     }
4039dba64beSDimitry Andric 
4049dba64beSDimitry Andric     FileSpec file;
4059dba64beSDimitry Andric     ReproducerProvider provider = eReproducerProviderNone;
4069dba64beSDimitry Andric   };
4079dba64beSDimitry Andric 
4089dba64beSDimitry Andric protected:
DoExecute(Args & command,CommandReturnObject & result)4099dba64beSDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
4109dba64beSDimitry Andric     if (!command.empty()) {
4119dba64beSDimitry Andric       result.AppendErrorWithFormat("'%s' takes no arguments",
4129dba64beSDimitry Andric                                    m_cmd_name.c_str());
4139dba64beSDimitry Andric       return false;
4149dba64beSDimitry Andric     }
4159dba64beSDimitry Andric 
4169dba64beSDimitry Andric     llvm::Optional<Loader> loader_storage;
417af732203SDimitry Andric     Loader *loader =
418af732203SDimitry Andric         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
419af732203SDimitry Andric     if (!loader)
4209dba64beSDimitry Andric       return false;
4219dba64beSDimitry Andric 
4229dba64beSDimitry Andric     switch (m_options.provider) {
4239dba64beSDimitry Andric     case eReproducerProviderFiles: {
4249dba64beSDimitry Andric       FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
4259dba64beSDimitry Andric 
4269dba64beSDimitry Andric       // Read the VFS mapping.
4279dba64beSDimitry Andric       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
4289dba64beSDimitry Andric           vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
4299dba64beSDimitry Andric       if (!buffer) {
4309dba64beSDimitry Andric         SetError(result, errorCodeToError(buffer.getError()));
4319dba64beSDimitry Andric         return false;
4329dba64beSDimitry Andric       }
4339dba64beSDimitry Andric 
4349dba64beSDimitry Andric       // Initialize a VFS from the given mapping.
4359dba64beSDimitry Andric       IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
4369dba64beSDimitry Andric           std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
4379dba64beSDimitry Andric 
4389dba64beSDimitry Andric       // Dump the VFS to a buffer.
4399dba64beSDimitry Andric       std::string str;
4409dba64beSDimitry Andric       raw_string_ostream os(str);
4419dba64beSDimitry Andric       static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
4429dba64beSDimitry Andric       os.flush();
4439dba64beSDimitry Andric 
4449dba64beSDimitry Andric       // Return the string.
4459dba64beSDimitry Andric       result.AppendMessage(str);
4469dba64beSDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
4479dba64beSDimitry Andric       return true;
4489dba64beSDimitry Andric     }
449af732203SDimitry Andric     case eReproducerProviderSymbolFiles: {
450af732203SDimitry Andric       Expected<std::string> symbol_files =
451af732203SDimitry Andric           loader->LoadBuffer<SymbolFileProvider>();
452af732203SDimitry Andric       if (!symbol_files) {
453af732203SDimitry Andric         SetError(result, symbol_files.takeError());
454af732203SDimitry Andric         return false;
455af732203SDimitry Andric       }
456af732203SDimitry Andric 
457af732203SDimitry Andric       std::vector<SymbolFileProvider::Entry> entries;
458af732203SDimitry Andric       llvm::yaml::Input yin(*symbol_files);
459af732203SDimitry Andric       yin >> entries;
460af732203SDimitry Andric 
461af732203SDimitry Andric       for (const auto &entry : entries) {
462af732203SDimitry Andric         result.AppendMessageWithFormat("- uuid:        %s\n",
463af732203SDimitry Andric                                        entry.uuid.c_str());
464af732203SDimitry Andric         result.AppendMessageWithFormat("  module path: %s\n",
465af732203SDimitry Andric                                        entry.module_path.c_str());
466af732203SDimitry Andric         result.AppendMessageWithFormat("  symbol path: %s\n",
467af732203SDimitry Andric                                        entry.symbol_path.c_str());
468af732203SDimitry Andric       }
469af732203SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
470af732203SDimitry Andric       return true;
471af732203SDimitry Andric     }
4729dba64beSDimitry Andric     case eReproducerProviderVersion: {
4739dba64beSDimitry Andric       Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
4749dba64beSDimitry Andric       if (!version) {
4759dba64beSDimitry Andric         SetError(result, version.takeError());
4769dba64beSDimitry Andric         return false;
4779dba64beSDimitry Andric       }
4789dba64beSDimitry Andric       result.AppendMessage(*version);
4799dba64beSDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
4809dba64beSDimitry Andric       return true;
4819dba64beSDimitry Andric     }
4829dba64beSDimitry Andric     case eReproducerProviderWorkingDirectory: {
4839dba64beSDimitry Andric       Expected<std::string> cwd =
484af732203SDimitry Andric           repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader);
4859dba64beSDimitry Andric       if (!cwd) {
4869dba64beSDimitry Andric         SetError(result, cwd.takeError());
4879dba64beSDimitry Andric         return false;
4889dba64beSDimitry Andric       }
4899dba64beSDimitry Andric       result.AppendMessage(*cwd);
4909dba64beSDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
4919dba64beSDimitry Andric       return true;
4929dba64beSDimitry Andric     }
493af732203SDimitry Andric     case eReproducerProviderHomeDirectory: {
494af732203SDimitry Andric       Expected<std::string> home =
495af732203SDimitry Andric           repro::GetDirectoryFrom<HomeDirectoryProvider>(loader);
496af732203SDimitry Andric       if (!home) {
497af732203SDimitry Andric         SetError(result, home.takeError());
498af732203SDimitry Andric         return false;
499af732203SDimitry Andric       }
500af732203SDimitry Andric       result.AppendMessage(*home);
501af732203SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
502af732203SDimitry Andric       return true;
503af732203SDimitry Andric     }
5049dba64beSDimitry Andric     case eReproducerProviderCommands: {
505480093f4SDimitry Andric       std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
506480093f4SDimitry Andric           repro::MultiLoader<repro::CommandProvider>::Create(loader);
507480093f4SDimitry Andric       if (!multi_loader) {
5089dba64beSDimitry Andric         SetError(result,
5095ffd83dbSDimitry Andric                  make_error<StringError>("Unable to create command loader.",
5105ffd83dbSDimitry Andric                                          llvm::inconvertibleErrorCode()));
5119dba64beSDimitry Andric         return false;
5129dba64beSDimitry Andric       }
5139dba64beSDimitry Andric 
5149dba64beSDimitry Andric       // Iterate over the command files and dump them.
515480093f4SDimitry Andric       llvm::Optional<std::string> command_file;
516480093f4SDimitry Andric       while ((command_file = multi_loader->GetNextFile())) {
5179dba64beSDimitry Andric         if (!command_file)
5189dba64beSDimitry Andric           break;
5199dba64beSDimitry Andric 
5209dba64beSDimitry Andric         auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
5219dba64beSDimitry Andric         if (auto err = command_buffer.getError()) {
5229dba64beSDimitry Andric           SetError(result, errorCodeToError(err));
5239dba64beSDimitry Andric           return false;
5249dba64beSDimitry Andric         }
5259dba64beSDimitry Andric         result.AppendMessage((*command_buffer)->getBuffer());
5269dba64beSDimitry Andric       }
5279dba64beSDimitry Andric 
5289dba64beSDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
5299dba64beSDimitry Andric       return true;
5309dba64beSDimitry Andric     }
5319dba64beSDimitry Andric     case eReproducerProviderGDB: {
532480093f4SDimitry Andric       std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
533480093f4SDimitry Andric           multi_loader =
534480093f4SDimitry Andric               repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
5355ffd83dbSDimitry Andric 
5365ffd83dbSDimitry Andric       if (!multi_loader) {
5375ffd83dbSDimitry Andric         SetError(result,
5385ffd83dbSDimitry Andric                  make_error<StringError>("Unable to create GDB loader.",
5395ffd83dbSDimitry Andric                                          llvm::inconvertibleErrorCode()));
5405ffd83dbSDimitry Andric         return false;
5415ffd83dbSDimitry Andric       }
5425ffd83dbSDimitry Andric 
543480093f4SDimitry Andric       llvm::Optional<std::string> gdb_file;
544480093f4SDimitry Andric       while ((gdb_file = multi_loader->GetNextFile())) {
5455ffd83dbSDimitry Andric         if (llvm::Expected<std::vector<GDBRemotePacket>> packets =
5465ffd83dbSDimitry Andric                 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) {
5475ffd83dbSDimitry Andric           for (GDBRemotePacket &packet : *packets) {
5489dba64beSDimitry Andric             packet.Dump(result.GetOutputStream());
5499dba64beSDimitry Andric           }
5505ffd83dbSDimitry Andric         } else {
5515ffd83dbSDimitry Andric           SetError(result, packets.takeError());
5525ffd83dbSDimitry Andric           return false;
5535ffd83dbSDimitry Andric         }
5545ffd83dbSDimitry Andric       }
5555ffd83dbSDimitry Andric 
5565ffd83dbSDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
5575ffd83dbSDimitry Andric       return true;
5585ffd83dbSDimitry Andric     }
5595ffd83dbSDimitry Andric     case eReproducerProviderProcessInfo: {
5605ffd83dbSDimitry Andric       std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
5615ffd83dbSDimitry Andric           multi_loader =
5625ffd83dbSDimitry Andric               repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
5635ffd83dbSDimitry Andric 
5645ffd83dbSDimitry Andric       if (!multi_loader) {
5655ffd83dbSDimitry Andric         SetError(result, make_error<StringError>(
5665ffd83dbSDimitry Andric                              llvm::inconvertibleErrorCode(),
5675ffd83dbSDimitry Andric                              "Unable to create process info loader."));
5685ffd83dbSDimitry Andric         return false;
5695ffd83dbSDimitry Andric       }
5705ffd83dbSDimitry Andric 
5715ffd83dbSDimitry Andric       llvm::Optional<std::string> process_file;
5725ffd83dbSDimitry Andric       while ((process_file = multi_loader->GetNextFile())) {
5735ffd83dbSDimitry Andric         if (llvm::Expected<ProcessInstanceInfoList> infos =
5745ffd83dbSDimitry Andric                 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) {
5755ffd83dbSDimitry Andric           for (ProcessInstanceInfo info : *infos)
5765ffd83dbSDimitry Andric             info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
5775ffd83dbSDimitry Andric         } else {
5785ffd83dbSDimitry Andric           SetError(result, infos.takeError());
5795ffd83dbSDimitry Andric           return false;
5805ffd83dbSDimitry Andric         }
581480093f4SDimitry Andric       }
5829dba64beSDimitry Andric 
5839dba64beSDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
5849dba64beSDimitry Andric       return true;
5859dba64beSDimitry Andric     }
5869dba64beSDimitry Andric     case eReproducerProviderNone:
587*5f7ddb14SDimitry Andric       result.AppendError("No valid provider specified.");
5889dba64beSDimitry Andric       return false;
5899dba64beSDimitry Andric     }
5909dba64beSDimitry Andric 
5919dba64beSDimitry Andric     result.SetStatus(eReturnStatusSuccessFinishNoResult);
5929dba64beSDimitry Andric     return result.Succeeded();
5939dba64beSDimitry Andric   }
5949dba64beSDimitry Andric 
5959dba64beSDimitry Andric private:
5969dba64beSDimitry Andric   CommandOptions m_options;
5979dba64beSDimitry Andric };
5989dba64beSDimitry Andric 
599af732203SDimitry Andric class CommandObjectReproducerVerify : public CommandObjectParsed {
600af732203SDimitry Andric public:
CommandObjectReproducerVerify(CommandInterpreter & interpreter)601af732203SDimitry Andric   CommandObjectReproducerVerify(CommandInterpreter &interpreter)
602af732203SDimitry Andric       : CommandObjectParsed(interpreter, "reproducer verify",
603af732203SDimitry Andric                             "Verify the contents of a reproducer. "
604af732203SDimitry Andric                             "If no reproducer is specified during replay, it "
605af732203SDimitry Andric                             "verifies the content of the current reproducer.",
606af732203SDimitry Andric                             nullptr) {}
607af732203SDimitry Andric 
608af732203SDimitry Andric   ~CommandObjectReproducerVerify() override = default;
609af732203SDimitry Andric 
GetOptions()610af732203SDimitry Andric   Options *GetOptions() override { return &m_options; }
611af732203SDimitry Andric 
612af732203SDimitry Andric   class CommandOptions : public Options {
613af732203SDimitry Andric   public:
CommandOptions()614af732203SDimitry Andric     CommandOptions() : Options(), file() {}
615af732203SDimitry Andric 
616af732203SDimitry Andric     ~CommandOptions() override = default;
617af732203SDimitry Andric 
SetOptionValue(uint32_t option_idx,StringRef option_arg,ExecutionContext * execution_context)618af732203SDimitry Andric     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
619af732203SDimitry Andric                           ExecutionContext *execution_context) override {
620af732203SDimitry Andric       Status error;
621af732203SDimitry Andric       const int short_option = m_getopt_table[option_idx].val;
622af732203SDimitry Andric 
623af732203SDimitry Andric       switch (short_option) {
624af732203SDimitry Andric       case 'f':
625af732203SDimitry Andric         file.SetFile(option_arg, FileSpec::Style::native);
626af732203SDimitry Andric         FileSystem::Instance().Resolve(file);
627af732203SDimitry Andric         break;
628af732203SDimitry Andric       default:
629af732203SDimitry Andric         llvm_unreachable("Unimplemented option");
630af732203SDimitry Andric       }
631af732203SDimitry Andric 
632af732203SDimitry Andric       return error;
633af732203SDimitry Andric     }
634af732203SDimitry Andric 
OptionParsingStarting(ExecutionContext * execution_context)635af732203SDimitry Andric     void OptionParsingStarting(ExecutionContext *execution_context) override {
636af732203SDimitry Andric       file.Clear();
637af732203SDimitry Andric     }
638af732203SDimitry Andric 
GetDefinitions()639af732203SDimitry Andric     ArrayRef<OptionDefinition> GetDefinitions() override {
640af732203SDimitry Andric       return makeArrayRef(g_reproducer_verify_options);
641af732203SDimitry Andric     }
642af732203SDimitry Andric 
643af732203SDimitry Andric     FileSpec file;
644af732203SDimitry Andric   };
645af732203SDimitry Andric 
646af732203SDimitry Andric protected:
DoExecute(Args & command,CommandReturnObject & result)647af732203SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
648af732203SDimitry Andric     if (!command.empty()) {
649af732203SDimitry Andric       result.AppendErrorWithFormat("'%s' takes no arguments",
650af732203SDimitry Andric                                    m_cmd_name.c_str());
651af732203SDimitry Andric       return false;
652af732203SDimitry Andric     }
653af732203SDimitry Andric 
654af732203SDimitry Andric     llvm::Optional<Loader> loader_storage;
655af732203SDimitry Andric     Loader *loader =
656af732203SDimitry Andric         GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
657af732203SDimitry Andric     if (!loader)
658af732203SDimitry Andric       return false;
659af732203SDimitry Andric 
660af732203SDimitry Andric     bool errors = false;
661af732203SDimitry Andric     auto error_callback = [&](llvm::StringRef error) {
662af732203SDimitry Andric       errors = true;
663af732203SDimitry Andric       result.AppendError(error);
664af732203SDimitry Andric     };
665af732203SDimitry Andric 
666af732203SDimitry Andric     bool warnings = false;
667af732203SDimitry Andric     auto warning_callback = [&](llvm::StringRef warning) {
668af732203SDimitry Andric       warnings = true;
669af732203SDimitry Andric       result.AppendWarning(warning);
670af732203SDimitry Andric     };
671af732203SDimitry Andric 
672af732203SDimitry Andric     auto note_callback = [&](llvm::StringRef warning) {
673af732203SDimitry Andric       result.AppendMessage(warning);
674af732203SDimitry Andric     };
675af732203SDimitry Andric 
676af732203SDimitry Andric     Verifier verifier(loader);
677af732203SDimitry Andric     verifier.Verify(error_callback, warning_callback, note_callback);
678af732203SDimitry Andric 
679af732203SDimitry Andric     if (warnings || errors) {
680af732203SDimitry Andric       result.AppendMessage("reproducer verification failed");
681af732203SDimitry Andric       result.SetStatus(eReturnStatusFailed);
682af732203SDimitry Andric     } else {
683af732203SDimitry Andric       result.AppendMessage("reproducer verification succeeded");
684af732203SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
685af732203SDimitry Andric     }
686af732203SDimitry Andric 
687af732203SDimitry Andric     return result.Succeeded();
688af732203SDimitry Andric   }
689af732203SDimitry Andric 
690af732203SDimitry Andric private:
691af732203SDimitry Andric   CommandOptions m_options;
692af732203SDimitry Andric };
693af732203SDimitry Andric 
CommandObjectReproducer(CommandInterpreter & interpreter)6940b57cec5SDimitry Andric CommandObjectReproducer::CommandObjectReproducer(
6950b57cec5SDimitry Andric     CommandInterpreter &interpreter)
6960b57cec5SDimitry Andric     : CommandObjectMultiword(
6970b57cec5SDimitry Andric           interpreter, "reproducer",
698480093f4SDimitry Andric           "Commands for manipulating reproducers. Reproducers make it "
699480093f4SDimitry Andric           "possible "
7009dba64beSDimitry Andric           "to capture full debug sessions with all its dependencies. The "
7019dba64beSDimitry Andric           "resulting reproducer is used to replay the debug session while "
7029dba64beSDimitry Andric           "debugging the debugger.\n"
7039dba64beSDimitry Andric           "Because reproducers need the whole the debug session from "
7049dba64beSDimitry Andric           "beginning to end, you need to launch the debugger in capture or "
7059dba64beSDimitry Andric           "replay mode, commonly though the command line driver.\n"
7069dba64beSDimitry Andric           "Reproducers are unrelated record-replay debugging, as you cannot "
7079dba64beSDimitry Andric           "interact with the debugger during replay.\n",
7089dba64beSDimitry Andric           "reproducer <subcommand> [<subcommand-options>]") {
7090b57cec5SDimitry Andric   LoadSubCommand(
7100b57cec5SDimitry Andric       "generate",
7110b57cec5SDimitry Andric       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
7120b57cec5SDimitry Andric   LoadSubCommand("status", CommandObjectSP(
7130b57cec5SDimitry Andric                                new CommandObjectReproducerStatus(interpreter)));
7149dba64beSDimitry Andric   LoadSubCommand("dump",
7159dba64beSDimitry Andric                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
716af732203SDimitry Andric   LoadSubCommand("verify", CommandObjectSP(
717af732203SDimitry Andric                                new CommandObjectReproducerVerify(interpreter)));
718480093f4SDimitry Andric   LoadSubCommand("xcrash", CommandObjectSP(
719480093f4SDimitry Andric                                new CommandObjectReproducerXCrash(interpreter)));
7200b57cec5SDimitry Andric }
7210b57cec5SDimitry Andric 
7220b57cec5SDimitry Andric CommandObjectReproducer::~CommandObjectReproducer() = default;
723