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