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