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 if (llvm::Error e = repro::Finalize(r.GetReproducerPath())) { 196 SetError(result, std::move(e)); 197 return result.Succeeded(); 198 } 199 } else if (r.IsReplaying()) { 200 // Make this operation a NO-OP in replay mode. 201 result.SetStatus(eReturnStatusSuccessFinishNoResult); 202 return result.Succeeded(); 203 } else { 204 result.AppendErrorWithFormat("Unable to get the reproducer generator"); 205 result.SetStatus(eReturnStatusFailed); 206 return false; 207 } 208 209 result.GetOutputStream() 210 << "Reproducer written to '" << r.GetReproducerPath() << "'\n"; 211 result.GetOutputStream() 212 << "Please have a look at the directory to assess if you're willing to " 213 "share the contained information.\n"; 214 215 m_interpreter.BroadcastEvent( 216 CommandInterpreter::eBroadcastBitQuitCommandReceived); 217 result.SetStatus(eReturnStatusQuit); 218 return result.Succeeded(); 219 } 220 }; 221 222 class CommandObjectReproducerXCrash : public CommandObjectParsed { 223 public: 224 CommandObjectReproducerXCrash(CommandInterpreter &interpreter) 225 : CommandObjectParsed(interpreter, "reproducer xcrash", 226 "Intentionally force the debugger to crash in " 227 "order to trigger and test reproducer generation.", 228 nullptr) {} 229 230 ~CommandObjectReproducerXCrash() override = default; 231 232 Options *GetOptions() override { return &m_options; } 233 234 class CommandOptions : public Options { 235 public: 236 CommandOptions() : Options() {} 237 238 ~CommandOptions() override = default; 239 240 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 241 ExecutionContext *execution_context) override { 242 Status error; 243 const int short_option = m_getopt_table[option_idx].val; 244 245 switch (short_option) { 246 case 's': 247 signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum( 248 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 249 if (!error.Success()) 250 error.SetErrorStringWithFormat("unrecognized value for signal '%s'", 251 option_arg.str().c_str()); 252 break; 253 default: 254 llvm_unreachable("Unimplemented option"); 255 } 256 257 return error; 258 } 259 260 void OptionParsingStarting(ExecutionContext *execution_context) override { 261 signal = eReproducerCrashSigsegv; 262 } 263 264 ArrayRef<OptionDefinition> GetDefinitions() override { 265 return makeArrayRef(g_reproducer_xcrash_options); 266 } 267 268 ReproducerCrashSignal signal = eReproducerCrashSigsegv; 269 }; 270 271 protected: 272 bool DoExecute(Args &command, CommandReturnObject &result) override { 273 if (!command.empty()) { 274 result.AppendErrorWithFormat("'%s' takes no arguments", 275 m_cmd_name.c_str()); 276 return false; 277 } 278 279 auto &r = Reproducer::Instance(); 280 281 if (!r.IsCapturing() && !r.IsReplaying()) { 282 result.SetError( 283 "forcing a crash is only supported when capturing a reproducer."); 284 result.SetStatus(eReturnStatusSuccessFinishNoResult); 285 return false; 286 } 287 288 switch (m_options.signal) { 289 case eReproducerCrashSigill: 290 std::raise(SIGILL); 291 break; 292 case eReproducerCrashSigsegv: 293 std::raise(SIGSEGV); 294 break; 295 } 296 297 result.SetStatus(eReturnStatusQuit); 298 return result.Succeeded(); 299 } 300 301 private: 302 CommandOptions m_options; 303 }; 304 305 class CommandObjectReproducerStatus : public CommandObjectParsed { 306 public: 307 CommandObjectReproducerStatus(CommandInterpreter &interpreter) 308 : CommandObjectParsed( 309 interpreter, "reproducer status", 310 "Show the current reproducer status. In capture mode the " 311 "debugger " 312 "is collecting all the information it needs to create a " 313 "reproducer. In replay mode the reproducer is replaying a " 314 "reproducer. When the reproducers are off, no data is collected " 315 "and no reproducer can be generated.", 316 nullptr) {} 317 318 ~CommandObjectReproducerStatus() override = default; 319 320 protected: 321 bool DoExecute(Args &command, CommandReturnObject &result) override { 322 if (!command.empty()) { 323 result.AppendErrorWithFormat("'%s' takes no arguments", 324 m_cmd_name.c_str()); 325 return false; 326 } 327 328 auto &r = Reproducer::Instance(); 329 if (r.IsCapturing()) { 330 result.GetOutputStream() << "Reproducer is in capture mode.\n"; 331 } else if (r.IsReplaying()) { 332 result.GetOutputStream() << "Reproducer is in replay mode.\n"; 333 } else { 334 result.GetOutputStream() << "Reproducer is off.\n"; 335 } 336 337 if (r.IsCapturing() || r.IsReplaying()) { 338 result.GetOutputStream() 339 << "Path: " << r.GetReproducerPath().GetPath() << '\n'; 340 } 341 342 // Auto generate is hidden unless enabled because this is mostly for 343 // development and testing. 344 if (Generator *g = r.GetGenerator()) { 345 if (g->IsAutoGenerate()) 346 result.GetOutputStream() << "Auto generate: on\n"; 347 } 348 349 result.SetStatus(eReturnStatusSuccessFinishResult); 350 return result.Succeeded(); 351 } 352 }; 353 354 class CommandObjectReproducerDump : public CommandObjectParsed { 355 public: 356 CommandObjectReproducerDump(CommandInterpreter &interpreter) 357 : CommandObjectParsed(interpreter, "reproducer dump", 358 "Dump the information contained in a reproducer. " 359 "If no reproducer is specified during replay, it " 360 "dumps the content of the current reproducer.", 361 nullptr) {} 362 363 ~CommandObjectReproducerDump() override = default; 364 365 Options *GetOptions() override { return &m_options; } 366 367 class CommandOptions : public Options { 368 public: 369 CommandOptions() : Options(), file() {} 370 371 ~CommandOptions() override = default; 372 373 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 374 ExecutionContext *execution_context) override { 375 Status error; 376 const int short_option = m_getopt_table[option_idx].val; 377 378 switch (short_option) { 379 case 'f': 380 file.SetFile(option_arg, FileSpec::Style::native); 381 FileSystem::Instance().Resolve(file); 382 break; 383 case 'p': 384 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum( 385 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 386 if (!error.Success()) 387 error.SetErrorStringWithFormat("unrecognized value for provider '%s'", 388 option_arg.str().c_str()); 389 break; 390 default: 391 llvm_unreachable("Unimplemented option"); 392 } 393 394 return error; 395 } 396 397 void OptionParsingStarting(ExecutionContext *execution_context) override { 398 file.Clear(); 399 provider = eReproducerProviderNone; 400 } 401 402 ArrayRef<OptionDefinition> GetDefinitions() override { 403 return makeArrayRef(g_reproducer_dump_options); 404 } 405 406 FileSpec file; 407 ReproducerProvider provider = eReproducerProviderNone; 408 }; 409 410 protected: 411 bool DoExecute(Args &command, CommandReturnObject &result) override { 412 if (!command.empty()) { 413 result.AppendErrorWithFormat("'%s' takes no arguments", 414 m_cmd_name.c_str()); 415 return false; 416 } 417 418 llvm::Optional<Loader> loader_storage; 419 Loader *loader = 420 GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file); 421 if (!loader) 422 return false; 423 424 switch (m_options.provider) { 425 case eReproducerProviderFiles: { 426 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>(); 427 428 // Read the VFS mapping. 429 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer = 430 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath()); 431 if (!buffer) { 432 SetError(result, errorCodeToError(buffer.getError())); 433 return false; 434 } 435 436 // Initialize a VFS from the given mapping. 437 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML( 438 std::move(buffer.get()), nullptr, vfs_mapping.GetPath()); 439 440 // Dump the VFS to a buffer. 441 std::string str; 442 raw_string_ostream os(str); 443 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os); 444 os.flush(); 445 446 // Return the string. 447 result.AppendMessage(str); 448 result.SetStatus(eReturnStatusSuccessFinishResult); 449 return true; 450 } 451 case eReproducerProviderSymbolFiles: { 452 Expected<std::string> symbol_files = 453 loader->LoadBuffer<SymbolFileProvider>(); 454 if (!symbol_files) { 455 SetError(result, symbol_files.takeError()); 456 return false; 457 } 458 459 std::vector<SymbolFileProvider::Entry> entries; 460 llvm::yaml::Input yin(*symbol_files); 461 yin >> entries; 462 463 for (const auto &entry : entries) { 464 result.AppendMessageWithFormat("- uuid: %s\n", 465 entry.uuid.c_str()); 466 result.AppendMessageWithFormat(" module path: %s\n", 467 entry.module_path.c_str()); 468 result.AppendMessageWithFormat(" symbol path: %s\n", 469 entry.symbol_path.c_str()); 470 } 471 result.SetStatus(eReturnStatusSuccessFinishResult); 472 return true; 473 } 474 case eReproducerProviderVersion: { 475 Expected<std::string> version = loader->LoadBuffer<VersionProvider>(); 476 if (!version) { 477 SetError(result, version.takeError()); 478 return false; 479 } 480 result.AppendMessage(*version); 481 result.SetStatus(eReturnStatusSuccessFinishResult); 482 return true; 483 } 484 case eReproducerProviderWorkingDirectory: { 485 Expected<std::string> cwd = 486 repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader); 487 if (!cwd) { 488 SetError(result, cwd.takeError()); 489 return false; 490 } 491 result.AppendMessage(*cwd); 492 result.SetStatus(eReturnStatusSuccessFinishResult); 493 return true; 494 } 495 case eReproducerProviderHomeDirectory: { 496 Expected<std::string> home = 497 repro::GetDirectoryFrom<HomeDirectoryProvider>(loader); 498 if (!home) { 499 SetError(result, home.takeError()); 500 return false; 501 } 502 result.AppendMessage(*home); 503 result.SetStatus(eReturnStatusSuccessFinishResult); 504 return true; 505 } 506 case eReproducerProviderCommands: { 507 std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader = 508 repro::MultiLoader<repro::CommandProvider>::Create(loader); 509 if (!multi_loader) { 510 SetError(result, 511 make_error<StringError>("Unable to create command loader.", 512 llvm::inconvertibleErrorCode())); 513 return false; 514 } 515 516 // Iterate over the command files and dump them. 517 llvm::Optional<std::string> command_file; 518 while ((command_file = multi_loader->GetNextFile())) { 519 if (!command_file) 520 break; 521 522 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file); 523 if (auto err = command_buffer.getError()) { 524 SetError(result, errorCodeToError(err)); 525 return false; 526 } 527 result.AppendMessage((*command_buffer)->getBuffer()); 528 } 529 530 result.SetStatus(eReturnStatusSuccessFinishResult); 531 return true; 532 } 533 case eReproducerProviderGDB: { 534 std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>> 535 multi_loader = 536 repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader); 537 538 if (!multi_loader) { 539 SetError(result, 540 make_error<StringError>("Unable to create GDB loader.", 541 llvm::inconvertibleErrorCode())); 542 return false; 543 } 544 545 llvm::Optional<std::string> gdb_file; 546 while ((gdb_file = multi_loader->GetNextFile())) { 547 if (llvm::Expected<std::vector<GDBRemotePacket>> packets = 548 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) { 549 for (GDBRemotePacket &packet : *packets) { 550 packet.Dump(result.GetOutputStream()); 551 } 552 } else { 553 SetError(result, packets.takeError()); 554 return false; 555 } 556 } 557 558 result.SetStatus(eReturnStatusSuccessFinishResult); 559 return true; 560 } 561 case eReproducerProviderProcessInfo: { 562 std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>> 563 multi_loader = 564 repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader); 565 566 if (!multi_loader) { 567 SetError(result, make_error<StringError>( 568 llvm::inconvertibleErrorCode(), 569 "Unable to create process info loader.")); 570 return false; 571 } 572 573 llvm::Optional<std::string> process_file; 574 while ((process_file = multi_loader->GetNextFile())) { 575 if (llvm::Expected<ProcessInstanceInfoList> infos = 576 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) { 577 for (ProcessInstanceInfo info : *infos) 578 info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver()); 579 } else { 580 SetError(result, infos.takeError()); 581 return false; 582 } 583 } 584 585 result.SetStatus(eReturnStatusSuccessFinishResult); 586 return true; 587 } 588 case eReproducerProviderNone: 589 result.SetError("No valid provider specified."); 590 return false; 591 } 592 593 result.SetStatus(eReturnStatusSuccessFinishNoResult); 594 return result.Succeeded(); 595 } 596 597 private: 598 CommandOptions m_options; 599 }; 600 601 class CommandObjectReproducerVerify : public CommandObjectParsed { 602 public: 603 CommandObjectReproducerVerify(CommandInterpreter &interpreter) 604 : CommandObjectParsed(interpreter, "reproducer verify", 605 "Verify the contents of a reproducer. " 606 "If no reproducer is specified during replay, it " 607 "verifies the content of the current reproducer.", 608 nullptr) {} 609 610 ~CommandObjectReproducerVerify() override = default; 611 612 Options *GetOptions() override { return &m_options; } 613 614 class CommandOptions : public Options { 615 public: 616 CommandOptions() : Options(), file() {} 617 618 ~CommandOptions() override = default; 619 620 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 621 ExecutionContext *execution_context) override { 622 Status error; 623 const int short_option = m_getopt_table[option_idx].val; 624 625 switch (short_option) { 626 case 'f': 627 file.SetFile(option_arg, FileSpec::Style::native); 628 FileSystem::Instance().Resolve(file); 629 break; 630 default: 631 llvm_unreachable("Unimplemented option"); 632 } 633 634 return error; 635 } 636 637 void OptionParsingStarting(ExecutionContext *execution_context) override { 638 file.Clear(); 639 } 640 641 ArrayRef<OptionDefinition> GetDefinitions() override { 642 return makeArrayRef(g_reproducer_verify_options); 643 } 644 645 FileSpec file; 646 }; 647 648 protected: 649 bool DoExecute(Args &command, CommandReturnObject &result) override { 650 if (!command.empty()) { 651 result.AppendErrorWithFormat("'%s' takes no arguments", 652 m_cmd_name.c_str()); 653 return false; 654 } 655 656 llvm::Optional<Loader> loader_storage; 657 Loader *loader = 658 GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file); 659 if (!loader) 660 return false; 661 662 bool errors = false; 663 auto error_callback = [&](llvm::StringRef error) { 664 errors = true; 665 result.AppendError(error); 666 }; 667 668 bool warnings = false; 669 auto warning_callback = [&](llvm::StringRef warning) { 670 warnings = true; 671 result.AppendWarning(warning); 672 }; 673 674 auto note_callback = [&](llvm::StringRef warning) { 675 result.AppendMessage(warning); 676 }; 677 678 Verifier verifier(loader); 679 verifier.Verify(error_callback, warning_callback, note_callback); 680 681 if (warnings || errors) { 682 result.AppendMessage("reproducer verification failed"); 683 result.SetStatus(eReturnStatusFailed); 684 } else { 685 result.AppendMessage("reproducer verification succeeded"); 686 result.SetStatus(eReturnStatusSuccessFinishResult); 687 } 688 689 return result.Succeeded(); 690 } 691 692 private: 693 CommandOptions m_options; 694 }; 695 696 CommandObjectReproducer::CommandObjectReproducer( 697 CommandInterpreter &interpreter) 698 : CommandObjectMultiword( 699 interpreter, "reproducer", 700 "Commands for manipulating reproducers. Reproducers make it " 701 "possible " 702 "to capture full debug sessions with all its dependencies. The " 703 "resulting reproducer is used to replay the debug session while " 704 "debugging the debugger.\n" 705 "Because reproducers need the whole the debug session from " 706 "beginning to end, you need to launch the debugger in capture or " 707 "replay mode, commonly though the command line driver.\n" 708 "Reproducers are unrelated record-replay debugging, as you cannot " 709 "interact with the debugger during replay.\n", 710 "reproducer <subcommand> [<subcommand-options>]") { 711 LoadSubCommand( 712 "generate", 713 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter))); 714 LoadSubCommand("status", CommandObjectSP( 715 new CommandObjectReproducerStatus(interpreter))); 716 LoadSubCommand("dump", 717 CommandObjectSP(new CommandObjectReproducerDump(interpreter))); 718 LoadSubCommand("verify", CommandObjectSP( 719 new CommandObjectReproducerVerify(interpreter))); 720 LoadSubCommand("xcrash", CommandObjectSP( 721 new CommandObjectReproducerXCrash(interpreter))); 722 } 723 724 CommandObjectReproducer::~CommandObjectReproducer() = default; 725