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