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