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