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