1 //===-- CommandObjectReproducer.cpp ---------------------------------------===// 2 // 3 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "CommandObjectReproducer.h" 10 11 #include "lldb/Host/HostInfo.h" 12 #include "lldb/Host/OptionParser.h" 13 #include "lldb/Interpreter/CommandInterpreter.h" 14 #include "lldb/Interpreter/CommandReturnObject.h" 15 #include "lldb/Interpreter/OptionArgParser.h" 16 #include "lldb/Utility/GDBRemote.h" 17 #include "lldb/Utility/ProcessInfo.h" 18 #include "lldb/Utility/Reproducer.h" 19 20 #include <csignal> 21 22 using namespace lldb; 23 using namespace llvm; 24 using namespace lldb_private; 25 using namespace lldb_private::repro; 26 27 enum ReproducerProvider { 28 eReproducerProviderCommands, 29 eReproducerProviderFiles, 30 eReproducerProviderSymbolFiles, 31 eReproducerProviderGDB, 32 eReproducerProviderProcessInfo, 33 eReproducerProviderVersion, 34 eReproducerProviderWorkingDirectory, 35 eReproducerProviderHomeDirectory, 36 eReproducerProviderNone 37 }; 38 39 static constexpr OptionEnumValueElement g_reproducer_provider_type[] = { 40 { 41 eReproducerProviderCommands, 42 "commands", 43 "Command Interpreter Commands", 44 }, 45 { 46 eReproducerProviderFiles, 47 "files", 48 "Files", 49 }, 50 { 51 eReproducerProviderSymbolFiles, 52 "symbol-files", 53 "Symbol Files", 54 }, 55 { 56 eReproducerProviderGDB, 57 "gdb", 58 "GDB Remote Packets", 59 }, 60 { 61 eReproducerProviderProcessInfo, 62 "processes", 63 "Process Info", 64 }, 65 { 66 eReproducerProviderVersion, 67 "version", 68 "Version", 69 }, 70 { 71 eReproducerProviderWorkingDirectory, 72 "cwd", 73 "Working Directory", 74 }, 75 { 76 eReproducerProviderHomeDirectory, 77 "home", 78 "Home Directory", 79 }, 80 { 81 eReproducerProviderNone, 82 "none", 83 "None", 84 }, 85 }; 86 87 static constexpr OptionEnumValues ReproducerProviderType() { 88 return OptionEnumValues(g_reproducer_provider_type); 89 } 90 91 #define LLDB_OPTIONS_reproducer_dump 92 #include "CommandOptions.inc" 93 94 enum ReproducerCrashSignal { 95 eReproducerCrashSigill, 96 eReproducerCrashSigsegv, 97 }; 98 99 static constexpr OptionEnumValueElement g_reproducer_signaltype[] = { 100 { 101 eReproducerCrashSigill, 102 "SIGILL", 103 "Illegal instruction", 104 }, 105 { 106 eReproducerCrashSigsegv, 107 "SIGSEGV", 108 "Segmentation fault", 109 }, 110 }; 111 112 static constexpr OptionEnumValues ReproducerSignalType() { 113 return OptionEnumValues(g_reproducer_signaltype); 114 } 115 116 #define LLDB_OPTIONS_reproducer_xcrash 117 #include "CommandOptions.inc" 118 119 #define LLDB_OPTIONS_reproducer_verify 120 #include "CommandOptions.inc" 121 122 template <typename T> 123 llvm::Expected<T> static ReadFromYAML(StringRef filename) { 124 auto error_or_file = MemoryBuffer::getFile(filename); 125 if (auto err = error_or_file.getError()) { 126 return errorCodeToError(err); 127 } 128 129 T t; 130 yaml::Input yin((*error_or_file)->getBuffer()); 131 yin >> t; 132 133 if (auto err = yin.error()) { 134 return errorCodeToError(err); 135 } 136 137 return t; 138 } 139 140 static void SetError(CommandReturnObject &result, Error err) { 141 result.GetErrorStream().Printf("error: %s\n", 142 toString(std::move(err)).c_str()); 143 result.SetStatus(eReturnStatusFailed); 144 } 145 146 /// Create a loader from the given path if specified. Otherwise use the current 147 /// loader used for replay. 148 static Loader * 149 GetLoaderFromPathOrCurrent(llvm::Optional<Loader> &loader_storage, 150 CommandReturnObject &result, 151 FileSpec reproducer_path) { 152 if (reproducer_path) { 153 loader_storage.emplace(reproducer_path); 154 Loader *loader = &(*loader_storage); 155 if (Error err = loader->LoadIndex()) { 156 // This is a hard error and will set the result to eReturnStatusFailed. 157 SetError(result, std::move(err)); 158 return nullptr; 159 } 160 return loader; 161 } 162 163 if (Loader *loader = Reproducer::Instance().GetLoader()) 164 return loader; 165 166 // This is a soft error because this is expected to fail during capture. 167 result.SetError("Not specifying a reproducer is only support during replay."); 168 result.SetStatus(eReturnStatusSuccessFinishNoResult); 169 return nullptr; 170 } 171 172 class CommandObjectReproducerGenerate : public CommandObjectParsed { 173 public: 174 CommandObjectReproducerGenerate(CommandInterpreter &interpreter) 175 : CommandObjectParsed( 176 interpreter, "reproducer generate", 177 "Generate reproducer on disk. When the debugger is in capture " 178 "mode, this command will output the reproducer to a directory on " 179 "disk and quit. In replay mode this command in a no-op.", 180 nullptr) {} 181 182 ~CommandObjectReproducerGenerate() override = default; 183 184 protected: 185 bool DoExecute(Args &command, CommandReturnObject &result) override { 186 if (!command.empty()) { 187 result.AppendErrorWithFormat("'%s' takes no arguments", 188 m_cmd_name.c_str()); 189 return false; 190 } 191 192 auto &r = Reproducer::Instance(); 193 if (auto generator = r.GetGenerator()) { 194 generator->Keep(); 195 } else if (r.IsReplaying()) { 196 // Make this operation a NO-OP in replay mode. 197 result.SetStatus(eReturnStatusSuccessFinishNoResult); 198 return result.Succeeded(); 199 } else { 200 result.AppendErrorWithFormat("Unable to get the reproducer generator"); 201 result.SetStatus(eReturnStatusFailed); 202 return false; 203 } 204 205 result.GetOutputStream() 206 << "Reproducer written to '" << r.GetReproducerPath() << "'\n"; 207 result.GetOutputStream() 208 << "Please have a look at the directory to assess if you're willing to " 209 "share the contained information.\n"; 210 211 m_interpreter.BroadcastEvent( 212 CommandInterpreter::eBroadcastBitQuitCommandReceived); 213 result.SetStatus(eReturnStatusQuit); 214 return result.Succeeded(); 215 } 216 }; 217 218 class CommandObjectReproducerXCrash : public CommandObjectParsed { 219 public: 220 CommandObjectReproducerXCrash(CommandInterpreter &interpreter) 221 : CommandObjectParsed(interpreter, "reproducer xcrash", 222 "Intentionally force the debugger to crash in " 223 "order to trigger and test reproducer generation.", 224 nullptr) {} 225 226 ~CommandObjectReproducerXCrash() override = default; 227 228 Options *GetOptions() override { return &m_options; } 229 230 class CommandOptions : public Options { 231 public: 232 CommandOptions() : Options() {} 233 234 ~CommandOptions() override = default; 235 236 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 237 ExecutionContext *execution_context) override { 238 Status error; 239 const int short_option = m_getopt_table[option_idx].val; 240 241 switch (short_option) { 242 case 's': 243 signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum( 244 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 245 if (!error.Success()) 246 error.SetErrorStringWithFormat("unrecognized value for signal '%s'", 247 option_arg.str().c_str()); 248 break; 249 default: 250 llvm_unreachable("Unimplemented option"); 251 } 252 253 return error; 254 } 255 256 void OptionParsingStarting(ExecutionContext *execution_context) override { 257 signal = eReproducerCrashSigsegv; 258 } 259 260 ArrayRef<OptionDefinition> GetDefinitions() override { 261 return makeArrayRef(g_reproducer_xcrash_options); 262 } 263 264 ReproducerCrashSignal signal = eReproducerCrashSigsegv; 265 }; 266 267 protected: 268 bool DoExecute(Args &command, CommandReturnObject &result) override { 269 if (!command.empty()) { 270 result.AppendErrorWithFormat("'%s' takes no arguments", 271 m_cmd_name.c_str()); 272 return false; 273 } 274 275 auto &r = Reproducer::Instance(); 276 277 if (!r.IsCapturing() && !r.IsReplaying()) { 278 result.SetError( 279 "forcing a crash is only supported when capturing a reproducer."); 280 result.SetStatus(eReturnStatusSuccessFinishNoResult); 281 return false; 282 } 283 284 switch (m_options.signal) { 285 case eReproducerCrashSigill: 286 std::raise(SIGILL); 287 break; 288 case eReproducerCrashSigsegv: 289 std::raise(SIGSEGV); 290 break; 291 } 292 293 result.SetStatus(eReturnStatusQuit); 294 return result.Succeeded(); 295 } 296 297 private: 298 CommandOptions m_options; 299 }; 300 301 class CommandObjectReproducerStatus : public CommandObjectParsed { 302 public: 303 CommandObjectReproducerStatus(CommandInterpreter &interpreter) 304 : CommandObjectParsed( 305 interpreter, "reproducer status", 306 "Show the current reproducer status. In capture mode the " 307 "debugger " 308 "is collecting all the information it needs to create a " 309 "reproducer. In replay mode the reproducer is replaying a " 310 "reproducer. When the reproducers are off, no data is collected " 311 "and no reproducer can be generated.", 312 nullptr) {} 313 314 ~CommandObjectReproducerStatus() override = default; 315 316 protected: 317 bool DoExecute(Args &command, CommandReturnObject &result) override { 318 if (!command.empty()) { 319 result.AppendErrorWithFormat("'%s' takes no arguments", 320 m_cmd_name.c_str()); 321 return false; 322 } 323 324 auto &r = Reproducer::Instance(); 325 if (r.IsCapturing()) { 326 result.GetOutputStream() << "Reproducer is in capture mode.\n"; 327 } else if (r.IsReplaying()) { 328 result.GetOutputStream() << "Reproducer is in replay mode.\n"; 329 } else { 330 result.GetOutputStream() << "Reproducer is off.\n"; 331 } 332 333 if (r.IsCapturing() || r.IsReplaying()) { 334 result.GetOutputStream() 335 << "Path: " << r.GetReproducerPath().GetPath() << '\n'; 336 } 337 338 // Auto generate is hidden unless enabled because this is mostly for 339 // development and testing. 340 if (Generator *g = r.GetGenerator()) { 341 if (g->IsAutoGenerate()) 342 result.GetOutputStream() << "Auto generate: on\n"; 343 } 344 345 result.SetStatus(eReturnStatusSuccessFinishResult); 346 return result.Succeeded(); 347 } 348 }; 349 350 class CommandObjectReproducerDump : public CommandObjectParsed { 351 public: 352 CommandObjectReproducerDump(CommandInterpreter &interpreter) 353 : CommandObjectParsed(interpreter, "reproducer dump", 354 "Dump the information contained in a reproducer. " 355 "If no reproducer is specified during replay, it " 356 "dumps the content of the current reproducer.", 357 nullptr) {} 358 359 ~CommandObjectReproducerDump() override = default; 360 361 Options *GetOptions() override { return &m_options; } 362 363 class CommandOptions : public Options { 364 public: 365 CommandOptions() : Options(), file() {} 366 367 ~CommandOptions() override = default; 368 369 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 370 ExecutionContext *execution_context) override { 371 Status error; 372 const int short_option = m_getopt_table[option_idx].val; 373 374 switch (short_option) { 375 case 'f': 376 file.SetFile(option_arg, FileSpec::Style::native); 377 FileSystem::Instance().Resolve(file); 378 break; 379 case 'p': 380 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum( 381 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 382 if (!error.Success()) 383 error.SetErrorStringWithFormat("unrecognized value for provider '%s'", 384 option_arg.str().c_str()); 385 break; 386 default: 387 llvm_unreachable("Unimplemented option"); 388 } 389 390 return error; 391 } 392 393 void OptionParsingStarting(ExecutionContext *execution_context) override { 394 file.Clear(); 395 provider = eReproducerProviderNone; 396 } 397 398 ArrayRef<OptionDefinition> GetDefinitions() override { 399 return makeArrayRef(g_reproducer_dump_options); 400 } 401 402 FileSpec file; 403 ReproducerProvider provider = eReproducerProviderNone; 404 }; 405 406 protected: 407 bool DoExecute(Args &command, CommandReturnObject &result) override { 408 if (!command.empty()) { 409 result.AppendErrorWithFormat("'%s' takes no arguments", 410 m_cmd_name.c_str()); 411 return false; 412 } 413 414 llvm::Optional<Loader> loader_storage; 415 Loader *loader = 416 GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file); 417 if (!loader) 418 return false; 419 420 switch (m_options.provider) { 421 case eReproducerProviderFiles: { 422 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>(); 423 424 // Read the VFS mapping. 425 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer = 426 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath()); 427 if (!buffer) { 428 SetError(result, errorCodeToError(buffer.getError())); 429 return false; 430 } 431 432 // Initialize a VFS from the given mapping. 433 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML( 434 std::move(buffer.get()), nullptr, vfs_mapping.GetPath()); 435 436 // Dump the VFS to a buffer. 437 std::string str; 438 raw_string_ostream os(str); 439 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os); 440 os.flush(); 441 442 // Return the string. 443 result.AppendMessage(str); 444 result.SetStatus(eReturnStatusSuccessFinishResult); 445 return true; 446 } 447 case eReproducerProviderSymbolFiles: { 448 Expected<std::string> symbol_files = 449 loader->LoadBuffer<SymbolFileProvider>(); 450 if (!symbol_files) { 451 SetError(result, symbol_files.takeError()); 452 return false; 453 } 454 455 std::vector<SymbolFileProvider::Entry> entries; 456 llvm::yaml::Input yin(*symbol_files); 457 yin >> entries; 458 459 for (const auto &entry : entries) { 460 result.AppendMessageWithFormat("- uuid: %s\n", 461 entry.uuid.c_str()); 462 result.AppendMessageWithFormat(" module path: %s\n", 463 entry.module_path.c_str()); 464 result.AppendMessageWithFormat(" symbol path: %s\n", 465 entry.symbol_path.c_str()); 466 } 467 result.SetStatus(eReturnStatusSuccessFinishResult); 468 return true; 469 } 470 case eReproducerProviderVersion: { 471 Expected<std::string> version = loader->LoadBuffer<VersionProvider>(); 472 if (!version) { 473 SetError(result, version.takeError()); 474 return false; 475 } 476 result.AppendMessage(*version); 477 result.SetStatus(eReturnStatusSuccessFinishResult); 478 return true; 479 } 480 case eReproducerProviderWorkingDirectory: { 481 Expected<std::string> cwd = 482 repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader); 483 if (!cwd) { 484 SetError(result, cwd.takeError()); 485 return false; 486 } 487 result.AppendMessage(*cwd); 488 result.SetStatus(eReturnStatusSuccessFinishResult); 489 return true; 490 } 491 case eReproducerProviderHomeDirectory: { 492 Expected<std::string> home = 493 repro::GetDirectoryFrom<HomeDirectoryProvider>(loader); 494 if (!home) { 495 SetError(result, home.takeError()); 496 return false; 497 } 498 result.AppendMessage(*home); 499 result.SetStatus(eReturnStatusSuccessFinishResult); 500 return true; 501 } 502 case eReproducerProviderCommands: { 503 std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader = 504 repro::MultiLoader<repro::CommandProvider>::Create(loader); 505 if (!multi_loader) { 506 SetError(result, 507 make_error<StringError>("Unable to create command loader.", 508 llvm::inconvertibleErrorCode())); 509 return false; 510 } 511 512 // Iterate over the command files and dump them. 513 llvm::Optional<std::string> command_file; 514 while ((command_file = multi_loader->GetNextFile())) { 515 if (!command_file) 516 break; 517 518 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file); 519 if (auto err = command_buffer.getError()) { 520 SetError(result, errorCodeToError(err)); 521 return false; 522 } 523 result.AppendMessage((*command_buffer)->getBuffer()); 524 } 525 526 result.SetStatus(eReturnStatusSuccessFinishResult); 527 return true; 528 } 529 case eReproducerProviderGDB: { 530 std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>> 531 multi_loader = 532 repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader); 533 534 if (!multi_loader) { 535 SetError(result, 536 make_error<StringError>("Unable to create GDB loader.", 537 llvm::inconvertibleErrorCode())); 538 return false; 539 } 540 541 llvm::Optional<std::string> gdb_file; 542 while ((gdb_file = multi_loader->GetNextFile())) { 543 if (llvm::Expected<std::vector<GDBRemotePacket>> packets = 544 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) { 545 for (GDBRemotePacket &packet : *packets) { 546 packet.Dump(result.GetOutputStream()); 547 } 548 } else { 549 SetError(result, packets.takeError()); 550 return false; 551 } 552 } 553 554 result.SetStatus(eReturnStatusSuccessFinishResult); 555 return true; 556 } 557 case eReproducerProviderProcessInfo: { 558 std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>> 559 multi_loader = 560 repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader); 561 562 if (!multi_loader) { 563 SetError(result, make_error<StringError>( 564 llvm::inconvertibleErrorCode(), 565 "Unable to create process info loader.")); 566 return false; 567 } 568 569 llvm::Optional<std::string> process_file; 570 while ((process_file = multi_loader->GetNextFile())) { 571 if (llvm::Expected<ProcessInstanceInfoList> infos = 572 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) { 573 for (ProcessInstanceInfo info : *infos) 574 info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver()); 575 } else { 576 SetError(result, infos.takeError()); 577 return false; 578 } 579 } 580 581 result.SetStatus(eReturnStatusSuccessFinishResult); 582 return true; 583 } 584 case eReproducerProviderNone: 585 result.SetError("No valid provider specified."); 586 return false; 587 } 588 589 result.SetStatus(eReturnStatusSuccessFinishNoResult); 590 return result.Succeeded(); 591 } 592 593 private: 594 CommandOptions m_options; 595 }; 596 597 class CommandObjectReproducerVerify : public CommandObjectParsed { 598 public: 599 CommandObjectReproducerVerify(CommandInterpreter &interpreter) 600 : CommandObjectParsed(interpreter, "reproducer verify", 601 "Verify the contents of a reproducer. " 602 "If no reproducer is specified during replay, it " 603 "verifies the content of the current reproducer.", 604 nullptr) {} 605 606 ~CommandObjectReproducerVerify() override = default; 607 608 Options *GetOptions() override { return &m_options; } 609 610 class CommandOptions : public Options { 611 public: 612 CommandOptions() : Options(), file() {} 613 614 ~CommandOptions() override = default; 615 616 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 617 ExecutionContext *execution_context) override { 618 Status error; 619 const int short_option = m_getopt_table[option_idx].val; 620 621 switch (short_option) { 622 case 'f': 623 file.SetFile(option_arg, FileSpec::Style::native); 624 FileSystem::Instance().Resolve(file); 625 break; 626 default: 627 llvm_unreachable("Unimplemented option"); 628 } 629 630 return error; 631 } 632 633 void OptionParsingStarting(ExecutionContext *execution_context) override { 634 file.Clear(); 635 } 636 637 ArrayRef<OptionDefinition> GetDefinitions() override { 638 return makeArrayRef(g_reproducer_verify_options); 639 } 640 641 FileSpec file; 642 }; 643 644 protected: 645 bool DoExecute(Args &command, CommandReturnObject &result) override { 646 if (!command.empty()) { 647 result.AppendErrorWithFormat("'%s' takes no arguments", 648 m_cmd_name.c_str()); 649 return false; 650 } 651 652 llvm::Optional<Loader> loader_storage; 653 Loader *loader = 654 GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file); 655 if (!loader) 656 return false; 657 658 bool errors = false; 659 auto error_callback = [&](llvm::StringRef error) { 660 errors = true; 661 result.AppendError(error); 662 }; 663 664 bool warnings = false; 665 auto warning_callback = [&](llvm::StringRef warning) { 666 warnings = true; 667 result.AppendWarning(warning); 668 }; 669 670 auto note_callback = [&](llvm::StringRef warning) { 671 result.AppendMessage(warning); 672 }; 673 674 Verifier verifier(loader); 675 verifier.Verify(error_callback, warning_callback, note_callback); 676 677 if (warnings || errors) { 678 result.AppendMessage("reproducer verification failed"); 679 result.SetStatus(eReturnStatusFailed); 680 } else { 681 result.AppendMessage("reproducer verification succeeded"); 682 result.SetStatus(eReturnStatusSuccessFinishResult); 683 } 684 685 return result.Succeeded(); 686 } 687 688 private: 689 CommandOptions m_options; 690 }; 691 692 CommandObjectReproducer::CommandObjectReproducer( 693 CommandInterpreter &interpreter) 694 : CommandObjectMultiword( 695 interpreter, "reproducer", 696 "Commands for manipulating reproducers. Reproducers make it " 697 "possible " 698 "to capture full debug sessions with all its dependencies. The " 699 "resulting reproducer is used to replay the debug session while " 700 "debugging the debugger.\n" 701 "Because reproducers need the whole the debug session from " 702 "beginning to end, you need to launch the debugger in capture or " 703 "replay mode, commonly though the command line driver.\n" 704 "Reproducers are unrelated record-replay debugging, as you cannot " 705 "interact with the debugger during replay.\n", 706 "reproducer <subcommand> [<subcommand-options>]") { 707 LoadSubCommand( 708 "generate", 709 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter))); 710 LoadSubCommand("status", CommandObjectSP( 711 new CommandObjectReproducerStatus(interpreter))); 712 LoadSubCommand("dump", 713 CommandObjectSP(new CommandObjectReproducerDump(interpreter))); 714 LoadSubCommand("verify", CommandObjectSP( 715 new CommandObjectReproducerVerify(interpreter))); 716 LoadSubCommand("xcrash", CommandObjectSP( 717 new CommandObjectReproducerXCrash(interpreter))); 718 } 719 720 CommandObjectReproducer::~CommandObjectReproducer() = default; 721