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 eReproducerProviderGDB, 31 eReproducerProviderProcessInfo, 32 eReproducerProviderVersion, 33 eReproducerProviderWorkingDirectory, 34 eReproducerProviderHomeDirectory, 35 eReproducerProviderNone 36 }; 37 38 static constexpr OptionEnumValueElement g_reproducer_provider_type[] = { 39 { 40 eReproducerProviderCommands, 41 "commands", 42 "Command Interpreter Commands", 43 }, 44 { 45 eReproducerProviderFiles, 46 "files", 47 "Files", 48 }, 49 { 50 eReproducerProviderGDB, 51 "gdb", 52 "GDB Remote Packets", 53 }, 54 { 55 eReproducerProviderProcessInfo, 56 "processes", 57 "Process Info", 58 }, 59 { 60 eReproducerProviderVersion, 61 "version", 62 "Version", 63 }, 64 { 65 eReproducerProviderWorkingDirectory, 66 "cwd", 67 "Working Directory", 68 }, 69 { 70 eReproducerProviderHomeDirectory, 71 "home", 72 "Home Directory", 73 }, 74 { 75 eReproducerProviderNone, 76 "none", 77 "None", 78 }, 79 }; 80 81 static constexpr OptionEnumValues ReproducerProviderType() { 82 return OptionEnumValues(g_reproducer_provider_type); 83 } 84 85 #define LLDB_OPTIONS_reproducer_dump 86 #include "CommandOptions.inc" 87 88 enum ReproducerCrashSignal { 89 eReproducerCrashSigill, 90 eReproducerCrashSigsegv, 91 }; 92 93 static constexpr OptionEnumValueElement g_reproducer_signaltype[] = { 94 { 95 eReproducerCrashSigill, 96 "SIGILL", 97 "Illegal instruction", 98 }, 99 { 100 eReproducerCrashSigsegv, 101 "SIGSEGV", 102 "Segmentation fault", 103 }, 104 }; 105 106 static constexpr OptionEnumValues ReproducerSignalType() { 107 return OptionEnumValues(g_reproducer_signaltype); 108 } 109 110 #define LLDB_OPTIONS_reproducer_xcrash 111 #include "CommandOptions.inc" 112 113 template <typename T> 114 llvm::Expected<T> static ReadFromYAML(StringRef filename) { 115 auto error_or_file = MemoryBuffer::getFile(filename); 116 if (auto err = error_or_file.getError()) { 117 return errorCodeToError(err); 118 } 119 120 T t; 121 yaml::Input yin((*error_or_file)->getBuffer()); 122 yin >> t; 123 124 if (auto err = yin.error()) { 125 return errorCodeToError(err); 126 } 127 128 return t; 129 } 130 131 class CommandObjectReproducerGenerate : public CommandObjectParsed { 132 public: 133 CommandObjectReproducerGenerate(CommandInterpreter &interpreter) 134 : CommandObjectParsed( 135 interpreter, "reproducer generate", 136 "Generate reproducer on disk. When the debugger is in capture " 137 "mode, this command will output the reproducer to a directory on " 138 "disk and quit. In replay mode this command in a no-op.", 139 nullptr) {} 140 141 ~CommandObjectReproducerGenerate() override = default; 142 143 protected: 144 bool DoExecute(Args &command, CommandReturnObject &result) override { 145 if (!command.empty()) { 146 result.AppendErrorWithFormat("'%s' takes no arguments", 147 m_cmd_name.c_str()); 148 return false; 149 } 150 151 auto &r = Reproducer::Instance(); 152 if (auto generator = r.GetGenerator()) { 153 generator->Keep(); 154 } else if (r.IsReplaying()) { 155 // Make this operation a NO-OP in replay mode. 156 result.SetStatus(eReturnStatusSuccessFinishNoResult); 157 return result.Succeeded(); 158 } else { 159 result.AppendErrorWithFormat("Unable to get the reproducer generator"); 160 result.SetStatus(eReturnStatusFailed); 161 return false; 162 } 163 164 result.GetOutputStream() 165 << "Reproducer written to '" << r.GetReproducerPath() << "'\n"; 166 result.GetOutputStream() 167 << "Please have a look at the directory to assess if you're willing to " 168 "share the contained information.\n"; 169 170 m_interpreter.BroadcastEvent( 171 CommandInterpreter::eBroadcastBitQuitCommandReceived); 172 result.SetStatus(eReturnStatusQuit); 173 return result.Succeeded(); 174 } 175 }; 176 177 class CommandObjectReproducerXCrash : public CommandObjectParsed { 178 public: 179 CommandObjectReproducerXCrash(CommandInterpreter &interpreter) 180 : CommandObjectParsed(interpreter, "reproducer xcrash", 181 "Intentionally force the debugger to crash in " 182 "order to trigger and test reproducer generation.", 183 nullptr) {} 184 185 ~CommandObjectReproducerXCrash() override = default; 186 187 Options *GetOptions() override { return &m_options; } 188 189 class CommandOptions : public Options { 190 public: 191 CommandOptions() : Options() {} 192 193 ~CommandOptions() override = default; 194 195 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 196 ExecutionContext *execution_context) override { 197 Status error; 198 const int short_option = m_getopt_table[option_idx].val; 199 200 switch (short_option) { 201 case 's': 202 signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum( 203 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 204 if (!error.Success()) 205 error.SetErrorStringWithFormat("unrecognized value for signal '%s'", 206 option_arg.str().c_str()); 207 break; 208 default: 209 llvm_unreachable("Unimplemented option"); 210 } 211 212 return error; 213 } 214 215 void OptionParsingStarting(ExecutionContext *execution_context) override { 216 signal = eReproducerCrashSigsegv; 217 } 218 219 ArrayRef<OptionDefinition> GetDefinitions() override { 220 return makeArrayRef(g_reproducer_xcrash_options); 221 } 222 223 ReproducerCrashSignal signal = eReproducerCrashSigsegv; 224 }; 225 226 protected: 227 bool DoExecute(Args &command, CommandReturnObject &result) override { 228 if (!command.empty()) { 229 result.AppendErrorWithFormat("'%s' takes no arguments", 230 m_cmd_name.c_str()); 231 return false; 232 } 233 234 auto &r = Reproducer::Instance(); 235 236 if (!r.IsCapturing() && !r.IsReplaying()) { 237 result.SetError( 238 "forcing a crash is only supported when capturing a reproducer."); 239 result.SetStatus(eReturnStatusSuccessFinishNoResult); 240 return false; 241 } 242 243 switch (m_options.signal) { 244 case eReproducerCrashSigill: 245 std::raise(SIGILL); 246 break; 247 case eReproducerCrashSigsegv: 248 std::raise(SIGSEGV); 249 break; 250 } 251 252 result.SetStatus(eReturnStatusQuit); 253 return result.Succeeded(); 254 } 255 256 private: 257 CommandOptions m_options; 258 }; 259 260 class CommandObjectReproducerStatus : public CommandObjectParsed { 261 public: 262 CommandObjectReproducerStatus(CommandInterpreter &interpreter) 263 : CommandObjectParsed( 264 interpreter, "reproducer status", 265 "Show the current reproducer status. In capture mode the " 266 "debugger " 267 "is collecting all the information it needs to create a " 268 "reproducer. In replay mode the reproducer is replaying a " 269 "reproducer. When the reproducers are off, no data is collected " 270 "and no reproducer can be generated.", 271 nullptr) {} 272 273 ~CommandObjectReproducerStatus() override = default; 274 275 protected: 276 bool DoExecute(Args &command, CommandReturnObject &result) override { 277 if (!command.empty()) { 278 result.AppendErrorWithFormat("'%s' takes no arguments", 279 m_cmd_name.c_str()); 280 return false; 281 } 282 283 auto &r = Reproducer::Instance(); 284 if (r.IsCapturing()) { 285 result.GetOutputStream() << "Reproducer is in capture mode.\n"; 286 } else if (r.IsReplaying()) { 287 result.GetOutputStream() << "Reproducer is in replay mode.\n"; 288 } else { 289 result.GetOutputStream() << "Reproducer is off.\n"; 290 } 291 292 if (r.IsCapturing() || r.IsReplaying()) { 293 result.GetOutputStream() 294 << "Path: " << r.GetReproducerPath().GetPath() << '\n'; 295 } 296 297 // Auto generate is hidden unless enabled because this is mostly for 298 // development and testing. 299 if (Generator *g = r.GetGenerator()) { 300 if (g->IsAutoGenerate()) 301 result.GetOutputStream() << "Auto generate: on\n"; 302 } 303 304 result.SetStatus(eReturnStatusSuccessFinishResult); 305 return result.Succeeded(); 306 } 307 }; 308 309 static void SetError(CommandReturnObject &result, Error err) { 310 result.GetErrorStream().Printf("error: %s\n", 311 toString(std::move(err)).c_str()); 312 result.SetStatus(eReturnStatusFailed); 313 } 314 315 class CommandObjectReproducerDump : public CommandObjectParsed { 316 public: 317 CommandObjectReproducerDump(CommandInterpreter &interpreter) 318 : CommandObjectParsed(interpreter, "reproducer dump", 319 "Dump the information contained in a reproducer. " 320 "If no reproducer is specified during replay, it " 321 "dumps the content of the current reproducer.", 322 nullptr) {} 323 324 ~CommandObjectReproducerDump() override = default; 325 326 Options *GetOptions() override { return &m_options; } 327 328 class CommandOptions : public Options { 329 public: 330 CommandOptions() : Options(), file() {} 331 332 ~CommandOptions() override = default; 333 334 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 335 ExecutionContext *execution_context) override { 336 Status error; 337 const int short_option = m_getopt_table[option_idx].val; 338 339 switch (short_option) { 340 case 'f': 341 file.SetFile(option_arg, FileSpec::Style::native); 342 FileSystem::Instance().Resolve(file); 343 break; 344 case 'p': 345 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum( 346 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 347 if (!error.Success()) 348 error.SetErrorStringWithFormat("unrecognized value for provider '%s'", 349 option_arg.str().c_str()); 350 break; 351 default: 352 llvm_unreachable("Unimplemented option"); 353 } 354 355 return error; 356 } 357 358 void OptionParsingStarting(ExecutionContext *execution_context) override { 359 file.Clear(); 360 provider = eReproducerProviderNone; 361 } 362 363 ArrayRef<OptionDefinition> GetDefinitions() override { 364 return makeArrayRef(g_reproducer_dump_options); 365 } 366 367 FileSpec file; 368 ReproducerProvider provider = eReproducerProviderNone; 369 }; 370 371 protected: 372 bool DoExecute(Args &command, CommandReturnObject &result) override { 373 if (!command.empty()) { 374 result.AppendErrorWithFormat("'%s' takes no arguments", 375 m_cmd_name.c_str()); 376 return false; 377 } 378 379 // If no reproducer path is specified, use the loader currently used for 380 // replay. Otherwise create a new loader just for dumping. 381 llvm::Optional<Loader> loader_storage; 382 Loader *loader = nullptr; 383 if (!m_options.file) { 384 loader = Reproducer::Instance().GetLoader(); 385 if (loader == nullptr) { 386 result.SetError( 387 "Not specifying a reproducer is only support during replay."); 388 result.SetStatus(eReturnStatusSuccessFinishNoResult); 389 return false; 390 } 391 } else { 392 loader_storage.emplace(m_options.file); 393 loader = &(*loader_storage); 394 if (Error err = loader->LoadIndex()) { 395 SetError(result, std::move(err)); 396 return false; 397 } 398 } 399 400 // If we get here we should have a valid loader. 401 assert(loader); 402 403 switch (m_options.provider) { 404 case eReproducerProviderFiles: { 405 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>(); 406 407 // Read the VFS mapping. 408 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer = 409 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath()); 410 if (!buffer) { 411 SetError(result, errorCodeToError(buffer.getError())); 412 return false; 413 } 414 415 // Initialize a VFS from the given mapping. 416 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML( 417 std::move(buffer.get()), nullptr, vfs_mapping.GetPath()); 418 419 // Dump the VFS to a buffer. 420 std::string str; 421 raw_string_ostream os(str); 422 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os); 423 os.flush(); 424 425 // Return the string. 426 result.AppendMessage(str); 427 result.SetStatus(eReturnStatusSuccessFinishResult); 428 return true; 429 } 430 case eReproducerProviderVersion: { 431 Expected<std::string> version = loader->LoadBuffer<VersionProvider>(); 432 if (!version) { 433 SetError(result, version.takeError()); 434 return false; 435 } 436 result.AppendMessage(*version); 437 result.SetStatus(eReturnStatusSuccessFinishResult); 438 return true; 439 } 440 case eReproducerProviderWorkingDirectory: { 441 Expected<std::string> cwd = 442 repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader); 443 if (!cwd) { 444 SetError(result, cwd.takeError()); 445 return false; 446 } 447 result.AppendMessage(*cwd); 448 result.SetStatus(eReturnStatusSuccessFinishResult); 449 return true; 450 } 451 case eReproducerProviderHomeDirectory: { 452 Expected<std::string> home = 453 repro::GetDirectoryFrom<HomeDirectoryProvider>(loader); 454 if (!home) { 455 SetError(result, home.takeError()); 456 return false; 457 } 458 result.AppendMessage(*home); 459 result.SetStatus(eReturnStatusSuccessFinishResult); 460 return true; 461 } 462 case eReproducerProviderCommands: { 463 std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader = 464 repro::MultiLoader<repro::CommandProvider>::Create(loader); 465 if (!multi_loader) { 466 SetError(result, 467 make_error<StringError>("Unable to create command loader.", 468 llvm::inconvertibleErrorCode())); 469 return false; 470 } 471 472 // Iterate over the command files and dump them. 473 llvm::Optional<std::string> command_file; 474 while ((command_file = multi_loader->GetNextFile())) { 475 if (!command_file) 476 break; 477 478 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file); 479 if (auto err = command_buffer.getError()) { 480 SetError(result, errorCodeToError(err)); 481 return false; 482 } 483 result.AppendMessage((*command_buffer)->getBuffer()); 484 } 485 486 result.SetStatus(eReturnStatusSuccessFinishResult); 487 return true; 488 } 489 case eReproducerProviderGDB: { 490 std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>> 491 multi_loader = 492 repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader); 493 494 if (!multi_loader) { 495 SetError(result, 496 make_error<StringError>("Unable to create GDB loader.", 497 llvm::inconvertibleErrorCode())); 498 return false; 499 } 500 501 llvm::Optional<std::string> gdb_file; 502 while ((gdb_file = multi_loader->GetNextFile())) { 503 if (llvm::Expected<std::vector<GDBRemotePacket>> packets = 504 ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) { 505 for (GDBRemotePacket &packet : *packets) { 506 packet.Dump(result.GetOutputStream()); 507 } 508 } else { 509 SetError(result, packets.takeError()); 510 return false; 511 } 512 } 513 514 result.SetStatus(eReturnStatusSuccessFinishResult); 515 return true; 516 } 517 case eReproducerProviderProcessInfo: { 518 std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>> 519 multi_loader = 520 repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader); 521 522 if (!multi_loader) { 523 SetError(result, make_error<StringError>( 524 llvm::inconvertibleErrorCode(), 525 "Unable to create process info loader.")); 526 return false; 527 } 528 529 llvm::Optional<std::string> process_file; 530 while ((process_file = multi_loader->GetNextFile())) { 531 if (llvm::Expected<ProcessInstanceInfoList> infos = 532 ReadFromYAML<ProcessInstanceInfoList>(*process_file)) { 533 for (ProcessInstanceInfo info : *infos) 534 info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver()); 535 } else { 536 SetError(result, infos.takeError()); 537 return false; 538 } 539 } 540 541 result.SetStatus(eReturnStatusSuccessFinishResult); 542 return true; 543 } 544 case eReproducerProviderNone: 545 result.SetError("No valid provider specified."); 546 return false; 547 } 548 549 result.SetStatus(eReturnStatusSuccessFinishNoResult); 550 return result.Succeeded(); 551 } 552 553 private: 554 CommandOptions m_options; 555 }; 556 557 CommandObjectReproducer::CommandObjectReproducer( 558 CommandInterpreter &interpreter) 559 : CommandObjectMultiword( 560 interpreter, "reproducer", 561 "Commands for manipulating reproducers. Reproducers make it " 562 "possible " 563 "to capture full debug sessions with all its dependencies. The " 564 "resulting reproducer is used to replay the debug session while " 565 "debugging the debugger.\n" 566 "Because reproducers need the whole the debug session from " 567 "beginning to end, you need to launch the debugger in capture or " 568 "replay mode, commonly though the command line driver.\n" 569 "Reproducers are unrelated record-replay debugging, as you cannot " 570 "interact with the debugger during replay.\n", 571 "reproducer <subcommand> [<subcommand-options>]") { 572 LoadSubCommand( 573 "generate", 574 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter))); 575 LoadSubCommand("status", CommandObjectSP( 576 new CommandObjectReproducerStatus(interpreter))); 577 LoadSubCommand("dump", 578 CommandObjectSP(new CommandObjectReproducerDump(interpreter))); 579 LoadSubCommand("xcrash", CommandObjectSP( 580 new CommandObjectReproducerXCrash(interpreter))); 581 } 582 583 CommandObjectReproducer::~CommandObjectReproducer() = default; 584