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 
1197fc8eb4SJonas Devlieghere #include "lldb/Host/OptionParser.h"
129e046f02SJonas Devlieghere #include "lldb/Utility/Reproducer.h"
138fc8d3feSJonas Devlieghere #include "lldb/Utility/GDBRemote.h"
149e046f02SJonas Devlieghere 
15df14b942SJonas Devlieghere #include "lldb/Interpreter/CommandInterpreter.h"
169e046f02SJonas Devlieghere #include "lldb/Interpreter/CommandReturnObject.h"
179e046f02SJonas Devlieghere #include "lldb/Interpreter/OptionArgParser.h"
189e046f02SJonas Devlieghere #include "lldb/Interpreter/OptionGroupBoolean.h"
199e046f02SJonas Devlieghere 
209e046f02SJonas Devlieghere using namespace lldb;
2197fc8eb4SJonas Devlieghere using namespace llvm;
229e046f02SJonas Devlieghere using namespace lldb_private;
2397fc8eb4SJonas Devlieghere using namespace lldb_private::repro;
2497fc8eb4SJonas Devlieghere 
2597fc8eb4SJonas Devlieghere enum ReproducerProvider {
2697fc8eb4SJonas Devlieghere   eReproducerProviderCommands,
2797fc8eb4SJonas Devlieghere   eReproducerProviderFiles,
2897fc8eb4SJonas Devlieghere   eReproducerProviderGDB,
2997fc8eb4SJonas Devlieghere   eReproducerProviderVersion,
3097fc8eb4SJonas Devlieghere   eReproducerProviderNone
3197fc8eb4SJonas Devlieghere };
3297fc8eb4SJonas Devlieghere 
3397fc8eb4SJonas Devlieghere static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
3497fc8eb4SJonas Devlieghere     {
3597fc8eb4SJonas Devlieghere         eReproducerProviderCommands,
3697fc8eb4SJonas Devlieghere         "commands",
3797fc8eb4SJonas Devlieghere         "Command Interpreter Commands",
3897fc8eb4SJonas Devlieghere     },
3997fc8eb4SJonas Devlieghere     {
4097fc8eb4SJonas Devlieghere         eReproducerProviderFiles,
4197fc8eb4SJonas Devlieghere         "files",
4297fc8eb4SJonas Devlieghere         "Files",
4397fc8eb4SJonas Devlieghere     },
4497fc8eb4SJonas Devlieghere     {
4597fc8eb4SJonas Devlieghere         eReproducerProviderGDB,
4697fc8eb4SJonas Devlieghere         "gdb",
4797fc8eb4SJonas Devlieghere         "GDB Remote Packets",
4897fc8eb4SJonas Devlieghere     },
4997fc8eb4SJonas Devlieghere     {
5097fc8eb4SJonas Devlieghere         eReproducerProviderVersion,
5197fc8eb4SJonas Devlieghere         "version",
5297fc8eb4SJonas Devlieghere         "Version",
5397fc8eb4SJonas Devlieghere     },
5497fc8eb4SJonas Devlieghere     {
5597fc8eb4SJonas Devlieghere         eReproducerProviderNone,
5697fc8eb4SJonas Devlieghere         "none",
5797fc8eb4SJonas Devlieghere         "None",
5897fc8eb4SJonas Devlieghere     },
5997fc8eb4SJonas Devlieghere };
6097fc8eb4SJonas Devlieghere 
6197fc8eb4SJonas Devlieghere static constexpr OptionEnumValues ReproducerProviderType() {
6297fc8eb4SJonas Devlieghere   return OptionEnumValues(g_reproducer_provider_type);
6397fc8eb4SJonas Devlieghere }
6497fc8eb4SJonas Devlieghere 
6597fc8eb4SJonas Devlieghere #define LLDB_OPTIONS_reproducer
6697fc8eb4SJonas Devlieghere #include "CommandOptions.inc"
679e046f02SJonas Devlieghere 
689e046f02SJonas Devlieghere class CommandObjectReproducerGenerate : public CommandObjectParsed {
699e046f02SJonas Devlieghere public:
709e046f02SJonas Devlieghere   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
71973d66eeSJonas Devlieghere       : CommandObjectParsed(
72973d66eeSJonas Devlieghere             interpreter, "reproducer generate",
73973d66eeSJonas Devlieghere             "Generate reproducer on disk. When the debugger is in capture "
74973d66eeSJonas Devlieghere             "mode, this command will output the reproducer to a directory on "
75973d66eeSJonas Devlieghere             "disk. In replay mode this command in a no-op.",
76973d66eeSJonas Devlieghere             nullptr) {}
779e046f02SJonas Devlieghere 
789e046f02SJonas Devlieghere   ~CommandObjectReproducerGenerate() override = default;
799e046f02SJonas Devlieghere 
809e046f02SJonas Devlieghere protected:
819e046f02SJonas Devlieghere   bool DoExecute(Args &command, CommandReturnObject &result) override {
829e046f02SJonas Devlieghere     if (!command.empty()) {
839e046f02SJonas Devlieghere       result.AppendErrorWithFormat("'%s' takes no arguments",
849e046f02SJonas Devlieghere                                    m_cmd_name.c_str());
859e046f02SJonas Devlieghere       return false;
869e046f02SJonas Devlieghere     }
879e046f02SJonas Devlieghere 
8897fc8eb4SJonas Devlieghere     auto &r = Reproducer::Instance();
899e046f02SJonas Devlieghere     if (auto generator = r.GetGenerator()) {
909e046f02SJonas Devlieghere       generator->Keep();
91865cd093SJonas Devlieghere     } else if (r.IsReplaying()) {
922dca6538SJonas Devlieghere       // Make this operation a NOP in replay mode.
932dca6538SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishNoResult);
942dca6538SJonas Devlieghere       return result.Succeeded();
959e046f02SJonas Devlieghere     } else {
969e046f02SJonas Devlieghere       result.AppendErrorWithFormat("Unable to get the reproducer generator");
972dca6538SJonas Devlieghere       result.SetStatus(eReturnStatusFailed);
989e046f02SJonas Devlieghere       return false;
999e046f02SJonas Devlieghere     }
1009e046f02SJonas Devlieghere 
1019e046f02SJonas Devlieghere     result.GetOutputStream()
1029e046f02SJonas Devlieghere         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
1031c5250abSJonas Devlieghere     result.GetOutputStream()
1041c5250abSJonas Devlieghere         << "Please have a look at the directory to assess if you're willing to "
1051c5250abSJonas Devlieghere            "share the contained information.\n";
1069e046f02SJonas Devlieghere 
1079e046f02SJonas Devlieghere     result.SetStatus(eReturnStatusSuccessFinishResult);
1089e046f02SJonas Devlieghere     return result.Succeeded();
1099e046f02SJonas Devlieghere   }
1109e046f02SJonas Devlieghere };
1119e046f02SJonas Devlieghere 
11215eacd74SJonas Devlieghere class CommandObjectReproducerStatus : public CommandObjectParsed {
1139e046f02SJonas Devlieghere public:
11415eacd74SJonas Devlieghere   CommandObjectReproducerStatus(CommandInterpreter &interpreter)
115973d66eeSJonas Devlieghere       : CommandObjectParsed(
116973d66eeSJonas Devlieghere             interpreter, "reproducer status",
117973d66eeSJonas Devlieghere             "Show the current reproducer status. In capture mode the debugger "
118973d66eeSJonas Devlieghere             "is collecting all the information it needs to create a "
119973d66eeSJonas Devlieghere             "reproducer.  In replay mode the reproducer is replaying a "
120973d66eeSJonas Devlieghere             "reproducer. When the reproducers are off, no data is collected "
121973d66eeSJonas Devlieghere             "and no reproducer can be generated.",
122973d66eeSJonas Devlieghere             nullptr) {}
1239e046f02SJonas Devlieghere 
12415eacd74SJonas Devlieghere   ~CommandObjectReproducerStatus() override = default;
1259e046f02SJonas Devlieghere 
1269e046f02SJonas Devlieghere protected:
1279e046f02SJonas Devlieghere   bool DoExecute(Args &command, CommandReturnObject &result) override {
12815eacd74SJonas Devlieghere     if (!command.empty()) {
12915eacd74SJonas Devlieghere       result.AppendErrorWithFormat("'%s' takes no arguments",
1309e046f02SJonas Devlieghere                                    m_cmd_name.c_str());
1319e046f02SJonas Devlieghere       return false;
1329e046f02SJonas Devlieghere     }
1339e046f02SJonas Devlieghere 
13497fc8eb4SJonas Devlieghere     auto &r = Reproducer::Instance();
135865cd093SJonas Devlieghere     if (r.IsCapturing()) {
13615eacd74SJonas Devlieghere       result.GetOutputStream() << "Reproducer is in capture mode.\n";
137865cd093SJonas Devlieghere     } else if (r.IsReplaying()) {
13815eacd74SJonas Devlieghere       result.GetOutputStream() << "Reproducer is in replay mode.\n";
13915eacd74SJonas Devlieghere     } else {
14015eacd74SJonas Devlieghere       result.GetOutputStream() << "Reproducer is off.\n";
1419e046f02SJonas Devlieghere     }
1429e046f02SJonas Devlieghere 
14315eacd74SJonas Devlieghere     result.SetStatus(eReturnStatusSuccessFinishResult);
1449e046f02SJonas Devlieghere     return result.Succeeded();
1459e046f02SJonas Devlieghere   }
1469e046f02SJonas Devlieghere };
1479e046f02SJonas Devlieghere 
14897fc8eb4SJonas Devlieghere static void SetError(CommandReturnObject &result, Error err) {
14997fc8eb4SJonas Devlieghere   result.GetErrorStream().Printf("error: %s\n",
15097fc8eb4SJonas Devlieghere                                  toString(std::move(err)).c_str());
15197fc8eb4SJonas Devlieghere   result.SetStatus(eReturnStatusFailed);
15297fc8eb4SJonas Devlieghere }
15397fc8eb4SJonas Devlieghere 
15497fc8eb4SJonas Devlieghere class CommandObjectReproducerDump : public CommandObjectParsed {
15597fc8eb4SJonas Devlieghere public:
15697fc8eb4SJonas Devlieghere   CommandObjectReproducerDump(CommandInterpreter &interpreter)
15797fc8eb4SJonas Devlieghere       : CommandObjectParsed(interpreter, "reproducer dump",
15897fc8eb4SJonas Devlieghere                             "Dump the information contained in a reproducer.",
15997fc8eb4SJonas Devlieghere                             nullptr) {}
16097fc8eb4SJonas Devlieghere 
16197fc8eb4SJonas Devlieghere   ~CommandObjectReproducerDump() override = default;
16297fc8eb4SJonas Devlieghere 
16397fc8eb4SJonas Devlieghere   Options *GetOptions() override { return &m_options; }
16497fc8eb4SJonas Devlieghere 
16597fc8eb4SJonas Devlieghere   class CommandOptions : public Options {
16697fc8eb4SJonas Devlieghere   public:
16797fc8eb4SJonas Devlieghere     CommandOptions() : Options(), file() {}
16897fc8eb4SJonas Devlieghere 
16997fc8eb4SJonas Devlieghere     ~CommandOptions() override = default;
17097fc8eb4SJonas Devlieghere 
17197fc8eb4SJonas Devlieghere     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
17297fc8eb4SJonas Devlieghere                           ExecutionContext *execution_context) override {
17397fc8eb4SJonas Devlieghere       Status error;
17497fc8eb4SJonas Devlieghere       const int short_option = m_getopt_table[option_idx].val;
17597fc8eb4SJonas Devlieghere 
17697fc8eb4SJonas Devlieghere       switch (short_option) {
17797fc8eb4SJonas Devlieghere       case 'f':
17897fc8eb4SJonas Devlieghere         file.SetFile(option_arg, FileSpec::Style::native);
17997fc8eb4SJonas Devlieghere         FileSystem::Instance().Resolve(file);
18097fc8eb4SJonas Devlieghere         break;
18197fc8eb4SJonas Devlieghere       case 'p':
18297fc8eb4SJonas Devlieghere         provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
18397fc8eb4SJonas Devlieghere             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
18497fc8eb4SJonas Devlieghere         if (!error.Success())
18597fc8eb4SJonas Devlieghere           error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
18697fc8eb4SJonas Devlieghere                                          option_arg.str().c_str());
18797fc8eb4SJonas Devlieghere         break;
18897fc8eb4SJonas Devlieghere       default:
18997fc8eb4SJonas Devlieghere         llvm_unreachable("Unimplemented option");
19097fc8eb4SJonas Devlieghere       }
19197fc8eb4SJonas Devlieghere 
19297fc8eb4SJonas Devlieghere       return error;
19397fc8eb4SJonas Devlieghere     }
19497fc8eb4SJonas Devlieghere 
19597fc8eb4SJonas Devlieghere     void OptionParsingStarting(ExecutionContext *execution_context) override {
19697fc8eb4SJonas Devlieghere       file.Clear();
19797fc8eb4SJonas Devlieghere       provider = eReproducerProviderNone;
19897fc8eb4SJonas Devlieghere     }
19997fc8eb4SJonas Devlieghere 
20097fc8eb4SJonas Devlieghere     ArrayRef<OptionDefinition> GetDefinitions() override {
20197fc8eb4SJonas Devlieghere       return makeArrayRef(g_reproducer_options);
20297fc8eb4SJonas Devlieghere     }
20397fc8eb4SJonas Devlieghere 
20497fc8eb4SJonas Devlieghere     FileSpec file;
20597fc8eb4SJonas Devlieghere     ReproducerProvider provider = eReproducerProviderNone;
20697fc8eb4SJonas Devlieghere   };
20797fc8eb4SJonas Devlieghere 
20897fc8eb4SJonas Devlieghere protected:
20997fc8eb4SJonas Devlieghere   bool DoExecute(Args &command, CommandReturnObject &result) override {
21097fc8eb4SJonas Devlieghere     if (!command.empty()) {
21197fc8eb4SJonas Devlieghere       result.AppendErrorWithFormat("'%s' takes no arguments",
21297fc8eb4SJonas Devlieghere                                    m_cmd_name.c_str());
21397fc8eb4SJonas Devlieghere       return false;
21497fc8eb4SJonas Devlieghere     }
21597fc8eb4SJonas Devlieghere 
21697fc8eb4SJonas Devlieghere     // If no reproducer path is specified, use the loader currently used for
21797fc8eb4SJonas Devlieghere     // replay. Otherwise create a new loader just for dumping.
21897fc8eb4SJonas Devlieghere     llvm::Optional<Loader> loader_storage;
21997fc8eb4SJonas Devlieghere     Loader *loader = nullptr;
22097fc8eb4SJonas Devlieghere     if (!m_options.file) {
22197fc8eb4SJonas Devlieghere       loader = Reproducer::Instance().GetLoader();
22297fc8eb4SJonas Devlieghere       if (loader == nullptr) {
22397fc8eb4SJonas Devlieghere         result.SetError(
22497fc8eb4SJonas Devlieghere             "Not specifying a reproducer is only support during replay.");
22597fc8eb4SJonas Devlieghere         result.SetStatus(eReturnStatusSuccessFinishNoResult);
22697fc8eb4SJonas Devlieghere         return false;
22797fc8eb4SJonas Devlieghere       }
22897fc8eb4SJonas Devlieghere     } else {
22997fc8eb4SJonas Devlieghere       loader_storage.emplace(m_options.file);
23097fc8eb4SJonas Devlieghere       loader = &(*loader_storage);
23197fc8eb4SJonas Devlieghere       if (Error err = loader->LoadIndex()) {
23297fc8eb4SJonas Devlieghere         SetError(result, std::move(err));
23397fc8eb4SJonas Devlieghere         return false;
23497fc8eb4SJonas Devlieghere       }
23597fc8eb4SJonas Devlieghere     }
23697fc8eb4SJonas Devlieghere 
23797fc8eb4SJonas Devlieghere     // If we get here we should have a valid loader.
23897fc8eb4SJonas Devlieghere     assert(loader);
23997fc8eb4SJonas Devlieghere 
24097fc8eb4SJonas Devlieghere     switch (m_options.provider) {
24197fc8eb4SJonas Devlieghere     case eReproducerProviderFiles: {
24297fc8eb4SJonas Devlieghere       FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
24397fc8eb4SJonas Devlieghere 
24497fc8eb4SJonas Devlieghere       // Read the VFS mapping.
24597fc8eb4SJonas Devlieghere       ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
24697fc8eb4SJonas Devlieghere           vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
24797fc8eb4SJonas Devlieghere       if (!buffer) {
24897fc8eb4SJonas Devlieghere         SetError(result, errorCodeToError(buffer.getError()));
24997fc8eb4SJonas Devlieghere         return false;
25097fc8eb4SJonas Devlieghere       }
25197fc8eb4SJonas Devlieghere 
25297fc8eb4SJonas Devlieghere       // Initialize a VFS from the given mapping.
25397fc8eb4SJonas Devlieghere       IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
25497fc8eb4SJonas Devlieghere           std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
25597fc8eb4SJonas Devlieghere 
25697fc8eb4SJonas Devlieghere       // Dump the VFS to a buffer.
25797fc8eb4SJonas Devlieghere       std::string str;
25897fc8eb4SJonas Devlieghere       raw_string_ostream os(str);
25997fc8eb4SJonas Devlieghere       static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
26097fc8eb4SJonas Devlieghere       os.flush();
26197fc8eb4SJonas Devlieghere 
26297fc8eb4SJonas Devlieghere       // Return the string.
26397fc8eb4SJonas Devlieghere       result.AppendMessage(str);
26497fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
26597fc8eb4SJonas Devlieghere       return true;
26697fc8eb4SJonas Devlieghere     }
26797fc8eb4SJonas Devlieghere     case eReproducerProviderVersion: {
268*b2575da9SJonas Devlieghere       Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
269*b2575da9SJonas Devlieghere       if (!version) {
270*b2575da9SJonas Devlieghere         SetError(result, version.takeError());
27197fc8eb4SJonas Devlieghere         return false;
27297fc8eb4SJonas Devlieghere       }
273*b2575da9SJonas Devlieghere       result.AppendMessage(*version);
27497fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
27597fc8eb4SJonas Devlieghere       return true;
27697fc8eb4SJonas Devlieghere     }
27797fc8eb4SJonas Devlieghere     case eReproducerProviderCommands: {
27897fc8eb4SJonas Devlieghere       // Create a new command loader.
27997fc8eb4SJonas Devlieghere       std::unique_ptr<repro::CommandLoader> command_loader =
28097fc8eb4SJonas Devlieghere           repro::CommandLoader::Create(loader);
28197fc8eb4SJonas Devlieghere       if (!command_loader) {
28297fc8eb4SJonas Devlieghere         SetError(result,
28397fc8eb4SJonas Devlieghere                  make_error<StringError>(llvm::inconvertibleErrorCode(),
28497fc8eb4SJonas Devlieghere                                          "Unable to create command loader."));
28597fc8eb4SJonas Devlieghere         return false;
28697fc8eb4SJonas Devlieghere       }
28797fc8eb4SJonas Devlieghere 
28897fc8eb4SJonas Devlieghere       // Iterate over the command files and dump them.
28997fc8eb4SJonas Devlieghere       while (true) {
29097fc8eb4SJonas Devlieghere         llvm::Optional<std::string> command_file =
29197fc8eb4SJonas Devlieghere             command_loader->GetNextFile();
29297fc8eb4SJonas Devlieghere         if (!command_file)
29397fc8eb4SJonas Devlieghere           break;
29497fc8eb4SJonas Devlieghere 
29597fc8eb4SJonas Devlieghere         auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
29697fc8eb4SJonas Devlieghere         if (auto err = command_buffer.getError()) {
29797fc8eb4SJonas Devlieghere           SetError(result, errorCodeToError(err));
29897fc8eb4SJonas Devlieghere           return false;
29997fc8eb4SJonas Devlieghere         }
30097fc8eb4SJonas Devlieghere         result.AppendMessage((*command_buffer)->getBuffer());
30197fc8eb4SJonas Devlieghere       }
30297fc8eb4SJonas Devlieghere 
30397fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
30497fc8eb4SJonas Devlieghere       return true;
30597fc8eb4SJonas Devlieghere     }
30697fc8eb4SJonas Devlieghere     case eReproducerProviderGDB: {
3078fc8d3feSJonas Devlieghere       FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
3088fc8d3feSJonas Devlieghere       auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath());
3098fc8d3feSJonas Devlieghere       if (auto err = error_or_file.getError()) {
3108fc8d3feSJonas Devlieghere         SetError(result, errorCodeToError(err));
3118fc8d3feSJonas Devlieghere         return false;
3128fc8d3feSJonas Devlieghere       }
3138fc8d3feSJonas Devlieghere 
3148fc8d3feSJonas Devlieghere       std::vector<GDBRemotePacket> packets;
3158fc8d3feSJonas Devlieghere       yaml::Input yin((*error_or_file)->getBuffer());
3168fc8d3feSJonas Devlieghere       yin >> packets;
3178fc8d3feSJonas Devlieghere 
3188fc8d3feSJonas Devlieghere       if (auto err = yin.error()) {
3198fc8d3feSJonas Devlieghere         SetError(result, errorCodeToError(err));
3208fc8d3feSJonas Devlieghere         return false;
3218fc8d3feSJonas Devlieghere       }
3228fc8d3feSJonas Devlieghere 
3238fc8d3feSJonas Devlieghere       for (GDBRemotePacket& packet : packets) {
3248fc8d3feSJonas Devlieghere         packet.Dump(result.GetOutputStream());
3258fc8d3feSJonas Devlieghere       }
3268fc8d3feSJonas Devlieghere 
32797fc8eb4SJonas Devlieghere       result.SetStatus(eReturnStatusSuccessFinishResult);
32897fc8eb4SJonas Devlieghere       return true;
32997fc8eb4SJonas Devlieghere     }
33097fc8eb4SJonas Devlieghere     case eReproducerProviderNone:
33197fc8eb4SJonas Devlieghere       result.SetError("No valid provider specified.");
33297fc8eb4SJonas Devlieghere       return false;
33397fc8eb4SJonas Devlieghere     }
33497fc8eb4SJonas Devlieghere 
33597fc8eb4SJonas Devlieghere     result.SetStatus(eReturnStatusSuccessFinishNoResult);
33697fc8eb4SJonas Devlieghere     return result.Succeeded();
33797fc8eb4SJonas Devlieghere   }
33897fc8eb4SJonas Devlieghere 
33997fc8eb4SJonas Devlieghere private:
34097fc8eb4SJonas Devlieghere   CommandOptions m_options;
34197fc8eb4SJonas Devlieghere };
34297fc8eb4SJonas Devlieghere 
3439e046f02SJonas Devlieghere CommandObjectReproducer::CommandObjectReproducer(
3449e046f02SJonas Devlieghere     CommandInterpreter &interpreter)
345973d66eeSJonas Devlieghere     : CommandObjectMultiword(
346973d66eeSJonas Devlieghere           interpreter, "reproducer",
347130ec068SJonas Devlieghere           "Commands for manipulate the reproducer functionality.",
348130ec068SJonas Devlieghere           "reproducer <subcommand> [<subcommand-options>]") {
3499e046f02SJonas Devlieghere   LoadSubCommand(
3509e046f02SJonas Devlieghere       "generate",
3519e046f02SJonas Devlieghere       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
35215eacd74SJonas Devlieghere   LoadSubCommand("status", CommandObjectSP(
35315eacd74SJonas Devlieghere                                new CommandObjectReproducerStatus(interpreter)));
35497fc8eb4SJonas Devlieghere   LoadSubCommand("dump",
35597fc8eb4SJonas Devlieghere                  CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
3569e046f02SJonas Devlieghere }
3579e046f02SJonas Devlieghere 
3589e046f02SJonas Devlieghere CommandObjectReproducer::~CommandObjectReproducer() = default;
359