blob: 560df25b97c6f84bb1b95fcd6933bcdcee7a88d0 [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 "
Jonas Devlieghere0cf86da2019-11-11 14:16:52 -080081 "disk and quit. In replay mode this command in a no-op.",
Jonas Devlieghere973d66e2019-05-03 00:10:31 +000082 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
Jonas Devlieghere0cf86da2019-11-11 14:16:52 -0800113 m_interpreter.BroadcastEvent(
114 CommandInterpreter::eBroadcastBitQuitCommandReceived);
115 result.SetStatus(eReturnStatusQuit);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000116 return result.Succeeded();
117 }
118};
119
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000120class CommandObjectReproducerStatus : public CommandObjectParsed {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000121public:
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000122 CommandObjectReproducerStatus(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000123 : CommandObjectParsed(
124 interpreter, "reproducer status",
125 "Show the current reproducer status. In capture mode the debugger "
126 "is collecting all the information it needs to create a "
127 "reproducer. In replay mode the reproducer is replaying a "
128 "reproducer. When the reproducers are off, no data is collected "
129 "and no reproducer can be generated.",
130 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000131
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000132 ~CommandObjectReproducerStatus() override = default;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000133
134protected:
135 bool DoExecute(Args &command, CommandReturnObject &result) override {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000136 if (!command.empty()) {
137 result.AppendErrorWithFormat("'%s' takes no arguments",
138 m_cmd_name.c_str());
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000139 return false;
140 }
141
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000142 auto &r = Reproducer::Instance();
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000143 if (r.IsCapturing()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000144 result.GetOutputStream() << "Reproducer is in capture mode.\n";
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000145 } else if (r.IsReplaying()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000146 result.GetOutputStream() << "Reproducer is in replay mode.\n";
147 } else {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000148 result.GetOutputStream() << "Reproducer is off.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000149 }
150
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000151 result.SetStatus(eReturnStatusSuccessFinishResult);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000152 return result.Succeeded();
153 }
154};
155
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000156static void SetError(CommandReturnObject &result, Error err) {
157 result.GetErrorStream().Printf("error: %s\n",
158 toString(std::move(err)).c_str());
159 result.SetStatus(eReturnStatusFailed);
160}
161
162class CommandObjectReproducerDump : public CommandObjectParsed {
163public:
164 CommandObjectReproducerDump(CommandInterpreter &interpreter)
165 : CommandObjectParsed(interpreter, "reproducer dump",
Jonas Devlieghere64b7d952019-10-18 21:47:31 +0000166 "Dump the information contained in a reproducer. "
167 "If no reproducer is specified during replay, it "
168 "dumps the content of the current reproducer.",
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000169 nullptr) {}
170
171 ~CommandObjectReproducerDump() override = default;
172
173 Options *GetOptions() override { return &m_options; }
174
175 class CommandOptions : public Options {
176 public:
177 CommandOptions() : Options(), file() {}
178
179 ~CommandOptions() override = default;
180
181 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
182 ExecutionContext *execution_context) override {
183 Status error;
184 const int short_option = m_getopt_table[option_idx].val;
185
186 switch (short_option) {
187 case 'f':
188 file.SetFile(option_arg, FileSpec::Style::native);
189 FileSystem::Instance().Resolve(file);
190 break;
191 case 'p':
192 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
193 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
194 if (!error.Success())
195 error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
196 option_arg.str().c_str());
197 break;
198 default:
199 llvm_unreachable("Unimplemented option");
200 }
201
202 return error;
203 }
204
205 void OptionParsingStarting(ExecutionContext *execution_context) override {
206 file.Clear();
207 provider = eReproducerProviderNone;
208 }
209
210 ArrayRef<OptionDefinition> GetDefinitions() override {
211 return makeArrayRef(g_reproducer_options);
212 }
213
214 FileSpec file;
215 ReproducerProvider provider = eReproducerProviderNone;
216 };
217
218protected:
219 bool DoExecute(Args &command, CommandReturnObject &result) override {
220 if (!command.empty()) {
221 result.AppendErrorWithFormat("'%s' takes no arguments",
222 m_cmd_name.c_str());
223 return false;
224 }
225
226 // If no reproducer path is specified, use the loader currently used for
227 // replay. Otherwise create a new loader just for dumping.
228 llvm::Optional<Loader> loader_storage;
229 Loader *loader = nullptr;
230 if (!m_options.file) {
231 loader = Reproducer::Instance().GetLoader();
232 if (loader == nullptr) {
233 result.SetError(
234 "Not specifying a reproducer is only support during replay.");
235 result.SetStatus(eReturnStatusSuccessFinishNoResult);
236 return false;
237 }
238 } else {
239 loader_storage.emplace(m_options.file);
240 loader = &(*loader_storage);
241 if (Error err = loader->LoadIndex()) {
242 SetError(result, std::move(err));
243 return false;
244 }
245 }
246
247 // If we get here we should have a valid loader.
248 assert(loader);
249
250 switch (m_options.provider) {
251 case eReproducerProviderFiles: {
252 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
253
254 // Read the VFS mapping.
255 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
256 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
257 if (!buffer) {
258 SetError(result, errorCodeToError(buffer.getError()));
259 return false;
260 }
261
262 // Initialize a VFS from the given mapping.
263 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
264 std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
265
266 // Dump the VFS to a buffer.
267 std::string str;
268 raw_string_ostream os(str);
269 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
270 os.flush();
271
272 // Return the string.
273 result.AppendMessage(str);
274 result.SetStatus(eReturnStatusSuccessFinishResult);
275 return true;
276 }
277 case eReproducerProviderVersion: {
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000278 Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
279 if (!version) {
280 SetError(result, version.takeError());
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000281 return false;
282 }
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000283 result.AppendMessage(*version);
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000284 result.SetStatus(eReturnStatusSuccessFinishResult);
285 return true;
286 }
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000287 case eReproducerProviderWorkingDirectory: {
288 Expected<std::string> cwd =
289 loader->LoadBuffer<WorkingDirectoryProvider>();
290 if (!cwd) {
291 SetError(result, cwd.takeError());
292 return false;
293 }
294 result.AppendMessage(*cwd);
295 result.SetStatus(eReturnStatusSuccessFinishResult);
296 return true;
297 }
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000298 case eReproducerProviderCommands: {
299 // Create a new command loader.
300 std::unique_ptr<repro::CommandLoader> command_loader =
301 repro::CommandLoader::Create(loader);
302 if (!command_loader) {
303 SetError(result,
304 make_error<StringError>(llvm::inconvertibleErrorCode(),
305 "Unable to create command loader."));
306 return false;
307 }
308
309 // Iterate over the command files and dump them.
310 while (true) {
311 llvm::Optional<std::string> command_file =
312 command_loader->GetNextFile();
313 if (!command_file)
314 break;
315
316 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
317 if (auto err = command_buffer.getError()) {
318 SetError(result, errorCodeToError(err));
319 return false;
320 }
321 result.AppendMessage((*command_buffer)->getBuffer());
322 }
323
324 result.SetStatus(eReturnStatusSuccessFinishResult);
325 return true;
326 }
327 case eReproducerProviderGDB: {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000328 FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
329 auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath());
330 if (auto err = error_or_file.getError()) {
331 SetError(result, errorCodeToError(err));
332 return false;
333 }
334
335 std::vector<GDBRemotePacket> packets;
336 yaml::Input yin((*error_or_file)->getBuffer());
337 yin >> packets;
338
339 if (auto err = yin.error()) {
340 SetError(result, errorCodeToError(err));
341 return false;
342 }
343
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000344 for (GDBRemotePacket &packet : packets) {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000345 packet.Dump(result.GetOutputStream());
346 }
347
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000348 result.SetStatus(eReturnStatusSuccessFinishResult);
349 return true;
350 }
351 case eReproducerProviderNone:
352 result.SetError("No valid provider specified.");
353 return false;
354 }
355
356 result.SetStatus(eReturnStatusSuccessFinishNoResult);
357 return result.Succeeded();
358 }
359
360private:
361 CommandOptions m_options;
362};
363
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000364CommandObjectReproducer::CommandObjectReproducer(
365 CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000366 : CommandObjectMultiword(
367 interpreter, "reproducer",
Jonas Devlieghere64b7d952019-10-18 21:47:31 +0000368 "Commands for manipulating reproducers. Reproducers make it possible "
369 "to capture full debug sessions with all its dependencies. The "
370 "resulting reproducer is used to replay the debug session while "
371 "debugging the debugger.\n"
372 "Because reproducers need the whole the debug session from "
373 "beginning to end, you need to launch the debugger in capture or "
374 "replay mode, commonly though the command line driver.\n"
375 "Reproducers are unrelated record-replay debugging, as you cannot "
376 "interact with the debugger during replay.\n",
Jonas Devlieghere130ec062019-07-30 18:06:38 +0000377 "reproducer <subcommand> [<subcommand-options>]") {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000378 LoadSubCommand(
379 "generate",
380 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000381 LoadSubCommand("status", CommandObjectSP(
382 new CommandObjectReproducerStatus(interpreter)));
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000383 LoadSubCommand("dump",
384 CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000385}
386
387CommandObjectReproducer::~CommandObjectReproducer() = default;