blob: a3b49b1979b36d1a355564bb12e75dfb9ebb67ab [file] [log] [blame]
Raphael Isemann80814282020-01-24 08:23:27 +01001//===-- CommandObjectReproducer.cpp ---------------------------------------===//
Jonas Devlieghere9e046f02018-11-13 19:18:16 +00002//
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"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000018
Jonas Devliegherec8dfe902019-11-19 17:28:46 -080019#include <csignal>
20
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000021using namespace lldb;
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000022using namespace llvm;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000023using namespace lldb_private;
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000024using namespace lldb_private::repro;
25
26enum ReproducerProvider {
27 eReproducerProviderCommands,
28 eReproducerProviderFiles,
29 eReproducerProviderGDB,
30 eReproducerProviderVersion,
Jonas Devliegheref4f12012019-10-17 00:02:00 +000031 eReproducerProviderWorkingDirectory,
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000032 eReproducerProviderNone
33};
34
35static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
36 {
37 eReproducerProviderCommands,
38 "commands",
39 "Command Interpreter Commands",
40 },
41 {
42 eReproducerProviderFiles,
43 "files",
44 "Files",
45 },
46 {
47 eReproducerProviderGDB,
48 "gdb",
49 "GDB Remote Packets",
50 },
51 {
52 eReproducerProviderVersion,
53 "version",
54 "Version",
55 },
56 {
Jonas Devliegheref4f12012019-10-17 00:02:00 +000057 eReproducerProviderWorkingDirectory,
58 "cwd",
59 "Working Directory",
60 },
61 {
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000062 eReproducerProviderNone,
63 "none",
64 "None",
65 },
66};
67
68static constexpr OptionEnumValues ReproducerProviderType() {
69 return OptionEnumValues(g_reproducer_provider_type);
70}
71
Jonas Devlieghere36eea5c2019-11-19 16:18:19 -080072#define LLDB_OPTIONS_reproducer_dump
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000073#include "CommandOptions.inc"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000074
Jonas Devliegherec8dfe902019-11-19 17:28:46 -080075enum ReproducerCrashSignal {
Jonas Devliegherec8dfe902019-11-19 17:28:46 -080076 eReproducerCrashSigill,
77 eReproducerCrashSigsegv,
78};
79
80static constexpr OptionEnumValueElement g_reproducer_signaltype[] = {
81 {
Jonas Devliegherec8dfe902019-11-19 17:28:46 -080082 eReproducerCrashSigill,
83 "SIGILL",
84 "Illegal instruction",
85 },
86 {
87 eReproducerCrashSigsegv,
88 "SIGSEGV",
89 "Segmentation fault",
90 },
91};
92
93static constexpr OptionEnumValues ReproducerSignalType() {
94 return OptionEnumValues(g_reproducer_signaltype);
95}
96
97#define LLDB_OPTIONS_reproducer_xcrash
98#include "CommandOptions.inc"
99
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000100class CommandObjectReproducerGenerate : public CommandObjectParsed {
101public:
102 CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000103 : CommandObjectParsed(
104 interpreter, "reproducer generate",
105 "Generate reproducer on disk. When the debugger is in capture "
106 "mode, this command will output the reproducer to a directory on "
Jonas Devlieghere0cf86da2019-11-11 14:16:52 -0800107 "disk and quit. In replay mode this command in a no-op.",
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000108 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000109
110 ~CommandObjectReproducerGenerate() override = default;
111
112protected:
113 bool DoExecute(Args &command, CommandReturnObject &result) override {
114 if (!command.empty()) {
115 result.AppendErrorWithFormat("'%s' takes no arguments",
116 m_cmd_name.c_str());
117 return false;
118 }
119
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000120 auto &r = Reproducer::Instance();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000121 if (auto generator = r.GetGenerator()) {
122 generator->Keep();
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000123 } else if (r.IsReplaying()) {
Jonas Devliegherebb090bb2019-11-21 13:34:01 -0800124 // Make this operation a NO-OP in replay mode.
Jonas Devlieghere2dca6532019-02-27 17:47:06 +0000125 result.SetStatus(eReturnStatusSuccessFinishNoResult);
126 return result.Succeeded();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000127 } else {
128 result.AppendErrorWithFormat("Unable to get the reproducer generator");
Jonas Devlieghere2dca6532019-02-27 17:47:06 +0000129 result.SetStatus(eReturnStatusFailed);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000130 return false;
131 }
132
133 result.GetOutputStream()
134 << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
Jonas Devlieghere1c5250a2019-04-02 18:23:16 +0000135 result.GetOutputStream()
136 << "Please have a look at the directory to assess if you're willing to "
137 "share the contained information.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000138
Jonas Devlieghere0cf86da2019-11-11 14:16:52 -0800139 m_interpreter.BroadcastEvent(
140 CommandInterpreter::eBroadcastBitQuitCommandReceived);
141 result.SetStatus(eReturnStatusQuit);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000142 return result.Succeeded();
143 }
144};
145
Jonas Devliegherec8dfe902019-11-19 17:28:46 -0800146class CommandObjectReproducerXCrash : public CommandObjectParsed {
147public:
148 CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
149 : CommandObjectParsed(interpreter, "reproducer xcrash",
150 "Intentionally force the debugger to crash in "
151 "order to trigger and test reproducer generation.",
152 nullptr) {}
153
154 ~CommandObjectReproducerXCrash() override = default;
155
156 Options *GetOptions() override { return &m_options; }
157
158 class CommandOptions : public Options {
159 public:
160 CommandOptions() : Options() {}
161
162 ~CommandOptions() override = default;
163
164 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
165 ExecutionContext *execution_context) override {
166 Status error;
167 const int short_option = m_getopt_table[option_idx].val;
168
169 switch (short_option) {
170 case 's':
171 signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
172 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
173 if (!error.Success())
174 error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
175 option_arg.str().c_str());
176 break;
177 default:
178 llvm_unreachable("Unimplemented option");
179 }
180
181 return error;
182 }
183
184 void OptionParsingStarting(ExecutionContext *execution_context) override {
185 signal = eReproducerCrashSigsegv;
186 }
187
188 ArrayRef<OptionDefinition> GetDefinitions() override {
189 return makeArrayRef(g_reproducer_xcrash_options);
190 }
191
192 ReproducerCrashSignal signal = eReproducerCrashSigsegv;
193 };
194
195protected:
196 bool DoExecute(Args &command, CommandReturnObject &result) override {
197 if (!command.empty()) {
198 result.AppendErrorWithFormat("'%s' takes no arguments",
199 m_cmd_name.c_str());
200 return false;
201 }
202
203 auto &r = Reproducer::Instance();
Jonas Devliegherebb090bb2019-11-21 13:34:01 -0800204
205 if (!r.IsCapturing() && !r.IsReplaying()) {
Jonas Devliegherec8dfe902019-11-19 17:28:46 -0800206 result.SetError(
207 "forcing a crash is only supported when capturing a reproducer.");
208 result.SetStatus(eReturnStatusSuccessFinishNoResult);
209 return false;
210 }
211
212 switch (m_options.signal) {
213 case eReproducerCrashSigill:
214 std::raise(SIGILL);
215 break;
Jonas Devliegherec8dfe902019-11-19 17:28:46 -0800216 case eReproducerCrashSigsegv:
217 std::raise(SIGSEGV);
218 break;
219 }
220
221 result.SetStatus(eReturnStatusQuit);
222 return result.Succeeded();
223 }
224
225private:
226 CommandOptions m_options;
227};
228
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000229class CommandObjectReproducerStatus : public CommandObjectParsed {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000230public:
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000231 CommandObjectReproducerStatus(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000232 : CommandObjectParsed(
233 interpreter, "reproducer status",
Jonas Devliegherec8dfe902019-11-19 17:28:46 -0800234 "Show the current reproducer status. In capture mode the "
235 "debugger "
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000236 "is collecting all the information it needs to create a "
237 "reproducer. In replay mode the reproducer is replaying a "
238 "reproducer. When the reproducers are off, no data is collected "
239 "and no reproducer can be generated.",
240 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000241
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000242 ~CommandObjectReproducerStatus() override = default;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000243
244protected:
245 bool DoExecute(Args &command, CommandReturnObject &result) override {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000246 if (!command.empty()) {
247 result.AppendErrorWithFormat("'%s' takes no arguments",
248 m_cmd_name.c_str());
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000249 return false;
250 }
251
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000252 auto &r = Reproducer::Instance();
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000253 if (r.IsCapturing()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000254 result.GetOutputStream() << "Reproducer is in capture mode.\n";
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000255 } else if (r.IsReplaying()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000256 result.GetOutputStream() << "Reproducer is in replay mode.\n";
257 } else {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000258 result.GetOutputStream() << "Reproducer is off.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000259 }
260
Jonas Devlieghere982a77b2020-01-15 20:22:20 -0800261 if (r.IsCapturing() || r.IsReplaying()) {
262 result.GetOutputStream()
263 << "Path: " << r.GetReproducerPath().GetPath() << '\n';
264 }
265
266 // Auto generate is hidden unless enabled because this is mostly for
267 // development and testing.
268 if (Generator *g = r.GetGenerator()) {
269 if (g->IsAutoGenerate())
270 result.GetOutputStream() << "Auto generate: on\n";
271 }
272
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000273 result.SetStatus(eReturnStatusSuccessFinishResult);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000274 return result.Succeeded();
275 }
276};
277
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000278static void SetError(CommandReturnObject &result, Error err) {
279 result.GetErrorStream().Printf("error: %s\n",
280 toString(std::move(err)).c_str());
281 result.SetStatus(eReturnStatusFailed);
282}
283
284class CommandObjectReproducerDump : public CommandObjectParsed {
285public:
286 CommandObjectReproducerDump(CommandInterpreter &interpreter)
287 : CommandObjectParsed(interpreter, "reproducer dump",
Jonas Devlieghere64b7d952019-10-18 21:47:31 +0000288 "Dump the information contained in a reproducer. "
289 "If no reproducer is specified during replay, it "
290 "dumps the content of the current reproducer.",
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000291 nullptr) {}
292
293 ~CommandObjectReproducerDump() override = default;
294
295 Options *GetOptions() override { return &m_options; }
296
297 class CommandOptions : public Options {
298 public:
299 CommandOptions() : Options(), file() {}
300
301 ~CommandOptions() override = default;
302
303 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
304 ExecutionContext *execution_context) override {
305 Status error;
306 const int short_option = m_getopt_table[option_idx].val;
307
308 switch (short_option) {
309 case 'f':
310 file.SetFile(option_arg, FileSpec::Style::native);
311 FileSystem::Instance().Resolve(file);
312 break;
313 case 'p':
314 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
315 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
316 if (!error.Success())
317 error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
318 option_arg.str().c_str());
319 break;
320 default:
321 llvm_unreachable("Unimplemented option");
322 }
323
324 return error;
325 }
326
327 void OptionParsingStarting(ExecutionContext *execution_context) override {
328 file.Clear();
329 provider = eReproducerProviderNone;
330 }
331
332 ArrayRef<OptionDefinition> GetDefinitions() override {
Jonas Devlieghere36eea5c2019-11-19 16:18:19 -0800333 return makeArrayRef(g_reproducer_dump_options);
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000334 }
335
336 FileSpec file;
337 ReproducerProvider provider = eReproducerProviderNone;
338 };
339
340protected:
341 bool DoExecute(Args &command, CommandReturnObject &result) override {
342 if (!command.empty()) {
343 result.AppendErrorWithFormat("'%s' takes no arguments",
344 m_cmd_name.c_str());
345 return false;
346 }
347
348 // If no reproducer path is specified, use the loader currently used for
349 // replay. Otherwise create a new loader just for dumping.
350 llvm::Optional<Loader> loader_storage;
351 Loader *loader = nullptr;
352 if (!m_options.file) {
353 loader = Reproducer::Instance().GetLoader();
354 if (loader == nullptr) {
355 result.SetError(
356 "Not specifying a reproducer is only support during replay.");
357 result.SetStatus(eReturnStatusSuccessFinishNoResult);
358 return false;
359 }
360 } else {
361 loader_storage.emplace(m_options.file);
362 loader = &(*loader_storage);
363 if (Error err = loader->LoadIndex()) {
364 SetError(result, std::move(err));
365 return false;
366 }
367 }
368
369 // If we get here we should have a valid loader.
370 assert(loader);
371
372 switch (m_options.provider) {
373 case eReproducerProviderFiles: {
374 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
375
376 // Read the VFS mapping.
377 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
378 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
379 if (!buffer) {
380 SetError(result, errorCodeToError(buffer.getError()));
381 return false;
382 }
383
384 // Initialize a VFS from the given mapping.
385 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
386 std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
387
388 // Dump the VFS to a buffer.
389 std::string str;
390 raw_string_ostream os(str);
391 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
392 os.flush();
393
394 // Return the string.
395 result.AppendMessage(str);
396 result.SetStatus(eReturnStatusSuccessFinishResult);
397 return true;
398 }
399 case eReproducerProviderVersion: {
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000400 Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
401 if (!version) {
402 SetError(result, version.takeError());
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000403 return false;
404 }
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000405 result.AppendMessage(*version);
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000406 result.SetStatus(eReturnStatusSuccessFinishResult);
407 return true;
408 }
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000409 case eReproducerProviderWorkingDirectory: {
410 Expected<std::string> cwd =
411 loader->LoadBuffer<WorkingDirectoryProvider>();
412 if (!cwd) {
413 SetError(result, cwd.takeError());
414 return false;
415 }
416 result.AppendMessage(*cwd);
417 result.SetStatus(eReturnStatusSuccessFinishResult);
418 return true;
419 }
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000420 case eReproducerProviderCommands: {
Eric Christopher1d41d1b2019-12-10 15:04:02 -0800421 std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
422 repro::MultiLoader<repro::CommandProvider>::Create(loader);
423 if (!multi_loader) {
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000424 SetError(result,
425 make_error<StringError>(llvm::inconvertibleErrorCode(),
426 "Unable to create command loader."));
427 return false;
428 }
429
430 // Iterate over the command files and dump them.
Eric Christopher1d41d1b2019-12-10 15:04:02 -0800431 llvm::Optional<std::string> command_file;
432 while ((command_file = multi_loader->GetNextFile())) {
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000433 if (!command_file)
434 break;
435
436 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
437 if (auto err = command_buffer.getError()) {
438 SetError(result, errorCodeToError(err));
439 return false;
440 }
441 result.AppendMessage((*command_buffer)->getBuffer());
442 }
443
444 result.SetStatus(eReturnStatusSuccessFinishResult);
445 return true;
446 }
447 case eReproducerProviderGDB: {
Eric Christopher1d41d1b2019-12-10 15:04:02 -0800448 std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
449 multi_loader =
450 repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
451 llvm::Optional<std::string> gdb_file;
452 while ((gdb_file = multi_loader->GetNextFile())) {
453 auto error_or_file = MemoryBuffer::getFile(*gdb_file);
454 if (auto err = error_or_file.getError()) {
455 SetError(result, errorCodeToError(err));
456 return false;
457 }
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000458
Eric Christopher1d41d1b2019-12-10 15:04:02 -0800459 std::vector<GDBRemotePacket> packets;
460 yaml::Input yin((*error_or_file)->getBuffer());
461 yin >> packets;
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000462
Eric Christopher1d41d1b2019-12-10 15:04:02 -0800463 if (auto err = yin.error()) {
464 SetError(result, errorCodeToError(err));
465 return false;
466 }
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000467
Eric Christopher1d41d1b2019-12-10 15:04:02 -0800468 for (GDBRemotePacket &packet : packets) {
469 packet.Dump(result.GetOutputStream());
470 }
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000471 }
472
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000473 result.SetStatus(eReturnStatusSuccessFinishResult);
474 return true;
475 }
476 case eReproducerProviderNone:
477 result.SetError("No valid provider specified.");
478 return false;
479 }
480
481 result.SetStatus(eReturnStatusSuccessFinishNoResult);
482 return result.Succeeded();
483 }
484
485private:
486 CommandOptions m_options;
487};
488
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000489CommandObjectReproducer::CommandObjectReproducer(
490 CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000491 : CommandObjectMultiword(
492 interpreter, "reproducer",
Jonas Devliegherec8dfe902019-11-19 17:28:46 -0800493 "Commands for manipulating reproducers. Reproducers make it "
494 "possible "
Jonas Devlieghere64b7d952019-10-18 21:47:31 +0000495 "to capture full debug sessions with all its dependencies. The "
496 "resulting reproducer is used to replay the debug session while "
497 "debugging the debugger.\n"
498 "Because reproducers need the whole the debug session from "
499 "beginning to end, you need to launch the debugger in capture or "
500 "replay mode, commonly though the command line driver.\n"
501 "Reproducers are unrelated record-replay debugging, as you cannot "
502 "interact with the debugger during replay.\n",
Jonas Devlieghere130ec062019-07-30 18:06:38 +0000503 "reproducer <subcommand> [<subcommand-options>]") {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000504 LoadSubCommand(
505 "generate",
506 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000507 LoadSubCommand("status", CommandObjectSP(
508 new CommandObjectReproducerStatus(interpreter)));
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000509 LoadSubCommand("dump",
510 CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
Jonas Devliegherec8dfe902019-11-19 17:28:46 -0800511 LoadSubCommand("xcrash", CommandObjectSP(
512 new CommandObjectReproducerXCrash(interpreter)));
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000513}
514
515CommandObjectReproducer::~CommandObjectReproducer() = default;