brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.0 KiB · 27516b2 Raw
770 lines · cpp
1//===-- lldb-dap.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ClientLauncher.h"10#include "DAP.h"11#include "DAPLog.h"12#include "EventHelper.h"13#include "Handler/RequestHandler.h"14#include "RunInTerminal.h"15#include "Transport.h"16#include "lldb/API/SBDebugger.h"17#include "lldb/API/SBStream.h"18#include "lldb/Host/Config.h"19#include "lldb/Host/File.h"20#include "lldb/Host/FileSystem.h"21#include "lldb/Host/MainLoop.h"22#include "lldb/Host/MainLoopBase.h"23#include "lldb/Host/MemoryMonitor.h"24#include "lldb/Host/Socket.h"25#include "lldb/Utility/AnsiTerminal.h"26#include "lldb/Utility/Status.h"27#include "lldb/Utility/UriParser.h"28#include "lldb/lldb-forward.h"29#include "llvm/ADT/ArrayRef.h"30#include "llvm/ADT/DenseMap.h"31#include "llvm/ADT/ScopeExit.h"32#include "llvm/ADT/SmallVector.h"33#include "llvm/ADT/StringExtras.h"34#include "llvm/ADT/StringRef.h"35#include "llvm/Option/Arg.h"36#include "llvm/Option/ArgList.h"37#include "llvm/Option/OptTable.h"38#include "llvm/Option/Option.h"39#include "llvm/Support/CommandLine.h"40#include "llvm/Support/Error.h"41#include "llvm/Support/FileSystem.h"42#include "llvm/Support/InitLLVM.h"43#include "llvm/Support/Path.h"44#include "llvm/Support/PrettyStackTrace.h"45#include "llvm/Support/Signals.h"46#include "llvm/Support/Threading.h"47#include "llvm/Support/WithColor.h"48#include "llvm/Support/raw_ostream.h"49#include <condition_variable>50#include <cstddef>51#include <cstdio>52#include <cstdlib>53#include <exception>54#include <fcntl.h>55#include <map>56#include <memory>57#include <mutex>58#include <string>59#include <system_error>60#include <thread>61#include <utility>62#include <vector>63 64#if defined(_WIN32)65// We need to #define NOMINMAX in order to skip `min()` and `max()` macro66// definitions that conflict with other system headers.67// We also need to #undef GetObject (which is defined to GetObjectW) because68// the JSON code we use also has methods named `GetObject()` and we conflict69// against these.70#define NOMINMAX71#include <windows.h>72#undef GetObject73#include <io.h>74typedef int socklen_t;75#else76#include <netinet/in.h>77#include <sys/socket.h>78#include <sys/un.h>79#include <termios.h>80#include <unistd.h>81#endif82 83#if defined(__linux__)84#include <sys/prctl.h>85#endif86 87using namespace lldb_dap;88using lldb_private::File;89using lldb_private::IOObject;90using lldb_private::MainLoop;91using lldb_private::MainLoopBase;92using lldb_private::NativeFile;93using lldb_private::Socket;94using lldb_private::Status;95 96namespace {97using namespace llvm::opt;98 99enum ID {100  OPT_INVALID = 0, // This is not an option ID.101#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),102#include "Options.inc"103#undef OPTION104};105 106#define OPTTABLE_STR_TABLE_CODE107#include "Options.inc"108#undef OPTTABLE_STR_TABLE_CODE109 110#define OPTTABLE_PREFIXES_TABLE_CODE111#include "Options.inc"112#undef OPTTABLE_PREFIXES_TABLE_CODE113 114static constexpr llvm::opt::OptTable::Info InfoTable[] = {115#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),116#include "Options.inc"117#undef OPTION118};119class LLDBDAPOptTable : public llvm::opt::GenericOptTable {120public:121  LLDBDAPOptTable()122      : llvm::opt::GenericOptTable(OptionStrTable, OptionPrefixesTable,123                                   InfoTable, true) {}124};125} // anonymous namespace126 127static void PrintHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) {128  std::string usage_str = tool_name.str() + " options";129  table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB DAP", false);130 131  llvm::outs() << R"___(132EXAMPLES:133  The debug adapter can be started in two modes.134 135  Running lldb-dap without any arguments will start communicating with the136  parent over stdio. Passing a --connection URI will cause lldb-dap to listen137  for a connection in the specified mode.138 139    lldb-dap --connection listen://localhost:<port>140 141  Passing --wait-for-debugger will pause the process at startup and wait for a142  debugger to attach to the process.143 144    lldb-dap -g145 146  You can also use lldb-dap to launch a supported client, for example the147  LLDB-DAP Visual Studio Code extension.148 149    lldb-dap --client vscode -- /path/to/binary <args>150 151)___";152}153 154static void PrintVersion() {155  llvm::outs() << "lldb-dap: ";156  llvm::cl::PrintVersionMessage();157  llvm::outs() << "liblldb: " << lldb::SBDebugger::GetVersionString() << '\n';158}159 160static llvm::Error LaunchClient(const llvm::opt::InputArgList &args) {161  auto *client_arg = args.getLastArg(OPT_client);162  assert(client_arg && "must have client arg");163 164  std::optional<ClientLauncher::Client> client =165      ClientLauncher::GetClientFrom(client_arg->getValue());166  if (!client)167    return llvm::createStringError(168        llvm::formatv("unsupported client: {0}", client_arg->getValue()));169 170  std::vector<llvm::StringRef> launch_args;171  if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {172    for (auto *value : arg->getValues()) {173      launch_args.push_back(value);174    }175  }176 177  if (launch_args.empty())178    return llvm::createStringError("no launch arguments provided");179 180  return ClientLauncher::GetLauncher(*client)->Launch(launch_args);181}182 183#if not defined(_WIN32)184struct FDGroup {185  int GetFlags() const {186    if (read && write)187      return O_NOCTTY | O_CREAT | O_RDWR;188    if (read)189      return O_NOCTTY | O_RDONLY;190    return O_NOCTTY | O_CREAT | O_WRONLY | O_TRUNC;191  }192 193  std::vector<int> fds;194  bool read = false;195  bool write = false;196};197 198static llvm::Error RedirectToFile(const FDGroup &fdg, llvm::StringRef file) {199  if (!fdg.read && !fdg.write)200    return llvm::Error::success();201  int target_fd = lldb_private::FileSystem::Instance().Open(202      file.str().c_str(), fdg.GetFlags(), 0666);203  if (target_fd == -1)204    return llvm::errorCodeToError(205        std::error_code(errno, std::generic_category()));206  for (int fd : fdg.fds) {207    if (target_fd == fd)208      continue;209    if (::dup2(target_fd, fd) == -1)210      return llvm::errorCodeToError(211          std::error_code(errno, std::generic_category()));212  }213  ::close(target_fd);214  return llvm::Error::success();215}216 217static llvm::Error218SetupIORedirection(const llvm::SmallVectorImpl<llvm::StringRef> &files) {219  llvm::SmallDenseMap<llvm::StringRef, FDGroup> groups;220  for (size_t i = 0; i < files.size(); i++) {221    if (files[i].empty())222      continue;223    auto group = groups.find(files[i]);224    if (group == groups.end())225      group = groups.insert({files[i], {{static_cast<int>(i)}}}).first;226    else227      group->second.fds.push_back(i);228    switch (i) {229    case 0:230      group->second.read = true;231      break;232    case 1:233    case 2:234      group->second.write = true;235      break;236    default:237      group->second.read = true;238      group->second.write = true;239      break;240    }241  }242  for (const auto &[file, group] : groups) {243    if (llvm::Error err = RedirectToFile(group, file))244      return llvm::createStringError(245          llvm::formatv("{0}: {1}", file, llvm::toString(std::move(err))));246  }247  return llvm::Error::success();248}249#endif250 251// If --launch-target is provided, this instance of lldb-dap becomes a252// runInTerminal launcher. It will ultimately launch the program specified in253// the --launch-target argument, which is the original program the user wanted254// to debug. This is done in such a way that the actual debug adapter can255// place breakpoints at the beginning of the program.256//257// The launcher will communicate with the debug adapter using a fifo file in the258// directory specified in the --comm-file argument.259//260// Regarding the actual flow, this launcher will first notify the debug adapter261// of its pid. Then, the launcher will be in a pending state waiting to be262// attached by the adapter.263//264// Once attached and resumed, the launcher will exec and become the program265// specified by --launch-target, which is the original target the266// user wanted to run.267//268// In case of errors launching the target, a suitable error message will be269// emitted to the debug adapter.270static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,271                                             llvm::StringRef comm_file,272                                             lldb::pid_t debugger_pid,273                                             llvm::StringRef stdio,274                                             char *argv[]) {275#if defined(_WIN32)276  return llvm::createStringError(277      "runInTerminal is only supported on POSIX systems");278#else279 280  // On Linux with the Yama security module enabled, a process can only attach281  // to its descendants by default. In the runInTerminal case the target282  // process is launched by the client so we need to allow tracing explicitly.283#if defined(__linux__)284  if (debugger_pid != LLDB_INVALID_PROCESS_ID)285    (void)prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);286#endif287 288  lldb_private::FileSystem::Initialize();289  if (!stdio.empty()) {290    llvm::SmallVector<llvm::StringRef, 3> files;291    stdio.split(files, ':');292    while (files.size() < 3)293      files.push_back(files.back());294    if (llvm::Error err = SetupIORedirection(files))295      return err;296  } else if ((isatty(STDIN_FILENO) != 0) &&297             llvm::StringRef(getenv("TERM")).starts_with_insensitive("xterm")) {298    // Clear the screen.299    llvm::outs() << ANSI_CSI_RESET_CURSOR ANSI_CSI_ERASE_VIEWPORT300            ANSI_CSI_ERASE_SCROLLBACK;301    // VS Code will reuse the same terminal for the same debug configuration302    // between runs. Clear the input buffer prior to starting the new process so303    // prior input is not carried forward to the new debug session.304    tcflush(STDIN_FILENO, TCIFLUSH);305  }306 307  RunInTerminalLauncherCommChannel comm_channel(comm_file);308  if (llvm::Error err = comm_channel.NotifyPid())309    return err;310 311  // We will wait to be attached with a timeout. We don't wait indefinitely312  // using a signal to prevent being paused forever.313 314  // This env var should be used only for tests.315  const char *timeout_env_var = getenv("LLDB_DAP_RIT_TIMEOUT_IN_MS");316  int timeout_in_ms =317      timeout_env_var != nullptr ? atoi(timeout_env_var) : 20000;318  if (llvm::Error err = comm_channel.WaitUntilDebugAdapterAttaches(319          std::chrono::milliseconds(timeout_in_ms))) {320    return err;321  }322 323  const char *target = target_arg.getValue();324  execvp(target, argv);325 326  std::string error = std::strerror(errno);327  comm_channel.NotifyError(error);328  return llvm::createStringError(llvm::inconvertibleErrorCode(),329                                 std::move(error));330#endif331}332 333/// used only by TestVSCode_redirection_to_console.py334static void redirection_test() {335  printf("stdout message\n");336  fprintf(stderr, "stderr message\n");337  fflush(stdout);338  fflush(stderr);339}340 341/// Duplicates a file descriptor, setting FD_CLOEXEC if applicable.342static int DuplicateFileDescriptor(int fd) {343#if defined(F_DUPFD_CLOEXEC)344  // Ensure FD_CLOEXEC is set.345  return ::fcntl(fd, F_DUPFD_CLOEXEC, 0);346#else347  return ::dup(fd);348#endif349}350 351static void352ResetConnectionTimeout(std::mutex &connection_timeout_mutex,353                       MainLoopBase::TimePoint &conncetion_timeout_time_point) {354  std::scoped_lock<std::mutex> lock(connection_timeout_mutex);355  conncetion_timeout_time_point = MainLoopBase::TimePoint();356}357 358static void359TrackConnectionTimeout(MainLoop &loop, std::mutex &connection_timeout_mutex,360                       MainLoopBase::TimePoint &conncetion_timeout_time_point,361                       std::chrono::seconds ttl_seconds) {362  MainLoopBase::TimePoint next_checkpoint =363      std::chrono::steady_clock::now() + std::chrono::seconds(ttl_seconds);364  {365    std::scoped_lock<std::mutex> lock(connection_timeout_mutex);366    // We don't need to take the max of `ttl_time_point` and `next_checkpoint`,367    // because `next_checkpoint` must be the latest.368    conncetion_timeout_time_point = next_checkpoint;369  }370  loop.AddCallback(371      [&connection_timeout_mutex, &conncetion_timeout_time_point,372       next_checkpoint](MainLoopBase &loop) {373        std::scoped_lock<std::mutex> lock(connection_timeout_mutex);374        if (conncetion_timeout_time_point == next_checkpoint)375          loop.RequestTermination();376      },377      next_checkpoint);378}379 380static llvm::Expected<std::pair<Socket::SocketProtocol, std::string>>381validateConnection(llvm::StringRef conn) {382  auto uri = lldb_private::URI::Parse(conn);383 384  auto make_error = [conn]() -> llvm::Error {385    return llvm::createStringError(386        "Unsupported connection specifier, expected 'accept:///path' or "387        "'listen://[host]:port', got '%s'.",388        conn.str().c_str());389  };390 391  if (!uri)392    return make_error();393 394  std::optional<Socket::ProtocolModePair> protocol_and_mode =395      Socket::GetProtocolAndMode(uri->scheme);396  if (!protocol_and_mode || protocol_and_mode->second != Socket::ModeAccept)397    return make_error();398 399  if (protocol_and_mode->first == Socket::ProtocolTcp) {400    return std::make_pair(401        Socket::ProtocolTcp,402        formatv("[{0}]:{1}", uri->hostname.empty() ? "0.0.0.0" : uri->hostname,403                uri->port.value_or(0)));404  }405 406  if (protocol_and_mode->first == Socket::ProtocolUnixDomain)407    return std::make_pair(Socket::ProtocolUnixDomain, uri->path.str());408 409  return make_error();410}411 412static llvm::Error serveConnection(413    const Socket::SocketProtocol &protocol, const std::string &name, Log *log,414    const ReplMode default_repl_mode,415    const std::vector<std::string> &pre_init_commands, bool no_lldbinit,416    std::optional<std::chrono::seconds> connection_timeout_seconds) {417  Status status;418  static std::unique_ptr<Socket> listener = Socket::Create(protocol, status);419  if (status.Fail()) {420    return status.takeError();421  }422 423  status = listener->Listen(name, /*backlog=*/5);424  if (status.Fail()) {425    return status.takeError();426  }427 428  std::string address = llvm::join(listener->GetListeningConnectionURI(), ", ");429  DAP_LOG(log, "started with connection listeners {0}", address);430 431  llvm::outs() << "Listening for: " << address << "\n";432  // Ensure listening address are flushed for calles to retrieve the resolve433  // address.434  llvm::outs().flush();435 436  static MainLoop g_loop;437  llvm::sys::SetInterruptFunction([]() {438    g_loop.AddPendingCallback(439        [](MainLoopBase &loop) { loop.RequestTermination(); });440  });441  static MainLoopBase::TimePoint g_connection_timeout_time_point;442  static std::mutex g_connection_timeout_mutex;443  if (connection_timeout_seconds)444    TrackConnectionTimeout(g_loop, g_connection_timeout_mutex,445                           g_connection_timeout_time_point,446                           connection_timeout_seconds.value());447  std::condition_variable dap_sessions_condition;448  unsigned int clientCount = 0;449  auto handle = listener->Accept(g_loop, [=, &clientCount](450                                             std::unique_ptr<Socket> sock) {451    // Reset the keep alive timer, because we won't be killing the server452    // while this connection is being served.453    if (connection_timeout_seconds)454      ResetConnectionTimeout(g_connection_timeout_mutex,455                             g_connection_timeout_time_point);456    std::string client_name = llvm::formatv("client_{0}", clientCount++).str();457    DAP_LOG(log, "({0}) client connected", client_name);458 459    lldb::IOObjectSP io(std::move(sock));460 461    // Move the client into a background thread to unblock accepting the next462    // client.463    std::thread client([=]() {464      llvm::set_thread_name(client_name + ".runloop");465      MainLoop loop;466      Transport transport(client_name, log, io, io);467      DAP dap(log, default_repl_mode, pre_init_commands, no_lldbinit,468              client_name, transport, loop);469 470      if (auto Err = dap.ConfigureIO()) {471        llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),472                                    "Failed to configure stdout redirect: ");473        return;474      }475 476      // Register the DAP session with the global manager.477      DAPSessionManager::GetInstance().RegisterSession(&loop, &dap);478 479      if (auto Err = dap.Loop()) {480        llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),481                                    "DAP session (" + client_name +482                                        ") error: ");483      }484 485      DAP_LOG(log, "({0}) client disconnected", client_name);486      // Unregister the DAP session from the global manager.487      DAPSessionManager::GetInstance().UnregisterSession(&loop);488      // Start the countdown to kill the server at the end of each connection.489      if (connection_timeout_seconds)490        TrackConnectionTimeout(g_loop, g_connection_timeout_mutex,491                               g_connection_timeout_time_point,492                               connection_timeout_seconds.value());493    });494    client.detach();495  });496 497  if (auto Err = handle.takeError()) {498    return Err;499  }500 501  status = g_loop.Run();502  if (status.Fail()) {503    return status.takeError();504  }505 506  DAP_LOG(507      log,508      "lldb-dap server shutdown requested, disconnecting remaining clients...");509 510  // Disconnect all active sessions using the global manager.511  DAPSessionManager::GetInstance().DisconnectAllSessions();512 513  // Wait for all clients to finish disconnecting and return any errors.514  return DAPSessionManager::GetInstance().WaitForAllSessionsToDisconnect();515}516 517int main(int argc, char *argv[]) {518  llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);519#if !defined(__APPLE__)520  llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL521                        " and include the crash backtrace.\n");522#else523  llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL524                        " and include the crash report from "525                        "~/Library/Logs/DiagnosticReports/.\n");526#endif527 528  llvm::SmallString<256> program_path(argv[0]);529  llvm::sys::fs::make_absolute(program_path);530  DAP::debug_adapter_path = program_path;531 532  LLDBDAPOptTable T;533  unsigned MAI, MAC;534  llvm::ArrayRef<const char *> ArgsArr = llvm::ArrayRef(argv + 1, argc);535  llvm::opt::InputArgList input_args = T.ParseArgs(ArgsArr, MAI, MAC);536 537  if (input_args.hasArg(OPT_help)) {538    PrintHelp(T, llvm::sys::path::filename(argv[0]));539    return EXIT_SUCCESS;540  }541 542  if (input_args.hasArg(OPT_version)) {543    PrintVersion();544    return EXIT_SUCCESS;545  }546 547  if (input_args.hasArg(OPT_client)) {548    if (llvm::Error error = LaunchClient(input_args)) {549      llvm::WithColor::error() << llvm::toString(std::move(error)) << '\n';550      return EXIT_FAILURE;551    }552    return EXIT_SUCCESS;553  }554 555  ReplMode default_repl_mode = ReplMode::Auto;556  if (input_args.hasArg(OPT_repl_mode)) {557    llvm::opt::Arg *repl_mode = input_args.getLastArg(OPT_repl_mode);558    llvm::StringRef repl_mode_value = repl_mode->getValue();559    if (repl_mode_value == "auto") {560      default_repl_mode = ReplMode::Auto;561    } else if (repl_mode_value == "variable") {562      default_repl_mode = ReplMode::Variable;563    } else if (repl_mode_value == "command") {564      default_repl_mode = ReplMode::Command;565    } else {566      llvm::errs() << "'" << repl_mode_value567                   << "' is not a valid option, use 'variable', 'command' or "568                      "'auto'.\n";569      return EXIT_FAILURE;570    }571  }572 573  if (llvm::opt::Arg *target_arg = input_args.getLastArg(OPT_launch_target)) {574    if (llvm::opt::Arg *comm_file = input_args.getLastArg(OPT_comm_file)) {575      lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;576      llvm::opt::Arg *debugger_pid = input_args.getLastArg(OPT_debugger_pid);577      if (debugger_pid) {578        llvm::StringRef debugger_pid_value = debugger_pid->getValue();579        if (debugger_pid_value.getAsInteger(10, pid)) {580          llvm::errs() << "'" << debugger_pid_value581                       << "' is not a valid "582                          "PID\n";583          return EXIT_FAILURE;584        }585      }586      int target_args_pos = argc;587      for (int i = 0; i < argc; i++) {588        if (strcmp(argv[i], "--launch-target") == 0) {589          target_args_pos = i + 1;590          break;591        }592      }593      llvm::StringRef stdio = input_args.getLastArgValue(OPT_stdio);594      if (llvm::Error err =595              LaunchRunInTerminalTarget(*target_arg, comm_file->getValue(), pid,596                                        stdio, argv + target_args_pos)) {597        llvm::errs() << llvm::toString(std::move(err)) << '\n';598        return EXIT_FAILURE;599      }600    } else {601      llvm::errs() << "\"--launch-target\" requires \"--comm-file\" to be "602                      "specified\n";603      return EXIT_FAILURE;604    }605  }606 607  std::string connection;608  if (auto *arg = input_args.getLastArg(OPT_connection)) {609    const auto *path = arg->getValue();610    connection.assign(path);611  }612 613  std::optional<std::chrono::seconds> connection_timeout_seconds;614  if (llvm::opt::Arg *connection_timeout_arg =615          input_args.getLastArg(OPT_connection_timeout)) {616    if (!connection.empty()) {617      llvm::StringRef connection_timeout_string_value =618          connection_timeout_arg->getValue();619      int connection_timeout_int_value;620      if (connection_timeout_string_value.getAsInteger(621              10, connection_timeout_int_value)) {622        llvm::errs() << "'" << connection_timeout_string_value623                     << "' is not a valid connection timeout value\n";624        return EXIT_FAILURE;625      }626      // Ignore non-positive values.627      if (connection_timeout_int_value > 0)628        connection_timeout_seconds =629            std::chrono::seconds(connection_timeout_int_value);630    } else {631      llvm::errs()632          << "\"--connection-timeout\" requires \"--connection\" to be "633             "specified\n";634      return EXIT_FAILURE;635    }636  }637 638#if !defined(_WIN32)639  if (input_args.hasArg(OPT_wait_for_debugger)) {640    printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());641    pause();642  }643#endif644 645  std::unique_ptr<Log> log = nullptr;646  const char *log_file_path = getenv("LLDBDAP_LOG");647  if (log_file_path) {648    std::error_code EC;649    log = std::make_unique<Log>(log_file_path, EC);650    if (EC) {651      llvm::logAllUnhandledErrors(llvm::errorCodeToError(EC), llvm::errs(),652                                  "Failed to create log file: ");653      return EXIT_FAILURE;654    }655  }656 657  // Initialize LLDB first before we do anything.658  lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling();659  if (error.Fail()) {660    lldb::SBStream os;661    error.GetDescription(os);662    llvm::errs() << "lldb initialize failed: " << os.GetData() << "\n";663    return EXIT_FAILURE;664  }665 666  // Create a memory monitor. This can return nullptr if the host platform is667  // not supported.668  std::unique_ptr<lldb_private::MemoryMonitor> memory_monitor =669      lldb_private::MemoryMonitor::Create([log = log.get()]() {670        DAP_LOG(log, "memory pressure detected");671        lldb::SBDebugger::MemoryPressureDetected();672      });673 674  if (memory_monitor)675    memory_monitor->Start();676 677  // Terminate the debugger before the C++ destructor chain kicks in.678  auto terminate_debugger = llvm::make_scope_exit([&] {679    if (memory_monitor)680      memory_monitor->Stop();681    lldb::SBDebugger::Terminate();682  });683 684  std::vector<std::string> pre_init_commands;685  for (const std::string &arg :686       input_args.getAllArgValues(OPT_pre_init_command)) {687    pre_init_commands.push_back(arg);688  }689 690  bool no_lldbinit = input_args.hasArg(OPT_no_lldbinit);691 692  if (!connection.empty()) {693    auto maybeProtoclAndName = validateConnection(connection);694    if (auto Err = maybeProtoclAndName.takeError()) {695      llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),696                                  "Invalid connection: ");697      return EXIT_FAILURE;698    }699 700    Socket::SocketProtocol protocol;701    std::string name;702    std::tie(protocol, name) = *maybeProtoclAndName;703    if (auto Err = serveConnection(protocol, name, log.get(), default_repl_mode,704                                   pre_init_commands, no_lldbinit,705                                   connection_timeout_seconds)) {706      llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),707                                  "Connection failed: ");708      return EXIT_FAILURE;709    }710 711    return EXIT_SUCCESS;712  }713 714#if defined(_WIN32)715  // Windows opens stdout and stdin in text mode which converts \n to 13,10716  // while the value is just 10 on Darwin/Linux. Setting the file mode to717  // binary fixes this.718  int result = _setmode(fileno(stdout), _O_BINARY);719  assert(result);720  result = _setmode(fileno(stdin), _O_BINARY);721  UNUSED_IF_ASSERT_DISABLED(result);722  assert(result);723#endif724 725  int stdout_fd = DuplicateFileDescriptor(fileno(stdout));726  if (stdout_fd == -1) {727    llvm::logAllUnhandledErrors(728        llvm::errorCodeToError(llvm::errnoAsErrorCode()), llvm::errs(),729        "Failed to configure stdout redirect: ");730    return EXIT_FAILURE;731  }732 733  lldb::IOObjectSP input = std::make_shared<NativeFile>(734      fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);735  lldb::IOObjectSP output = std::make_shared<NativeFile>(736      stdout_fd, File::eOpenOptionWriteOnly, NativeFile::Unowned);737 738  constexpr llvm::StringLiteral client_name = "stdio";739  MainLoop loop;740  Transport transport(client_name, log.get(), input, output);741  DAP dap(log.get(), default_repl_mode, pre_init_commands, no_lldbinit,742          client_name, transport, loop);743 744  // stdout/stderr redirection to the IDE's console745  if (auto Err = dap.ConfigureIO(stdout, stderr)) {746    llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),747                                "Failed to configure stdout redirect: ");748    return EXIT_FAILURE;749  }750 751  // Register the DAP session with the global manager for stdio mode.752  // This is needed for the event handling to find the correct DAP instance.753  DAPSessionManager::GetInstance().RegisterSession(&loop, &dap);754 755  // used only by TestVSCode_redirection_to_console.py756  if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)757    redirection_test();758 759  if (auto Err = dap.Loop()) {760    DAP_LOG(log.get(), "({0}) DAP session error: {1}", client_name,761            llvm::toStringWithoutConsuming(Err));762    llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),763                                "DAP session error: ");764    DAPSessionManager::GetInstance().UnregisterSession(&loop);765    return EXIT_FAILURE;766  }767  DAPSessionManager::GetInstance().UnregisterSession(&loop);768  return EXIT_SUCCESS;769}770