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