1 //===-- Driver.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 "Driver.h" 10 11 #include "lldb/API/SBCommandInterpreter.h" 12 #include "lldb/API/SBCommandInterpreterRunOptions.h" 13 #include "lldb/API/SBCommandReturnObject.h" 14 #include "lldb/API/SBDebugger.h" 15 #include "lldb/API/SBFile.h" 16 #include "lldb/API/SBHostOS.h" 17 #include "lldb/API/SBLanguageRuntime.h" 18 #include "lldb/API/SBReproducer.h" 19 #include "lldb/API/SBStream.h" 20 #include "lldb/API/SBStringList.h" 21 #include "lldb/API/SBStructuredData.h" 22 23 #include "llvm/ADT/StringRef.h" 24 #include "llvm/Support/Format.h" 25 #include "llvm/Support/InitLLVM.h" 26 #include "llvm/Support/Path.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/Support/WithColor.h" 29 #include "llvm/Support/raw_ostream.h" 30 31 #include <algorithm> 32 #include <atomic> 33 #include <bitset> 34 #include <clocale> 35 #include <csignal> 36 #include <string> 37 #include <thread> 38 #include <utility> 39 40 #include <climits> 41 #include <cstdio> 42 #include <cstdlib> 43 #include <cstring> 44 #include <fcntl.h> 45 46 #if !defined(__APPLE__) 47 #include "llvm/Support/DataTypes.h" 48 #endif 49 50 using namespace lldb; 51 using namespace llvm; 52 53 namespace { 54 enum ID { 55 OPT_INVALID = 0, // This is not an option ID. 56 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 57 HELPTEXT, METAVAR, VALUES) \ 58 OPT_##ID, 59 #include "Options.inc" 60 #undef OPTION 61 }; 62 63 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 64 #include "Options.inc" 65 #undef PREFIX 66 67 const opt::OptTable::Info InfoTable[] = { 68 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 69 HELPTEXT, METAVAR, VALUES) \ 70 { \ 71 PREFIX, NAME, HELPTEXT, \ 72 METAVAR, OPT_##ID, opt::Option::KIND##Class, \ 73 PARAM, FLAGS, OPT_##GROUP, \ 74 OPT_##ALIAS, ALIASARGS, VALUES}, 75 #include "Options.inc" 76 #undef OPTION 77 }; 78 79 class LLDBOptTable : public opt::OptTable { 80 public: 81 LLDBOptTable() : OptTable(InfoTable) {} 82 }; 83 } // namespace 84 85 static void reset_stdin_termios(); 86 static bool g_old_stdin_termios_is_valid = false; 87 static struct termios g_old_stdin_termios; 88 89 static Driver *g_driver = nullptr; 90 91 // In the Driver::MainLoop, we change the terminal settings. This function is 92 // added as an atexit handler to make sure we clean them up. 93 static void reset_stdin_termios() { 94 if (g_old_stdin_termios_is_valid) { 95 g_old_stdin_termios_is_valid = false; 96 ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios); 97 } 98 } 99 100 Driver::Driver() 101 : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) { 102 // We want to be able to handle CTRL+D in the terminal to have it terminate 103 // certain input 104 m_debugger.SetCloseInputOnEOF(false); 105 g_driver = this; 106 } 107 108 Driver::~Driver() { 109 SBDebugger::Destroy(m_debugger); 110 g_driver = nullptr; 111 } 112 113 void Driver::OptionData::AddInitialCommand(std::string command, 114 CommandPlacement placement, 115 bool is_file, SBError &error) { 116 std::vector<InitialCmdEntry> *command_set; 117 switch (placement) { 118 case eCommandPlacementBeforeFile: 119 command_set = &(m_initial_commands); 120 break; 121 case eCommandPlacementAfterFile: 122 command_set = &(m_after_file_commands); 123 break; 124 case eCommandPlacementAfterCrash: 125 command_set = &(m_after_crash_commands); 126 break; 127 } 128 129 if (is_file) { 130 SBFileSpec file(command.c_str()); 131 if (file.Exists()) 132 command_set->push_back(InitialCmdEntry(command, is_file)); 133 else if (file.ResolveExecutableLocation()) { 134 char final_path[PATH_MAX]; 135 file.GetPath(final_path, sizeof(final_path)); 136 command_set->push_back(InitialCmdEntry(final_path, is_file)); 137 } else 138 error.SetErrorStringWithFormat( 139 "file specified in --source (-s) option doesn't exist: '%s'", 140 command.c_str()); 141 } else 142 command_set->push_back(InitialCmdEntry(command, is_file)); 143 } 144 145 void Driver::WriteCommandsForSourcing(CommandPlacement placement, 146 SBStream &strm) { 147 std::vector<OptionData::InitialCmdEntry> *command_set; 148 switch (placement) { 149 case eCommandPlacementBeforeFile: 150 command_set = &m_option_data.m_initial_commands; 151 break; 152 case eCommandPlacementAfterFile: 153 command_set = &m_option_data.m_after_file_commands; 154 break; 155 case eCommandPlacementAfterCrash: 156 command_set = &m_option_data.m_after_crash_commands; 157 break; 158 } 159 160 for (const auto &command_entry : *command_set) { 161 const char *command = command_entry.contents.c_str(); 162 if (command_entry.is_file) { 163 bool source_quietly = 164 m_option_data.m_source_quietly || command_entry.source_quietly; 165 strm.Printf("command source -s %i '%s'\n", 166 static_cast<int>(source_quietly), command); 167 } else 168 strm.Printf("%s\n", command); 169 } 170 } 171 172 // Check the arguments that were passed to this program to make sure they are 173 // valid and to get their argument values (if any). Return a boolean value 174 // indicating whether or not to start up the full debugger (i.e. the Command 175 // Interpreter) or not. Return FALSE if the arguments were invalid OR if the 176 // user only wanted help or version information. 177 SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) { 178 SBError error; 179 180 // This is kind of a pain, but since we make the debugger in the Driver's 181 // constructor, we can't know at that point whether we should read in init 182 // files yet. So we don't read them in in the Driver constructor, then set 183 // the flags back to "read them in" here, and then if we see the "-n" flag, 184 // we'll turn it off again. Finally we have to read them in by hand later in 185 // the main loop. 186 m_debugger.SkipLLDBInitFiles(false); 187 m_debugger.SkipAppInitFiles(false); 188 189 if (args.hasArg(OPT_version)) { 190 m_option_data.m_print_version = true; 191 } 192 193 if (args.hasArg(OPT_python_path)) { 194 m_option_data.m_print_python_path = true; 195 } 196 if (args.hasArg(OPT_print_script_interpreter_info)) { 197 m_option_data.m_print_script_interpreter_info = true; 198 } 199 200 if (args.hasArg(OPT_batch)) { 201 m_option_data.m_batch = true; 202 } 203 204 if (auto *arg = args.getLastArg(OPT_core)) { 205 auto arg_value = arg->getValue(); 206 SBFileSpec file(arg_value); 207 if (!file.Exists()) { 208 error.SetErrorStringWithFormat( 209 "file specified in --core (-c) option doesn't exist: '%s'", 210 arg_value); 211 return error; 212 } 213 m_option_data.m_core_file = arg_value; 214 } 215 216 if (args.hasArg(OPT_editor)) { 217 m_option_data.m_use_external_editor = true; 218 } 219 220 if (args.hasArg(OPT_no_lldbinit)) { 221 m_debugger.SkipLLDBInitFiles(true); 222 m_debugger.SkipAppInitFiles(true); 223 } 224 225 if (args.hasArg(OPT_local_lldbinit)) { 226 lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true", 227 m_debugger.GetInstanceName()); 228 } 229 230 if (args.hasArg(OPT_no_use_colors)) { 231 m_debugger.SetUseColor(false); 232 m_option_data.m_debug_mode = true; 233 } 234 235 if (auto *arg = args.getLastArg(OPT_file)) { 236 auto arg_value = arg->getValue(); 237 SBFileSpec file(arg_value); 238 if (file.Exists()) { 239 m_option_data.m_args.emplace_back(arg_value); 240 } else if (file.ResolveExecutableLocation()) { 241 char path[PATH_MAX]; 242 file.GetPath(path, sizeof(path)); 243 m_option_data.m_args.emplace_back(path); 244 } else { 245 error.SetErrorStringWithFormat( 246 "file specified in --file (-f) option doesn't exist: '%s'", 247 arg_value); 248 return error; 249 } 250 } 251 252 if (auto *arg = args.getLastArg(OPT_arch)) { 253 auto arg_value = arg->getValue(); 254 if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) { 255 error.SetErrorStringWithFormat( 256 "invalid architecture in the -a or --arch option: '%s'", arg_value); 257 return error; 258 } 259 } 260 261 if (auto *arg = args.getLastArg(OPT_script_language)) { 262 auto arg_value = arg->getValue(); 263 m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value)); 264 } 265 266 if (args.hasArg(OPT_source_quietly)) { 267 m_option_data.m_source_quietly = true; 268 } 269 270 if (auto *arg = args.getLastArg(OPT_attach_name)) { 271 auto arg_value = arg->getValue(); 272 m_option_data.m_process_name = arg_value; 273 } 274 275 if (args.hasArg(OPT_wait_for)) { 276 m_option_data.m_wait_for = true; 277 } 278 279 if (auto *arg = args.getLastArg(OPT_attach_pid)) { 280 auto arg_value = arg->getValue(); 281 char *remainder; 282 m_option_data.m_process_pid = strtol(arg_value, &remainder, 0); 283 if (remainder == arg_value || *remainder != '\0') { 284 error.SetErrorStringWithFormat( 285 "Could not convert process PID: \"%s\" into a pid.", arg_value); 286 return error; 287 } 288 } 289 290 if (auto *arg = args.getLastArg(OPT_repl_language)) { 291 auto arg_value = arg->getValue(); 292 m_option_data.m_repl_lang = 293 SBLanguageRuntime::GetLanguageTypeFromString(arg_value); 294 if (m_option_data.m_repl_lang == eLanguageTypeUnknown) { 295 error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"", 296 arg_value); 297 return error; 298 } 299 } 300 301 if (args.hasArg(OPT_repl)) { 302 m_option_data.m_repl = true; 303 } 304 305 if (auto *arg = args.getLastArg(OPT_repl_)) { 306 m_option_data.m_repl = true; 307 if (auto arg_value = arg->getValue()) 308 m_option_data.m_repl_options = arg_value; 309 } 310 311 // We need to process the options below together as their relative order 312 // matters. 313 for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash, 314 OPT_source, OPT_source_before_file, 315 OPT_one_line, OPT_one_line_before_file)) { 316 auto arg_value = arg->getValue(); 317 if (arg->getOption().matches(OPT_source_on_crash)) { 318 m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash, 319 true, error); 320 if (error.Fail()) 321 return error; 322 } 323 324 if (arg->getOption().matches(OPT_one_line_on_crash)) { 325 m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash, 326 false, error); 327 if (error.Fail()) 328 return error; 329 } 330 331 if (arg->getOption().matches(OPT_source)) { 332 m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile, 333 true, error); 334 if (error.Fail()) 335 return error; 336 } 337 338 if (arg->getOption().matches(OPT_source_before_file)) { 339 m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile, 340 true, error); 341 if (error.Fail()) 342 return error; 343 } 344 345 if (arg->getOption().matches(OPT_one_line)) { 346 m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile, 347 false, error); 348 if (error.Fail()) 349 return error; 350 } 351 352 if (arg->getOption().matches(OPT_one_line_before_file)) { 353 m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile, 354 false, error); 355 if (error.Fail()) 356 return error; 357 } 358 } 359 360 if (m_option_data.m_process_name.empty() && 361 m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) { 362 363 for (auto *arg : args.filtered(OPT_INPUT)) 364 m_option_data.m_args.push_back(arg->getAsString((args))); 365 366 // Any argument following -- is an argument for the inferior. 367 if (auto *arg = args.getLastArgNoClaim(OPT_REM)) { 368 for (auto value : arg->getValues()) 369 m_option_data.m_args.emplace_back(value); 370 } 371 } else if (args.getLastArgNoClaim() != nullptr) { 372 WithColor::warning() << "program arguments are ignored when attaching.\n"; 373 } 374 375 if (m_option_data.m_print_version) { 376 llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n'; 377 exiting = true; 378 return error; 379 } 380 381 if (m_option_data.m_print_python_path) { 382 SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath(); 383 if (python_file_spec.IsValid()) { 384 char python_path[PATH_MAX]; 385 size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX); 386 if (num_chars < PATH_MAX) { 387 llvm::outs() << python_path << '\n'; 388 } else 389 llvm::outs() << "<PATH TOO LONG>\n"; 390 } else 391 llvm::outs() << "<COULD NOT FIND PATH>\n"; 392 exiting = true; 393 return error; 394 } 395 396 if (m_option_data.m_print_script_interpreter_info) { 397 SBStructuredData info = 398 m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage()); 399 if (!info) { 400 error.SetErrorString("no script interpreter."); 401 } else { 402 SBStream stream; 403 error = info.GetAsJSON(stream); 404 if (error.Success()) { 405 llvm::outs() << stream.GetData() << '\n'; 406 } 407 } 408 exiting = true; 409 return error; 410 } 411 412 return error; 413 } 414 415 std::string EscapeString(std::string arg) { 416 std::string::size_type pos = 0; 417 while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) { 418 arg.insert(pos, 1, '\\'); 419 pos += 2; 420 } 421 return '"' + arg + '"'; 422 } 423 424 int Driver::MainLoop() { 425 if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) { 426 g_old_stdin_termios_is_valid = true; 427 atexit(reset_stdin_termios); 428 } 429 430 #ifndef _MSC_VER 431 // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets 432 // which causes it to miss newlines depending on whether there have been an 433 // odd or even number of characters. Bug has been reported to MS via Connect. 434 ::setbuf(stdin, nullptr); 435 #endif 436 ::setbuf(stdout, nullptr); 437 438 m_debugger.SetErrorFileHandle(stderr, false); 439 m_debugger.SetOutputFileHandle(stdout, false); 440 // Don't take ownership of STDIN yet... 441 m_debugger.SetInputFileHandle(stdin, false); 442 443 m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); 444 445 struct winsize window_size; 446 if ((isatty(STDIN_FILENO) != 0) && 447 ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { 448 if (window_size.ws_col > 0) 449 m_debugger.SetTerminalWidth(window_size.ws_col); 450 } 451 452 SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter(); 453 454 // Before we handle any options from the command line, we parse the 455 // REPL init file or the default file in the user's home directory. 456 SBCommandReturnObject result; 457 sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl); 458 if (m_option_data.m_debug_mode) { 459 result.PutError(m_debugger.GetErrorFile()); 460 result.PutOutput(m_debugger.GetOutputFile()); 461 } 462 463 // Source the local .lldbinit file if it exists and we're allowed to source. 464 // Here we want to always print the return object because it contains the 465 // warning and instructions to load local lldbinit files. 466 sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result); 467 result.PutError(m_debugger.GetErrorFile()); 468 result.PutOutput(m_debugger.GetOutputFile()); 469 470 // We allow the user to specify an exit code when calling quit which we will 471 // return when exiting. 472 m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true); 473 474 // Now we handle options we got from the command line 475 SBStream commands_stream; 476 477 // First source in the commands specified to be run before the file arguments 478 // are processed. 479 WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream); 480 481 // If we're not in --repl mode, add the commands to process the file 482 // arguments, and the commands specified to run afterwards. 483 if (!m_option_data.m_repl) { 484 const size_t num_args = m_option_data.m_args.size(); 485 if (num_args > 0) { 486 char arch_name[64]; 487 if (lldb::SBDebugger::GetDefaultArchitecture(arch_name, 488 sizeof(arch_name))) 489 commands_stream.Printf("target create --arch=%s %s", arch_name, 490 EscapeString(m_option_data.m_args[0]).c_str()); 491 else 492 commands_stream.Printf("target create %s", 493 EscapeString(m_option_data.m_args[0]).c_str()); 494 495 if (!m_option_data.m_core_file.empty()) { 496 commands_stream.Printf(" --core %s", 497 EscapeString(m_option_data.m_core_file).c_str()); 498 } 499 commands_stream.Printf("\n"); 500 501 if (num_args > 1) { 502 commands_stream.Printf("settings set -- target.run-args "); 503 for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx) 504 commands_stream.Printf( 505 " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str()); 506 commands_stream.Printf("\n"); 507 } 508 } else if (!m_option_data.m_core_file.empty()) { 509 commands_stream.Printf("target create --core %s\n", 510 EscapeString(m_option_data.m_core_file).c_str()); 511 } else if (!m_option_data.m_process_name.empty()) { 512 commands_stream.Printf( 513 "process attach --name %s", 514 EscapeString(m_option_data.m_process_name).c_str()); 515 516 if (m_option_data.m_wait_for) 517 commands_stream.Printf(" --waitfor"); 518 519 commands_stream.Printf("\n"); 520 521 } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) { 522 commands_stream.Printf("process attach --pid %" PRIu64 "\n", 523 m_option_data.m_process_pid); 524 } 525 526 WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream); 527 } else if (!m_option_data.m_after_file_commands.empty()) { 528 // We're in repl mode and after-file-load commands were specified. 529 WithColor::warning() << "commands specified to run after file load (via -o " 530 "or -s) are ignored in REPL mode.\n"; 531 } 532 533 if (m_option_data.m_debug_mode) { 534 result.PutError(m_debugger.GetErrorFile()); 535 result.PutOutput(m_debugger.GetOutputFile()); 536 } 537 538 const bool handle_events = true; 539 const bool spawn_thread = false; 540 541 // Check if we have any data in the commands stream, and if so, save it to a 542 // temp file 543 // so we can then run the command interpreter using the file contents. 544 bool go_interactive = true; 545 if ((commands_stream.GetData() != nullptr) && 546 (commands_stream.GetSize() != 0u)) { 547 SBError error = m_debugger.SetInputString(commands_stream.GetData()); 548 if (error.Fail()) { 549 WithColor::error() << error.GetCString() << '\n'; 550 return 1; 551 } 552 553 // Set the debugger into Sync mode when running the command file. Otherwise 554 // command files that run the target won't run in a sensible way. 555 bool old_async = m_debugger.GetAsync(); 556 m_debugger.SetAsync(false); 557 558 SBCommandInterpreterRunOptions options; 559 options.SetAutoHandleEvents(true); 560 options.SetSpawnThread(false); 561 options.SetStopOnError(true); 562 options.SetStopOnCrash(m_option_data.m_batch); 563 options.SetEchoCommands(!m_option_data.m_source_quietly); 564 565 SBCommandInterpreterRunResult results = 566 m_debugger.RunCommandInterpreter(options); 567 if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested) 568 go_interactive = false; 569 if (m_option_data.m_batch && 570 results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash) 571 go_interactive = false; 572 573 // When running in batch mode and stopped because of an error, exit with a 574 // non-zero exit status. 575 if (m_option_data.m_batch && 576 results.GetResult() == lldb::eCommandInterpreterResultCommandError) 577 return 1; 578 579 if (m_option_data.m_batch && 580 results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash && 581 !m_option_data.m_after_crash_commands.empty()) { 582 SBStream crash_commands_stream; 583 WriteCommandsForSourcing(eCommandPlacementAfterCrash, 584 crash_commands_stream); 585 SBError error = 586 m_debugger.SetInputString(crash_commands_stream.GetData()); 587 if (error.Success()) { 588 SBCommandInterpreterRunResult local_results = 589 m_debugger.RunCommandInterpreter(options); 590 if (local_results.GetResult() == 591 lldb::eCommandInterpreterResultQuitRequested) 592 go_interactive = false; 593 594 // When running in batch mode and an error occurred while sourcing 595 // the crash commands, exit with a non-zero exit status. 596 if (m_option_data.m_batch && 597 local_results.GetResult() == 598 lldb::eCommandInterpreterResultCommandError) 599 return 1; 600 } 601 } 602 m_debugger.SetAsync(old_async); 603 } 604 605 // Now set the input file handle to STDIN and run the command interpreter 606 // again in interactive mode or repl mode and let the debugger take ownership 607 // of stdin. 608 if (go_interactive) { 609 m_debugger.SetInputFileHandle(stdin, true); 610 611 if (m_option_data.m_repl) { 612 const char *repl_options = nullptr; 613 if (!m_option_data.m_repl_options.empty()) 614 repl_options = m_option_data.m_repl_options.c_str(); 615 SBError error( 616 m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options)); 617 if (error.Fail()) { 618 const char *error_cstr = error.GetCString(); 619 if ((error_cstr != nullptr) && (error_cstr[0] != 0)) 620 WithColor::error() << error_cstr << '\n'; 621 else 622 WithColor::error() << error.GetError() << '\n'; 623 } 624 } else { 625 m_debugger.RunCommandInterpreter(handle_events, spawn_thread); 626 } 627 } 628 629 reset_stdin_termios(); 630 fclose(stdin); 631 632 return sb_interpreter.GetQuitStatus(); 633 } 634 635 void Driver::ResizeWindow(unsigned short col) { 636 GetDebugger().SetTerminalWidth(col); 637 } 638 639 void sigwinch_handler(int signo) { 640 struct winsize window_size; 641 if ((isatty(STDIN_FILENO) != 0) && 642 ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { 643 if ((window_size.ws_col > 0) && g_driver != nullptr) { 644 g_driver->ResizeWindow(window_size.ws_col); 645 } 646 } 647 } 648 649 void sigint_handler(int signo) { 650 #ifdef _WIN32 // Restore handler as it is not persistent on Windows 651 signal(SIGINT, sigint_handler); 652 #endif 653 static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; 654 if (g_driver != nullptr) { 655 if (!g_interrupt_sent.test_and_set()) { 656 g_driver->GetDebugger().DispatchInputInterrupt(); 657 g_interrupt_sent.clear(); 658 return; 659 } 660 } 661 662 _exit(signo); 663 } 664 665 void sigtstp_handler(int signo) { 666 if (g_driver != nullptr) 667 g_driver->GetDebugger().SaveInputTerminalState(); 668 669 signal(signo, SIG_DFL); 670 kill(getpid(), signo); 671 signal(signo, sigtstp_handler); 672 } 673 674 void sigcont_handler(int signo) { 675 if (g_driver != nullptr) 676 g_driver->GetDebugger().RestoreInputTerminalState(); 677 678 signal(signo, SIG_DFL); 679 kill(getpid(), signo); 680 signal(signo, sigcont_handler); 681 } 682 683 void reproducer_handler(void *finalize_cmd) { 684 if (SBReproducer::Generate()) { 685 int result = std::system(static_cast<const char *>(finalize_cmd)); 686 (void)result; 687 fflush(stdout); 688 } 689 } 690 691 static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { 692 std::string usage_str = tool_name.str() + " [options]"; 693 table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false); 694 695 std::string examples = R"___( 696 EXAMPLES: 697 The debugger can be started in several modes. 698 699 Passing an executable as a positional argument prepares lldb to debug the 700 given executable. To disambiguate between arguments passed to lldb and 701 arguments passed to the debugged executable, arguments starting with a - must 702 be passed after --. 703 704 lldb --arch x86_64 /path/to/program program argument -- --arch armv7 705 706 For convenience, passing the executable after -- is also supported. 707 708 lldb --arch x86_64 -- /path/to/program program argument --arch armv7 709 710 Passing one of the attach options causes lldb to immediately attach to the 711 given process. 712 713 lldb -p <pid> 714 lldb -n <process-name> 715 716 Passing --repl starts lldb in REPL mode. 717 718 lldb -r 719 720 Passing --core causes lldb to debug the core file. 721 722 lldb -c /path/to/core 723 724 Command options can be combined with these modes and cause lldb to run the 725 specified commands before or after events, like loading the file or crashing, 726 in the order provided on the command line. 727 728 lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt' 729 lldb -S /source/before/file -s /source/after/file 730 lldb -K /source/before/crash -k /source/after/crash 731 732 Note: In REPL mode no file is loaded, so commands specified to run after 733 loading the file (via -o or -s) will be ignored.)___"; 734 llvm::outs() << examples << '\n'; 735 } 736 737 static llvm::Optional<int> InitializeReproducer(llvm::StringRef argv0, 738 opt::InputArgList &input_args) { 739 bool capture = input_args.hasArg(OPT_capture); 740 bool generate_on_exit = input_args.hasArg(OPT_generate_on_exit); 741 auto *capture_path = input_args.getLastArg(OPT_capture_path); 742 743 if (generate_on_exit && !capture) { 744 WithColor::warning() 745 << "-reproducer-generate-on-exit specified without -capture\n"; 746 } 747 748 if (capture || capture_path) { 749 if (capture_path) { 750 if (!capture) 751 WithColor::warning() << "-capture-path specified without -capture\n"; 752 if (const char *error = SBReproducer::Capture(capture_path->getValue())) { 753 WithColor::error() << "reproducer capture failed: " << error << '\n'; 754 return 1; 755 } 756 } else { 757 const char *error = SBReproducer::Capture(); 758 if (error) { 759 WithColor::error() << "reproducer capture failed: " << error << '\n'; 760 return 1; 761 } 762 } 763 if (generate_on_exit) 764 SBReproducer::SetAutoGenerate(true); 765 } 766 767 return llvm::None; 768 } 769 770 int main(int argc, char const *argv[]) { 771 // Editline uses for example iswprint which is dependent on LC_CTYPE. 772 std::setlocale(LC_ALL, ""); 773 std::setlocale(LC_CTYPE, ""); 774 775 // Setup LLVM signal handlers and make sure we call llvm_shutdown() on 776 // destruction. 777 llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false); 778 779 // Parse arguments. 780 LLDBOptTable T; 781 unsigned MissingArgIndex; 782 unsigned MissingArgCount; 783 ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1); 784 opt::InputArgList input_args = 785 T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount); 786 llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]); 787 788 if (input_args.hasArg(OPT_help)) { 789 printHelp(T, argv0); 790 return 0; 791 } 792 793 // Check for missing argument error. 794 if (MissingArgCount) { 795 WithColor::error() << "argument to '" 796 << input_args.getArgString(MissingArgIndex) 797 << "' is missing\n"; 798 } 799 // Error out on unknown options. 800 if (input_args.hasArg(OPT_UNKNOWN)) { 801 for (auto *arg : input_args.filtered(OPT_UNKNOWN)) { 802 WithColor::error() << "unknown option: " << arg->getSpelling() << '\n'; 803 } 804 } 805 if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) { 806 llvm::errs() << "Use '" << argv0 807 << " --help' for a complete list of options.\n"; 808 return 1; 809 } 810 811 if (auto exit_code = InitializeReproducer(argv[0], input_args)) { 812 return *exit_code; 813 } 814 815 SBError error = SBDebugger::InitializeWithErrorHandling(); 816 if (error.Fail()) { 817 WithColor::error() << "initialization failed: " << error.GetCString() 818 << '\n'; 819 return 1; 820 } 821 SBHostOS::ThreadCreated("<lldb.driver.main-thread>"); 822 823 signal(SIGINT, sigint_handler); 824 #if !defined(_MSC_VER) 825 signal(SIGPIPE, SIG_IGN); 826 signal(SIGWINCH, sigwinch_handler); 827 signal(SIGTSTP, sigtstp_handler); 828 signal(SIGCONT, sigcont_handler); 829 #endif 830 831 int exit_code = 0; 832 // Create a scope for driver so that the driver object will destroy itself 833 // before SBDebugger::Terminate() is called. 834 { 835 Driver driver; 836 837 bool exiting = false; 838 SBError error(driver.ProcessArgs(input_args, exiting)); 839 if (error.Fail()) { 840 exit_code = 1; 841 if (const char *error_cstr = error.GetCString()) 842 WithColor::error() << error_cstr << '\n'; 843 } else if (!exiting) { 844 exit_code = driver.MainLoop(); 845 } 846 } 847 848 SBDebugger::Terminate(); 849 return exit_code; 850 } 851