1 //===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "CommandObjectReproducer.h"
11 
12 #include "lldb/Utility/Reproducer.h"
13 
14 #include "lldb/Interpreter/CommandReturnObject.h"
15 #include "lldb/Interpreter/OptionArgParser.h"
16 #include "lldb/Interpreter/OptionGroupBoolean.h"
17 
18 using namespace lldb;
19 using namespace lldb_private;
20 
21 static void AppendErrorToResult(llvm::Error e, CommandReturnObject &result) {
22   std::string error_str = llvm::toString(std::move(e));
23   result.AppendErrorWithFormat("%s", error_str.c_str());
24 }
25 
26 class CommandObjectReproducerCaptureEnable : public CommandObjectParsed {
27 public:
28   CommandObjectReproducerCaptureEnable(CommandInterpreter &interpreter)
29       : CommandObjectParsed(interpreter, "reproducer capture enable",
30                             "Enable gathering information for reproducer",
31                             nullptr) {}
32 
33   ~CommandObjectReproducerCaptureEnable() override = default;
34 
35 protected:
36   bool DoExecute(Args &command, CommandReturnObject &result) override {
37     if (!command.empty()) {
38       result.AppendErrorWithFormat("'%s' takes no arguments",
39                                    m_cmd_name.c_str());
40       return false;
41     }
42 
43     auto &r = repro::Reproducer::Instance();
44     if (auto e = r.SetGenerateReproducer(true)) {
45       AppendErrorToResult(std::move(e), result);
46       return false;
47     }
48 
49     result.SetStatus(eReturnStatusSuccessFinishNoResult);
50     return result.Succeeded();
51   }
52 };
53 
54 class CommandObjectReproducerCaptureDisable : public CommandObjectParsed {
55 public:
56   CommandObjectReproducerCaptureDisable(CommandInterpreter &interpreter)
57       : CommandObjectParsed(interpreter, "reproducer capture enable",
58                             "Disable gathering information for reproducer",
59                             nullptr) {}
60 
61   ~CommandObjectReproducerCaptureDisable() override = default;
62 
63 protected:
64   bool DoExecute(Args &command, CommandReturnObject &result) override {
65     if (!command.empty()) {
66       result.AppendErrorWithFormat("'%s' takes no arguments",
67                                    m_cmd_name.c_str());
68       return false;
69     }
70 
71     auto &r = repro::Reproducer::Instance();
72     if (auto e = r.SetGenerateReproducer(false)) {
73       AppendErrorToResult(std::move(e), result);
74       return false;
75     }
76 
77     result.SetStatus(eReturnStatusSuccessFinishNoResult);
78     return result.Succeeded();
79   }
80 };
81 
82 class CommandObjectReproducerGenerate : public CommandObjectParsed {
83 public:
84   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
85       : CommandObjectParsed(interpreter, "reproducer generate",
86                             "Generate reproducer on disk.", nullptr) {}
87 
88   ~CommandObjectReproducerGenerate() override = default;
89 
90 protected:
91   bool DoExecute(Args &command, CommandReturnObject &result) override {
92     if (!command.empty()) {
93       result.AppendErrorWithFormat("'%s' takes no arguments",
94                                    m_cmd_name.c_str());
95       return false;
96     }
97 
98     auto &r = repro::Reproducer::Instance();
99     if (auto generator = r.GetGenerator()) {
100       generator->Keep();
101     } else {
102       result.AppendErrorWithFormat("Unable to get the reproducer generator");
103       return false;
104     }
105 
106     result.GetOutputStream()
107         << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
108 
109     result.SetStatus(eReturnStatusSuccessFinishResult);
110     return result.Succeeded();
111   }
112 };
113 
114 class CommandObjectReproducerReplay : public CommandObjectParsed {
115 public:
116   CommandObjectReproducerReplay(CommandInterpreter &interpreter)
117       : CommandObjectParsed(interpreter, "reproducer capture",
118                             "Enable or disable gathering of information needed "
119                             "to generate a reproducer.",
120                             nullptr) {
121     CommandArgumentEntry arg1;
122     CommandArgumentData path_arg;
123 
124     // Define the first (and only) variant of this arg.
125     path_arg.arg_type = eArgTypePath;
126     path_arg.arg_repetition = eArgRepeatPlain;
127 
128     // There is only one variant this argument could be; put it into the
129     // argument entry.
130     arg1.push_back(path_arg);
131 
132     // Push the data for the first argument into the m_arguments vector.
133     m_arguments.push_back(arg1);
134   }
135 
136   ~CommandObjectReproducerReplay() override = default;
137 
138 protected:
139   bool DoExecute(Args &command, CommandReturnObject &result) override {
140     if (command.empty()) {
141       result.AppendErrorWithFormat(
142           "'%s' takes a single argument: the reproducer path",
143           m_cmd_name.c_str());
144       return false;
145     }
146 
147     auto &r = repro::Reproducer::Instance();
148 
149     if (auto e = r.SetReplayReproducer(true)) {
150       std::string error_str = llvm::toString(std::move(e));
151       result.AppendErrorWithFormat("%s", error_str.c_str());
152       return false;
153     }
154 
155     if (auto loader = r.GetLoader()) {
156       const char *repro_path = command.GetArgumentAtIndex(0);
157       if (auto e = loader->LoadIndex(FileSpec(repro_path))) {
158         std::string error_str = llvm::toString(std::move(e));
159         result.AppendErrorWithFormat("Unable to load reproducer: %s",
160                                      error_str.c_str());
161         return false;
162       }
163     } else {
164       result.AppendErrorWithFormat("Unable to get the reproducer loader");
165       return false;
166     }
167 
168     result.SetStatus(eReturnStatusSuccessFinishNoResult);
169     return result.Succeeded();
170   }
171 };
172 
173 class CommandObjectReproducerCapture : public CommandObjectMultiword {
174 private:
175 public:
176   CommandObjectReproducerCapture(CommandInterpreter &interpreter)
177       : CommandObjectMultiword(
178             interpreter, "reproducer capture",
179             "Manage gathering of information needed to generate a reproducer.",
180             NULL) {
181     LoadSubCommand(
182         "enable",
183         CommandObjectSP(new CommandObjectReproducerCaptureEnable(interpreter)));
184     LoadSubCommand("disable",
185                    CommandObjectSP(
186                        new CommandObjectReproducerCaptureDisable(interpreter)));
187   }
188 
189   ~CommandObjectReproducerCapture() {}
190 };
191 
192 CommandObjectReproducer::CommandObjectReproducer(
193     CommandInterpreter &interpreter)
194     : CommandObjectMultiword(interpreter, "reproducer",
195                              "Commands controlling LLDB reproducers.",
196                              "log <subcommand> [<command-options>]") {
197   LoadSubCommand("capture", CommandObjectSP(new CommandObjectReproducerCapture(
198                                 interpreter)));
199   LoadSubCommand(
200       "generate",
201       CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
202   LoadSubCommand("replay", CommandObjectSP(
203                                new CommandObjectReproducerReplay(interpreter)));
204 }
205 
206 CommandObjectReproducer::~CommandObjectReproducer() = default;
207