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