19e046f02SJonas Devlieghere //===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
29e046f02SJonas Devlieghere //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69e046f02SJonas Devlieghere //
79e046f02SJonas Devlieghere //===----------------------------------------------------------------------===//
89e046f02SJonas Devlieghere 
99e046f02SJonas Devlieghere #include "CommandObjectReproducer.h"
109e046f02SJonas Devlieghere 
11*97fc8eb4SJonas Devlieghere #include "lldb/Host/OptionParser.h"
129e046f02SJonas Devlieghere #include "lldb/Utility/Reproducer.h"
139e046f02SJonas Devlieghere 
14df14b942SJonas Devlieghere #include "lldb/Interpreter/CommandInterpreter.h"
159e046f02SJonas Devlieghere #include "lldb/Interpreter/CommandReturnObject.h"
169e046f02SJonas Devlieghere #include "lldb/Interpreter/OptionArgParser.h"
179e046f02SJonas Devlieghere #include "lldb/Interpreter/OptionGroupBoolean.h"
189e046f02SJonas Devlieghere 
199e046f02SJonas Devlieghere using namespace lldb;
20*97fc8eb4SJonas Devlieghere using namespace llvm;
219e046f02SJonas Devlieghere using namespace lldb_private;
22*97fc8eb4SJonas Devlieghere using namespace lldb_private::repro;
23*97fc8eb4SJonas Devlieghere 
24*97fc8eb4SJonas Devlieghere enum ReproducerProvider {
25*97fc8eb4SJonas Devlieghere   eReproducerProviderCommands,
26*97fc8eb4SJonas Devlieghere   eReproducerProviderFiles,
27*97fc8eb4SJonas Devlieghere   eReproducerProviderGDB,
28*97fc8eb4SJonas Devlieghere   eReproducerProviderVersion,
29*97fc8eb4SJonas Devlieghere   eReproducerProviderNone
30*97fc8eb4SJonas Devlieghere };
31*97fc8eb4SJonas Devlieghere 
32*97fc8eb4SJonas Devlieghere static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
33*97fc8eb4SJonas Devlieghere     {
34*97fc8eb4SJonas Devlieghere         eReproducerProviderCommands,
35*97fc8eb4SJonas Devlieghere         "commands",
36*97fc8eb4SJonas Devlieghere         "Command Interpreter Commands",
37*97fc8eb4SJonas Devlieghere     },
38*97fc8eb4SJonas Devlieghere     {
39*97fc8eb4SJonas Devlieghere         eReproducerProviderFiles,
40*97fc8eb4SJonas Devlieghere         "files",
41*97fc8eb4SJonas Devlieghere         "Files",
42*97fc8eb4SJonas Devlieghere     },
43*97fc8eb4SJonas Devlieghere     {
44*97fc8eb4SJonas Devlieghere         eReproducerProviderGDB,
45*97fc8eb4SJonas Devlieghere         "gdb",
46*97fc8eb4SJonas Devlieghere         "GDB Remote Packets",
47*97fc8eb4SJonas Devlieghere     },
48*97fc8eb4SJonas Devlieghere     {
49*97fc8eb4SJonas Devlieghere         eReproducerProviderVersion,
50*97fc8eb4SJonas Devlieghere         "version",
51*97fc8eb4SJonas Devlieghere         "Version",
52*97fc8eb4SJonas Devlieghere     },
53*97fc8eb4SJonas Devlieghere     {
54*97fc8eb4SJonas Devlieghere         eReproducerProviderNone,
55*97fc8eb4SJonas Devlieghere         "none",
56*97fc8eb4SJonas Devlieghere         "None",
57*97fc8eb4SJonas Devlieghere     },
58*97fc8eb4SJonas Devlieghere };
59*97fc8eb4SJonas Devlieghere 
60*97fc8eb4SJonas Devlieghere static constexpr OptionEnumValues ReproducerProviderType() {
61*97fc8eb4SJonas Devlieghere   return OptionEnumValues(g_reproducer_provider_type);
62*97fc8eb4SJonas Devlieghere }
63*97fc8eb4SJonas Devlieghere 
64*97fc8eb4SJonas Devlieghere #define LLDB_OPTIONS_reproducer
65*97fc8eb4SJonas Devlieghere #include "CommandOptions.inc"
669e046f02SJonas Devlieghere 
679e046f02SJonas Devlieghere class CommandObjectReproducerGenerate : public CommandObjectParsed {
689e046f02SJonas Devlieghere public:
699e046f02SJonas Devlieghere   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
70973d66eeSJonas Devlieghere       : CommandObjectParsed(
71973d66eeSJonas Devlieghere             interpreter, "reproducer generate",
72973d66eeSJonas Devlieghere             "Generate reproducer on disk. When the debugger is in capture "
73973d66eeSJonas Devlieghere             "mode, this command will output the reproducer to a directory on "
74973d66eeSJonas Devlieghere             "disk. In replay mode this command in a no-op.",
75973d66eeSJonas Devlieghere             nullptr) {}
769e046f02SJonas Devlieghere 
779e046f02SJonas Devlieghere   ~CommandObjectReproducerGenerate() override = default;
789e046f02SJonas Devlieghere 
799e046f02SJonas Devlieghere protected:
809e046f02SJonas Devlieghere   bool DoExecute(Args &command, CommandReturnObject &result) override {
819e046f02SJonas Devlieghere     if (!command.empty()) {
829e046f02SJonas Devlieghere       result.AppendErrorWithFormat("'%s' takes no arguments",
839e046f02SJonas Devlieghere                                    m_cmd_name.c_str());
849e046f02SJonas Devlieghere       return false;
859e046f02SJonas Devlieghere     }
869e046f02SJonas Devlieghere 
87*97fc8eb4SJonas Devlieghere     auto &r = Reproducer::Instance();
889e046f02SJonas Devlieghere     if (auto generator = r.GetGenerator()) {
899e046f02SJonas Devlieghere       generator->Keep();
902dca6538SJonas Devlieghere     } else if (r.GetLoader()) {
912dca6538SJonas Devlieghere       // Make this operation a NOP in replay mode.
922dca6538SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishNoResult);
932dca6538SJonas Devlieghere       return result.Succeeded();
949e046f02SJonas Devlieghere     } else {
959e046f02SJonas Devlieghere       result.AppendErrorWithFormat("Unable to get the reproducer generator");
962dca6538SJonas Devlieghere       result.SetStatus(eReturnStatusFailed);
979e046f02SJonas Devlieghere       return false;
989e046f02SJonas Devlieghere     }
999e046f02SJonas Devlieghere 
1009e046f02SJonas Devlieghere     result.GetOutputStream()
1019e046f02SJonas Devlieghere         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
1021c5250abSJonas Devlieghere     result.GetOutputStream()
1031c5250abSJonas Devlieghere         << "Please have a look at the directory to assess if you're willing to "
1041c5250abSJonas Devlieghere            "share the contained information.\n";
1059e046f02SJonas Devlieghere 
1069e046f02SJonas Devlieghere     result.SetStatus(eReturnStatusSuccessFinishResult);
1079e046f02SJonas Devlieghere     return result.Succeeded();
1089e046f02SJonas Devlieghere   }
1099e046f02SJonas Devlieghere };
1109e046f02SJonas Devlieghere 
11115eacd74SJonas Devlieghere class CommandObjectReproducerStatus : public CommandObjectParsed {
1129e046f02SJonas Devlieghere public:
11315eacd74SJonas Devlieghere   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
114973d66eeSJonas Devlieghere       : CommandObjectParsed(
115973d66eeSJonas Devlieghere             interpreter, "reproducer status",
116973d66eeSJonas Devlieghere             "Show the current reproducer status. In capture mode the debugger "
117973d66eeSJonas Devlieghere             "is collecting all the information it needs to create a "
118973d66eeSJonas Devlieghere             "reproducer.  In replay mode the reproducer is replaying a "
119973d66eeSJonas Devlieghere             "reproducer. When the reproducers are off, no data is collected "
120973d66eeSJonas Devlieghere             "and no reproducer can be generated.",
121973d66eeSJonas Devlieghere             nullptr) {}
1229e046f02SJonas Devlieghere 
12315eacd74SJonas Devlieghere   ~CommandObjectReproducerStatus() override = default;
1249e046f02SJonas Devlieghere 
1259e046f02SJonas Devlieghere protected:
1269e046f02SJonas Devlieghere   bool DoExecute(Args &command, CommandReturnObject &result) override {
12715eacd74SJonas Devlieghere     if (!command.empty()) {
12815eacd74SJonas Devlieghere       result.AppendErrorWithFormat("'%s' takes no arguments",
1299e046f02SJonas Devlieghere                                    m_cmd_name.c_str());
1309e046f02SJonas Devlieghere       return false;
1319e046f02SJonas Devlieghere     }
1329e046f02SJonas Devlieghere 
133*97fc8eb4SJonas Devlieghere     auto &r = Reproducer::Instance();
13452f8f343SZachary Turner     if (r.GetGenerator()) {
13515eacd74SJonas Devlieghere       result.GetOutputStream() << "Reproducer is in capture mode.\n";
13652f8f343SZachary Turner     } else if (r.GetLoader()) {
13715eacd74SJonas Devlieghere       result.GetOutputStream() << "Reproducer is in replay mode.\n";
13815eacd74SJonas Devlieghere     } else {
13915eacd74SJonas Devlieghere       result.GetOutputStream() << "Reproducer is off.\n";
1409e046f02SJonas Devlieghere     }
1419e046f02SJonas Devlieghere 
14215eacd74SJonas Devlieghere     result.SetStatus(eReturnStatusSuccessFinishResult);
1439e046f02SJonas Devlieghere     return result.Succeeded();
1449e046f02SJonas Devlieghere   }
1459e046f02SJonas Devlieghere };
1469e046f02SJonas Devlieghere 
147*97fc8eb4SJonas Devlieghere static void SetError(CommandReturnObject &result, Error err) {
148*97fc8eb4SJonas Devlieghere   result.GetErrorStream().Printf("error: %s\n",
149*97fc8eb4SJonas Devlieghere                                  toString(std::move(err)).c_str());
150*97fc8eb4SJonas Devlieghere   result.SetStatus(eReturnStatusFailed);
151*97fc8eb4SJonas Devlieghere }
152*97fc8eb4SJonas Devlieghere 
153*97fc8eb4SJonas Devlieghere class CommandObjectReproducerDump : public CommandObjectParsed {
154*97fc8eb4SJonas Devlieghere public:
155*97fc8eb4SJonas Devlieghere   CommandObjectReproducerDump(CommandInterpreter &interpreter)
156*97fc8eb4SJonas Devlieghere       : CommandObjectParsed(interpreter, "reproducer dump",
157*97fc8eb4SJonas Devlieghere                             "Dump the information contained in a reproducer.",
158*97fc8eb4SJonas Devlieghere                             nullptr) {}
159*97fc8eb4SJonas Devlieghere 
160*97fc8eb4SJonas Devlieghere   ~CommandObjectReproducerDump() override = default;
161*97fc8eb4SJonas Devlieghere 
162*97fc8eb4SJonas Devlieghere   Options *GetOptions() override { return &m_options; }
163*97fc8eb4SJonas Devlieghere 
164*97fc8eb4SJonas Devlieghere   class CommandOptions : public Options {
165*97fc8eb4SJonas Devlieghere   public:
166*97fc8eb4SJonas Devlieghere     CommandOptions() : Options(), file() {}
167*97fc8eb4SJonas Devlieghere 
168*97fc8eb4SJonas Devlieghere     ~CommandOptions() override = default;
169*97fc8eb4SJonas Devlieghere 
170*97fc8eb4SJonas Devlieghere     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
171*97fc8eb4SJonas Devlieghere                           ExecutionContext *execution_context) override {
172*97fc8eb4SJonas Devlieghere       Status error;
173*97fc8eb4SJonas Devlieghere       const int short_option = m_getopt_table[option_idx].val;
174*97fc8eb4SJonas Devlieghere 
175*97fc8eb4SJonas Devlieghere       switch (short_option) {
176*97fc8eb4SJonas Devlieghere       case 'f':
177*97fc8eb4SJonas Devlieghere         file.SetFile(option_arg, FileSpec::Style::native);
178*97fc8eb4SJonas Devlieghere         FileSystem::Instance().Resolve(file);
179*97fc8eb4SJonas Devlieghere         break;
180*97fc8eb4SJonas Devlieghere       case 'p':
181*97fc8eb4SJonas Devlieghere         provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
182*97fc8eb4SJonas Devlieghere             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
183*97fc8eb4SJonas Devlieghere         if (!error.Success())
184*97fc8eb4SJonas Devlieghere           error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
185*97fc8eb4SJonas Devlieghere                                          option_arg.str().c_str());
186*97fc8eb4SJonas Devlieghere         break;
187*97fc8eb4SJonas Devlieghere       default:
188*97fc8eb4SJonas Devlieghere         llvm_unreachable("Unimplemented option");
189*97fc8eb4SJonas Devlieghere       }
190*97fc8eb4SJonas Devlieghere 
191*97fc8eb4SJonas Devlieghere       return error;
192*97fc8eb4SJonas Devlieghere     }
193*97fc8eb4SJonas Devlieghere 
194*97fc8eb4SJonas Devlieghere     void OptionParsingStarting(ExecutionContext *execution_context) override {
195*97fc8eb4SJonas Devlieghere       file.Clear();
196*97fc8eb4SJonas Devlieghere       provider = eReproducerProviderNone;
197*97fc8eb4SJonas Devlieghere     }
198*97fc8eb4SJonas Devlieghere 
199*97fc8eb4SJonas Devlieghere     ArrayRef<OptionDefinition> GetDefinitions() override {
200*97fc8eb4SJonas Devlieghere       return makeArrayRef(g_reproducer_options);
201*97fc8eb4SJonas Devlieghere     }
202*97fc8eb4SJonas Devlieghere 
203*97fc8eb4SJonas Devlieghere     FileSpec file;
204*97fc8eb4SJonas Devlieghere     ReproducerProvider provider = eReproducerProviderNone;
205*97fc8eb4SJonas Devlieghere   };
206*97fc8eb4SJonas Devlieghere 
207*97fc8eb4SJonas Devlieghere protected:
208*97fc8eb4SJonas Devlieghere   bool DoExecute(Args &command, CommandReturnObject &result) override {
209*97fc8eb4SJonas Devlieghere     if (!command.empty()) {
210*97fc8eb4SJonas Devlieghere       result.AppendErrorWithFormat("'%s' takes no arguments",
211*97fc8eb4SJonas Devlieghere                                    m_cmd_name.c_str());
212*97fc8eb4SJonas Devlieghere       return false;
213*97fc8eb4SJonas Devlieghere     }
214*97fc8eb4SJonas Devlieghere 
215*97fc8eb4SJonas Devlieghere     // If no reproducer path is specified, use the loader currently used for
216*97fc8eb4SJonas Devlieghere     // replay. Otherwise create a new loader just for dumping.
217*97fc8eb4SJonas Devlieghere     llvm::Optional<Loader> loader_storage;
218*97fc8eb4SJonas Devlieghere     Loader *loader = nullptr;
219*97fc8eb4SJonas Devlieghere     if (!m_options.file) {
220*97fc8eb4SJonas Devlieghere       loader = Reproducer::Instance().GetLoader();
221*97fc8eb4SJonas Devlieghere       if (loader == nullptr) {
222*97fc8eb4SJonas Devlieghere         result.SetError(
223*97fc8eb4SJonas Devlieghere             "Not specifying a reproducer is only support during replay.");
224*97fc8eb4SJonas Devlieghere         result.SetStatus(eReturnStatusSuccessFinishNoResult);
225*97fc8eb4SJonas Devlieghere         return false;
226*97fc8eb4SJonas Devlieghere       }
227*97fc8eb4SJonas Devlieghere     } else {
228*97fc8eb4SJonas Devlieghere       loader_storage.emplace(m_options.file);
229*97fc8eb4SJonas Devlieghere       loader = &(*loader_storage);
230*97fc8eb4SJonas Devlieghere       if (Error err = loader->LoadIndex()) {
231*97fc8eb4SJonas Devlieghere         SetError(result, std::move(err));
232*97fc8eb4SJonas Devlieghere         return false;
233*97fc8eb4SJonas Devlieghere       }
234*97fc8eb4SJonas Devlieghere     }
235*97fc8eb4SJonas Devlieghere 
236*97fc8eb4SJonas Devlieghere     // If we get here we should have a valid loader.
237*97fc8eb4SJonas Devlieghere     assert(loader);
238*97fc8eb4SJonas Devlieghere 
239*97fc8eb4SJonas Devlieghere     switch (m_options.provider) {
240*97fc8eb4SJonas Devlieghere     case eReproducerProviderFiles: {
241*97fc8eb4SJonas Devlieghere       FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
242*97fc8eb4SJonas Devlieghere 
243*97fc8eb4SJonas Devlieghere       // Read the VFS mapping.
244*97fc8eb4SJonas Devlieghere       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
245*97fc8eb4SJonas Devlieghere           vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
246*97fc8eb4SJonas Devlieghere       if (!buffer) {
247*97fc8eb4SJonas Devlieghere         SetError(result, errorCodeToError(buffer.getError()));
248*97fc8eb4SJonas Devlieghere         return false;
249*97fc8eb4SJonas Devlieghere       }
250*97fc8eb4SJonas Devlieghere 
251*97fc8eb4SJonas Devlieghere       // Initialize a VFS from the given mapping.
252*97fc8eb4SJonas Devlieghere       IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
253*97fc8eb4SJonas Devlieghere           std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
254*97fc8eb4SJonas Devlieghere 
255*97fc8eb4SJonas Devlieghere       // Dump the VFS to a buffer.
256*97fc8eb4SJonas Devlieghere       std::string str;
257*97fc8eb4SJonas Devlieghere       raw_string_ostream os(str);
258*97fc8eb4SJonas Devlieghere       static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
259*97fc8eb4SJonas Devlieghere       os.flush();
260*97fc8eb4SJonas Devlieghere 
261*97fc8eb4SJonas Devlieghere       // Return the string.
262*97fc8eb4SJonas Devlieghere       result.AppendMessage(str);
263*97fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
264*97fc8eb4SJonas Devlieghere       return true;
265*97fc8eb4SJonas Devlieghere     }
266*97fc8eb4SJonas Devlieghere     case eReproducerProviderVersion: {
267*97fc8eb4SJonas Devlieghere       FileSpec version_file = loader->GetFile<VersionProvider::Info>();
268*97fc8eb4SJonas Devlieghere 
269*97fc8eb4SJonas Devlieghere       // Load the version info into a buffer.
270*97fc8eb4SJonas Devlieghere       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
271*97fc8eb4SJonas Devlieghere           vfs::getRealFileSystem()->getBufferForFile(version_file.GetPath());
272*97fc8eb4SJonas Devlieghere       if (!buffer) {
273*97fc8eb4SJonas Devlieghere         SetError(result, errorCodeToError(buffer.getError()));
274*97fc8eb4SJonas Devlieghere         return false;
275*97fc8eb4SJonas Devlieghere       }
276*97fc8eb4SJonas Devlieghere 
277*97fc8eb4SJonas Devlieghere       // Return the version string.
278*97fc8eb4SJonas Devlieghere       StringRef version = (*buffer)->getBuffer();
279*97fc8eb4SJonas Devlieghere       result.AppendMessage(version.str());
280*97fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
281*97fc8eb4SJonas Devlieghere       return true;
282*97fc8eb4SJonas Devlieghere     }
283*97fc8eb4SJonas Devlieghere     case eReproducerProviderCommands: {
284*97fc8eb4SJonas Devlieghere       // Create a new command loader.
285*97fc8eb4SJonas Devlieghere       std::unique_ptr<repro::CommandLoader> command_loader =
286*97fc8eb4SJonas Devlieghere           repro::CommandLoader::Create(loader);
287*97fc8eb4SJonas Devlieghere       if (!command_loader) {
288*97fc8eb4SJonas Devlieghere         SetError(result,
289*97fc8eb4SJonas Devlieghere                  make_error<StringError>(llvm::inconvertibleErrorCode(),
290*97fc8eb4SJonas Devlieghere                                          "Unable to create command loader."));
291*97fc8eb4SJonas Devlieghere         return false;
292*97fc8eb4SJonas Devlieghere       }
293*97fc8eb4SJonas Devlieghere 
294*97fc8eb4SJonas Devlieghere       // Iterate over the command files and dump them.
295*97fc8eb4SJonas Devlieghere       while (true) {
296*97fc8eb4SJonas Devlieghere         llvm::Optional<std::string> command_file =
297*97fc8eb4SJonas Devlieghere             command_loader->GetNextFile();
298*97fc8eb4SJonas Devlieghere         if (!command_file)
299*97fc8eb4SJonas Devlieghere           break;
300*97fc8eb4SJonas Devlieghere 
301*97fc8eb4SJonas Devlieghere         auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
302*97fc8eb4SJonas Devlieghere         if (auto err = command_buffer.getError()) {
303*97fc8eb4SJonas Devlieghere           SetError(result, errorCodeToError(err));
304*97fc8eb4SJonas Devlieghere           return false;
305*97fc8eb4SJonas Devlieghere         }
306*97fc8eb4SJonas Devlieghere         result.AppendMessage((*command_buffer)->getBuffer());
307*97fc8eb4SJonas Devlieghere       }
308*97fc8eb4SJonas Devlieghere 
309*97fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
310*97fc8eb4SJonas Devlieghere       return true;
311*97fc8eb4SJonas Devlieghere     }
312*97fc8eb4SJonas Devlieghere     case eReproducerProviderGDB: {
313*97fc8eb4SJonas Devlieghere       // FIXME: Dumping the GDB remote packets means moving the
314*97fc8eb4SJonas Devlieghere       // (de)serialization code out of the GDB-remote plugin.
315*97fc8eb4SJonas Devlieghere       result.AppendMessage("Dumping GDB remote packets isn't implemented yet.");
316*97fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
317*97fc8eb4SJonas Devlieghere       return true;
318*97fc8eb4SJonas Devlieghere     }
319*97fc8eb4SJonas Devlieghere     case eReproducerProviderNone:
320*97fc8eb4SJonas Devlieghere       result.SetError("No valid provider specified.");
321*97fc8eb4SJonas Devlieghere       return false;
322*97fc8eb4SJonas Devlieghere     }
323*97fc8eb4SJonas Devlieghere 
324*97fc8eb4SJonas Devlieghere     result.SetStatus(eReturnStatusSuccessFinishNoResult);
325*97fc8eb4SJonas Devlieghere     return result.Succeeded();
326*97fc8eb4SJonas Devlieghere   }
327*97fc8eb4SJonas Devlieghere 
328*97fc8eb4SJonas Devlieghere private:
329*97fc8eb4SJonas Devlieghere   CommandOptions m_options;
330*97fc8eb4SJonas Devlieghere };
331*97fc8eb4SJonas Devlieghere 
3329e046f02SJonas Devlieghere CommandObjectReproducer::CommandObjectReproducer(
3339e046f02SJonas Devlieghere     CommandInterpreter &interpreter)
334973d66eeSJonas Devlieghere     : CommandObjectMultiword(
335973d66eeSJonas Devlieghere           interpreter, "reproducer",
336130ec068SJonas Devlieghere           "Commands for manipulate the reproducer functionality.",
337130ec068SJonas Devlieghere           "reproducer <subcommand> [<subcommand-options>]") {
3389e046f02SJonas Devlieghere   LoadSubCommand(
3399e046f02SJonas Devlieghere       "generate",
3409e046f02SJonas Devlieghere       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
34115eacd74SJonas Devlieghere   LoadSubCommand("status", CommandObjectSP(
34215eacd74SJonas Devlieghere                                new CommandObjectReproducerStatus(interpreter)));
343*97fc8eb4SJonas Devlieghere   LoadSubCommand("dump",
344*97fc8eb4SJonas Devlieghere                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
3459e046f02SJonas Devlieghere }
3469e046f02SJonas Devlieghere 
3479e046f02SJonas Devlieghere CommandObjectReproducer::~CommandObjectReproducer() = default;
348