brintos

brintos / llvm-project-archived public Read only

0
0
Text · 30.9 KiB · 4810771 Raw
956 lines · cpp
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-exception6//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/SBStream.h"19#include "lldb/API/SBStringList.h"20#include "lldb/API/SBStructuredData.h"21#include "lldb/Host/Config.h"22#include "lldb/Host/MainLoop.h"23#include "lldb/Host/MainLoopBase.h"24#include "lldb/Utility/Status.h"25#include "llvm/ADT/SmallString.h"26#include "llvm/ADT/StringRef.h"27#include "llvm/Support/ConvertUTF.h"28#include "llvm/Support/FileSystem.h"29#include "llvm/Support/Format.h"30#include "llvm/Support/InitLLVM.h"31#include "llvm/Support/Path.h"32#include "llvm/Support/Signals.h"33#include "llvm/Support/WithColor.h"34#include "llvm/Support/raw_ostream.h"35 36#ifdef _WIN3237#include "llvm/Support/Windows/WindowsSupport.h"38#endif39 40#include <algorithm>41#include <atomic>42#include <bitset>43#include <clocale>44#include <csignal>45#include <future>46#include <string>47#include <thread>48#include <utility>49 50#include <climits>51#include <cstdio>52#include <cstdlib>53#include <cstring>54#include <fcntl.h>55 56#if !defined(__APPLE__)57#include "llvm/Support/DataTypes.h"58#endif59 60using namespace lldb;61using namespace llvm;62using lldb_private::MainLoop;63using lldb_private::MainLoopBase;64using lldb_private::Status;65 66namespace {67using namespace llvm::opt;68 69enum ID {70  OPT_INVALID = 0, // This is not an option ID.71#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),72#include "Options.inc"73#undef OPTION74};75 76#define OPTTABLE_STR_TABLE_CODE77#include "Options.inc"78#undef OPTTABLE_STR_TABLE_CODE79 80#define OPTTABLE_PREFIXES_TABLE_CODE81#include "Options.inc"82#undef OPTTABLE_PREFIXES_TABLE_CODE83 84static constexpr opt::OptTable::Info InfoTable[] = {85#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),86#include "Options.inc"87#undef OPTION88};89 90class LLDBOptTable : public opt::GenericOptTable {91public:92  LLDBOptTable()93      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}94};95} // namespace96 97static void reset_stdin_termios();98static bool g_old_stdin_termios_is_valid = false;99static struct termios g_old_stdin_termios;100 101static bool disable_color(const raw_ostream &OS) { return false; }102 103static Driver *g_driver = nullptr;104 105// In the Driver::MainLoop, we change the terminal settings.  This function is106// added as an atexit handler to make sure we clean them up.107static void reset_stdin_termios() {108  if (g_old_stdin_termios_is_valid) {109    g_old_stdin_termios_is_valid = false;110    ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios);111  }112}113 114Driver::Driver()115    : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {116  // We want to be able to handle CTRL+D in the terminal to have it terminate117  // certain input118  m_debugger.SetCloseInputOnEOF(false);119  g_driver = this;120}121 122Driver::~Driver() {123  SBDebugger::Destroy(m_debugger);124  g_driver = nullptr;125}126 127void Driver::OptionData::AddInitialCommand(std::string command,128                                           CommandPlacement placement,129                                           bool is_file, SBError &error) {130  std::vector<InitialCmdEntry> *command_set;131  switch (placement) {132  case eCommandPlacementBeforeFile:133    command_set = &(m_initial_commands);134    break;135  case eCommandPlacementAfterFile:136    command_set = &(m_after_file_commands);137    break;138  case eCommandPlacementAfterCrash:139    command_set = &(m_after_crash_commands);140    break;141  }142 143  if (is_file) {144    SBFileSpec file(command.c_str());145    if (file.Exists())146      command_set->push_back(InitialCmdEntry(command, is_file));147    else if (file.ResolveExecutableLocation()) {148      char final_path[PATH_MAX];149      file.GetPath(final_path, sizeof(final_path));150      command_set->push_back(InitialCmdEntry(final_path, is_file));151    } else152      error.SetErrorStringWithFormat(153          "file specified in --source (-s) option doesn't exist: '%s'",154          command.c_str());155  } else156    command_set->push_back(InitialCmdEntry(command, is_file));157}158 159void Driver::WriteCommandsForSourcing(CommandPlacement placement,160                                      SBStream &strm) {161  std::vector<OptionData::InitialCmdEntry> *command_set;162  switch (placement) {163  case eCommandPlacementBeforeFile:164    command_set = &m_option_data.m_initial_commands;165    break;166  case eCommandPlacementAfterFile:167    command_set = &m_option_data.m_after_file_commands;168    break;169  case eCommandPlacementAfterCrash:170    command_set = &m_option_data.m_after_crash_commands;171    break;172  }173 174  for (const auto &command_entry : *command_set) {175    const char *command = command_entry.contents.c_str();176    if (command_entry.is_file) {177      bool source_quietly =178          m_option_data.m_source_quietly || command_entry.source_quietly;179      strm.Printf("command source -s %i '%s'\n",180                  static_cast<int>(source_quietly), command);181    } else182      strm.Printf("%s\n", command);183  }184}185 186// Check the arguments that were passed to this program to make sure they are187// valid and to get their argument values (if any).  Return a boolean value188// indicating whether or not to start up the full debugger (i.e. the Command189// Interpreter) or not.  Return FALSE if the arguments were invalid OR if the190// user only wanted help or version information.191SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {192  SBError error;193 194  // This is kind of a pain, but since we make the debugger in the Driver's195  // constructor, we can't know at that point whether we should read in init196  // files yet.  So we don't read them in in the Driver constructor, then set197  // the flags back to "read them in" here, and then if we see the "-n" flag,198  // we'll turn it off again.  Finally we have to read them in by hand later in199  // the main loop.200  m_debugger.SkipLLDBInitFiles(false);201  m_debugger.SkipAppInitFiles(false);202 203  if (args.hasArg(OPT_no_use_colors)) {204    m_debugger.SetUseColor(false);205    WithColor::setAutoDetectFunction(disable_color);206  }207 208  if (args.hasArg(OPT_version)) {209    m_option_data.m_print_version = true;210  }211 212  if (args.hasArg(OPT_python_path)) {213    m_option_data.m_print_python_path = true;214  }215  if (args.hasArg(OPT_print_script_interpreter_info)) {216    m_option_data.m_print_script_interpreter_info = true;217  }218 219  if (args.hasArg(OPT_batch)) {220    m_option_data.m_batch = true;221  }222 223  if (auto *arg = args.getLastArg(OPT_core)) {224    auto *arg_value = arg->getValue();225    SBFileSpec file(arg_value);226    if (!file.Exists()) {227      error.SetErrorStringWithFormat(228          "file specified in --core (-c) option doesn't exist: '%s'",229          arg_value);230      return error;231    }232    m_option_data.m_core_file = arg_value;233  }234 235  if (args.hasArg(OPT_editor)) {236    m_option_data.m_use_external_editor = true;237  }238 239  if (args.hasArg(OPT_no_lldbinit)) {240    m_debugger.SkipLLDBInitFiles(true);241    m_debugger.SkipAppInitFiles(true);242  }243 244  if (args.hasArg(OPT_local_lldbinit)) {245    lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true",246                                          m_debugger.GetInstanceName());247  }248 249  if (auto *arg = args.getLastArg(OPT_file)) {250    auto *arg_value = arg->getValue();251    SBFileSpec file(arg_value);252    if (file.Exists()) {253      m_option_data.m_args.emplace_back(arg_value);254    } else if (file.ResolveExecutableLocation()) {255      char path[PATH_MAX];256      file.GetPath(path, sizeof(path));257      m_option_data.m_args.emplace_back(path);258    } else {259      error.SetErrorStringWithFormat(260          "file specified in --file (-f) option doesn't exist: '%s'",261          arg_value);262      return error;263    }264  }265 266  if (auto *arg = args.getLastArg(OPT_arch)) {267    auto *arg_value = arg->getValue();268    if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) {269      error.SetErrorStringWithFormat(270          "invalid architecture in the -a or --arch option: '%s'", arg_value);271      return error;272    }273  }274 275  if (auto *arg = args.getLastArg(OPT_script_language)) {276    auto *arg_value = arg->getValue();277    m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value));278  }279 280  if (args.hasArg(OPT_source_quietly)) {281    m_option_data.m_source_quietly = true;282  }283 284  if (auto *arg = args.getLastArg(OPT_attach_name)) {285    auto *arg_value = arg->getValue();286    m_option_data.m_process_name = arg_value;287  }288 289  if (args.hasArg(OPT_wait_for)) {290    if (!args.hasArg(OPT_attach_name)) {291      error.SetErrorStringWithFormat(292          "--wait-for requires a name (--attach-name)");293      return error;294    }295 296    m_option_data.m_wait_for = true;297  }298 299  if (auto *arg = args.getLastArg(OPT_attach_pid)) {300    auto *arg_value = arg->getValue();301    char *remainder;302    m_option_data.m_process_pid = strtol(arg_value, &remainder, 0);303    if (remainder == arg_value || *remainder != '\0') {304      error.SetErrorStringWithFormat(305          "Could not convert process PID: \"%s\" into a pid.", arg_value);306      return error;307    }308  }309 310  if (auto *arg = args.getLastArg(OPT_repl_language)) {311    auto *arg_value = arg->getValue();312    m_option_data.m_repl_lang =313        SBLanguageRuntime::GetLanguageTypeFromString(arg_value);314    if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {315      error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",316                                     arg_value);317      return error;318    }319    m_debugger.SetREPLLanguage(m_option_data.m_repl_lang);320  }321 322  if (args.hasArg(OPT_repl)) {323    m_option_data.m_repl = true;324  }325 326  if (auto *arg = args.getLastArg(OPT_repl_)) {327    m_option_data.m_repl = true;328    if (auto *arg_value = arg->getValue())329      m_option_data.m_repl_options = arg_value;330  }331 332  // We need to process the options below together as their relative order333  // matters.334  for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash,335                                 OPT_source, OPT_source_before_file,336                                 OPT_one_line, OPT_one_line_before_file)) {337    auto *arg_value = arg->getValue();338    if (arg->getOption().matches(OPT_source_on_crash)) {339      m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,340                                      true, error);341      if (error.Fail())342        return error;343    }344 345    if (arg->getOption().matches(OPT_one_line_on_crash)) {346      m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,347                                      false, error);348      if (error.Fail())349        return error;350    }351 352    if (arg->getOption().matches(OPT_source)) {353      m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,354                                      true, error);355      if (error.Fail())356        return error;357    }358 359    if (arg->getOption().matches(OPT_source_before_file)) {360      m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,361                                      true, error);362      if (error.Fail())363        return error;364    }365 366    if (arg->getOption().matches(OPT_one_line)) {367      m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,368                                      false, error);369      if (error.Fail())370        return error;371    }372 373    if (arg->getOption().matches(OPT_one_line_before_file)) {374      m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,375                                      false, error);376      if (error.Fail())377        return error;378    }379  }380 381  if (m_option_data.m_process_name.empty() &&382      m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {383 384    for (auto *arg : args.filtered(OPT_INPUT))385      m_option_data.m_args.push_back(arg->getAsString((args)));386 387    // Any argument following -- is an argument for the inferior.388    if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {389      for (auto *value : arg->getValues())390        m_option_data.m_args.emplace_back(value);391    }392  } else if (args.getLastArgNoClaim() != nullptr) {393    WithColor::warning() << "program arguments are ignored when attaching.\n";394  }395 396  if (m_option_data.m_print_version) {397    llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';398    exiting = true;399    return error;400  }401 402  if (m_option_data.m_print_python_path) {403    SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();404    if (python_file_spec.IsValid()) {405      char python_path[PATH_MAX];406      size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX);407      if (num_chars < PATH_MAX) {408        llvm::outs() << python_path << '\n';409      } else410        llvm::outs() << "<PATH TOO LONG>\n";411    } else412      llvm::outs() << "<COULD NOT FIND PATH>\n";413    exiting = true;414    return error;415  }416 417  if (m_option_data.m_print_script_interpreter_info) {418    SBStructuredData info =419        m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage());420    if (!info) {421      error.SetErrorString("no script interpreter.");422    } else {423      SBStream stream;424      error = info.GetAsJSON(stream);425      if (error.Success()) {426        llvm::outs() << stream.GetData() << '\n';427      }428    }429    exiting = true;430    return error;431  }432 433  return error;434}435 436#ifdef _WIN32437#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH438/// Returns the full path to the lldb.exe executable.439inline std::wstring GetPathToExecutableW() {440  // Iterate until we reach the Windows API maximum path length (32,767).441  std::vector<WCHAR> buffer;442  buffer.resize(MAX_PATH /*=260*/);443  while (buffer.size() < 32767) {444    if (GetModuleFileNameW(NULL, buffer.data(), buffer.size()) < buffer.size())445      return std::wstring(buffer.begin(), buffer.end());446    buffer.resize(buffer.size() * 2);447  }448  return L"";449}450 451/// \brief Resolve the full path of the directory defined by452/// LLDB_PYTHON_DLL_RELATIVE_PATH. If it exists, add it to the list of DLL453/// search directories.454/// \return `true` if the library was added to the search path.455/// `false` otherwise.456bool AddPythonDLLToSearchPath() {457  std::wstring modulePath = GetPathToExecutableW();458  if (modulePath.empty())459    return false;460 461  SmallVector<char, MAX_PATH> utf8Path;462  if (sys::windows::UTF16ToUTF8(modulePath.c_str(), modulePath.length(),463                                utf8Path))464    return false;465  sys::path::remove_filename(utf8Path);466  sys::path::append(utf8Path, LLDB_PYTHON_DLL_RELATIVE_PATH);467  sys::fs::make_absolute(utf8Path);468 469  SmallVector<wchar_t, 1> widePath;470  if (sys::windows::widenPath(utf8Path.data(), widePath))471    return false;472 473  if (sys::fs::exists(utf8Path))474    return SetDllDirectoryW(widePath.data());475  return false;476}477#endif478 479#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME480/// Returns true if `python3x.dll` can be loaded.481bool IsPythonDLLInPath() {482#define WIDEN2(x) L##x483#define WIDEN(x) WIDEN2(x)484  HMODULE h = LoadLibraryW(WIDEN(LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME));485  if (!h)486    return false;487  FreeLibrary(h);488  return true;489#undef WIDEN2490#undef WIDEN491}492#endif493 494/// Try to setup the DLL search path for the Python Runtime Library495/// (python3xx.dll).496///497/// If `LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME` is set, we first check if498/// python3xx.dll is in the search path. If it's not, we try to add it and499/// check for it a second time.500/// If only `LLDB_PYTHON_DLL_RELATIVE_PATH` is set, we try to add python3xx.dll501/// to the search path python.dll is already in the search path or not.502void SetupPythonRuntimeLibrary() {503#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME504  if (IsPythonDLLInPath())505    return;506#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH507  if (AddPythonDLLToSearchPath() && IsPythonDLLInPath())508    return;509#endif510  WithColor::error() << "unable to find '"511                     << LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME << "'.\n";512  return;513#elif defined(LLDB_PYTHON_DLL_RELATIVE_PATH)514  if (!AddPythonDLLToSearchPath())515    WithColor::error() << "unable to find the Python runtime library.\n";516#endif517}518#endif519 520std::string EscapeString(std::string arg) {521  std::string::size_type pos = 0;522  while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {523    arg.insert(pos, 1, '\\');524    pos += 2;525  }526  return '"' + arg + '"';527}528 529int Driver::MainLoop() {530  if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) {531    g_old_stdin_termios_is_valid = true;532    atexit(reset_stdin_termios);533  }534 535#ifndef _MSC_VER536  // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets537  // which causes it to miss newlines depending on whether there have been an538  // odd or even number of characters.  Bug has been reported to MS via Connect.539  ::setbuf(stdin, nullptr);540#endif541  ::setbuf(stdout, nullptr);542 543  m_debugger.SetErrorFileHandle(stderr, false);544  m_debugger.SetOutputFileHandle(stdout, false);545  // Don't take ownership of STDIN yet...546  m_debugger.SetInputFileHandle(stdin, false);547 548  m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor);549  m_debugger.SetShowInlineDiagnostics(true);550 551  // Set the terminal dimensions.552  UpdateWindowSize();553 554  SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();555 556  // Process lldbinit files before handling any options from the command line.557  SBCommandReturnObject result;558  sb_interpreter.SourceInitFileInGlobalDirectory(result);559  sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);560 561  // Source the local .lldbinit file if it exists and we're allowed to source.562  // Here we want to always print the return object because it contains the563  // warning and instructions to load local lldbinit files.564  sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);565  result.PutError(m_debugger.GetErrorFile());566  result.PutOutput(m_debugger.GetOutputFile());567 568  // We allow the user to specify an exit code when calling quit which we will569  // return when exiting.570  m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);571 572  // Now we handle options we got from the command line573  SBStream commands_stream;574 575  // First source in the commands specified to be run before the file arguments576  // are processed.577  WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);578 579  // If we're not in --repl mode, add the commands to process the file580  // arguments, and the commands specified to run afterwards.581  if (!m_option_data.m_repl) {582    const size_t num_args = m_option_data.m_args.size();583    if (num_args > 0) {584      char arch_name[64];585      if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,586                                                   sizeof(arch_name)))587        commands_stream.Printf("target create --arch=%s %s", arch_name,588                               EscapeString(m_option_data.m_args[0]).c_str());589      else590        commands_stream.Printf("target create %s",591                               EscapeString(m_option_data.m_args[0]).c_str());592 593      if (!m_option_data.m_core_file.empty()) {594        commands_stream.Printf(" --core %s",595                               EscapeString(m_option_data.m_core_file).c_str());596      }597      commands_stream.Printf("\n");598 599      if (num_args > 1) {600        commands_stream.Printf("settings set -- target.run-args ");601        for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)602          commands_stream.Printf(603              " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());604        commands_stream.Printf("\n");605      }606    } else if (!m_option_data.m_core_file.empty()) {607      commands_stream.Printf("target create --core %s\n",608                             EscapeString(m_option_data.m_core_file).c_str());609    } else if (!m_option_data.m_process_name.empty()) {610      commands_stream.Printf(611          "process attach --name %s",612          EscapeString(m_option_data.m_process_name).c_str());613 614      if (m_option_data.m_wait_for)615        commands_stream.Printf(" --waitfor");616 617      commands_stream.Printf("\n");618 619    } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {620      commands_stream.Printf("process attach --pid %" PRIu64 "\n",621                             m_option_data.m_process_pid);622    }623 624    WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);625  } else if (!m_option_data.m_after_file_commands.empty()) {626    // We're in repl mode and after-file-load commands were specified.627    WithColor::warning() << "commands specified to run after file load (via -o "628                            "or -s) are ignored in REPL mode.\n";629  }630 631  const bool handle_events = true;632  const bool spawn_thread = false;633 634  // Check if we have any data in the commands stream, and if so, save it to a635  // temp file636  // so we can then run the command interpreter using the file contents.637  bool go_interactive = true;638  if ((commands_stream.GetData() != nullptr) &&639      (commands_stream.GetSize() != 0u)) {640    SBError error = m_debugger.SetInputString(commands_stream.GetData());641    if (error.Fail()) {642      WithColor::error() << error.GetCString() << '\n';643      return 1;644    }645 646    // Set the debugger into Sync mode when running the command file. Otherwise647    // command files that run the target won't run in a sensible way.648    bool old_async = m_debugger.GetAsync();649    m_debugger.SetAsync(false);650 651    SBCommandInterpreterRunOptions options;652    options.SetAutoHandleEvents(true);653    options.SetSpawnThread(false);654    options.SetStopOnError(true);655    options.SetStopOnCrash(m_option_data.m_batch);656    options.SetEchoCommands(!m_option_data.m_source_quietly);657 658    SBCommandInterpreterRunResult results =659        m_debugger.RunCommandInterpreter(options);660    if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)661      go_interactive = false;662    if (m_option_data.m_batch &&663        results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)664      go_interactive = false;665 666    // When running in batch mode and stopped because of an error, exit with a667    // non-zero exit status.668    if (m_option_data.m_batch &&669        results.GetResult() == lldb::eCommandInterpreterResultCommandError)670      return 1;671 672    if (m_option_data.m_batch &&673        results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&674        !m_option_data.m_after_crash_commands.empty()) {675      SBStream crash_commands_stream;676      WriteCommandsForSourcing(eCommandPlacementAfterCrash,677                               crash_commands_stream);678      SBError error =679          m_debugger.SetInputString(crash_commands_stream.GetData());680      if (error.Success()) {681        SBCommandInterpreterRunResult local_results =682            m_debugger.RunCommandInterpreter(options);683        if (local_results.GetResult() ==684            lldb::eCommandInterpreterResultQuitRequested)685          go_interactive = false;686 687        // When running in batch mode and an error occurred while sourcing688        // the crash commands, exit with a non-zero exit status.689        if (m_option_data.m_batch &&690            local_results.GetResult() ==691                lldb::eCommandInterpreterResultCommandError)692          return 1;693      }694    }695    m_debugger.SetAsync(old_async);696  }697 698  // Now set the input file handle to STDIN and run the command interpreter699  // again in interactive mode or repl mode and let the debugger take ownership700  // of stdin.701  if (go_interactive) {702    m_debugger.SetInputFileHandle(stdin, true);703 704    if (m_option_data.m_repl) {705      const char *repl_options = nullptr;706      if (!m_option_data.m_repl_options.empty())707        repl_options = m_option_data.m_repl_options.c_str();708      SBError error(709          m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));710      if (error.Fail()) {711        const char *error_cstr = error.GetCString();712        if ((error_cstr != nullptr) && (error_cstr[0] != 0))713          WithColor::error() << error_cstr << '\n';714        else715          WithColor::error() << error.GetError() << '\n';716      }717    } else {718      m_debugger.RunCommandInterpreter(handle_events, spawn_thread);719    }720  }721 722  reset_stdin_termios();723  fclose(stdin);724 725  return sb_interpreter.GetQuitStatus();726}727 728void Driver::UpdateWindowSize() {729  struct winsize window_size;730  if ((isatty(STDIN_FILENO) != 0) &&731      ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {732    if (window_size.ws_col > 0)733      m_debugger.SetTerminalWidth(window_size.ws_col);734#ifndef _WIN32735    if (window_size.ws_row > 0)736      m_debugger.SetTerminalHeight(window_size.ws_row);737#endif738  }739}740 741void sigint_handler(int signo) {742#ifdef _WIN32743  // Restore handler as it is not persistent on Windows.744  signal(SIGINT, sigint_handler);745#endif746 747  static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;748  if (g_driver != nullptr) {749    if (!g_interrupt_sent.test_and_set()) {750      g_driver->GetDebugger().DispatchInputInterrupt();751      g_interrupt_sent.clear();752      return;753    }754  }755 756  _exit(signo);757}758 759static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {760  std::string usage_str = tool_name.str() + " [options]";761  table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);762 763  std::string examples = R"___(764EXAMPLES:765  The debugger can be started in several modes.766 767  Passing an executable as a positional argument prepares lldb to debug the768  given executable. To disambiguate between arguments passed to lldb and769  arguments passed to the debugged executable, arguments starting with a - must770  be passed after --.771 772    lldb --arch x86_64 /path/to/program program argument -- --arch armv7773 774  For convenience, passing the executable after -- is also supported.775 776    lldb --arch x86_64 -- /path/to/program program argument --arch armv7777 778  Passing one of the attach options causes lldb to immediately attach to the779  given process.780 781    lldb -p <pid>782    lldb -n <process-name>783 784  Passing --repl starts lldb in REPL mode.785 786    lldb -r787 788  Passing --core causes lldb to debug the core file.789 790    lldb -c /path/to/core791 792  Command options can be combined with these modes and cause lldb to run the793  specified commands before or after events, like loading the file or crashing,794  in the order provided on the command line.795 796    lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'797    lldb -S /source/before/file -s /source/after/file798    lldb -K /source/before/crash -k /source/after/crash799 800  Note: In REPL mode no file is loaded, so commands specified to run after801  loading the file (via -o or -s) will be ignored.)___";802  llvm::outs() << examples << '\n';803}804 805int main(int argc, char const *argv[]) {806  // Editline uses for example iswprint which is dependent on LC_CTYPE.807  std::setlocale(LC_ALL, "");808  std::setlocale(LC_CTYPE, "");809 810  // Setup LLVM signal handlers and make sure we call llvm_shutdown() on811  // destruction.812  llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);813#if !defined(__APPLE__)814  llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL815                        " and include the crash backtrace.\n");816#else817  llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL818                        " and include the crash report from "819                        "~/Library/Logs/DiagnosticReports/.\n");820#endif821 822#ifdef _WIN32823  SetupPythonRuntimeLibrary();824#endif825 826  // Parse arguments.827  LLDBOptTable T;828  unsigned MissingArgIndex;829  unsigned MissingArgCount;830  ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);831  opt::InputArgList input_args =832      T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);833  llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);834 835  if (input_args.hasArg(OPT_help)) {836    printHelp(T, argv0);837    return 0;838  }839 840  // Check for missing argument error.841  if (MissingArgCount) {842    WithColor::error() << "argument to '"843                       << input_args.getArgString(MissingArgIndex)844                       << "' is missing\n";845  }846  // Error out on unknown options.847  if (input_args.hasArg(OPT_UNKNOWN)) {848    for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {849      WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';850    }851  }852  if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {853    llvm::errs() << "Use '" << argv0854                 << " --help' for a complete list of options.\n";855    return 1;856  }857 858  SBError error = SBDebugger::InitializeWithErrorHandling();859  if (error.Fail()) {860    WithColor::error() << "initialization failed: " << error.GetCString()861                       << '\n';862    return 1;863  }864 865  // Setup LLDB signal handlers once the debugger has been initialized.866  SBDebugger::PrintDiagnosticsOnError();867 868  //  FIXME: Migrate the SIGINT handler to be handled by the signal loop below.869  signal(SIGINT, sigint_handler);870#if !defined(_WIN32)871  signal(SIGPIPE, SIG_IGN);872 873  // Handle signals in a MainLoop running on a separate thread.874  MainLoop signal_loop;875  Status signal_status;876 877  auto sigwinch_handler = signal_loop.RegisterSignal(878      SIGWINCH,879      [&](MainLoopBase &) {880        if (g_driver)881          g_driver->UpdateWindowSize();882      },883      signal_status);884  assert(sigwinch_handler && signal_status.Success());885 886  auto sigtstp_handler = signal_loop.RegisterSignal(887      SIGTSTP,888      [&](MainLoopBase &) {889        if (g_driver)890          g_driver->GetDebugger().SaveInputTerminalState();891 892        struct sigaction old_action;893        struct sigaction new_action = {};894        new_action.sa_handler = SIG_DFL;895        sigemptyset(&new_action.sa_mask);896        sigaddset(&new_action.sa_mask, SIGTSTP);897 898        int ret = sigaction(SIGTSTP, &new_action, &old_action);899        UNUSED_IF_ASSERT_DISABLED(ret);900        assert(ret == 0 && "sigaction failed");901 902        raise(SIGTSTP);903 904        ret = sigaction(SIGTSTP, &old_action, nullptr);905        UNUSED_IF_ASSERT_DISABLED(ret);906        assert(ret == 0 && "sigaction failed");907 908        if (g_driver)909          g_driver->GetDebugger().RestoreInputTerminalState();910      },911      signal_status);912  assert(sigtstp_handler && signal_status.Success());913 914  std::thread signal_thread([&] { signal_loop.Run(); });915#endif916 917  int exit_code = 0;918  // Create a scope for driver so that the driver object will destroy itself919  // before SBDebugger::Terminate() is called.920  {921    Driver driver;922 923    bool exiting = false;924    SBError error(driver.ProcessArgs(input_args, exiting));925    if (error.Fail()) {926      exit_code = 1;927      if (const char *error_cstr = error.GetCString())928        WithColor::error() << error_cstr << '\n';929    } else if (!exiting) {930      exit_code = driver.MainLoop();931    }932  }933 934  // When terminating the debugger we have to wait on all the background tasks935  // to complete, which can take a while. Print a message when this takes longer936  // than 1 second.937  {938    std::future<void> future =939        std::async(std::launch::async, []() { SBDebugger::Terminate(); });940 941    if (future.wait_for(std::chrono::seconds(1)) == std::future_status::timeout)942      fprintf(stderr, "Waiting for background tasks to complete...\n");943 944    future.wait();945  }946 947#if !defined(_WIN32)948  // Try to interrupt the signal thread.  If that succeeds, wait for it to exit.949  if (signal_loop.AddPendingCallback(950          [](MainLoopBase &loop) { loop.RequestTermination(); }))951    signal_thread.join();952#endif953 954  return exit_code;955}956