blob: dc4579c20fc2e2d2c4f88cf1fbcbd9d21944fdff [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 Devlieghere8fc8d3f2019-09-16 23:31:06 +000012#include "lldb/Utility/GDBRemote.h"
Jonas Devliegheref4f12012019-10-17 00:02:00 +000013#include "lldb/Utility/Reproducer.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,
Jonas Devliegheref4f12012019-10-17 00:02:00 +000030 eReproducerProviderWorkingDirectory,
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000031 eReproducerProviderNone
32};
33
34static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
35 {
36 eReproducerProviderCommands,
37 "commands",
38 "Command Interpreter Commands",
39 },
40 {
41 eReproducerProviderFiles,
42 "files",
43 "Files",
44 },
45 {
46 eReproducerProviderGDB,
47 "gdb",
48 "GDB Remote Packets",
49 },
50 {
51 eReproducerProviderVersion,
52 "version",
53 "Version",
54 },
55 {
Jonas Devliegheref4f12012019-10-17 00:02:00 +000056 eReproducerProviderWorkingDirectory,
57 "cwd",
58 "Working Directory",
59 },
60 {
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000061 eReproducerProviderNone,
62 "none",
63 "None",
64 },
65};
66
67static constexpr OptionEnumValues ReproducerProviderType() {
68 return OptionEnumValues(g_reproducer_provider_type);
69}
70
71#define LLDB_OPTIONS_reproducer
72#include "CommandOptions.inc"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000073
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000074class CommandObjectReproducerGenerate : public CommandObjectParsed {
75public:
76 CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +000077 : CommandObjectParsed(
78 interpreter, "reproducer generate",
79 "Generate reproducer on disk. When the debugger is in capture "
80 "mode, this command will output the reproducer to a directory on "
81 "disk. In replay mode this command in a no-op.",
82 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000083
84 ~CommandObjectReproducerGenerate() override = default;
85
86protected:
87 bool DoExecute(Args &command, CommandReturnObject &result) override {
88 if (!command.empty()) {
89 result.AppendErrorWithFormat("'%s' takes no arguments",
90 m_cmd_name.c_str());
91 return false;
92 }
93
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000094 auto &r = Reproducer::Instance();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000095 if (auto generator = r.GetGenerator()) {
96 generator->Keep();
Jonas Devlieghere865cd092019-10-09 21:47:49 +000097 } else if (r.IsReplaying()) {
Jonas Devlieghere2dca6532019-02-27 17:47:06 +000098 // Make this operation a NOP in replay mode.
99 result.SetStatus(eReturnStatusSuccessFinishNoResult);
100 return result.Succeeded();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000101 } else {
102 result.AppendErrorWithFormat("Unable to get the reproducer generator");
Jonas Devlieghere2dca6532019-02-27 17:47:06 +0000103 result.SetStatus(eReturnStatusFailed);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000104 return false;
105 }
106
107 result.GetOutputStream()
108 << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
Jonas Devlieghere1c5250a2019-04-02 18:23:16 +0000109 result.GetOutputStream()
110 << "Please have a look at the directory to assess if you're willing to "
111 "share the contained information.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000112
113 result.SetStatus(eReturnStatusSuccessFinishResult);
114 return result.Succeeded();
115 }
116};
117
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000118class CommandObjectReproducerStatus : public CommandObjectParsed {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000119public:
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000120 CommandObjectReproducerStatus(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000121 : CommandObjectParsed(
122 interpreter, "reproducer status",
123 "Show the current reproducer status. In capture mode the debugger "
124 "is collecting all the information it needs to create a "
125 "reproducer. In replay mode the reproducer is replaying a "
126 "reproducer. When the reproducers are off, no data is collected "
127 "and no reproducer can be generated.",
128 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000129
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000130 ~CommandObjectReproducerStatus() override = default;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000131
132protected:
133 bool DoExecute(Args &command, CommandReturnObject &result) override {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000134 if (!command.empty()) {
135 result.AppendErrorWithFormat("'%s' takes no arguments",
136 m_cmd_name.c_str());
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000137 return false;
138 }
139
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000140 auto &r = Reproducer::Instance();
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000141 if (r.IsCapturing()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000142 result.GetOutputStream() << "Reproducer is in capture mode.\n";
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000143 } else if (r.IsReplaying()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000144 result.GetOutputStream() << "Reproducer is in replay mode.\n";
145 } else {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000146 result.GetOutputStream() << "Reproducer is off.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000147 }
148
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000149 result.SetStatus(eReturnStatusSuccessFinishResult);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000150 return result.Succeeded();
151 }
152};
153
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000154static void SetError(CommandReturnObject &result, Error err) {
155 result.GetErrorStream().Printf("error: %s\n",
156 toString(std::move(err)).c_str());
157 result.SetStatus(eReturnStatusFailed);
158}
159
160class CommandObjectReproducerDump : public CommandObjectParsed {
161public:
162 CommandObjectReproducerDump(CommandInterpreter &interpreter)
163 : CommandObjectParsed(interpreter, "reproducer dump",
Jonas Devlieghere64b7d952019-10-18 21:47:31 +0000164 "Dump the information contained in a reproducer. "
165 "If no reproducer is specified during replay, it "
166 "dumps the content of the current reproducer.",
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000167 nullptr) {}
168
169 ~CommandObjectReproducerDump() override = default;
170
171 Options *GetOptions() override { return &m_options; }
172
173 class CommandOptions : public Options {
174 public:
175 CommandOptions() : Options(), file() {}
176
177 ~CommandOptions() override = default;
178
179 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
180 ExecutionContext *execution_context) override {
181 Status error;
182 const int short_option = m_getopt_table[option_idx].val;
183
184 switch (short_option) {
185 case 'f':
186 file.SetFile(option_arg, FileSpec::Style::native);
187 FileSystem::Instance().Resolve(file);
188 break;
189 case 'p':
190 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
191 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
192 if (!error.Success())
193 error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
194 option_arg.str().c_str());
195 break;
196 default:
197 llvm_unreachable("Unimplemented option");
198 }
199
200 return error;
201 }
202
203 void OptionParsingStarting(ExecutionContext *execution_context) override {
204 file.Clear();
205 provider = eReproducerProviderNone;
206 }
207
208 ArrayRef<OptionDefinition> GetDefinitions() override {
209 return makeArrayRef(g_reproducer_options);
210 }
211
212 FileSpec file;
213 ReproducerProvider provider = eReproducerProviderNone;
214 };
215
216protected:
217 bool DoExecute(Args &command, CommandReturnObject &result) override {
218 if (!command.empty()) {
219 result.AppendErrorWithFormat("'%s' takes no arguments",
220 m_cmd_name.c_str());
221 return false;
222 }
223
224 // If no reproducer path is specified, use the loader currently used for
225 // replay. Otherwise create a new loader just for dumping.
226 llvm::Optional<Loader> loader_storage;
227 Loader *loader = nullptr;
228 if (!m_options.file) {
229 loader = Reproducer::Instance().GetLoader();
230 if (loader == nullptr) {
231 result.SetError(
232 "Not specifying a reproducer is only support during replay.");
233 result.SetStatus(eReturnStatusSuccessFinishNoResult);
234 return false;
235 }
236 } else {
237 loader_storage.emplace(m_options.file);
238 loader = &(*loader_storage);
239 if (Error err = loader->LoadIndex()) {
240 SetError(result, std::move(err));
241 return false;
242 }
243 }
244
245 // If we get here we should have a valid loader.
246 assert(loader);
247
248 switch (m_options.provider) {
249 case eReproducerProviderFiles: {
250 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
251
252 // Read the VFS mapping.
253 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
254 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
255 if (!buffer) {
256 SetError(result, errorCodeToError(buffer.getError()));
257 return false;
258 }
259
260 // Initialize a VFS from the given mapping.
261 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
262 std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
263
264 // Dump the VFS to a buffer.
265 std::string str;
266 raw_string_ostream os(str);
267 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
268 os.flush();
269
270 // Return the string.
271 result.AppendMessage(str);
272 result.SetStatus(eReturnStatusSuccessFinishResult);
273 return true;
274 }
275 case eReproducerProviderVersion: {
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000276 Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
277 if (!version) {
278 SetError(result, version.takeError());
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000279 return false;
280 }
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000281 result.AppendMessage(*version);
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000282 result.SetStatus(eReturnStatusSuccessFinishResult);
283 return true;
284 }
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000285 case eReproducerProviderWorkingDirectory: {
286 Expected<std::string> cwd =
287 loader->LoadBuffer<WorkingDirectoryProvider>();
288 if (!cwd) {
289 SetError(result, cwd.takeError());
290 return false;
291 }
292 result.AppendMessage(*cwd);
293 result.SetStatus(eReturnStatusSuccessFinishResult);
294 return true;
295 }
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000296 case eReproducerProviderCommands: {
297 // Create a new command loader.
298 std::unique_ptr<repro::CommandLoader> command_loader =
299 repro::CommandLoader::Create(loader);
300 if (!command_loader) {
301 SetError(result,
302 make_error<StringError>(llvm::inconvertibleErrorCode(),
303 "Unable to create command loader."));
304 return false;
305 }
306
307 // Iterate over the command files and dump them.
308 while (true) {
309 llvm::Optional<std::string> command_file =
310 command_loader->GetNextFile();
311 if (!command_file)
312 break;
313
314 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
315 if (auto err = command_buffer.getError()) {
316 SetError(result, errorCodeToError(err));
317 return false;
318 }
319 result.AppendMessage((*command_buffer)->getBuffer());
320 }
321
322 result.SetStatus(eReturnStatusSuccessFinishResult);
323 return true;
324 }
325 case eReproducerProviderGDB: {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000326 FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
327 auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath());
328 if (auto err = error_or_file.getError()) {
329 SetError(result, errorCodeToError(err));
330 return false;
331 }
332
333 std::vector<GDBRemotePacket> packets;
334 yaml::Input yin((*error_or_file)->getBuffer());
335 yin >> packets;
336
337 if (auto err = yin.error()) {
338 SetError(result, errorCodeToError(err));
339 return false;
340 }
341
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000342 for (GDBRemotePacket &packet : packets) {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000343 packet.Dump(result.GetOutputStream());
344 }
345
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000346 result.SetStatus(eReturnStatusSuccessFinishResult);
347 return true;
348 }
349 case eReproducerProviderNone:
350 result.SetError("No valid provider specified.");
351 return false;
352 }
353
354 result.SetStatus(eReturnStatusSuccessFinishNoResult);
355 return result.Succeeded();
356 }
357
358private:
359 CommandOptions m_options;
360};
361
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000362CommandObjectReproducer::CommandObjectReproducer(
363 CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000364 : CommandObjectMultiword(
365 interpreter, "reproducer",
Jonas Devlieghere64b7d952019-10-18 21:47:31 +0000366 "Commands for manipulating reproducers. Reproducers make it possible "
367 "to capture full debug sessions with all its dependencies. The "
368 "resulting reproducer is used to replay the debug session while "
369 "debugging the debugger.\n"
370 "Because reproducers need the whole the debug session from "
371 "beginning to end, you need to launch the debugger in capture or "
372 "replay mode, commonly though the command line driver.\n"
373 "Reproducers are unrelated record-replay debugging, as you cannot "
374 "interact with the debugger during replay.\n",
Jonas Devlieghere130ec062019-07-30 18:06:38 +0000375 "reproducer <subcommand> [<subcommand-options>]") {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000376 LoadSubCommand(
377 "generate",
378 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000379 LoadSubCommand("status", CommandObjectSP(
380 new CommandObjectReproducerStatus(interpreter)));
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000381 LoadSubCommand("dump",
382 CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000383}
384
385CommandObjectReproducer::~CommandObjectReproducer() = default;