blob: 404702d3640e4d25b5d562ee77ba430b6340d4e3 [file] [log] [blame]
Jonas Devlieghere9e046f02018-11-13 19:18:16 +00001//===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Jonas Devlieghere9e046f02018-11-13 19:18:16 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "CommandObjectReproducer.h"
10
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000011#include "lldb/Host/OptionParser.h"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000012#include "lldb/Utility/Reproducer.h"
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +000013#include "lldb/Utility/GDBRemote.h"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000014
Jonas Devliegheredf14b942018-11-15 01:05:40 +000015#include "lldb/Interpreter/CommandInterpreter.h"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000016#include "lldb/Interpreter/CommandReturnObject.h"
17#include "lldb/Interpreter/OptionArgParser.h"
18#include "lldb/Interpreter/OptionGroupBoolean.h"
19
20using namespace lldb;
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000021using namespace llvm;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000022using namespace lldb_private;
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000023using namespace lldb_private::repro;
24
25enum ReproducerProvider {
26 eReproducerProviderCommands,
27 eReproducerProviderFiles,
28 eReproducerProviderGDB,
29 eReproducerProviderVersion,
30 eReproducerProviderNone
31};
32
33static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
34 {
35 eReproducerProviderCommands,
36 "commands",
37 "Command Interpreter Commands",
38 },
39 {
40 eReproducerProviderFiles,
41 "files",
42 "Files",
43 },
44 {
45 eReproducerProviderGDB,
46 "gdb",
47 "GDB Remote Packets",
48 },
49 {
50 eReproducerProviderVersion,
51 "version",
52 "Version",
53 },
54 {
55 eReproducerProviderNone,
56 "none",
57 "None",
58 },
59};
60
61static constexpr OptionEnumValues ReproducerProviderType() {
62 return OptionEnumValues(g_reproducer_provider_type);
63}
64
65#define LLDB_OPTIONS_reproducer
66#include "CommandOptions.inc"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000067
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000068class CommandObjectReproducerGenerate : public CommandObjectParsed {
69public:
70 CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +000071 : CommandObjectParsed(
72 interpreter, "reproducer generate",
73 "Generate reproducer on disk. When the debugger is in capture "
74 "mode, this command will output the reproducer to a directory on "
75 "disk. In replay mode this command in a no-op.",
76 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000077
78 ~CommandObjectReproducerGenerate() override = default;
79
80protected:
81 bool DoExecute(Args &command, CommandReturnObject &result) override {
82 if (!command.empty()) {
83 result.AppendErrorWithFormat("'%s' takes no arguments",
84 m_cmd_name.c_str());
85 return false;
86 }
87
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000088 auto &r = Reproducer::Instance();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000089 if (auto generator = r.GetGenerator()) {
90 generator->Keep();
Jonas Devlieghere2dca6532019-02-27 17:47:06 +000091 } else if (r.GetLoader()) {
92 // Make this operation a NOP in replay mode.
93 result.SetStatus(eReturnStatusSuccessFinishNoResult);
94 return result.Succeeded();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000095 } else {
96 result.AppendErrorWithFormat("Unable to get the reproducer generator");
Jonas Devlieghere2dca6532019-02-27 17:47:06 +000097 result.SetStatus(eReturnStatusFailed);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000098 return false;
99 }
100
101 result.GetOutputStream()
102 << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
Jonas Devlieghere1c5250a2019-04-02 18:23:16 +0000103 result.GetOutputStream()
104 << "Please have a look at the directory to assess if you're willing to "
105 "share the contained information.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000106
107 result.SetStatus(eReturnStatusSuccessFinishResult);
108 return result.Succeeded();
109 }
110};
111
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000112class CommandObjectReproducerStatus : public CommandObjectParsed {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000113public:
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000114 CommandObjectReproducerStatus(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000115 : CommandObjectParsed(
116 interpreter, "reproducer status",
117 "Show the current reproducer status. In capture mode the debugger "
118 "is collecting all the information it needs to create a "
119 "reproducer. In replay mode the reproducer is replaying a "
120 "reproducer. When the reproducers are off, no data is collected "
121 "and no reproducer can be generated.",
122 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000123
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000124 ~CommandObjectReproducerStatus() override = default;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000125
126protected:
127 bool DoExecute(Args &command, CommandReturnObject &result) override {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000128 if (!command.empty()) {
129 result.AppendErrorWithFormat("'%s' takes no arguments",
130 m_cmd_name.c_str());
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000131 return false;
132 }
133
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000134 auto &r = Reproducer::Instance();
Zachary Turner52f8f342019-01-29 22:55:21 +0000135 if (r.GetGenerator()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000136 result.GetOutputStream() << "Reproducer is in capture mode.\n";
Zachary Turner52f8f342019-01-29 22:55:21 +0000137 } else if (r.GetLoader()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000138 result.GetOutputStream() << "Reproducer is in replay mode.\n";
139 } else {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000140 result.GetOutputStream() << "Reproducer is off.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000141 }
142
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000143 result.SetStatus(eReturnStatusSuccessFinishResult);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000144 return result.Succeeded();
145 }
146};
147
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000148static void SetError(CommandReturnObject &result, Error err) {
149 result.GetErrorStream().Printf("error: %s\n",
150 toString(std::move(err)).c_str());
151 result.SetStatus(eReturnStatusFailed);
152}
153
154class CommandObjectReproducerDump : public CommandObjectParsed {
155public:
156 CommandObjectReproducerDump(CommandInterpreter &interpreter)
157 : CommandObjectParsed(interpreter, "reproducer dump",
158 "Dump the information contained in a reproducer.",
159 nullptr) {}
160
161 ~CommandObjectReproducerDump() override = default;
162
163 Options *GetOptions() override { return &m_options; }
164
165 class CommandOptions : public Options {
166 public:
167 CommandOptions() : Options(), file() {}
168
169 ~CommandOptions() override = default;
170
171 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
172 ExecutionContext *execution_context) override {
173 Status error;
174 const int short_option = m_getopt_table[option_idx].val;
175
176 switch (short_option) {
177 case 'f':
178 file.SetFile(option_arg, FileSpec::Style::native);
179 FileSystem::Instance().Resolve(file);
180 break;
181 case 'p':
182 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
183 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
184 if (!error.Success())
185 error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
186 option_arg.str().c_str());
187 break;
188 default:
189 llvm_unreachable("Unimplemented option");
190 }
191
192 return error;
193 }
194
195 void OptionParsingStarting(ExecutionContext *execution_context) override {
196 file.Clear();
197 provider = eReproducerProviderNone;
198 }
199
200 ArrayRef<OptionDefinition> GetDefinitions() override {
201 return makeArrayRef(g_reproducer_options);
202 }
203
204 FileSpec file;
205 ReproducerProvider provider = eReproducerProviderNone;
206 };
207
208protected:
209 bool DoExecute(Args &command, CommandReturnObject &result) override {
210 if (!command.empty()) {
211 result.AppendErrorWithFormat("'%s' takes no arguments",
212 m_cmd_name.c_str());
213 return false;
214 }
215
216 // If no reproducer path is specified, use the loader currently used for
217 // replay. Otherwise create a new loader just for dumping.
218 llvm::Optional<Loader> loader_storage;
219 Loader *loader = nullptr;
220 if (!m_options.file) {
221 loader = Reproducer::Instance().GetLoader();
222 if (loader == nullptr) {
223 result.SetError(
224 "Not specifying a reproducer is only support during replay.");
225 result.SetStatus(eReturnStatusSuccessFinishNoResult);
226 return false;
227 }
228 } else {
229 loader_storage.emplace(m_options.file);
230 loader = &(*loader_storage);
231 if (Error err = loader->LoadIndex()) {
232 SetError(result, std::move(err));
233 return false;
234 }
235 }
236
237 // If we get here we should have a valid loader.
238 assert(loader);
239
240 switch (m_options.provider) {
241 case eReproducerProviderFiles: {
242 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
243
244 // Read the VFS mapping.
245 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
246 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
247 if (!buffer) {
248 SetError(result, errorCodeToError(buffer.getError()));
249 return false;
250 }
251
252 // Initialize a VFS from the given mapping.
253 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
254 std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
255
256 // Dump the VFS to a buffer.
257 std::string str;
258 raw_string_ostream os(str);
259 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
260 os.flush();
261
262 // Return the string.
263 result.AppendMessage(str);
264 result.SetStatus(eReturnStatusSuccessFinishResult);
265 return true;
266 }
267 case eReproducerProviderVersion: {
268 FileSpec version_file = loader->GetFile<VersionProvider::Info>();
269
270 // Load the version info into a buffer.
271 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
272 vfs::getRealFileSystem()->getBufferForFile(version_file.GetPath());
273 if (!buffer) {
274 SetError(result, errorCodeToError(buffer.getError()));
275 return false;
276 }
277
278 // Return the version string.
279 StringRef version = (*buffer)->getBuffer();
280 result.AppendMessage(version.str());
281 result.SetStatus(eReturnStatusSuccessFinishResult);
282 return true;
283 }
284 case eReproducerProviderCommands: {
285 // Create a new command loader.
286 std::unique_ptr<repro::CommandLoader> command_loader =
287 repro::CommandLoader::Create(loader);
288 if (!command_loader) {
289 SetError(result,
290 make_error<StringError>(llvm::inconvertibleErrorCode(),
291 "Unable to create command loader."));
292 return false;
293 }
294
295 // Iterate over the command files and dump them.
296 while (true) {
297 llvm::Optional<std::string> command_file =
298 command_loader->GetNextFile();
299 if (!command_file)
300 break;
301
302 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
303 if (auto err = command_buffer.getError()) {
304 SetError(result, errorCodeToError(err));
305 return false;
306 }
307 result.AppendMessage((*command_buffer)->getBuffer());
308 }
309
310 result.SetStatus(eReturnStatusSuccessFinishResult);
311 return true;
312 }
313 case eReproducerProviderGDB: {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000314 FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
315 auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath());
316 if (auto err = error_or_file.getError()) {
317 SetError(result, errorCodeToError(err));
318 return false;
319 }
320
321 std::vector<GDBRemotePacket> packets;
322 yaml::Input yin((*error_or_file)->getBuffer());
323 yin >> packets;
324
325 if (auto err = yin.error()) {
326 SetError(result, errorCodeToError(err));
327 return false;
328 }
329
330 for (GDBRemotePacket& packet : packets) {
331 packet.Dump(result.GetOutputStream());
332 }
333
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000334 result.SetStatus(eReturnStatusSuccessFinishResult);
335 return true;
336 }
337 case eReproducerProviderNone:
338 result.SetError("No valid provider specified.");
339 return false;
340 }
341
342 result.SetStatus(eReturnStatusSuccessFinishNoResult);
343 return result.Succeeded();
344 }
345
346private:
347 CommandOptions m_options;
348};
349
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000350CommandObjectReproducer::CommandObjectReproducer(
351 CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000352 : CommandObjectMultiword(
353 interpreter, "reproducer",
Jonas Devlieghere130ec062019-07-30 18:06:38 +0000354 "Commands for manipulate the reproducer functionality.",
355 "reproducer <subcommand> [<subcommand-options>]") {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000356 LoadSubCommand(
357 "generate",
358 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000359 LoadSubCommand("status", CommandObjectSP(
360 new CommandObjectReproducerStatus(interpreter)));
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000361 LoadSubCommand("dump",
362 CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000363}
364
365CommandObjectReproducer::~CommandObjectReproducer() = default;