[Lldb-commits] [lldb] [lldb-dap] Refactoring lldb-dap port listening mode to allow multiple connections. (PR #116392)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 26 15:05:12 PST 2024


https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/116392

>From 8f69fb64cde9582bbeb64774ff506ace6877fddd Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Fri, 15 Nov 2024 09:56:43 -0500
Subject: [PATCH 1/5] [lldb-da] Refactoring lldb-dap port listening mode to
 allow multiple connections.

This refactors the port listening mode to allocate a new DAP object for each connection, allowing multiple connections to run concurrently.
---
 .../Python/lldbsuite/test/lldbtest.py         |   8 +
 .../test/tools/lldb-dap/dap_server.py         |  75 +++-
 .../test/tools/lldb-dap/lldbdap_testcase.py   |  32 +-
 lldb/test/API/tools/lldb-dap/server/Makefile  |   3 +
 .../tools/lldb-dap/server/TestDAP_server.py   |  66 ++++
 lldb/test/API/tools/lldb-dap/server/main.c    |   6 +
 lldb/tools/lldb-dap/DAP.cpp                   |  23 +-
 lldb/tools/lldb-dap/DAP.h                     |  50 +--
 lldb/tools/lldb-dap/Options.td                |   9 +
 lldb/tools/lldb-dap/OutputRedirector.cpp      |   7 +-
 lldb/tools/lldb-dap/OutputRedirector.h        |  12 +-
 lldb/tools/lldb-dap/lldb-dap.cpp              | 366 ++++++++++++++----
 12 files changed, 506 insertions(+), 151 deletions(-)
 create mode 100644 lldb/test/API/tools/lldb-dap/server/Makefile
 create mode 100644 lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
 create mode 100644 lldb/test/API/tools/lldb-dap/server/main.c

diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 8884ef5933ada8..a899854bb5ae14 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -39,6 +39,7 @@
 import signal
 from subprocess import *
 import sys
+import socket
 import time
 import traceback
 
@@ -250,6 +251,13 @@ def which(program):
     return None
 
 
+def pickrandomport():
+    """Returns a random open port."""
+    with socket.socket() as sock:
+        sock.bind(("", 0))
+        return sock.getsockname()[1]
+
+
 class ValueCheck:
     def __init__(
         self,
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index c29992ce9c7848..e4a53fe0d45907 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -903,7 +903,7 @@ def request_setBreakpoints(self, file_path, line_array, data=None):
             "sourceModified": False,
         }
         if line_array is not None:
-            args_dict["lines"] = "%s" % line_array
+            args_dict["lines"] = line_array
             breakpoints = []
             for i, line in enumerate(line_array):
                 breakpoint_data = None
@@ -1154,34 +1154,38 @@ class DebugAdaptorServer(DebugCommunication):
     def __init__(
         self,
         executable=None,
+        launch=True,
         port=None,
+        unix_socket=None,
         init_commands=[],
         log_file=None,
         env=None,
     ):
         self.process = None
-        if executable is not None:
-            adaptor_env = os.environ.copy()
-            if env is not None:
-                adaptor_env.update(env)
-
-            if log_file:
-                adaptor_env["LLDBDAP_LOG"] = log_file
-            self.process = subprocess.Popen(
-                [executable],
-                stdin=subprocess.PIPE,
-                stdout=subprocess.PIPE,
-                stderr=subprocess.PIPE,
-                env=adaptor_env,
+        if launch:
+            self.process = DebugAdaptorServer.launch(
+                executable,
+                port=port,
+                unix_socket=unix_socket,
+                log_file=log_file,
+                env=env,
             )
-            DebugCommunication.__init__(
-                self, self.process.stdout, self.process.stdin, init_commands, log_file
-            )
-        elif port is not None:
+
+        if port:
             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             s.connect(("127.0.0.1", port))
             DebugCommunication.__init__(
-                self, s.makefile("r"), s.makefile("w"), init_commands
+                self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
+            )
+        elif unix_socket:
+            s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            s.connect(unix_socket)
+            DebugCommunication.__init__(
+                self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
+            )
+        else:
+            DebugCommunication.__init__(
+                self, self.process.stdout, self.process.stdin, init_commands, log_file
             )
 
     def get_pid(self):
@@ -1196,6 +1200,39 @@ def terminate(self):
             self.process.wait()
             self.process = None
 
+    @classmethod
+    def launch(
+        cls, executable: str, /, port=None, unix_socket=None, log_file=None, env=None
+    ) -> subprocess.Popen:
+        adaptor_env = os.environ.copy()
+        if env:
+            adaptor_env.update(env)
+
+        if log_file:
+            adaptor_env["LLDBDAP_LOG"] = log_file
+
+        args = [executable]
+        if port:
+            args.append("--port")
+            args.append(str(port))
+        elif unix_socket:
+            args.append("--unix-socket")
+            args.append(unix_socket)
+
+        proc = subprocess.Popen(
+            args,
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=sys.stdout,
+            env=adaptor_env,
+        )
+
+        if port or unix_socket:
+            # Wait for the server to startup.
+            time.sleep(0.1)
+
+        return proc
+
 
 def attach_options_specified(options):
     if options.pid is not None:
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index a25466f07fa557..3fcc08e9ff55cb 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -13,7 +13,7 @@ class DAPTestCaseBase(TestBase):
     timeoutval = 10 * (10 if ('ASAN_OPTIONS' in os.environ) else 1)
     NO_DEBUG_INFO_TESTCASE = True
 
-    def create_debug_adaptor(self, lldbDAPEnv=None):
+    def create_debug_adaptor(self, env=None, launch=True, port=None, unix_socket=None):
         """Create the Visual Studio Code debug adaptor"""
         self.assertTrue(
             is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
@@ -21,14 +21,28 @@ def create_debug_adaptor(self, lldbDAPEnv=None):
         log_file_path = self.getBuildArtifact("dap.txt")
         self.dap_server = dap_server.DebugAdaptorServer(
             executable=self.lldbDAPExec,
+            launch=launch,
+            port=port,
+            unix_socket=unix_socket,
             init_commands=self.setUpCommands(),
             log_file=log_file_path,
-            env=lldbDAPEnv,
+            env=env,
         )
 
-    def build_and_create_debug_adaptor(self, lldbDAPEnv=None):
+    def build_and_create_debug_adaptor(
+        self,
+        lldbDAPEnv=None,
+        lldbDAPLaunch=True,
+        lldbDAPPort=None,
+        lldbDAPUnixSocket=None,
+    ):
         self.build()
-        self.create_debug_adaptor(lldbDAPEnv)
+        self.create_debug_adaptor(
+            env=lldbDAPEnv,
+            launch=lldbDAPLaunch,
+            port=lldbDAPPort,
+            unix_socket=lldbDAPUnixSocket,
+        )
 
     def set_source_breakpoints(self, source_path, lines, data=None):
         """Sets source breakpoints and returns an array of strings containing
@@ -475,11 +489,19 @@ def build_and_launch(
         customThreadFormat=None,
         launchCommands=None,
         expectFailure=False,
+        lldbDAPPort=None,
+        lldbDAPUnixSocket=None,
+        lldbDAPLaunch=True,
     ):
         """Build the default Makefile target, create the DAP debug adaptor,
         and launch the process.
         """
-        self.build_and_create_debug_adaptor(lldbDAPEnv)
+        self.build_and_create_debug_adaptor(
+            lldbDAPEnv=lldbDAPEnv,
+            lldbDAPLaunch=lldbDAPLaunch,
+            lldbDAPPort=lldbDAPPort,
+            lldbDAPUnixSocket=lldbDAPUnixSocket,
+        )
         self.assertTrue(os.path.exists(program), "executable must exist")
 
         return self.launch(
diff --git a/lldb/test/API/tools/lldb-dap/server/Makefile b/lldb/test/API/tools/lldb-dap/server/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
new file mode 100644
index 00000000000000..46b992a77a4815
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -0,0 +1,66 @@
+"""
+Test lldb-dap server integration.
+"""
+
+import os
+import tempfile
+
+import dap_server
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbdap_testcase
+
+
+class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
+    def do_test_server(self, port=None, unix_socket=None):
+        log_file_path = self.getBuildArtifact("dap.txt")
+        server = dap_server.DebugAdaptorServer.launch(
+            self.lldbDAPExec, port=port, unix_socket=unix_socket, log_file=log_file_path
+        )
+
+        def cleanup():
+            server.terminate()
+            server.wait()
+
+        self.addTearDownHook(cleanup)
+
+        self.build()
+        program = self.getBuildArtifact("a.out")
+        source = "main.c"
+        breakpoint_line = line_number(source, "// breakpoint")
+
+        # Initial connection over the port.
+        self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
+        self.launch(
+            program,
+            disconnectAutomatically=False,
+        )
+        self.set_source_breakpoints(source, [breakpoint_line])
+        self.continue_to_next_stop()
+        self.continue_to_exit()
+        output = self.get_stdout()
+        self.assertEquals(output, "hello world!\r\n")
+        self.dap_server.request_disconnect()
+
+        # Second connection over the port.
+        self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
+        self.launch(program)
+        self.set_source_breakpoints(source, [breakpoint_line])
+        self.continue_to_next_stop()
+        self.continue_to_exit()
+        output = self.get_stdout()
+        self.assertEquals(output, "hello world!\r\n")
+
+    def test_server_port(self):
+        """
+        Test launching a binary with a lldb-dap in server mode on a specific port.
+        """
+        port = pickrandomport()
+        self.do_test_server(port=port)
+
+    def test_server_unix_socket(self):
+        """
+        Test launching a binary with a lldb-dap in server mode on a unix socket.
+        """
+        dir = tempfile.gettempdir()
+        self.do_test_server(unix_socket=dir + "/dap-connection-" + str(os.getpid()))
diff --git a/lldb/test/API/tools/lldb-dap/server/main.c b/lldb/test/API/tools/lldb-dap/server/main.c
new file mode 100644
index 00000000000000..9a6326f3b57d45
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[]) {
+  printf("hello world!\n"); // breakpoint 1
+  return 0;
+}
\ No newline at end of file
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 35250d9eef608a..8998036fbedf6b 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -32,10 +32,11 @@ using namespace lldb_dap;
 
 namespace lldb_dap {
 
-DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
+DAP::DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
+         ReplMode repl_mode, std::vector<std::string> pre_init_commands)
     : debug_adaptor_path(path), broadcaster("lldb-dap"),
-      exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
-      stop_at_entry(false), is_attach(false),
+      log(log), exception_breakpoints(), pre_init_commands(pre_init_commands),
+      focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
       enable_auto_variable_summaries(false),
       enable_synthetic_child_debugging(false),
       display_extended_backtrace(false),
@@ -43,21 +44,7 @@ DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
       configuration_done_sent(false), waiting_for_run_in_terminal(false),
       progress_event_reporter(
           [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
-      reverse_request_seq(0), repl_mode(repl_mode) {
-  const char *log_file_path = getenv("LLDBDAP_LOG");
-#if defined(_WIN32)
-  // Windows opens stdout and stdin in text mode which converts \n to 13,10
-  // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
-  // fixes this.
-  int result = _setmode(fileno(stdout), _O_BINARY);
-  assert(result);
-  result = _setmode(fileno(stdin), _O_BINARY);
-  UNUSED_IF_ASSERT_DISABLED(result);
-  assert(result);
-#endif
-  if (log_file_path)
-    log.reset(new std::ofstream(log_file_path));
-}
+      reverse_request_seq(0), repl_mode(repl_mode) {}
 
 DAP::~DAP() = default;
 
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index ae496236f13369..ec70da67edac14 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -9,36 +9,33 @@
 #ifndef LLDB_TOOLS_LLDB_DAP_DAP_H
 #define LLDB_TOOLS_LLDB_DAP_DAP_H
 
-#include <cstdio>
-#include <iosfwd>
-#include <map>
-#include <optional>
-#include <thread>
-
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/JSON.h"
-#include "llvm/Support/Threading.h"
-#include "llvm/Support/raw_ostream.h"
-
+#include "ExceptionBreakpoint.h"
+#include "FunctionBreakpoint.h"
+#include "IOStream.h"
+#include "InstructionBreakpoint.h"
+#include "ProgressEvent.h"
+#include "SourceBreakpoint.h"
 #include "lldb/API/SBAttachInfo.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBCommandReturnObject.h"
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFile.h"
 #include "lldb/API/SBFormat.h"
 #include "lldb/API/SBLaunchInfo.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBThread.h"
-
-#include "ExceptionBreakpoint.h"
-#include "FunctionBreakpoint.h"
-#include "IOStream.h"
-#include "InstructionBreakpoint.h"
-#include "ProgressEvent.h"
-#include "SourceBreakpoint.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+#include <iosfwd>
+#include <map>
+#include <optional>
+#include <thread>
 
 #define VARREF_LOCALS (int64_t)1
 #define VARREF_GLOBALS (int64_t)2
@@ -140,13 +137,16 @@ struct DAP {
   llvm::StringRef debug_adaptor_path;
   InputStream input;
   OutputStream output;
+  lldb::SBFile in;
+  lldb::SBFile out;
+  lldb::SBFile err;
   lldb::SBDebugger debugger;
   lldb::SBTarget target;
   Variables variables;
   lldb::SBBroadcaster broadcaster;
   std::thread event_thread;
   std::thread progress_event_thread;
-  std::unique_ptr<std::ofstream> log;
+  std::shared_ptr<std::ofstream> log;
   llvm::StringMap<SourceBreakpointMap> source_breakpoints;
   FunctionBreakpointMap function_breakpoints;
   InstructionBreakpointMap instruction_breakpoints;
@@ -198,10 +198,14 @@ struct DAP {
   // will contain that expression.
   std::string last_nonempty_var_expression;
 
-  DAP(llvm::StringRef path, ReplMode repl_mode);
+  DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
+      ReplMode repl_mode, std::vector<std::string> pre_init_commands);
   ~DAP();
+
+  DAP() = delete;
   DAP(const DAP &rhs) = delete;
   void operator=(const DAP &rhs) = delete;
+
   ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
   ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
 
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index d7b4a065abec01..dfca79b2884ac8 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -22,8 +22,17 @@ def port: S<"port">,
   HelpText<"Communicate with the lldb-dap tool over the defined port.">;
 def: Separate<["-"], "p">,
   Alias<port>,
+  MetaVarName<"<port>">,
   HelpText<"Alias for --port">;
 
+def unix_socket: S<"unix-socket">,
+  MetaVarName<"<path>">,
+  HelpText<"Communicate with the lldb-dap tool over the unix socket or named pipe.">;
+def: Separate<["-"], "u">,
+  Alias<unix_socket>,
+  MetaVarName<"<path>">,
+  HelpText<"Alias for --unix_socket">;
+
 def launch_target: S<"launch-target">,
   MetaVarName<"<target>">,
   HelpText<"Launch a target for the launchInTerminal request. Any argument "
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index 2c2f49569869b4..0b725e1901b9fd 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -21,7 +21,8 @@ using namespace llvm;
 
 namespace lldb_dap {
 
-Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
+Expected<int> RedirectFd(int fd,
+                         std::function<void(llvm::StringRef)> callback) {
   int new_fd[2];
 #if defined(_WIN32)
   if (_pipe(new_fd, 4096, O_TEXT) == -1) {
@@ -34,7 +35,7 @@ Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
                              strerror(error));
   }
 
-  if (dup2(new_fd[1], fd) == -1) {
+  if (fd != -1 && dup2(new_fd[1], fd) == -1) {
     int error = errno;
     return createStringError(inconvertibleErrorCode(),
                              "Couldn't override the fd %d. %s", fd,
@@ -57,7 +58,7 @@ Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
     }
   });
   t.detach();
-  return Error::success();
+  return new_fd[1];
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h
index e26d1648b104f9..418b8bac102c7f 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.h
+++ b/lldb/tools/lldb-dap/OutputRedirector.h
@@ -16,10 +16,16 @@ namespace lldb_dap {
 
 /// Redirects the output of a given file descriptor to a callback.
 ///
+/// \param[in] fd
+///     Either -1 or the fd duplicate into the new handle.
+///
+/// \param[in] callback
+///     A callback invoked each time the file is written.
+///
 /// \return
-///     \a Error::success if the redirection was set up correctly, or an error
-///     otherwise.
-llvm::Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback);
+///     A new file handle for the output.
+llvm::Expected<int> RedirectFd(int fd,
+                               std::function<void(llvm::StringRef)> callback);
 
 } // namespace lldb_dap
 
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 3bfc578806021e..51197587ffccec 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -14,6 +14,7 @@
 #include "RunInTerminal.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
+#include "lldb/API/SBFile.h"
 #include "lldb/API/SBInstruction.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
@@ -66,6 +67,7 @@
 #else
 #include <netinet/in.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 #endif
 
@@ -116,6 +118,8 @@ enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
 /// Page size used for reporting addtional frames in the 'stackTrace' request.
 constexpr int StackPageSize = 20;
 
+void RegisterRequestCallbacks(DAP &dap);
+
 /// Prints a welcome message on the editor if the preprocessor variable
 /// LLDB_DAP_WELCOME_MESSAGE is defined.
 static void PrintWelcomeMessage(DAP &dap) {
@@ -137,42 +141,232 @@ lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
   }
 }
 
-SOCKET AcceptConnection(DAP &dap, int portno) {
-  // Accept a socket connection from any host on "portno".
-  SOCKET newsockfd = -1;
-  struct sockaddr_in serv_addr, cli_addr;
+/// Redirect stdout and stderr fo the IDE's console output.
+///
+/// Errors in this operation will be printed to the log file and the IDE's
+/// console output as well.
+///
+/// \return
+///     A fd pointing to the original stdout.
+void SetupRedirection(DAP &dap, int stdoutfd = -1, int stderrfd = -1) {
+  auto output_callback_stderr = [&dap](llvm::StringRef data) {
+    dap.SendOutput(OutputType::Stderr, data);
+  };
+  auto output_callback_stdout = [&dap](llvm::StringRef data) {
+    dap.SendOutput(OutputType::Stdout, data);
+  };
+
+  llvm::Expected<int> new_stdout_fd =
+      RedirectFd(stdoutfd, output_callback_stdout);
+  if (auto err = new_stdout_fd.takeError()) {
+    std::string error_message = llvm::toString(std::move(err));
+    if (dap.log)
+      *dap.log << error_message << std::endl;
+    output_callback_stderr(error_message);
+  }
+  dap.out = lldb::SBFile(new_stdout_fd.get(), "w", false);
+
+  llvm::Expected<int> new_stderr_fd =
+      RedirectFd(stderrfd, output_callback_stderr);
+  if (auto err = new_stderr_fd.takeError()) {
+    std::string error_message = llvm::toString(std::move(err));
+    if (dap.log)
+      *dap.log << error_message << std::endl;
+    output_callback_stderr(error_message);
+  }
+  dap.err = lldb::SBFile(new_stderr_fd.get(), "w", false);
+}
+
+void HandleClient(int clientfd, llvm::StringRef program_path,
+                  const std::vector<std::string> &pre_init_commands,
+                  std::shared_ptr<std::ofstream> log,
+                  ReplMode default_repl_mode) {
+  if (log)
+    *log << "client[" << clientfd << "] connected\n";
+  DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
+  dap.debug_adaptor_path = program_path;
+
+  SetupRedirection(dap);
+  RegisterRequestCallbacks(dap);
+
+  dap.input.descriptor = StreamDescriptor::from_socket(clientfd, false);
+  dap.output.descriptor = StreamDescriptor::from_socket(clientfd, false);
+
+  for (const std::string &arg : pre_init_commands) {
+    dap.pre_init_commands.push_back(arg);
+  }
+
+  if (auto Err = dap.Loop()) {
+    if (log)
+      *log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
+  }
+
+  if (log)
+    *log << "client[" << clientfd << "] connection closed\n";
+#if defined(_WIN32)
+  closesocket(clientfd);
+#else
+  close(clientfd);
+#endif
+}
+
+std::error_code getLastSocketErrorCode() {
+#ifdef _WIN32
+  return std::error_code(::WSAGetLastError(), std::system_category());
+#else
+  return llvm::errnoAsErrorCode();
+#endif
+}
+
+llvm::Expected<int> getSocketFD(llvm::StringRef path) {
+  if (llvm::sys::fs::exists(path) && (::remove(path.str().c_str()) == -1)) {
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Remove existing socket failed");
+  }
+
+  SOCKET sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (sockfd == -1) {
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Create socket failed");
+  }
+
+  struct sockaddr_un addr;
+  bzero(&addr, sizeof(addr));
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, path.str().c_str(), sizeof(addr.sun_path) - 1);
+
+  if (::bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+#if defined(_WIN32)
+    closesocket(sockfd);
+#else
+    close(sockfd);
+#endif
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Socket bind() failed");
+  }
+
+  if (listen(sockfd, llvm::hardware_concurrency().compute_thread_count()) < 0) {
+#if defined(_WIN32)
+    closesocket(sockfd);
+#else
+    close(sockfd);
+#endif
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Socket listen() failed");
+  }
+
+  return sockfd;
+}
+
+llvm::Expected<int> getSocketFD(int portno) {
   SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if (sockfd < 0) {
-    if (dap.log)
-      *dap.log << "error: opening socket (" << strerror(errno) << ")"
-               << std::endl;
-  } else {
-    memset((char *)&serv_addr, 0, sizeof(serv_addr));
-    serv_addr.sin_family = AF_INET;
-    // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-    serv_addr.sin_port = htons(portno);
-    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
-      if (dap.log)
-        *dap.log << "error: binding socket (" << strerror(errno) << ")"
-                 << std::endl;
-    } else {
-      listen(sockfd, 5);
-      socklen_t clilen = sizeof(cli_addr);
-      newsockfd =
-          llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
-                                      (struct sockaddr *)&cli_addr, &clilen);
-      if (newsockfd < 0)
-        if (dap.log)
-          *dap.log << "error: accept (" << strerror(errno) << ")" << std::endl;
-    }
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Create socket failed");
+  }
+
+  struct sockaddr_in serv_addr;
+  bzero(&serv_addr, sizeof(serv_addr));
+  serv_addr.sin_family = AF_INET;
+  // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  serv_addr.sin_port = htons(portno);
+  if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
 #if defined(_WIN32)
     closesocket(sockfd);
 #else
     close(sockfd);
 #endif
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Socket bind() failed");
   }
-  return newsockfd;
+
+  if (listen(sockfd, llvm::hardware_concurrency().compute_thread_count()) < 0) {
+#if defined(_WIN32)
+    closesocket(sockfd);
+#else
+    close(sockfd);
+#endif
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "Socket listen() failed");
+  }
+
+  return sockfd;
+}
+
+int AcceptConnection(llvm::StringRef program_path,
+                     const std::vector<std::string> &pre_init_commands,
+                     std::shared_ptr<std::ofstream> log,
+                     ReplMode default_repl_mode,
+                     llvm::StringRef unix_socket_path) {
+  auto listening = getSocketFD(unix_socket_path);
+  if (auto E = listening.takeError()) {
+    llvm::errs() << "Listening on " << unix_socket_path
+                 << " failed: " << llvm::toString(std::move(E)) << "\n";
+    return EXIT_FAILURE;
+  }
+
+  while (true) {
+    struct sockaddr_un cli_addr;
+    bzero(&cli_addr, sizeof(struct sockaddr_un));
+    socklen_t clilen = sizeof(cli_addr);
+    SOCKET clientfd =
+        llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, *listening,
+                                    (struct sockaddr *)&cli_addr, &clilen);
+    if (clientfd < 0) {
+      llvm::errs() << "Client accept failed: "
+                   << getLastSocketErrorCode().message() << "\n";
+      return EXIT_FAILURE;
+    }
+
+    std::thread t(HandleClient, clientfd, program_path, pre_init_commands, log,
+                  default_repl_mode);
+    t.detach();
+  }
+
+#if defined(_WIN32)
+  closesocket(*listening);
+#else
+  close(*listening);
+#endif
+  return 0;
+}
+
+int AcceptConnection(llvm::StringRef program_path,
+                     const std::vector<std::string> &pre_init_commands,
+                     std::shared_ptr<std::ofstream> log,
+                     ReplMode default_repl_mode, int portno) {
+  auto listening = getSocketFD(portno);
+  if (auto E = listening.takeError()) {
+    llvm::errs() << "Listening on " << portno
+                 << " failed: " << llvm::toString(std::move(E)) << "\n";
+    return EXIT_FAILURE;
+  }
+
+  while (true) {
+    struct sockaddr_in cli_addr;
+    bzero(&cli_addr, sizeof(struct sockaddr_in));
+    socklen_t clilen = sizeof(cli_addr);
+    SOCKET clientfd =
+        llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, *listening,
+                                    (struct sockaddr *)&cli_addr, &clilen);
+    if (clientfd < 0) {
+      llvm::errs() << "Client accept failed: "
+                   << getLastSocketErrorCode().message() << "\n";
+      return EXIT_FAILURE;
+    }
+
+    std::thread t(HandleClient, clientfd, program_path, pre_init_commands, log,
+                  default_repl_mode);
+    t.detach();
+  }
+
+#if defined(_WIN32)
+  closesocket(*listening);
+#else
+  close(*listening);
+#endif
+  return 0;
 }
 
 std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
@@ -1868,7 +2062,21 @@ void request_initialize(DAP &dap, const llvm::json::Object &request) {
   // which may affect the outcome of tests.
   bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
 
-  dap.debugger = lldb::SBDebugger::Create(source_init_file);
+  lldb::SBDebugger debugger = lldb::SBDebugger::Create(false);
+  debugger.SetInputFile(dap.in);
+  debugger.SetOutputFile(dap.out);
+  debugger.SetErrorFile(dap.err);
+
+  lldb::SBCommandInterpreter interp = debugger.GetCommandInterpreter();
+
+  if (source_init_file) {
+    lldb::SBCommandReturnObject result;
+    interp.SourceInitFileInCurrentWorkingDirectory(result);
+    interp.SourceInitFileInHomeDirectory(result, false);
+  }
+
+  dap.debugger = debugger;
+
   if (llvm::Error err = dap.RunPreInitCommands()) {
     response["success"] = false;
     EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
@@ -4907,38 +5115,6 @@ static void redirection_test() {
   fflush(stderr);
 }
 
-/// Redirect stdout and stderr fo the IDE's console output.
-///
-/// Errors in this operation will be printed to the log file and the IDE's
-/// console output as well.
-///
-/// \return
-///     A fd pointing to the original stdout.
-static int SetupStdoutStderrRedirection(DAP &dap) {
-  int stdoutfd = fileno(stdout);
-  int new_stdout_fd = dup(stdoutfd);
-  auto output_callback_stderr = [&dap](llvm::StringRef data) {
-    dap.SendOutput(OutputType::Stderr, data);
-  };
-  auto output_callback_stdout = [&dap](llvm::StringRef data) {
-    dap.SendOutput(OutputType::Stdout, data);
-  };
-  if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) {
-    std::string error_message = llvm::toString(std::move(err));
-    if (dap.log)
-      *dap.log << error_message << std::endl;
-    output_callback_stderr(error_message);
-  }
-  if (llvm::Error err = RedirectFd(fileno(stderr), output_callback_stderr)) {
-    std::string error_message = llvm::toString(std::move(err));
-    if (dap.log)
-      *dap.log << error_message << std::endl;
-    output_callback_stderr(error_message);
-  }
-
-  return new_stdout_fd;
-}
-
 int main(int argc, char *argv[]) {
   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
 #if !defined(__APPLE__)
@@ -5020,6 +5196,20 @@ int main(int argc, char *argv[]) {
     }
   }
 
+  std::string unix_socket_path;
+  if (auto *arg = input_args.getLastArg(OPT_unix_socket)) {
+    const auto *path = arg->getValue();
+    unix_socket_path.assign(path);
+  }
+
+  const char *log_file_path = getenv("LLDBDAP_LOG");
+  std::shared_ptr<std::ofstream> log;
+  if (log_file_path)
+    log = std::make_shared<std::ofstream>(log_file_path);
+
+  const auto pre_init_commands =
+      input_args.getAllArgValues(OPT_pre_init_command);
+
 #if !defined(_WIN32)
   if (input_args.hasArg(OPT_wait_for_debugger)) {
     printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());
@@ -5028,36 +5218,52 @@ int main(int argc, char *argv[]) {
 #endif
 
   // Initialize LLDB first before we do anything.
-  lldb::SBDebugger::Initialize();
+  lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling();
+  if (error.Fail()) {
+    llvm::errs() << "Failed to initialize LLDB: " << error.GetCString() << "\n";
+    return EXIT_FAILURE;
+  }
 
   // Terminate the debugger before the C++ destructor chain kicks in.
   auto terminate_debugger =
       llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
 
-  DAP dap = DAP(program_path.str(), default_repl_mode);
+  if (portno != -1) {
+    llvm::errs() << llvm::format("Listening on port %i...\n", portno);
+    return AcceptConnection(program_path.str(), pre_init_commands, log,
+                            default_repl_mode, portno);
+  }
+
+  if (!unix_socket_path.empty()) {
+    return AcceptConnection(program_path.str(), pre_init_commands, log,
+                            default_repl_mode, unix_socket_path);
+  }
+
+  DAP dap = DAP(program_path.str(), log, default_repl_mode, pre_init_commands);
 
   RegisterRequestCallbacks(dap);
 
+#if defined(_WIN32)
+  // Windows opens stdout and stdin in text mode which converts \n to 13,10
+  // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
+  // fixes this.
+  int result = _setmode(fileno(stdout), _O_BINARY);
+  assert(result);
+  result = _setmode(fileno(stdin), _O_BINARY);
+  UNUSED_IF_ASSERT_DISABLED(result);
+  assert(result);
+#endif
+
   // stdout/stderr redirection to the IDE's console
-  int new_stdout_fd = SetupStdoutStderrRedirection(dap);
+  int new_stdout_fd = dup(fileno(stdout));
+  SetupRedirection(dap, fileno(stdout), fileno(stderr));
 
-  if (portno != -1) {
-    printf("Listening on port %i...\n", portno);
-    SOCKET socket_fd = AcceptConnection(dap, portno);
-    if (socket_fd >= 0) {
-      dap.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
-      dap.output.descriptor = StreamDescriptor::from_socket(socket_fd, false);
-    } else {
-      return EXIT_FAILURE;
-    }
-  } else {
-    dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
-    dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
+  dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
+  dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
 
-    /// used only by TestVSCode_redirection_to_console.py
-    if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
-      redirection_test();
-  }
+  /// used only by TestVSCode_redirection_to_console.py
+  if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
+    redirection_test();
 
   for (const std::string &arg :
        input_args.getAllArgValues(OPT_pre_init_command)) {

>From 8591c84c0dd3fed765766e4a2e33862af60a1df4 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Fri, 22 Nov 2024 16:02:43 -0800
Subject: [PATCH 2/5] Working to address some of the basic architectural issues
 with the socket listeners.

* Moved the socket handling logic into lldb/tools/lldb-dap/Socket.{h,cpp}
* Reworked the unit tests to read the socket information from the DAP process, this allows for lldb-dap to start with a connection like 'tcp://localhost:0' and pick a random port for the test. The output will be printed to stdout, listing all the addresses that were resolved from the connection parameter.
---
 .../test/tools/lldb-dap/dap_server.py         |  79 ++-
 .../test/tools/lldb-dap/lldbdap_testcase.py   |  19 +-
 .../tools/lldb-dap/server/TestDAP_server.py   |  17 +-
 lldb/tools/lldb-dap/CMakeLists.txt            |   5 +-
 lldb/tools/lldb-dap/DAP.cpp                   |  50 +-
 lldb/tools/lldb-dap/DAP.h                     |  21 +-
 lldb/tools/lldb-dap/IOStream.cpp              |  10 +-
 lldb/tools/lldb-dap/IOStream.h                |   8 +-
 lldb/tools/lldb-dap/Options.td                |  22 +-
 lldb/tools/lldb-dap/Socket.cpp                | 500 ++++++++++++++++++
 lldb/tools/lldb-dap/Socket.h                  | 182 +++++++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 370 ++++---------
 12 files changed, 923 insertions(+), 360 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Socket.cpp
 create mode 100644 lldb/tools/lldb-dap/Socket.h

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index e4a53fe0d45907..8a71b9e709f138 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -9,6 +9,7 @@
 import string
 import subprocess
 import sys
+import selectors
 import threading
 import time
 
@@ -1150,36 +1151,37 @@ def request_setInstructionBreakpoints(self, memory_reference=[]):
         }
         return self.send_recv(command_dict)
 
+
 class DebugAdaptorServer(DebugCommunication):
     def __init__(
         self,
         executable=None,
         launch=True,
-        port=None,
-        unix_socket=None,
+        connection=None,
         init_commands=[],
         log_file=None,
         env=None,
     ):
         self.process = None
         if launch:
-            self.process = DebugAdaptorServer.launch(
+            self.process, connection = DebugAdaptorServer.launch(
                 executable,
-                port=port,
-                unix_socket=unix_socket,
+                connection=connection,
                 log_file=log_file,
                 env=env,
             )
 
-        if port:
-            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            s.connect(("127.0.0.1", port))
-            DebugCommunication.__init__(
-                self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
-            )
-        elif unix_socket:
-            s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            s.connect(unix_socket)
+        if connection:
+            print("attempting connection", connection)
+            if connection.startswith("unix://"):  # unix:///path
+                s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+                s.connect(connection.removeprefix("unix://"))
+            elif connection.startswith("tcp://"):  # tcp://host:port
+                host, port = connection.removeprefix("tcp://").split(":", 1)
+                # create_connection with try both ipv4 and ipv6.
+                s = socket.create_connection((host, int(port)))
+            else:
+                raise ValueError("invalid connection: {}".format(connection))
             DebugCommunication.__init__(
                 self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
             )
@@ -1202,8 +1204,8 @@ def terminate(self):
 
     @classmethod
     def launch(
-        cls, executable: str, /, port=None, unix_socket=None, log_file=None, env=None
-    ) -> subprocess.Popen:
+        cls, executable: str, /, connection=None, log_file=None, env=None
+    ) -> tuple[subprocess.Popen, str]:
         adaptor_env = os.environ.copy()
         if env:
             adaptor_env.update(env)
@@ -1211,13 +1213,13 @@ def launch(
         if log_file:
             adaptor_env["LLDBDAP_LOG"] = log_file
 
+        if os.uname().sysname == "Darwin":
+            adaptor_env["NSUnbufferedIO"] = "YES"
+
         args = [executable]
-        if port:
-            args.append("--port")
-            args.append(str(port))
-        elif unix_socket:
-            args.append("--unix-socket")
-            args.append(unix_socket)
+        if connection:
+            args.append("--connection")
+            args.append(connection)
 
         proc = subprocess.Popen(
             args,
@@ -1227,11 +1229,34 @@ def launch(
             env=adaptor_env,
         )
 
-        if port or unix_socket:
-            # Wait for the server to startup.
-            time.sleep(0.1)
-
-        return proc
+        if connection:
+            # If a conneciton is specified, lldb-dap will print the listening
+            # address once the listener is made to stdout. The listener is
+            # formatted like `tcp://host:port` or `unix:///path`.
+            with selectors.DefaultSelector() as sel:
+                print("Reading stdout for the listening connection")
+                os.set_blocking(proc.stdout.fileno(), False)
+                stdout_key = sel.register(proc.stdout, selectors.EVENT_READ)
+                rdy_fds = sel.select(timeout=10.0)
+                for key, _ in rdy_fds:
+                    if key != stdout_key:
+                        continue
+
+                    outs = proc.stdout.read(1024).decode()
+                    os.set_blocking(proc.stdout.fileno(), True)
+                    for line in outs.split("\n"):
+                        if not line.startswith("Listening for: "):
+                            continue
+                        # If the listener expanded into multiple addresses, use the first.
+                        connection = line.removeprefix("Listening for: ").split(",")[0]
+                        print("")
+                        return proc, connection
+                proc.kill()
+                raise ValueError(
+                    "lldb-dap started with a connection but failed to write the listening address to stdout."
+                )
+
+        return proc, None
 
 
 def attach_options_specified(options):
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 3fcc08e9ff55cb..6b1216c8837f7c 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -10,10 +10,10 @@
 class DAPTestCaseBase(TestBase):
     # set timeout based on whether ASAN was enabled or not. Increase
     # timeout by a factor of 10 if ASAN is enabled.
-    timeoutval = 10 * (10 if ('ASAN_OPTIONS' in os.environ) else 1)
+    timeoutval = 10 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)
     NO_DEBUG_INFO_TESTCASE = True
 
-    def create_debug_adaptor(self, env=None, launch=True, port=None, unix_socket=None):
+    def create_debug_adaptor(self, env=None, launch=True, connection=None):
         """Create the Visual Studio Code debug adaptor"""
         self.assertTrue(
             is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
@@ -22,8 +22,7 @@ def create_debug_adaptor(self, env=None, launch=True, port=None, unix_socket=Non
         self.dap_server = dap_server.DebugAdaptorServer(
             executable=self.lldbDAPExec,
             launch=launch,
-            port=port,
-            unix_socket=unix_socket,
+            connection=connection,
             init_commands=self.setUpCommands(),
             log_file=log_file_path,
             env=env,
@@ -33,15 +32,13 @@ def build_and_create_debug_adaptor(
         self,
         lldbDAPEnv=None,
         lldbDAPLaunch=True,
-        lldbDAPPort=None,
-        lldbDAPUnixSocket=None,
+        lldbDAPConnection=None,
     ):
         self.build()
         self.create_debug_adaptor(
             env=lldbDAPEnv,
             launch=lldbDAPLaunch,
-            port=lldbDAPPort,
-            unix_socket=lldbDAPUnixSocket,
+            connection=lldbDAPConnection,
         )
 
     def set_source_breakpoints(self, source_path, lines, data=None):
@@ -489,9 +486,8 @@ def build_and_launch(
         customThreadFormat=None,
         launchCommands=None,
         expectFailure=False,
-        lldbDAPPort=None,
-        lldbDAPUnixSocket=None,
         lldbDAPLaunch=True,
+        lldbDAPConnection=None,
     ):
         """Build the default Makefile target, create the DAP debug adaptor,
         and launch the process.
@@ -499,8 +495,7 @@ def build_and_launch(
         self.build_and_create_debug_adaptor(
             lldbDAPEnv=lldbDAPEnv,
             lldbDAPLaunch=lldbDAPLaunch,
-            lldbDAPPort=lldbDAPPort,
-            lldbDAPUnixSocket=lldbDAPUnixSocket,
+            lldbDAPConnection=lldbDAPConnection,
         )
         self.assertTrue(os.path.exists(program), "executable must exist")
 
diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
index 46b992a77a4815..27bd583d4f6290 100644
--- a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -12,10 +12,10 @@
 
 
 class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
-    def do_test_server(self, port=None, unix_socket=None):
+    def do_test_server(self, connection):
         log_file_path = self.getBuildArtifact("dap.txt")
-        server = dap_server.DebugAdaptorServer.launch(
-            self.lldbDAPExec, port=port, unix_socket=unix_socket, log_file=log_file_path
+        server, connection = dap_server.DebugAdaptorServer.launch(
+            self.lldbDAPExec, connection, log_file=log_file_path
         )
 
         def cleanup():
@@ -30,7 +30,7 @@ def cleanup():
         breakpoint_line = line_number(source, "// breakpoint")
 
         # Initial connection over the port.
-        self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
+        self.create_debug_adaptor(launch=False, connection=connection)
         self.launch(
             program,
             disconnectAutomatically=False,
@@ -43,7 +43,7 @@ def cleanup():
         self.dap_server.request_disconnect()
 
         # Second connection over the port.
-        self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
+        self.create_debug_adaptor(launch=False, connection=connection)
         self.launch(program)
         self.set_source_breakpoints(source, [breakpoint_line])
         self.continue_to_next_stop()
@@ -55,12 +55,13 @@ def test_server_port(self):
         """
         Test launching a binary with a lldb-dap in server mode on a specific port.
         """
-        port = pickrandomport()
-        self.do_test_server(port=port)
+        self.do_test_server(connection="tcp://localhost:0")
 
     def test_server_unix_socket(self):
         """
         Test launching a binary with a lldb-dap in server mode on a unix socket.
         """
         dir = tempfile.gettempdir()
-        self.do_test_server(unix_socket=dir + "/dap-connection-" + str(os.getpid()))
+        self.do_test_server(
+            connection="unix://" + dir + "/dap-connection-" + str(os.getpid())
+        )
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index d68098bf7b3266..f81be385c62aab 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -26,19 +26,20 @@ add_lldb_tool(lldb-dap
   lldb-dap.cpp
   Breakpoint.cpp
   BreakpointBase.cpp
+  DAP.cpp
   ExceptionBreakpoint.cpp
   FifoFiles.cpp
   FunctionBreakpoint.cpp
+  InstructionBreakpoint.cpp
   IOStream.cpp
   JSONUtils.cpp
   LLDBUtils.cpp
   OutputRedirector.cpp
   ProgressEvent.cpp
   RunInTerminal.cpp
+  Socket.cpp
   SourceBreakpoint.cpp
-  DAP.cpp
   Watchpoint.cpp
-  InstructionBreakpoint.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 8998036fbedf6b..29e200f2475eb8 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -14,11 +14,14 @@
 #include "DAP.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
+#include "OutputRedirector.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBLanguageRuntime.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBStream.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FormatVariadic.h"
 
 #if defined(_WIN32)
@@ -32,10 +35,10 @@ using namespace lldb_dap;
 
 namespace lldb_dap {
 
-DAP::DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
+DAP::DAP(llvm::StringRef path, std::shared_ptr<llvm::raw_ostream> log,
          ReplMode repl_mode, std::vector<std::string> pre_init_commands)
-    : debug_adaptor_path(path), broadcaster("lldb-dap"),
-      log(log), exception_breakpoints(), pre_init_commands(pre_init_commands),
+    : debug_adaptor_path(path), broadcaster("lldb-dap"), log(log),
+      exception_breakpoints(), pre_init_commands(pre_init_commands),
       focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
       enable_auto_variable_summaries(false),
       enable_synthetic_child_debugging(false),
@@ -48,6 +51,28 @@ DAP::DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
 
 DAP::~DAP() = default;
 
+llvm::Error DAP::ConfigureIO(int out_fd, int err_fd) {
+  llvm::Expected<int> new_stdout_fd =
+      RedirectFd(out_fd, [this](llvm::StringRef data) {
+        SendOutput(OutputType::Stdout, data);
+      });
+  if (auto Err = new_stdout_fd.takeError()) {
+    return Err;
+  }
+  llvm::Expected<int> new_stderr_fd =
+      RedirectFd(err_fd, [this](llvm::StringRef data) {
+        SendOutput(OutputType::Stderr, data);
+      });
+  if (auto Err = new_stderr_fd.takeError()) {
+    return Err;
+  }
+
+  out = lldb::SBFile(new_stdout_fd.get(), "w", false);
+  err = lldb::SBFile(new_stderr_fd.get(), "w", false);
+
+  return llvm::Error::success();
+}
+
 /// Return string with first character capitalized.
 static std::string capitalize(llvm::StringRef str) {
   if (str.empty())
@@ -183,9 +208,9 @@ void DAP::SendJSON(const llvm::json::Value &json) {
   if (log) {
     auto now = std::chrono::duration<double>(
         std::chrono::system_clock::now().time_since_epoch());
-    *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << std::endl
+    *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << "\n"
          << "Content-Length: " << json_str.size() << "\r\n\r\n"
-         << llvm::formatv("{0:2}", json).str() << std::endl;
+         << llvm::formatv("{0:2}", json).str() << "\n";
   }
 }
 
@@ -213,8 +238,8 @@ std::string DAP::ReadJSON() {
   if (log) {
     auto now = std::chrono::duration<double>(
         std::chrono::system_clock::now().time_since_epoch());
-    *log << llvm::formatv("{0:f9} --> ", now.count()).str() << std::endl
-         << "Content-Length: " << length << "\r\n\r\n";
+    *log << llvm::formatv("{0:f9} --> ", now.count()).str()
+         << "\nContent-Length: " << length << "\r\n\r\n";
   }
   return json_str;
 }
@@ -654,20 +679,20 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
       std::string error_str;
       llvm::raw_string_ostream strm(error_str);
       strm << error;
-      *log << "error: failed to parse JSON: " << error_str << std::endl
-           << json << std::endl;
+      *log << "error: failed to parse JSON: " << error_str << "\n"
+           << json << "\n";
     }
     return PacketStatus::JSONMalformed;
   }
 
   if (log) {
-    *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
+    *log << llvm::formatv("{0:2}", *json_value).str() << "\n";
   }
 
   llvm::json::Object *object_ptr = json_value->getAsObject();
   if (!object_ptr) {
     if (log)
-      *log << "error: json packet isn't a object" << std::endl;
+      *log << "error: json packet isn't a object\n";
     return PacketStatus::JSONNotObject;
   }
   object = *object_ptr;
@@ -681,8 +706,7 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
     auto handler_pos = request_handlers.find(command);
     if (handler_pos == request_handlers.end()) {
       if (log)
-        *log << "error: unhandled command \"" << command.data() << "\""
-             << std::endl;
+        *log << "error: unhandled command \"" << command.data() << "\"\n";
       return false; // Fail
     }
 
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index ec70da67edac14..8f847c1fdffbe4 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -146,7 +146,7 @@ struct DAP {
   lldb::SBBroadcaster broadcaster;
   std::thread event_thread;
   std::thread progress_event_thread;
-  std::shared_ptr<std::ofstream> log;
+  std::shared_ptr<llvm::raw_ostream> log;
   llvm::StringMap<SourceBreakpointMap> source_breakpoints;
   FunctionBreakpointMap function_breakpoints;
   InstructionBreakpointMap instruction_breakpoints;
@@ -198,7 +198,7 @@ struct DAP {
   // will contain that expression.
   std::string last_nonempty_var_expression;
 
-  DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
+  DAP(llvm::StringRef path, std::shared_ptr<llvm::raw_ostream> log,
       ReplMode repl_mode, std::vector<std::string> pre_init_commands);
   ~DAP();
 
@@ -206,6 +206,23 @@ struct DAP {
   DAP(const DAP &rhs) = delete;
   void operator=(const DAP &rhs) = delete;
 
+  void disconnect();
+
+  /// Configures the DAP session stdout and stderr to redirect to the DAP
+  /// connection.
+  ///
+  /// Errors in this operation will be printed to the log file and the IDE's
+  /// console output as well.
+  ///
+  /// \param[in] out_fd
+  ///    If not -1, uses the given file descriptor as stdout.
+  ///
+  /// \param[in] err_fd
+  ///    If not -1, uses the given file descriptor as stderr.
+  ///
+  /// \return An error indiciating if the configuration was applied.
+  llvm::Error ConfigureIO(int out_fd = -1, int err_fd = -1);
+
   ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
   ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
 
diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
index d2e8ec40b0a7b8..188f6ec1fae9d9 100644
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ b/lldb/tools/lldb-dap/IOStream.cpp
@@ -7,6 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "IOStream.h"
+#include <string>
+#include "llvm/Support/raw_ostream.h"
 
 #if defined(_WIN32)
 #include <io.h>
@@ -16,8 +18,6 @@
 #include <unistd.h>
 #endif
 
-#include <fstream>
-#include <string>
 
 using namespace lldb_dap;
 
@@ -87,7 +87,7 @@ bool OutputStream::write_full(llvm::StringRef str) {
   return true;
 }
 
-bool InputStream::read_full(std::ofstream *log, size_t length,
+bool InputStream::read_full(llvm::raw_ostream *log, size_t length,
                             std::string &text) {
   std::string data;
   data.resize(length);
@@ -131,7 +131,7 @@ bool InputStream::read_full(std::ofstream *log, size_t length,
   return true;
 }
 
-bool InputStream::read_line(std::ofstream *log, std::string &line) {
+bool InputStream::read_line(llvm::raw_ostream *log, std::string &line) {
   line.clear();
   while (true) {
     if (!read_full(log, 1, line))
@@ -144,7 +144,7 @@ bool InputStream::read_line(std::ofstream *log, std::string &line) {
   return true;
 }
 
-bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
+bool InputStream::read_expected(llvm::raw_ostream *log, llvm::StringRef expected) {
   std::string result;
   if (!read_full(log, expected.size(), result))
     return false;
diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h
index 57d5fd458b7165..1933399e075363 100644
--- a/lldb/tools/lldb-dap/IOStream.h
+++ b/lldb/tools/lldb-dap/IOStream.h
@@ -22,7 +22,7 @@ typedef int SOCKET;
 #endif
 
 #include "llvm/ADT/StringRef.h"
-
+#include "llvm/Support/raw_ostream.h"
 #include <fstream>
 #include <string>
 
@@ -52,11 +52,11 @@ struct StreamDescriptor {
 struct InputStream {
   StreamDescriptor descriptor;
 
-  bool read_full(std::ofstream *log, size_t length, std::string &text);
+  bool read_full(llvm::raw_ostream *log, size_t length, std::string &text);
 
-  bool read_line(std::ofstream *log, std::string &line);
+  bool read_line(llvm::raw_ostream *log, std::string &line);
 
-  bool read_expected(std::ofstream *log, llvm::StringRef expected);
+  bool read_expected(llvm::raw_ostream *log, llvm::StringRef expected);
 };
 
 struct OutputStream {
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index dfca79b2884ac8..41d8912ed6c6bf 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -17,21 +17,10 @@ def: Flag<["-"], "g">,
   Alias<wait_for_debugger>,
   HelpText<"Alias for --wait-for-debugger">;
 
-def port: S<"port">,
-  MetaVarName<"<port>">,
-  HelpText<"Communicate with the lldb-dap tool over the defined port.">;
-def: Separate<["-"], "p">,
-  Alias<port>,
-  MetaVarName<"<port>">,
-  HelpText<"Alias for --port">;
-
-def unix_socket: S<"unix-socket">,
-  MetaVarName<"<path>">,
-  HelpText<"Communicate with the lldb-dap tool over the unix socket or named pipe.">;
-def: Separate<["-"], "u">,
-  Alias<unix_socket>,
-  MetaVarName<"<path>">,
-  HelpText<"Alias for --unix_socket">;
+def connection: S<"connection">,
+  MetaVarName<"<connection>">,
+  HelpText<"Communicate with the lldb-dap tool over the specified connection. "
+    "Connections are specified like 'tcp://[host]:port' or 'unix:///path'.">;
 
 def launch_target: S<"launch-target">,
   MetaVarName<"<target>">,
@@ -51,7 +40,8 @@ def debugger_pid: S<"debugger-pid">,
 
 def repl_mode: S<"repl-mode">,
   MetaVarName<"<mode>">,
-  HelpText<"The mode for handling repl evaluation requests, supported modes: variable, command, auto.">;
+  HelpText<"The mode for handling repl evaluation requests, supported modes: "
+    "variable, command, auto.">;
 
 def pre_init_command: S<"pre-init-command">,
   MetaVarName<"<command>">,
diff --git a/lldb/tools/lldb-dap/Socket.cpp b/lldb/tools/lldb-dap/Socket.cpp
new file mode 100644
index 00000000000000..c5c90a81547878
--- /dev/null
+++ b/lldb/tools/lldb-dap/Socket.cpp
@@ -0,0 +1,500 @@
+//===-- Socket.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Socket.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/Threading.h"
+#include <cstdint>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/_types/_socklen_t.h>
+#include <sys/poll.h>
+#include <system_error>
+
+#ifndef _WIN32
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#else
+#include "llvm/Support/Windows/WindowsSupport.h"
+// winsock2.h must be included before afunix.h. Briefly turn off clang-format to
+// avoid error.
+// clang-format off
+#include <winsock2.h>
+#include <afunix.h>
+// clang-format on
+#include <io.h>
+#endif // _WIN32
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+namespace {
+
+enum SocketProtocol { ProtocolTcp, ProtocolUnixDomain };
+
+std::error_code getLastSocketErrorCode() {
+#ifdef _WIN32
+  return std::error_code(::WSAGetLastError(), std::system_category());
+#else
+  return llvm::errnoAsErrorCode();
+#endif
+}
+
+llvm::Expected<std::vector<lldb_dap::SocketAddress>>
+getAddressInfo(llvm::StringRef host) {
+  std::vector<lldb_dap::SocketAddress> add_list;
+
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = IPPROTO_TCP;
+  hints.ai_flags = 0;
+
+  struct addrinfo *service_info_list = nullptr;
+
+  int err =
+      getaddrinfo(host.str().c_str(), nullptr, &hints, &service_info_list);
+  if (err != 0)
+    return llvm::createStringError("getaddrinfo failed: %s", gai_strerror(err));
+
+  for (struct addrinfo *service_ptr = service_info_list; service_ptr != nullptr;
+       service_ptr = service_ptr->ai_next) {
+    add_list.emplace_back(lldb_dap::SocketAddress(service_ptr));
+  }
+
+  if (service_info_list)
+    ::freeaddrinfo(service_info_list);
+
+  return add_list;
+}
+
+int CloseSocket(NativeSocket sockfd) {
+#ifdef _WIN32
+  return ::closesocket(sockfd);
+#else
+  return ::close(sockfd);
+#endif
+}
+
+llvm::Expected<std::pair<llvm::StringRef, int>>
+detectHostAndPort(llvm::StringRef name) {
+  llvm::StringRef host;
+  llvm::StringRef port;
+  std::tie(host, port) = name.split(":");
+
+  if (host == "" || host == "*") {
+    host = "0.0.0.0";
+  }
+
+  if (port == "") {
+    port = "0";
+  }
+
+  int portnu = 0;
+  if (!llvm::to_integer(port, portnu, 10)) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "invalid host:port specification: '%s'",
+                                   name.str().c_str());
+  }
+
+  return std::make_pair(host, portnu);
+}
+
+} // namespace
+
+namespace lldb_dap {
+
+#ifdef _WIN32
+WSABalancer::WSABalancer() {
+  WSADATA WsaData;
+  ::memset(&WsaData, 0, sizeof(WsaData));
+  if (WSAStartup(MAKEWORD(2, 2), &WsaData) != 0) {
+    llvm::report_fatal_error("WSAStartup failed");
+  }
+}
+
+WSABalancer::~WSABalancer() { WSACleanup(); }
+#endif // _WIN32
+
+SocketAddress::SocketAddress() { clear(); }
+
+SocketAddress::SocketAddress(const struct addrinfo *info) {
+  clear();
+  if (info && info->ai_addrlen > 0 &&
+      size_t(info->ai_addrlen) <= sizeof m_address) {
+    memcpy(&m_address.sa, info->ai_addr, static_cast<size_t>(info->ai_addrlen));
+    m_address.sa.sa_len = info->ai_addrlen;
+  }
+}
+
+SocketAddress::SocketAddress(const struct sockaddr &sa) { m_address.sa = sa; }
+
+SocketAddress::SocketAddress(const struct sockaddr_in &sa) {
+  m_address.sa_ipv4 = sa;
+}
+
+SocketAddress::SocketAddress(const struct sockaddr_in6 &sa) {
+  m_address.sa_ipv6 = sa;
+}
+
+SocketAddress::SocketAddress(const struct sockaddr_un &su) {
+  m_address.su = su;
+}
+
+std::string SocketAddress::getPath() const {
+  if (getFamily() == AF_UNIX)
+    return std::string(m_address.su.sun_path);
+  return "";
+}
+
+std::string SocketAddress::getName() const {
+  if (getFamily() == AF_UNIX)
+    return "unix://" + getPath();
+
+  uint64_t port = getPort();
+  std::string hostname;
+  if (isLocalhost())
+    hostname = "localhost";
+  else if (isAnyAddr())
+    hostname = getFamily() == AF_INET ? "0.0.0.0" : "[::]";
+  else {
+    char hbuf[NI_MAXHOST];
+    if (getnameinfo(&m_address.sa, getLength(), hbuf, sizeof(hbuf), nullptr, 0,
+                    NI_NUMERICHOST) == 0) {
+      hostname = std::string(hbuf);
+    }
+  }
+
+  return "tcp://" + hostname + ":" + std::to_string(port);
+}
+
+bool SocketAddress::isLocalhost() const {
+  switch (getFamily()) {
+  case AF_INET:
+    return m_address.sa_ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK);
+  case AF_INET6:
+    return 0 == memcmp(&m_address.sa_ipv6.sin6_addr, &in6addr_loopback, 16);
+  }
+  return false;
+}
+
+bool SocketAddress::isAnyAddr() const {
+  switch (getFamily()) {
+  case AF_INET:
+    return m_address.sa_ipv4.sin_addr.s_addr == htonl(INADDR_ANY);
+  case AF_INET6:
+    return 0 == memcmp(&m_address.sa_ipv6.sin6_addr, &in6addr_any, 16);
+  }
+  return false;
+}
+
+void SocketAddress::setFamily(sa_family_t family) {
+  m_address.sa.sa_family = family;
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \
+    defined(__OpenBSD__)
+  switch (family) {
+  case AF_INET:
+    m_address.sa.sa_len = sizeof(struct sockaddr_in);
+    break;
+
+  case AF_INET6:
+    m_address.sa.sa_len = sizeof(struct sockaddr_in6);
+    break;
+
+  case AF_UNIX:
+    m_address.sa.sa_len = SUN_LEN(&m_address.su);
+    break;
+
+  default:
+    assert(0 && "unsupported socket family");
+  }
+#endif
+}
+
+SocketAddress SocketAddress::addressOf(const Socket &s, std::error_code &EC) {
+  SocketAddress sa;
+  socklen_t len = sizeof(sockaddr_t);
+
+  if (::getsockname(s.fd, &sa.sockaddr(), &len) == -1) {
+    EC = getLastSocketErrorCode();
+    return SocketAddress{};
+  }
+
+  // If the socket is a struct sockaddr_un then the length is returned by the
+  // len parameter not the sa_len field. Update the field to match the
+  // returned address.
+  sa.m_address.sa.sa_len = len;
+  return sa;
+}
+
+bool SocketAddress::setToAnyAddress(sa_family_t family, uint16_t port) {
+  switch (family) {
+  case AF_INET:
+    setFamily(family);
+    if (setPort(port)) {
+      m_address.sa_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
+      return true;
+    }
+    break;
+
+  case AF_INET6:
+    setFamily(family);
+    if (setPort(port)) {
+      m_address.sa_ipv6.sin6_addr = in6addr_any;
+      return true;
+    }
+    break;
+  }
+
+  clear();
+  return false;
+}
+
+Socket::Socket(NativeSocket fd) : fd(fd) {}
+Socket::Socket(Socket &&S) : fd(S.fd) { S.fd = -1; }
+
+Socket::~Socket() {
+  close();
+}
+
+void Socket::close() {
+  if (fd == -1) {
+    return;
+  }
+
+  CloseSocket(fd);
+  fd = -1;
+}
+
+SocketListener::SocketListener(std::vector<Socket> sockets, int fds[2])
+    : m_listening(true), m_sockets(std::move(sockets)), m_pipe{fds[0], fds[1]} {
+}
+
+SocketListener::SocketListener(SocketListener &&SL)
+    : m_listening(SL.m_listening.load()), m_sockets(std::move(SL.m_sockets)),
+      m_pipe{SL.m_pipe[0], SL.m_pipe[1]} {
+  SL.m_listening = false;
+  SL.m_sockets.clear();
+  SL.m_pipe[0] = -1;
+  SL.m_pipe[1] = -1;
+}
+
+llvm::Expected<SocketListener>
+SocketListener::createListener(llvm::StringRef name) {
+  SocketProtocol protocol;
+
+  if (name.consume_front("tcp://") || name.consume_front("tcp:") ||
+      name.starts_with(":")) {
+    protocol = ProtocolTcp;
+  } else if (name.consume_front("unix://") || name.consume_front("unix:") ||
+             name.starts_with("/")) {
+    protocol = ProtocolUnixDomain;
+  } else if (name.contains(":")) {
+    protocol = ProtocolTcp;
+  } else {
+    return llvm::createStringError(
+        "invalid address, expected '[tcp://][host]:port' or "
+        "'[unix://]/path' but got %s.",
+        name.str().c_str());
+  }
+
+  std::vector<SocketAddress> addresses;
+  if (protocol == ProtocolTcp) {
+    auto maybeHostAndPort = detectHostAndPort(name);
+    if (auto Err = maybeHostAndPort.takeError())
+      return Err;
+
+    llvm::StringRef host;
+    int port;
+    std::tie(host, port) = *maybeHostAndPort;
+
+    llvm::Expected<std::vector<SocketAddress>> maybeAddresses =
+        getAddressInfo(host);
+    if (auto Err = maybeAddresses.takeError()) {
+      return Err;
+    }
+
+    for (auto &address : *maybeAddresses) {
+      SocketAddress listen_address = address;
+      if (!listen_address.isLocalhost())
+        listen_address.setToAnyAddress(address.getFamily(), port);
+      else
+        listen_address.setPort(port);
+
+      addresses.push_back(std::move(listen_address));
+    }
+  } else {
+    llvm::StringRef path = name;
+    if (llvm::sys::fs::exists(path)) {
+      return llvm::make_error<llvm::StringError>(
+          std::make_error_code(std::errc::file_exists), "file exists at path");
+    }
+
+    struct sockaddr_un addr;
+    bzero(&addr, sizeof(struct sockaddr_un));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, path.str().c_str(), sizeof(addr.sun_path) - 1);
+    addr.sun_len = SUN_LEN(&addr);
+
+    addresses.push_back(SocketAddress(addr));
+  }
+
+  std::vector<Socket> sockets;
+  for (auto &address : addresses) {
+    bool isTCP =
+        address.getFamily() == AF_INET || address.getFamily() == AF_INET6;
+
+    NativeSocket fd =
+        ::socket(address.getFamily(), SOCK_STREAM, isTCP ? IPPROTO_TCP : 0);
+    if (fd == -1) {
+      return llvm::createStringError(getLastSocketErrorCode(),
+                                     "socket() failed");
+    }
+
+    int val = 1;
+    if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1) {
+      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                                 "setsockopt() failed");
+    }
+
+    if (::bind(fd, &address.sockaddr(), address.getLength()) == -1) {
+      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                                 "bind() failed");
+    }
+
+    if (::listen(fd, llvm::hardware_concurrency().compute_thread_count()) ==
+        -1) {
+      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                                 "listen() failed");
+    }
+
+    sockets.emplace_back(fd);
+  }
+
+  int pipeFD[2];
+#ifdef _WIN32
+  // Reserve 1 byte for the pipe and use default textmode
+  if (::_pipe(pipeFD, 1, 0) == -1)
+#else
+  if (::pipe(pipeFD) == -1)
+#endif // _WIN32
+    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                               "pipe failed");
+
+  return SocketListener{std::move(sockets), pipeFD};
+}
+
+SocketListener::~SocketListener() {
+  shutdown();
+  if (m_pipe[0] != -1)
+    close(m_pipe[0]);
+  if (m_pipe[1] != -1)
+    close(m_pipe[1]);
+}
+
+llvm::Expected<std::vector<std::string>> SocketListener::addresses() const {
+  std::vector<std::string> addrs;
+  std::error_code EC;
+  for (auto &s : m_sockets) {
+    auto addr = SocketAddress::addressOf(s, EC);
+    if (EC)
+      return llvm::make_error<llvm::StringError>(EC);
+
+    addrs.push_back(addr.getName());
+  }
+  return addrs;
+}
+
+void SocketListener::shutdown() {
+  bool listening = m_listening;
+
+  if (!listening)
+    return;
+
+  if (!m_listening.compare_exchange_strong(listening, false))
+    return;
+
+  for (auto &s : m_sockets) {
+    // If the socket has a path (e.g. a unix:// socket), remove it after
+    // closing.
+    std::error_code _; // ignoring failures during shutdown.
+    std::string path = SocketAddress::addressOf(s, _).getPath();
+    s.close();
+    if (!path.empty())
+      unlink(path.c_str());
+  }
+
+  // Write to the pipe to indiciate that accept() should exit immediately.
+  char byte = 'A';
+  ssize_t written = ::write(m_pipe[1], &byte, 1);
+
+  // Ignore any write() error
+  (void)written;
+}
+
+llvm::Expected<Socket> SocketListener::accept() {
+  if (m_sockets.empty()) {
+    return llvm::createStringError(
+        std::make_error_code(std::errc::bad_file_descriptor), "no open socket");
+  }
+
+  std::vector<struct pollfd> pollfds;
+
+  for (auto &s : m_sockets)
+    pollfds.emplace_back(pollfd{s.fd, POLLIN, 0});
+
+  pollfds.emplace_back(pollfd{m_pipe[0], POLLIN, 0});
+
+  while (m_listening.load()) {
+    int status;
+#ifdef _WIN32
+    status = WSAPoll(pollfds.data(), pollfds.size(), -1);
+#else
+    status = ::poll(pollfds.data(), pollfds.size(), -1);
+#endif
+
+    if (status == -1) {
+      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                                 "poll() failed");
+    }
+
+    for (auto &pfd : pollfds) {
+      if (pfd.revents & POLLIN) {
+        // Check if shutdown was requested.
+        if (pfd.fd == m_pipe[0]) {
+          break;
+        }
+
+        struct sockaddr client;
+        socklen_t len;
+        int fd =
+            llvm::sys::RetryAfterSignal(-1, ::accept, pfd.fd, &client, &len);
+        if (fd == -1) {
+          return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+                                                     "accept() failed");
+        }
+
+        return Socket{fd};
+      }
+    }
+  }
+
+  return llvm::make_error<llvm::StringError>(
+      std::make_error_code(std::errc::connection_aborted), "socket shutdown");
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Socket.h b/lldb/tools/lldb-dap/Socket.h
new file mode 100644
index 00000000000000..98923032f91417
--- /dev/null
+++ b/lldb/tools/lldb-dap/Socket.h
@@ -0,0 +1,182 @@
+//===-- Socket.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_SOCKET_H
+#define LLDB_TOOLS_LLDB_DAP_SOCKET_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#include <winsock2.h>
+#include <ws2tcpip.h>
+typedef ADDRESS_FAMILY sa_family_t;
+typedef SOCKET NativeSocket;
+#else
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+typedef int NativeSocket;
+#endif
+
+namespace lldb_dap {
+
+struct Socket;
+
+/// Manages socket addresses for socket based communication.
+struct SocketAddress {
+  SocketAddress();
+  explicit SocketAddress(const struct addrinfo *);
+  explicit SocketAddress(const struct sockaddr &);
+  explicit SocketAddress(const struct sockaddr_in &);
+  explicit SocketAddress(const struct sockaddr_in6 &);
+  explicit SocketAddress(const struct sockaddr_un &);
+
+  static SocketAddress addressOf(const Socket &s, std::error_code &EC);
+
+  sa_family_t getFamily() const { return m_address.sa.sa_family; }
+  uint16_t getPort() const {
+    switch (getFamily()) {
+    case AF_INET:
+      return ntohs(m_address.sa_ipv4.sin_port);
+    case AF_INET6:
+      return ntohs(m_address.sa_ipv6.sin6_port);
+    }
+    return 0;
+  }
+  std::string getName() const;
+  /// Returns the path for AF_UNIX sockets, otherwise "".
+  std::string getPath() const;
+  /// Get the length for the current socket address family
+  socklen_t getLength() const { return m_address.sa.sa_len; };
+
+  bool isLocalhost() const;
+  bool isAnyAddr() const;
+
+  void setFamily(sa_family_t family);
+  bool setToAnyAddress(sa_family_t family, uint16_t port);
+  bool setPort(uint16_t port) {
+    switch (getFamily()) {
+    case AF_INET:
+      m_address.sa_ipv4.sin_port = htons(port);
+      return true;
+    case AF_INET6:
+      m_address.sa_ipv6.sin6_port = htons(port);
+      return true;
+    }
+    return false;
+  }
+
+  void clear() { bzero(&m_address, sizeof(sockaddr_t)); }
+
+  struct sockaddr &sockaddr() { return m_address.sa; }
+  const struct sockaddr &sockaddr() const { return m_address.sa; }
+
+private:
+  typedef union sockaddr_tag {
+    struct sockaddr sa;
+    struct sockaddr_in sa_ipv4;
+    struct sockaddr_in6 sa_ipv6;
+    struct sockaddr_un su;
+    struct sockaddr_storage ss;
+  } sockaddr_t;
+  sockaddr_t m_address;
+};
+
+/// Manages the lifetime of a socket.
+struct Socket {
+  NativeSocket fd;
+
+  /// Close the socket.
+  void close();
+
+  explicit Socket(NativeSocket fd);
+  ~Socket();
+  Socket(Socket &&S);
+  Socket(const Socket &S) = delete;
+  Socket &operator=(const Socket &S) = delete;
+};
+
+#ifdef _WIN32
+/// Ensures proper initialization and cleanup of winsock resources
+///
+/// Make sure that calls to WSAStartup and WSACleanup are balanced.
+class WSABalancer {
+public:
+  WSABalancer();
+  ~WSABalancer();
+};
+#endif // _WIN32
+
+/// Manages listening for socket connections.
+///
+/// SocketListener handles listening for both unix and tcp based connections.
+struct SocketListener {
+  SocketListener(const SocketListener &S) = delete;
+  SocketListener &operator=(const SocketListener &S) = delete;
+  SocketListener(SocketListener &&SL);
+  ~SocketListener();
+
+  /// Creates a listening socket bound to the specified name.
+  ///
+  /// Handles the socket creation, binding, and immediately starts listening for
+  /// incoming connections.
+  ///
+  /// \param[in] name
+  ///     Socket names formatted like `protocol:name`.
+  ///
+  ///     Supported protocols include tcp and unix sockets.
+  ///
+  ///     Names must follow the following formats:
+  ///
+  ///     * tcp://host:port
+  ///     * tcp:host:port
+  ///     * tcp:port (host will be assumed 0.0.0.0)
+  ///     * :port (implicit tcp)
+  ///     * unix:///path
+  ///     * /path (implicit unix)
+  static llvm::Expected<SocketListener> createListener(llvm::StringRef name);
+
+  /// Returns an array of active listening sockets.
+  llvm::Expected<std::vector<std::string>> addresses() const;
+
+  /// Shutdown the socket listener.
+  void shutdown();
+
+  /// Returns true if listening, otherwise false.
+  bool isListening() const { return m_listening; }
+
+  /// Accept returns the next client connection, blocking until a connection is
+  /// made or Shutdown is called.
+  llvm::Expected<Socket> accept();
+
+private:
+  SocketListener(std::vector<Socket> sockets, int fds[2]);
+
+  std::atomic<bool> m_listening;
+  std::vector<Socket> m_sockets;
+
+  /// If a separate thread calls shutdown, the listening file descriptor
+  /// could be closed while ::poll is waiting for it to be ready to perform a
+  /// I/O operations. ::poll will continue to block even after FD is closed so
+  /// use a self-pipe mechanism to get ::poll to return
+  int m_pipe[2] = {};
+
+#ifdef _WIN32
+  WSABalancer _;
+#endif // _WIN32
+};
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 51197587ffccec..0c602ecbbbe836 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -10,8 +10,8 @@
 #include "FifoFiles.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
-#include "OutputRedirector.h"
 #include "RunInTerminal.h"
+#include "Socket.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
 #include "lldb/API/SBFile.h"
@@ -31,11 +31,12 @@
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
 #include "llvm/Support/Base64.h"
-#include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <array>
@@ -45,6 +46,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <iostream>
 #include <map>
 #include <memory>
 #include <optional>
@@ -52,6 +54,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <thread>
+#include <utility>
 #include <vector>
 
 #if defined(_WIN32)
@@ -141,234 +144,6 @@ lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
   }
 }
 
-/// Redirect stdout and stderr fo the IDE's console output.
-///
-/// Errors in this operation will be printed to the log file and the IDE's
-/// console output as well.
-///
-/// \return
-///     A fd pointing to the original stdout.
-void SetupRedirection(DAP &dap, int stdoutfd = -1, int stderrfd = -1) {
-  auto output_callback_stderr = [&dap](llvm::StringRef data) {
-    dap.SendOutput(OutputType::Stderr, data);
-  };
-  auto output_callback_stdout = [&dap](llvm::StringRef data) {
-    dap.SendOutput(OutputType::Stdout, data);
-  };
-
-  llvm::Expected<int> new_stdout_fd =
-      RedirectFd(stdoutfd, output_callback_stdout);
-  if (auto err = new_stdout_fd.takeError()) {
-    std::string error_message = llvm::toString(std::move(err));
-    if (dap.log)
-      *dap.log << error_message << std::endl;
-    output_callback_stderr(error_message);
-  }
-  dap.out = lldb::SBFile(new_stdout_fd.get(), "w", false);
-
-  llvm::Expected<int> new_stderr_fd =
-      RedirectFd(stderrfd, output_callback_stderr);
-  if (auto err = new_stderr_fd.takeError()) {
-    std::string error_message = llvm::toString(std::move(err));
-    if (dap.log)
-      *dap.log << error_message << std::endl;
-    output_callback_stderr(error_message);
-  }
-  dap.err = lldb::SBFile(new_stderr_fd.get(), "w", false);
-}
-
-void HandleClient(int clientfd, llvm::StringRef program_path,
-                  const std::vector<std::string> &pre_init_commands,
-                  std::shared_ptr<std::ofstream> log,
-                  ReplMode default_repl_mode) {
-  if (log)
-    *log << "client[" << clientfd << "] connected\n";
-  DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
-  dap.debug_adaptor_path = program_path;
-
-  SetupRedirection(dap);
-  RegisterRequestCallbacks(dap);
-
-  dap.input.descriptor = StreamDescriptor::from_socket(clientfd, false);
-  dap.output.descriptor = StreamDescriptor::from_socket(clientfd, false);
-
-  for (const std::string &arg : pre_init_commands) {
-    dap.pre_init_commands.push_back(arg);
-  }
-
-  if (auto Err = dap.Loop()) {
-    if (log)
-      *log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
-  }
-
-  if (log)
-    *log << "client[" << clientfd << "] connection closed\n";
-#if defined(_WIN32)
-  closesocket(clientfd);
-#else
-  close(clientfd);
-#endif
-}
-
-std::error_code getLastSocketErrorCode() {
-#ifdef _WIN32
-  return std::error_code(::WSAGetLastError(), std::system_category());
-#else
-  return llvm::errnoAsErrorCode();
-#endif
-}
-
-llvm::Expected<int> getSocketFD(llvm::StringRef path) {
-  if (llvm::sys::fs::exists(path) && (::remove(path.str().c_str()) == -1)) {
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Remove existing socket failed");
-  }
-
-  SOCKET sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-  if (sockfd == -1) {
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Create socket failed");
-  }
-
-  struct sockaddr_un addr;
-  bzero(&addr, sizeof(addr));
-  addr.sun_family = AF_UNIX;
-  strncpy(addr.sun_path, path.str().c_str(), sizeof(addr.sun_path) - 1);
-
-  if (::bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
-#if defined(_WIN32)
-    closesocket(sockfd);
-#else
-    close(sockfd);
-#endif
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Socket bind() failed");
-  }
-
-  if (listen(sockfd, llvm::hardware_concurrency().compute_thread_count()) < 0) {
-#if defined(_WIN32)
-    closesocket(sockfd);
-#else
-    close(sockfd);
-#endif
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Socket listen() failed");
-  }
-
-  return sockfd;
-}
-
-llvm::Expected<int> getSocketFD(int portno) {
-  SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
-  if (sockfd < 0) {
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Create socket failed");
-  }
-
-  struct sockaddr_in serv_addr;
-  bzero(&serv_addr, sizeof(serv_addr));
-  serv_addr.sin_family = AF_INET;
-  // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-  serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-  serv_addr.sin_port = htons(portno);
-  if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
-#if defined(_WIN32)
-    closesocket(sockfd);
-#else
-    close(sockfd);
-#endif
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Socket bind() failed");
-  }
-
-  if (listen(sockfd, llvm::hardware_concurrency().compute_thread_count()) < 0) {
-#if defined(_WIN32)
-    closesocket(sockfd);
-#else
-    close(sockfd);
-#endif
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "Socket listen() failed");
-  }
-
-  return sockfd;
-}
-
-int AcceptConnection(llvm::StringRef program_path,
-                     const std::vector<std::string> &pre_init_commands,
-                     std::shared_ptr<std::ofstream> log,
-                     ReplMode default_repl_mode,
-                     llvm::StringRef unix_socket_path) {
-  auto listening = getSocketFD(unix_socket_path);
-  if (auto E = listening.takeError()) {
-    llvm::errs() << "Listening on " << unix_socket_path
-                 << " failed: " << llvm::toString(std::move(E)) << "\n";
-    return EXIT_FAILURE;
-  }
-
-  while (true) {
-    struct sockaddr_un cli_addr;
-    bzero(&cli_addr, sizeof(struct sockaddr_un));
-    socklen_t clilen = sizeof(cli_addr);
-    SOCKET clientfd =
-        llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, *listening,
-                                    (struct sockaddr *)&cli_addr, &clilen);
-    if (clientfd < 0) {
-      llvm::errs() << "Client accept failed: "
-                   << getLastSocketErrorCode().message() << "\n";
-      return EXIT_FAILURE;
-    }
-
-    std::thread t(HandleClient, clientfd, program_path, pre_init_commands, log,
-                  default_repl_mode);
-    t.detach();
-  }
-
-#if defined(_WIN32)
-  closesocket(*listening);
-#else
-  close(*listening);
-#endif
-  return 0;
-}
-
-int AcceptConnection(llvm::StringRef program_path,
-                     const std::vector<std::string> &pre_init_commands,
-                     std::shared_ptr<std::ofstream> log,
-                     ReplMode default_repl_mode, int portno) {
-  auto listening = getSocketFD(portno);
-  if (auto E = listening.takeError()) {
-    llvm::errs() << "Listening on " << portno
-                 << " failed: " << llvm::toString(std::move(E)) << "\n";
-    return EXIT_FAILURE;
-  }
-
-  while (true) {
-    struct sockaddr_in cli_addr;
-    bzero(&cli_addr, sizeof(struct sockaddr_in));
-    socklen_t clilen = sizeof(cli_addr);
-    SOCKET clientfd =
-        llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, *listening,
-                                    (struct sockaddr *)&cli_addr, &clilen);
-    if (clientfd < 0) {
-      llvm::errs() << "Client accept failed: "
-                   << getLastSocketErrorCode().message() << "\n";
-      return EXIT_FAILURE;
-    }
-
-    std::thread t(HandleClient, clientfd, program_path, pre_init_commands, log,
-                  default_repl_mode);
-    t.detach();
-  }
-
-#if defined(_WIN32)
-  closesocket(*listening);
-#else
-  close(*listening);
-#endif
-  return 0;
-}
-
 std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
   // Create and return an array of "const char *", one for each C string in
   // "strs" and terminate the list with a NULL. This can be used for argument
@@ -500,12 +275,11 @@ void SendThreadStoppedEvent(DAP &dap) {
       if (dap.log)
         *dap.log << "error: SendThreadStoppedEvent() when process"
                     " isn't stopped ("
-                 << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl;
+                 << lldb::SBDebugger::StateAsCString(state) << ")\n";
     }
   } else {
     if (dap.log)
-      *dap.log << "error: SendThreadStoppedEvent() invalid process"
-               << std::endl;
+      *dap.log << "error: SendThreadStoppedEvent() invalid process\n";
   }
   dap.RunStopCommands();
 }
@@ -5115,6 +4889,8 @@ static void redirection_test() {
   fflush(stderr);
 }
 
+static SocketListener *g_listener = nullptr;
+
 int main(int argc, char *argv[]) {
   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
 #if !defined(__APPLE__)
@@ -5185,27 +4961,23 @@ int main(int argc, char *argv[]) {
     }
   }
 
-  int portno = -1;
-  if (auto *arg = input_args.getLastArg(OPT_port)) {
-    const auto *optarg = arg->getValue();
-    char *remainder;
-    portno = strtol(optarg, &remainder, 0);
-    if (remainder == optarg || *remainder != '\0') {
-      fprintf(stderr, "'%s' is not a valid port number.\n", optarg);
-      return EXIT_FAILURE;
-    }
-  }
-
-  std::string unix_socket_path;
-  if (auto *arg = input_args.getLastArg(OPT_unix_socket)) {
+  std::string connection;
+  if (auto *arg = input_args.getLastArg(OPT_connection)) {
     const auto *path = arg->getValue();
-    unix_socket_path.assign(path);
+    connection.assign(path);
   }
 
   const char *log_file_path = getenv("LLDBDAP_LOG");
-  std::shared_ptr<std::ofstream> log;
-  if (log_file_path)
-    log = std::make_shared<std::ofstream>(log_file_path);
+  std::shared_ptr<llvm::raw_ostream> log;
+  if (log_file_path) {
+    std::error_code EC;
+    log.reset(new llvm::raw_fd_ostream(log_file_path, EC));
+    if (EC) {
+      llvm::errs() << "Could not open log file: " << EC.message() << ", "
+                   << log_file_path << '\n';
+      return EXIT_FAILURE;
+    }
+  }
 
   const auto pre_init_commands =
       input_args.getAllArgValues(OPT_pre_init_command);
@@ -5228,25 +5000,90 @@ int main(int argc, char *argv[]) {
   auto terminate_debugger =
       llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
 
-  if (portno != -1) {
-    llvm::errs() << llvm::format("Listening on port %i...\n", portno);
-    return AcceptConnection(program_path.str(), pre_init_commands, log,
-                            default_repl_mode, portno);
-  }
+  if (!connection.empty()) {
+    auto maybeListener = SocketListener::createListener(connection);
+    if (auto Err = maybeListener.takeError()) {
+      llvm::errs() << "Failed to listen for connections: "
+                   << llvm::toString(std::move(Err)) << "\n";
+      return EXIT_FAILURE;
+    }
+
+    g_listener = &*maybeListener;
+
+    llvm::sys::SetInterruptFunction([]() {
+      if (g_listener)
+        g_listener->shutdown();
+    });
+
+    auto maybeListenerAddresses = g_listener->addresses();
+    if (auto Err = maybeListenerAddresses.takeError()) {
+      llvm::errs() << "listener failed to retrieve listening socket address: "
+                   << Err << "\n";
+      return EXIT_FAILURE;
+    }
+
+    if (log)
+      *log << "started with connection listeners "
+           << llvm::join(*maybeListenerAddresses, ", ") << "\n";
+
+    llvm::outs() << "Listening for: "
+                 << llvm::join(*maybeListenerAddresses, ", ") << "\n";
+    // Ensure listening address are flushed for calles to retrieve the resolve address.
+    llvm::outs().flush();
+
+    while (g_listener && g_listener->isListening()) {
+      llvm::Expected<Socket> maybeClient = g_listener->accept();
+      if (auto Err = maybeClient.takeError()) {
+        if (log)
+          *log << "client accept failed: " << Err << "\n";
+        llvm::errs() << "client connection failed: " << Err << "\n";
+        continue;
+      }
+
+      Socket client = std::move(*maybeClient);
+      std::thread t([=, client = std::move(client)]() mutable {
+        DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
+
+        if (auto Err = dap.ConfigureIO()) {
+          llvm::errs() << "failed to configure client connect: "
+                       << llvm::toString(std::move(Err)) << "\n";
+          return;
+        }
+
+        RegisterRequestCallbacks(dap);
+        dap.input.descriptor = StreamDescriptor::from_socket(client.fd, false);
+        dap.output.descriptor = StreamDescriptor::from_socket(client.fd, false);
+        if (auto Err = dap.Loop()) {
+          if (log)
+            *log << "client[" << client.fd
+                 << "] Transport Error: " << llvm::toString(std::move(Err))
+                 << "\n";
+        }
+
+        if (log)
+          *log << "client[" << client.fd << "] connection closed\n";
+
+        client.close();
+      });
+      t.detach();
+    }
 
-  if (!unix_socket_path.empty()) {
-    return AcceptConnection(program_path.str(), pre_init_commands, log,
-                            default_repl_mode, unix_socket_path);
+    return EXIT_SUCCESS;
   }
 
+  // stdout/stderr redirection to the IDE's console
+  int new_stdout_fd = dup(fileno(stdout));
   DAP dap = DAP(program_path.str(), log, default_repl_mode, pre_init_commands);
-
+  if (auto Err = dap.ConfigureIO(fileno(stdout), fileno(stderr))) {
+    llvm::errs() << "Failed to create lldb-dap instance: " << Err << "\n";
+    return EXIT_FAILURE;
+  }
   RegisterRequestCallbacks(dap);
 
 #if defined(_WIN32)
   // Windows opens stdout and stdin in text mode which converts \n to 13,10
-  // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
-  // fixes this.
+  // while the value is just 10 on Darwin/Linux. Setting the file mode to
+  // binary fixes this.
   int result = _setmode(fileno(stdout), _O_BINARY);
   assert(result);
   result = _setmode(fileno(stdin), _O_BINARY);
@@ -5254,10 +5091,6 @@ int main(int argc, char *argv[]) {
   assert(result);
 #endif
 
-  // stdout/stderr redirection to the IDE's console
-  int new_stdout_fd = dup(fileno(stdout));
-  SetupRedirection(dap, fileno(stdout), fileno(stderr));
-
   dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
   dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
 
@@ -5265,15 +5098,10 @@ int main(int argc, char *argv[]) {
   if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
     redirection_test();
 
-  for (const std::string &arg :
-       input_args.getAllArgValues(OPT_pre_init_command)) {
-    dap.pre_init_commands.push_back(arg);
-  }
-
   bool CleanExit = true;
   if (auto Err = dap.Loop()) {
     if (dap.log)
-      *dap.log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
+      *dap.log << "Transport Error: " << Err << "\n";
     CleanExit = false;
   }
 

>From b9713bbe60a4b8778cd517bc92d8ae0bf46dd1fe Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Fri, 22 Nov 2024 17:12:47 -0800
Subject: [PATCH 3/5] Addressing review feedback.

---
 lldb/test/API/tools/lldb-dap/server/main.c |  2 +-
 lldb/tools/lldb-dap/DAP.cpp                | 16 +++++++---------
 lldb/tools/lldb-dap/DAP.h                  |  7 +++----
 lldb/tools/lldb-dap/IOStream.cpp           |  6 +++---
 lldb/tools/lldb-dap/IOStream.h             |  1 -
 lldb/tools/lldb-dap/lldb-dap.cpp           | 11 ++++++-----
 6 files changed, 20 insertions(+), 23 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/server/main.c b/lldb/test/API/tools/lldb-dap/server/main.c
index 9a6326f3b57d45..c3599057621276 100644
--- a/lldb/test/API/tools/lldb-dap/server/main.c
+++ b/lldb/test/API/tools/lldb-dap/server/main.c
@@ -3,4 +3,4 @@
 int main(int argc, char const *argv[]) {
   printf("hello world!\n"); // breakpoint 1
   return 0;
-}
\ No newline at end of file
+}
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 29e200f2475eb8..46d7d15df4e324 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -8,7 +8,6 @@
 
 #include <chrono>
 #include <cstdarg>
-#include <fstream>
 #include <mutex>
 
 #include "DAP.h"
@@ -35,8 +34,8 @@ using namespace lldb_dap;
 
 namespace lldb_dap {
 
-DAP::DAP(llvm::StringRef path, std::shared_ptr<llvm::raw_ostream> log,
-         ReplMode repl_mode, std::vector<std::string> pre_init_commands)
+DAP::DAP(llvm::StringRef path, llvm::raw_ostream *log, ReplMode repl_mode,
+         std::vector<std::string> pre_init_commands)
     : debug_adaptor_path(path), broadcaster("lldb-dap"), log(log),
       exception_breakpoints(), pre_init_commands(pre_init_commands),
       focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
@@ -220,19 +219,19 @@ std::string DAP::ReadJSON() {
   std::string json_str;
   int length;
 
-  if (!input.read_expected(log.get(), "Content-Length: "))
+  if (!input.read_expected(log, "Content-Length: "))
     return json_str;
 
-  if (!input.read_line(log.get(), length_str))
+  if (!input.read_line(log, length_str))
     return json_str;
 
   if (!llvm::to_integer(length_str, length))
     return json_str;
 
-  if (!input.read_expected(log.get(), "\r\n"))
+  if (!input.read_expected(log, "\r\n"))
     return json_str;
 
-  if (!input.read_full(log.get(), length, json_str))
+  if (!input.read_full(log, length, json_str))
     return json_str;
 
   if (log) {
@@ -685,9 +684,8 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
     return PacketStatus::JSONMalformed;
   }
 
-  if (log) {
+  if (log)
     *log << llvm::formatv("{0:2}", *json_value).str() << "\n";
-  }
 
   llvm::json::Object *object_ptr = json_value->getAsObject();
   if (!object_ptr) {
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 8f847c1fdffbe4..5570320b7fc46e 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -32,7 +32,6 @@
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/raw_ostream.h"
-#include <iosfwd>
 #include <map>
 #include <optional>
 #include <thread>
@@ -146,7 +145,7 @@ struct DAP {
   lldb::SBBroadcaster broadcaster;
   std::thread event_thread;
   std::thread progress_event_thread;
-  std::shared_ptr<llvm::raw_ostream> log;
+  llvm::raw_ostream *log;
   llvm::StringMap<SourceBreakpointMap> source_breakpoints;
   FunctionBreakpointMap function_breakpoints;
   InstructionBreakpointMap instruction_breakpoints;
@@ -198,8 +197,8 @@ struct DAP {
   // will contain that expression.
   std::string last_nonempty_var_expression;
 
-  DAP(llvm::StringRef path, std::shared_ptr<llvm::raw_ostream> log,
-      ReplMode repl_mode, std::vector<std::string> pre_init_commands);
+  DAP(llvm::StringRef path, llvm::raw_ostream *log, ReplMode repl_mode,
+      std::vector<std::string> pre_init_commands);
   ~DAP();
 
   DAP() = delete;
diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
index 188f6ec1fae9d9..82e4477943656f 100644
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ b/lldb/tools/lldb-dap/IOStream.cpp
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "IOStream.h"
-#include <string>
 #include "llvm/Support/raw_ostream.h"
+#include <string>
 
 #if defined(_WIN32)
 #include <io.h>
@@ -18,7 +18,6 @@
 #include <unistd.h>
 #endif
 
-
 using namespace lldb_dap;
 
 StreamDescriptor::StreamDescriptor() = default;
@@ -144,7 +143,8 @@ bool InputStream::read_line(llvm::raw_ostream *log, std::string &line) {
   return true;
 }
 
-bool InputStream::read_expected(llvm::raw_ostream *log, llvm::StringRef expected) {
+bool InputStream::read_expected(llvm::raw_ostream *log,
+                                llvm::StringRef expected) {
   std::string result;
   if (!read_full(log, expected.size(), result))
     return false;
diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h
index 1933399e075363..d7af0dbe94ce17 100644
--- a/lldb/tools/lldb-dap/IOStream.h
+++ b/lldb/tools/lldb-dap/IOStream.h
@@ -23,7 +23,6 @@ typedef int SOCKET;
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
-#include <fstream>
 #include <string>
 
 // Windows requires different system calls for dealing with sockets and other
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 0c602ecbbbe836..3821704236919e 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -4968,10 +4968,10 @@ int main(int argc, char *argv[]) {
   }
 
   const char *log_file_path = getenv("LLDBDAP_LOG");
-  std::shared_ptr<llvm::raw_ostream> log;
+  llvm::raw_ostream *log;
   if (log_file_path) {
     std::error_code EC;
-    log.reset(new llvm::raw_fd_ostream(log_file_path, EC));
+    log = new llvm::raw_fd_ostream(log_file_path, EC);
     if (EC) {
       llvm::errs() << "Could not open log file: " << EC.message() << ", "
                    << log_file_path << '\n';
@@ -5028,7 +5028,8 @@ int main(int argc, char *argv[]) {
 
     llvm::outs() << "Listening for: "
                  << llvm::join(*maybeListenerAddresses, ", ") << "\n";
-    // Ensure listening address are flushed for calles to retrieve the resolve address.
+    // Ensure listening address are flushed for calles to retrieve the resolve
+    // address.
     llvm::outs().flush();
 
     while (g_listener && g_listener->isListening()) {
@@ -5100,8 +5101,8 @@ int main(int argc, char *argv[]) {
 
   bool CleanExit = true;
   if (auto Err = dap.Loop()) {
-    if (dap.log)
-      *dap.log << "Transport Error: " << Err << "\n";
+    if (log)
+      *log << "Transport Error: " << Err << "\n";
     CleanExit = false;
   }
 

>From 0ff097a7ab38db979f7062595a1ed2aa7e0023de Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 26 Nov 2024 12:14:19 -0800
Subject: [PATCH 4/5] Refactoring the socket management for lldb-dap server
 mode to use lldb/Host/Socket.h'.

---
 lldb/include/lldb/Host/Socket.h               |   3 +
 lldb/include/lldb/Host/common/TCPSocket.h     |   2 +
 lldb/include/lldb/Host/posix/DomainSocket.h   |   2 +
 .../Python/lldbsuite/test/lldbtest.py         |   8 -
 .../test/tools/lldb-dap/dap_server.py         |  47 +-
 lldb/source/Host/common/TCPSocket.cpp         |  22 +-
 lldb/source/Host/posix/DomainSocket.cpp       |  17 +-
 .../tools/lldb-dap/server/TestDAP_server.py   |   9 +-
 lldb/tools/lldb-dap/CMakeLists.txt            |   6 +-
 lldb/tools/lldb-dap/DAP.cpp                   |  72 ++-
 lldb/tools/lldb-dap/DAP.h                     |   8 +-
 lldb/tools/lldb-dap/IOStream.cpp              |   9 +-
 lldb/tools/lldb-dap/IOStream.h                |   8 +-
 lldb/tools/lldb-dap/OutputRedirector.cpp      |  43 +-
 lldb/tools/lldb-dap/OutputRedirector.h        |  41 +-
 lldb/tools/lldb-dap/Socket.cpp                | 500 ------------------
 lldb/tools/lldb-dap/Socket.h                  | 182 -------
 lldb/tools/lldb-dap/lldb-dap.cpp              | 198 ++++---
 18 files changed, 311 insertions(+), 866 deletions(-)
 delete mode 100644 lldb/tools/lldb-dap/Socket.cpp
 delete mode 100644 lldb/tools/lldb-dap/Socket.h

diff --git a/lldb/include/lldb/Host/Socket.h b/lldb/include/lldb/Host/Socket.h
index 14468c98ac5a3a..0bde0e14c9c155 100644
--- a/lldb/include/lldb/Host/Socket.h
+++ b/lldb/include/lldb/Host/Socket.h
@@ -152,6 +152,9 @@ class Socket : public IOObject {
   // If this Socket is connected then return the URI used to connect.
   virtual std::string GetRemoteConnectionURI() const { return ""; };
 
+  // If the Socket is listening then return the URI for clients to connect.
+  virtual std::string GetListeningConnectionURI() const { return ""; }
+
 protected:
   Socket(SocketProtocol protocol, bool should_close,
          bool m_child_process_inherit);
diff --git a/lldb/include/lldb/Host/common/TCPSocket.h b/lldb/include/lldb/Host/common/TCPSocket.h
index eefe0240fe4a95..09408d6431c16d 100644
--- a/lldb/include/lldb/Host/common/TCPSocket.h
+++ b/lldb/include/lldb/Host/common/TCPSocket.h
@@ -53,6 +53,8 @@ class TCPSocket : public Socket {
 
   std::string GetRemoteConnectionURI() const override;
 
+  std::string GetListeningConnectionURI() const override;
+
 private:
   TCPSocket(NativeSocket socket, const TCPSocket &listen_socket);
 
diff --git a/lldb/include/lldb/Host/posix/DomainSocket.h b/lldb/include/lldb/Host/posix/DomainSocket.h
index 983f43bd633719..26f3c42d39e5d0 100644
--- a/lldb/include/lldb/Host/posix/DomainSocket.h
+++ b/lldb/include/lldb/Host/posix/DomainSocket.h
@@ -28,6 +28,8 @@ class DomainSocket : public Socket {
 
   std::string GetRemoteConnectionURI() const override;
 
+  std::string GetListeningConnectionURI() const override;
+
 protected:
   DomainSocket(SocketProtocol protocol, bool child_processes_inherit);
 
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index a899854bb5ae14..8884ef5933ada8 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -39,7 +39,6 @@
 import signal
 from subprocess import *
 import sys
-import socket
 import time
 import traceback
 
@@ -251,13 +250,6 @@ def which(program):
     return None
 
 
-def pickrandomport():
-    """Returns a random open port."""
-    with socket.socket() as sock:
-        sock.bind(("", 0))
-        return sock.getsockname()[1]
-
-
 class ValueCheck:
     def __init__(
         self,
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 8a71b9e709f138..37679462118531 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -9,7 +9,6 @@
 import string
 import subprocess
 import sys
-import selectors
 import threading
 import time
 
@@ -1172,14 +1171,13 @@ def __init__(
             )
 
         if connection:
-            print("attempting connection", connection)
-            if connection.startswith("unix://"):  # unix:///path
+            if connection.startswith("unix-connect://"):  # unix-connect:///path
                 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-                s.connect(connection.removeprefix("unix://"))
-            elif connection.startswith("tcp://"):  # tcp://host:port
-                host, port = connection.removeprefix("tcp://").split(":", 1)
+                s.connect(connection.removeprefix("unix-connect://"))
+            elif connection.startswith("connection://"):  # connection://[host]:port
+                host, port = connection.removeprefix("connection://").rsplit(":", 1)
                 # create_connection with try both ipv4 and ipv6.
-                s = socket.create_connection((host, int(port)))
+                s = socket.create_connection((host.strip("[]"), int(port)))
             else:
                 raise ValueError("invalid connection: {}".format(connection))
             DebugCommunication.__init__(
@@ -1213,16 +1211,16 @@ def launch(
         if log_file:
             adaptor_env["LLDBDAP_LOG"] = log_file
 
-        if os.uname().sysname == "Darwin":
-            adaptor_env["NSUnbufferedIO"] = "YES"
-
         args = [executable]
+        bufsize = -1
         if connection:
+            bufsize = 0
             args.append("--connection")
             args.append(connection)
 
         proc = subprocess.Popen(
             args,
+            bufsize=bufsize,
             stdin=subprocess.PIPE,
             stdout=subprocess.PIPE,
             stderr=sys.stdout,
@@ -1233,29 +1231,20 @@ def launch(
             # If a conneciton is specified, lldb-dap will print the listening
             # address once the listener is made to stdout. The listener is
             # formatted like `tcp://host:port` or `unix:///path`.
-            with selectors.DefaultSelector() as sel:
-                print("Reading stdout for the listening connection")
-                os.set_blocking(proc.stdout.fileno(), False)
-                stdout_key = sel.register(proc.stdout, selectors.EVENT_READ)
-                rdy_fds = sel.select(timeout=10.0)
-                for key, _ in rdy_fds:
-                    if key != stdout_key:
-                        continue
-
-                    outs = proc.stdout.read(1024).decode()
-                    os.set_blocking(proc.stdout.fileno(), True)
-                    for line in outs.split("\n"):
-                        if not line.startswith("Listening for: "):
-                            continue
-                        # If the listener expanded into multiple addresses, use the first.
-                        connection = line.removeprefix("Listening for: ").split(",")[0]
-                        print("")
-                        return proc, connection
+            expected_prefix = "Listening for: "
+            out = proc.stdout.readline().decode()
+            if not out.startswith(expected_prefix):
                 proc.kill()
                 raise ValueError(
-                    "lldb-dap started with a connection but failed to write the listening address to stdout."
+                    "lldb-dap failed to print listening address, expected '{}', got '{}'".format(
+                        expected_prefix, out
+                    )
                 )
 
+            # If the listener expanded into multiple addresses, use the first.
+            connection = out.removeprefix(expected_prefix).rstrip("\r\n")
+            return proc, connection
+
         return proc, None
 
 
diff --git a/lldb/source/Host/common/TCPSocket.cpp b/lldb/source/Host/common/TCPSocket.cpp
index 2d16b605af9497..54a86e54674055 100644
--- a/lldb/source/Host/common/TCPSocket.cpp
+++ b/lldb/source/Host/common/TCPSocket.cpp
@@ -84,6 +84,12 @@ std::string TCPSocket::GetLocalIPAddress() const {
     socklen_t sock_addr_len = sock_addr.GetMaxLength();
     if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0)
       return sock_addr.GetIPAddress();
+  } else if (!m_listen_sockets.empty()) {
+    SocketAddress sock_addr;
+    socklen_t sock_addr_len = sock_addr.GetMaxLength();
+    if (::getsockname(m_listen_sockets.begin()->first, sock_addr,
+                      &sock_addr_len) == 0)
+      return sock_addr.GetIPAddress();
   }
   return "";
 }
@@ -118,6 +124,15 @@ std::string TCPSocket::GetRemoteConnectionURI() const {
   return "";
 }
 
+std::string TCPSocket::GetListeningConnectionURI() const {
+  if (!m_listen_sockets.empty()) {
+    return std::string(llvm::formatv(
+        "connection://[{0}]:{1}", GetLocalIPAddress(), GetLocalPortNumber()));
+  }
+
+  return "";
+}
+
 Status TCPSocket::CreateSocket(int domain) {
   Status error;
   if (IsValid())
@@ -180,8 +195,9 @@ Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
 
   if (host_port->hostname == "*")
     host_port->hostname = "0.0.0.0";
-  std::vector<SocketAddress> addresses = SocketAddress::GetAddressInfo(
-      host_port->hostname.c_str(), nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+  std::vector<SocketAddress> addresses =
+      SocketAddress::GetAddressInfo(host_port->hostname.c_str(), nullptr,
+                                    AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
   for (SocketAddress &address : addresses) {
     int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP,
                                   m_child_processes_inherit, error);
@@ -195,7 +211,7 @@ Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
     }
 
     SocketAddress listen_address = address;
-    if(!listen_address.IsLocalhost())
+    if (!listen_address.IsLocalhost())
       listen_address.SetToAnyAddress(address.GetFamily(), host_port->port);
     else
       listen_address.SetPort(host_port->port);
diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp
index 369123f2239302..f957f885102fb9 100644
--- a/lldb/source/Host/posix/DomainSocket.cpp
+++ b/lldb/source/Host/posix/DomainSocket.cpp
@@ -90,7 +90,8 @@ Status DomainSocket::Connect(llvm::StringRef name) {
   if (error.Fail())
     return error;
   if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
-        (struct sockaddr *)&saddr_un, saddr_un_len) < 0)
+                                  (struct sockaddr *)&saddr_un,
+                                  saddr_un_len) < 0)
     SetLastError(error);
 
   return error;
@@ -181,3 +182,17 @@ std::string DomainSocket::GetRemoteConnectionURI() const {
       "{0}://{1}",
       GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", name);
 }
+
+std::string DomainSocket::GetListeningConnectionURI() const {
+  if (m_socket == kInvalidSocketValue)
+    return "";
+
+  struct sockaddr_un addr;
+  bzero(&addr, sizeof(struct sockaddr_un));
+  addr.sun_family = AF_UNIX;
+  socklen_t addr_len = sizeof(struct sockaddr_un);
+  if (::getsockname(m_socket, (struct sockaddr *)&addr, &addr_len) != 0)
+    return "";
+
+  return llvm::formatv("unix-connect://{0}", addr.sun_path);
+}
diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
index 27bd583d4f6290..d6b2bb24ca707a 100644
--- a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -57,11 +57,14 @@ def test_server_port(self):
         """
         self.do_test_server(connection="tcp://localhost:0")
 
+    @skipIfWindows
     def test_server_unix_socket(self):
         """
         Test launching a binary with a lldb-dap in server mode on a unix socket.
         """
         dir = tempfile.gettempdir()
-        self.do_test_server(
-            connection="unix://" + dir + "/dap-connection-" + str(os.getpid())
-        )
+        name = dir + "/dap-connection-" + str(os.getpid())
+        def cleanup():
+            os.unlink(name)
+        self.addTearDownHook(cleanup)
+        self.do_test_server(connection="unix://" + name)
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index f81be385c62aab..43fc18873feb33 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -1,7 +1,3 @@
-if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" )
-  list(APPEND extra_libs lldbHost)
-endif ()
-
 if (HAVE_LIBPTHREAD)
   list(APPEND extra_libs pthread)
 endif ()
@@ -37,12 +33,12 @@ add_lldb_tool(lldb-dap
   OutputRedirector.cpp
   ProgressEvent.cpp
   RunInTerminal.cpp
-  Socket.cpp
   SourceBreakpoint.cpp
   Watchpoint.cpp
 
   LINK_LIBS
     liblldb
+    lldbHost
     ${extra_libs}
 
   LINK_COMPONENTS
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 46d7d15df4e324..243117782dbe84 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -8,6 +8,7 @@
 
 #include <chrono>
 #include <cstdarg>
+#include <fstream>
 #include <mutex>
 
 #include "DAP.h"
@@ -34,7 +35,7 @@ using namespace lldb_dap;
 
 namespace lldb_dap {
 
-DAP::DAP(llvm::StringRef path, llvm::raw_ostream *log, ReplMode repl_mode,
+DAP::DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
          std::vector<std::string> pre_init_commands)
     : debug_adaptor_path(path), broadcaster("lldb-dap"), log(log),
       exception_breakpoints(), pre_init_commands(pre_init_commands),
@@ -52,22 +53,24 @@ DAP::~DAP() = default;
 
 llvm::Error DAP::ConfigureIO(int out_fd, int err_fd) {
   llvm::Expected<int> new_stdout_fd =
-      RedirectFd(out_fd, [this](llvm::StringRef data) {
+      redirector.RedirectFd(out_fd, [this](llvm::StringRef data) {
         SendOutput(OutputType::Stdout, data);
       });
   if (auto Err = new_stdout_fd.takeError()) {
     return Err;
   }
   llvm::Expected<int> new_stderr_fd =
-      RedirectFd(err_fd, [this](llvm::StringRef data) {
+      redirector.RedirectFd(err_fd, [this](llvm::StringRef data) {
         SendOutput(OutputType::Stderr, data);
       });
   if (auto Err = new_stderr_fd.takeError()) {
     return Err;
   }
 
-  out = lldb::SBFile(new_stdout_fd.get(), "w", false);
-  err = lldb::SBFile(new_stderr_fd.get(), "w", false);
+  out = lldb::SBFile(/*fd=*/new_stdout_fd.get(), /*mode=*/"w",
+                     /*transfer_ownership=*/false);
+  err = lldb::SBFile(/*fd=*/new_stderr_fd.get(), /*mode=*/"w",
+                     /*transfer_ownership=*/false);
 
   return llvm::Error::success();
 }
@@ -207,9 +210,9 @@ void DAP::SendJSON(const llvm::json::Value &json) {
   if (log) {
     auto now = std::chrono::duration<double>(
         std::chrono::system_clock::now().time_since_epoch());
-    *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << "\n"
+    *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << std::endl
          << "Content-Length: " << json_str.size() << "\r\n\r\n"
-         << llvm::formatv("{0:2}", json).str() << "\n";
+         << llvm::formatv("{0:2}", json).str() << std::endl;
   }
 }
 
@@ -237,8 +240,8 @@ std::string DAP::ReadJSON() {
   if (log) {
     auto now = std::chrono::duration<double>(
         std::chrono::system_clock::now().time_since_epoch());
-    *log << llvm::formatv("{0:f9} --> ", now.count()).str()
-         << "\nContent-Length: " << length << "\r\n\r\n";
+    *log << llvm::formatv("{0:f9} --> ", now.count()).str() << std::endl
+         << "Content-Length: " << length << "\r\n\r\n";
   }
   return json_str;
 }
@@ -537,11 +540,14 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
 
     // If we have both a variable and command, warn the user about the conflict.
     if (term_is_command && term_is_variable) {
-      llvm::errs()
-          << "Warning: Expression '" << term
-          << "' is both an LLDB command and variable. It will be evaluated as "
-             "a variable. To evaluate the expression as an LLDB command, use '"
-          << command_escape_prefix << "' as a prefix.\n";
+      SendOutput(
+          OutputType::Stderr,
+          llvm::formatv(
+              "Warning: Expression '{0}' is both an LLDB command and variable. "
+              "It will be evaluated as a variable. To evaluate the expression "
+              "as an LLDB command, use '{1}' as a prefix.",
+              term, command_escape_prefix)
+              .str());
     }
 
     // Variables take preference to commands in auto, since commands can always
@@ -678,19 +684,19 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
       std::string error_str;
       llvm::raw_string_ostream strm(error_str);
       strm << error;
-      *log << "error: failed to parse JSON: " << error_str << "\n"
-           << json << "\n";
+      *log << "error: failed to parse JSON: " << error_str << std::endl
+           << json << std::endl;
     }
     return PacketStatus::JSONMalformed;
   }
 
   if (log)
-    *log << llvm::formatv("{0:2}", *json_value).str() << "\n";
+    *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
 
   llvm::json::Object *object_ptr = json_value->getAsObject();
   if (!object_ptr) {
     if (log)
-      *log << "error: json packet isn't a object\n";
+      *log << "error: json packet isn't a object" << std::endl;
     return PacketStatus::JSONNotObject;
   }
   object = *object_ptr;
@@ -704,7 +710,8 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
     auto handler_pos = request_handlers.find(command);
     if (handler_pos == request_handlers.end()) {
       if (log)
-        *log << "error: unhandled command \"" << command.data() << "\"\n";
+        *log << "error: unhandled command \"" << command.data() << "\""
+             << std::endl;
       return false; // Fail
     }
 
@@ -714,8 +721,15 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
 
   if (packet_type == "response") {
     auto id = GetSigned(object, "request_seq", 0);
-    ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) {
-      llvm::errs() << "Unhandled response\n";
+    ResponseCallback response_handler = [](auto dap, auto value) {
+      if (value)
+        dap.SendOutput(OutputType::Stderr,
+                       llvm::formatv("unexpected response: {0}", *value).str());
+      else
+        dap.SendOutput(OutputType::Stderr,
+                       llvm::formatv("unexpected response: {0}",
+                                     llvm::toString(value.takeError()))
+                           .str());
     };
 
     {
@@ -733,14 +747,15 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
       if (auto *B = object.get("body")) {
         Result = std::move(*B);
       }
-      response_handler(Result);
+      response_handler(*this, Result);
     } else {
       llvm::StringRef message = GetString(object, "message");
       if (message.empty()) {
         message = "Unknown error, response failed";
       }
-      response_handler(llvm::createStringError(
-          std::error_code(-1, std::generic_category()), message));
+      response_handler(
+          *this, llvm::createStringError(
+                     std::error_code(-1, std::generic_category()), message));
     }
 
     return true;
@@ -913,11 +928,14 @@ bool StartDebuggingRequestHandler::DoExecute(
       "startDebugging",
       llvm::json::Object{{"request", request},
                          {"configuration", std::move(*configuration)}},
-      [](llvm::Expected<llvm::json::Value> value) {
+      [](auto dap, auto value) {
         if (!value) {
           llvm::Error err = value.takeError();
-          llvm::errs() << "reverse start debugging request failed: "
-                       << llvm::toString(std::move(err)) << "\n";
+          dap.SendOutput(
+              OutputType::Console,
+              llvm::formatv("reverse start debugging request failed: {0}",
+                            llvm::toString(std::move(err)))
+                  .str());
         }
       });
 
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 5570320b7fc46e..4b17b0ceb011af 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -13,6 +13,7 @@
 #include "FunctionBreakpoint.h"
 #include "IOStream.h"
 #include "InstructionBreakpoint.h"
+#include "OutputRedirector.h"
 #include "ProgressEvent.h"
 #include "SourceBreakpoint.h"
 #include "lldb/API/SBAttachInfo.h"
@@ -60,7 +61,7 @@ enum DAPBroadcasterBits {
 };
 
 typedef void (*RequestCallback)(DAP &dap, const llvm::json::Object &command);
-typedef void (*ResponseCallback)(llvm::Expected<llvm::json::Value> value);
+typedef void (*ResponseCallback)(DAP &dap, llvm::Expected<llvm::json::Value> value);
 
 enum class PacketStatus {
   Success = 0,
@@ -145,7 +146,7 @@ struct DAP {
   lldb::SBBroadcaster broadcaster;
   std::thread event_thread;
   std::thread progress_event_thread;
-  llvm::raw_ostream *log;
+  std::ofstream *log;
   llvm::StringMap<SourceBreakpointMap> source_breakpoints;
   FunctionBreakpointMap function_breakpoints;
   InstructionBreakpointMap instruction_breakpoints;
@@ -196,8 +197,9 @@ struct DAP {
   // empty; if the previous expression was a variable expression, this string
   // will contain that expression.
   std::string last_nonempty_var_expression;
+  OutputRedirector redirector;
 
-  DAP(llvm::StringRef path, llvm::raw_ostream *log, ReplMode repl_mode,
+  DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
       std::vector<std::string> pre_init_commands);
   ~DAP();
 
diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
index 82e4477943656f..7d0f363440f53d 100644
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ b/lldb/tools/lldb-dap/IOStream.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "IOStream.h"
-#include "llvm/Support/raw_ostream.h"
+#include <fstream>
 #include <string>
 
 #if defined(_WIN32)
@@ -86,7 +86,7 @@ bool OutputStream::write_full(llvm::StringRef str) {
   return true;
 }
 
-bool InputStream::read_full(llvm::raw_ostream *log, size_t length,
+bool InputStream::read_full(std::ofstream *log, size_t length,
                             std::string &text) {
   std::string data;
   data.resize(length);
@@ -130,7 +130,7 @@ bool InputStream::read_full(llvm::raw_ostream *log, size_t length,
   return true;
 }
 
-bool InputStream::read_line(llvm::raw_ostream *log, std::string &line) {
+bool InputStream::read_line(std::ofstream *log, std::string &line) {
   line.clear();
   while (true) {
     if (!read_full(log, 1, line))
@@ -143,8 +143,7 @@ bool InputStream::read_line(llvm::raw_ostream *log, std::string &line) {
   return true;
 }
 
-bool InputStream::read_expected(llvm::raw_ostream *log,
-                                llvm::StringRef expected) {
+bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
   std::string result;
   if (!read_full(log, expected.size(), result))
     return false;
diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h
index d7af0dbe94ce17..3284b0899397ce 100644
--- a/lldb/tools/lldb-dap/IOStream.h
+++ b/lldb/tools/lldb-dap/IOStream.h
@@ -22,7 +22,7 @@ typedef int SOCKET;
 #endif
 
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/raw_ostream.h"
+#include <fstream>
 #include <string>
 
 // Windows requires different system calls for dealing with sockets and other
@@ -51,11 +51,11 @@ struct StreamDescriptor {
 struct InputStream {
   StreamDescriptor descriptor;
 
-  bool read_full(llvm::raw_ostream *log, size_t length, std::string &text);
+  bool read_full(std::ofstream *log, size_t length, std::string &text);
 
-  bool read_line(llvm::raw_ostream *log, std::string &line);
+  bool read_line(std::ofstream *log, std::string &line);
 
-  bool read_expected(llvm::raw_ostream *log, llvm::StringRef expected);
+  bool read_expected(std::ofstream *log, llvm::StringRef expected);
 };
 
 struct OutputStream {
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index 0b725e1901b9fd..6a63ed5a95e319 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -16,30 +16,45 @@
 #include "DAP.h"
 #include "OutputRedirector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <thread>
+#include <utility>
+#include <vector>
 
 using namespace llvm;
 
 namespace lldb_dap {
 
-Expected<int> RedirectFd(int fd,
-                         std::function<void(llvm::StringRef)> callback) {
+OutputRedirector::~OutputRedirector() {
+  // Make a best effort cleanup of any redirected FDs.
+  for (auto &[from, to] : m_redirects)
+    ::dup2(to, from); // ignoring errors
+
+  for (const auto &fd : m_fds)
+    close(fd);
+}
+
+Expected<int>
+OutputRedirector::RedirectFd(int fd,
+                             std::function<void(llvm::StringRef)> callback) {
   int new_fd[2];
 #if defined(_WIN32)
-  if (_pipe(new_fd, 4096, O_TEXT) == -1) {
+  if (_pipe(new_fd, 4096, O_TEXT) == -1)
 #else
-  if (pipe(new_fd) == -1) {
+  if (pipe(new_fd) == -1)
 #endif
-    int error = errno;
-    return createStringError(inconvertibleErrorCode(),
-                             "Couldn't create new pipe for fd %d. %s", fd,
-                             strerror(error));
-  }
+    return createStringError(llvm::errnoAsErrorCode(),
+                             "Couldn't create new pipe for fd %d.", fd);
+
+  m_fds.push_back(new_fd[0]);
+  m_fds.push_back(new_fd[1]);
+
+  if (fd != -1) {
+    if (dup2(new_fd[1], fd) == -1)
+      return createStringError(llvm::errnoAsErrorCode(),
+                               "Couldn't override the fd %d.", fd);
 
-  if (fd != -1 && dup2(new_fd[1], fd) == -1) {
-    int error = errno;
-    return createStringError(inconvertibleErrorCode(),
-                             "Couldn't override the fd %d. %s", fd,
-                             strerror(error));
+    m_redirects.push_back(std::make_pair(new_fd[1], fd));
   }
 
   int read_fd = new_fd[0];
diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h
index 418b8bac102c7f..59de19d5ff1b6b 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.h
+++ b/lldb/tools/lldb-dap/OutputRedirector.h
@@ -11,21 +11,38 @@
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
+#include <utility>
+#include <vector>
 
 namespace lldb_dap {
 
-/// Redirects the output of a given file descriptor to a callback.
-///
-/// \param[in] fd
-///     Either -1 or the fd duplicate into the new handle.
-///
-/// \param[in] callback
-///     A callback invoked each time the file is written.
-///
-/// \return
-///     A new file handle for the output.
-llvm::Expected<int> RedirectFd(int fd,
-                               std::function<void(llvm::StringRef)> callback);
+/// Manages the lifetime of file descriptor redirects.
+struct OutputRedirector {
+  OutputRedirector() = default;
+
+  OutputRedirector(const OutputRedirector &) = delete;
+  OutputRedirector &operator=(const OutputRedirector &) = delete;
+
+  ~OutputRedirector();
+
+  /// Redirects the output of a given file descriptor to a callback.
+  ///
+  /// \param[in] fd
+  ///     Either -1 or the fd duplicate into the new handle.
+  ///
+  /// \param[in] callback
+  ///     A callback invoked each time the file is written.
+  ///
+  /// \return
+  ///     A new file handle for the output.
+  llvm::Expected<int> RedirectFd(int fd,
+                                 std::function<void(llvm::StringRef)> callback);
+
+private:
+  std::vector<int> m_fds; // owned fds, closed on dealloc.
+  std::vector<std::pair<int, int>>
+      m_redirects; // pairs (new, old) of redirected fds.
+};
 
 } // namespace lldb_dap
 
diff --git a/lldb/tools/lldb-dap/Socket.cpp b/lldb/tools/lldb-dap/Socket.cpp
deleted file mode 100644
index c5c90a81547878..00000000000000
--- a/lldb/tools/lldb-dap/Socket.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-//===-- Socket.cpp ----------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "Socket.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/config.h"
-#include "llvm/Support/Errno.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Signals.h"
-#include "llvm/Support/Threading.h"
-#include <cstdint>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/_types/_socklen_t.h>
-#include <sys/poll.h>
-#include <system_error>
-
-#ifndef _WIN32
-#include <poll.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#else
-#include "llvm/Support/Windows/WindowsSupport.h"
-// winsock2.h must be included before afunix.h. Briefly turn off clang-format to
-// avoid error.
-// clang-format off
-#include <winsock2.h>
-#include <afunix.h>
-// clang-format on
-#include <io.h>
-#endif // _WIN32
-
-#if defined(HAVE_UNISTD_H)
-#include <unistd.h>
-#endif
-
-namespace {
-
-enum SocketProtocol { ProtocolTcp, ProtocolUnixDomain };
-
-std::error_code getLastSocketErrorCode() {
-#ifdef _WIN32
-  return std::error_code(::WSAGetLastError(), std::system_category());
-#else
-  return llvm::errnoAsErrorCode();
-#endif
-}
-
-llvm::Expected<std::vector<lldb_dap::SocketAddress>>
-getAddressInfo(llvm::StringRef host) {
-  std::vector<lldb_dap::SocketAddress> add_list;
-
-  struct addrinfo hints;
-  memset(&hints, 0, sizeof(hints));
-  hints.ai_family = AF_UNSPEC;
-  hints.ai_socktype = SOCK_STREAM;
-  hints.ai_protocol = IPPROTO_TCP;
-  hints.ai_flags = 0;
-
-  struct addrinfo *service_info_list = nullptr;
-
-  int err =
-      getaddrinfo(host.str().c_str(), nullptr, &hints, &service_info_list);
-  if (err != 0)
-    return llvm::createStringError("getaddrinfo failed: %s", gai_strerror(err));
-
-  for (struct addrinfo *service_ptr = service_info_list; service_ptr != nullptr;
-       service_ptr = service_ptr->ai_next) {
-    add_list.emplace_back(lldb_dap::SocketAddress(service_ptr));
-  }
-
-  if (service_info_list)
-    ::freeaddrinfo(service_info_list);
-
-  return add_list;
-}
-
-int CloseSocket(NativeSocket sockfd) {
-#ifdef _WIN32
-  return ::closesocket(sockfd);
-#else
-  return ::close(sockfd);
-#endif
-}
-
-llvm::Expected<std::pair<llvm::StringRef, int>>
-detectHostAndPort(llvm::StringRef name) {
-  llvm::StringRef host;
-  llvm::StringRef port;
-  std::tie(host, port) = name.split(":");
-
-  if (host == "" || host == "*") {
-    host = "0.0.0.0";
-  }
-
-  if (port == "") {
-    port = "0";
-  }
-
-  int portnu = 0;
-  if (!llvm::to_integer(port, portnu, 10)) {
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "invalid host:port specification: '%s'",
-                                   name.str().c_str());
-  }
-
-  return std::make_pair(host, portnu);
-}
-
-} // namespace
-
-namespace lldb_dap {
-
-#ifdef _WIN32
-WSABalancer::WSABalancer() {
-  WSADATA WsaData;
-  ::memset(&WsaData, 0, sizeof(WsaData));
-  if (WSAStartup(MAKEWORD(2, 2), &WsaData) != 0) {
-    llvm::report_fatal_error("WSAStartup failed");
-  }
-}
-
-WSABalancer::~WSABalancer() { WSACleanup(); }
-#endif // _WIN32
-
-SocketAddress::SocketAddress() { clear(); }
-
-SocketAddress::SocketAddress(const struct addrinfo *info) {
-  clear();
-  if (info && info->ai_addrlen > 0 &&
-      size_t(info->ai_addrlen) <= sizeof m_address) {
-    memcpy(&m_address.sa, info->ai_addr, static_cast<size_t>(info->ai_addrlen));
-    m_address.sa.sa_len = info->ai_addrlen;
-  }
-}
-
-SocketAddress::SocketAddress(const struct sockaddr &sa) { m_address.sa = sa; }
-
-SocketAddress::SocketAddress(const struct sockaddr_in &sa) {
-  m_address.sa_ipv4 = sa;
-}
-
-SocketAddress::SocketAddress(const struct sockaddr_in6 &sa) {
-  m_address.sa_ipv6 = sa;
-}
-
-SocketAddress::SocketAddress(const struct sockaddr_un &su) {
-  m_address.su = su;
-}
-
-std::string SocketAddress::getPath() const {
-  if (getFamily() == AF_UNIX)
-    return std::string(m_address.su.sun_path);
-  return "";
-}
-
-std::string SocketAddress::getName() const {
-  if (getFamily() == AF_UNIX)
-    return "unix://" + getPath();
-
-  uint64_t port = getPort();
-  std::string hostname;
-  if (isLocalhost())
-    hostname = "localhost";
-  else if (isAnyAddr())
-    hostname = getFamily() == AF_INET ? "0.0.0.0" : "[::]";
-  else {
-    char hbuf[NI_MAXHOST];
-    if (getnameinfo(&m_address.sa, getLength(), hbuf, sizeof(hbuf), nullptr, 0,
-                    NI_NUMERICHOST) == 0) {
-      hostname = std::string(hbuf);
-    }
-  }
-
-  return "tcp://" + hostname + ":" + std::to_string(port);
-}
-
-bool SocketAddress::isLocalhost() const {
-  switch (getFamily()) {
-  case AF_INET:
-    return m_address.sa_ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK);
-  case AF_INET6:
-    return 0 == memcmp(&m_address.sa_ipv6.sin6_addr, &in6addr_loopback, 16);
-  }
-  return false;
-}
-
-bool SocketAddress::isAnyAddr() const {
-  switch (getFamily()) {
-  case AF_INET:
-    return m_address.sa_ipv4.sin_addr.s_addr == htonl(INADDR_ANY);
-  case AF_INET6:
-    return 0 == memcmp(&m_address.sa_ipv6.sin6_addr, &in6addr_any, 16);
-  }
-  return false;
-}
-
-void SocketAddress::setFamily(sa_family_t family) {
-  m_address.sa.sa_family = family;
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \
-    defined(__OpenBSD__)
-  switch (family) {
-  case AF_INET:
-    m_address.sa.sa_len = sizeof(struct sockaddr_in);
-    break;
-
-  case AF_INET6:
-    m_address.sa.sa_len = sizeof(struct sockaddr_in6);
-    break;
-
-  case AF_UNIX:
-    m_address.sa.sa_len = SUN_LEN(&m_address.su);
-    break;
-
-  default:
-    assert(0 && "unsupported socket family");
-  }
-#endif
-}
-
-SocketAddress SocketAddress::addressOf(const Socket &s, std::error_code &EC) {
-  SocketAddress sa;
-  socklen_t len = sizeof(sockaddr_t);
-
-  if (::getsockname(s.fd, &sa.sockaddr(), &len) == -1) {
-    EC = getLastSocketErrorCode();
-    return SocketAddress{};
-  }
-
-  // If the socket is a struct sockaddr_un then the length is returned by the
-  // len parameter not the sa_len field. Update the field to match the
-  // returned address.
-  sa.m_address.sa.sa_len = len;
-  return sa;
-}
-
-bool SocketAddress::setToAnyAddress(sa_family_t family, uint16_t port) {
-  switch (family) {
-  case AF_INET:
-    setFamily(family);
-    if (setPort(port)) {
-      m_address.sa_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
-      return true;
-    }
-    break;
-
-  case AF_INET6:
-    setFamily(family);
-    if (setPort(port)) {
-      m_address.sa_ipv6.sin6_addr = in6addr_any;
-      return true;
-    }
-    break;
-  }
-
-  clear();
-  return false;
-}
-
-Socket::Socket(NativeSocket fd) : fd(fd) {}
-Socket::Socket(Socket &&S) : fd(S.fd) { S.fd = -1; }
-
-Socket::~Socket() {
-  close();
-}
-
-void Socket::close() {
-  if (fd == -1) {
-    return;
-  }
-
-  CloseSocket(fd);
-  fd = -1;
-}
-
-SocketListener::SocketListener(std::vector<Socket> sockets, int fds[2])
-    : m_listening(true), m_sockets(std::move(sockets)), m_pipe{fds[0], fds[1]} {
-}
-
-SocketListener::SocketListener(SocketListener &&SL)
-    : m_listening(SL.m_listening.load()), m_sockets(std::move(SL.m_sockets)),
-      m_pipe{SL.m_pipe[0], SL.m_pipe[1]} {
-  SL.m_listening = false;
-  SL.m_sockets.clear();
-  SL.m_pipe[0] = -1;
-  SL.m_pipe[1] = -1;
-}
-
-llvm::Expected<SocketListener>
-SocketListener::createListener(llvm::StringRef name) {
-  SocketProtocol protocol;
-
-  if (name.consume_front("tcp://") || name.consume_front("tcp:") ||
-      name.starts_with(":")) {
-    protocol = ProtocolTcp;
-  } else if (name.consume_front("unix://") || name.consume_front("unix:") ||
-             name.starts_with("/")) {
-    protocol = ProtocolUnixDomain;
-  } else if (name.contains(":")) {
-    protocol = ProtocolTcp;
-  } else {
-    return llvm::createStringError(
-        "invalid address, expected '[tcp://][host]:port' or "
-        "'[unix://]/path' but got %s.",
-        name.str().c_str());
-  }
-
-  std::vector<SocketAddress> addresses;
-  if (protocol == ProtocolTcp) {
-    auto maybeHostAndPort = detectHostAndPort(name);
-    if (auto Err = maybeHostAndPort.takeError())
-      return Err;
-
-    llvm::StringRef host;
-    int port;
-    std::tie(host, port) = *maybeHostAndPort;
-
-    llvm::Expected<std::vector<SocketAddress>> maybeAddresses =
-        getAddressInfo(host);
-    if (auto Err = maybeAddresses.takeError()) {
-      return Err;
-    }
-
-    for (auto &address : *maybeAddresses) {
-      SocketAddress listen_address = address;
-      if (!listen_address.isLocalhost())
-        listen_address.setToAnyAddress(address.getFamily(), port);
-      else
-        listen_address.setPort(port);
-
-      addresses.push_back(std::move(listen_address));
-    }
-  } else {
-    llvm::StringRef path = name;
-    if (llvm::sys::fs::exists(path)) {
-      return llvm::make_error<llvm::StringError>(
-          std::make_error_code(std::errc::file_exists), "file exists at path");
-    }
-
-    struct sockaddr_un addr;
-    bzero(&addr, sizeof(struct sockaddr_un));
-    addr.sun_family = AF_UNIX;
-    strncpy(addr.sun_path, path.str().c_str(), sizeof(addr.sun_path) - 1);
-    addr.sun_len = SUN_LEN(&addr);
-
-    addresses.push_back(SocketAddress(addr));
-  }
-
-  std::vector<Socket> sockets;
-  for (auto &address : addresses) {
-    bool isTCP =
-        address.getFamily() == AF_INET || address.getFamily() == AF_INET6;
-
-    NativeSocket fd =
-        ::socket(address.getFamily(), SOCK_STREAM, isTCP ? IPPROTO_TCP : 0);
-    if (fd == -1) {
-      return llvm::createStringError(getLastSocketErrorCode(),
-                                     "socket() failed");
-    }
-
-    int val = 1;
-    if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1) {
-      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                                 "setsockopt() failed");
-    }
-
-    if (::bind(fd, &address.sockaddr(), address.getLength()) == -1) {
-      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                                 "bind() failed");
-    }
-
-    if (::listen(fd, llvm::hardware_concurrency().compute_thread_count()) ==
-        -1) {
-      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                                 "listen() failed");
-    }
-
-    sockets.emplace_back(fd);
-  }
-
-  int pipeFD[2];
-#ifdef _WIN32
-  // Reserve 1 byte for the pipe and use default textmode
-  if (::_pipe(pipeFD, 1, 0) == -1)
-#else
-  if (::pipe(pipeFD) == -1)
-#endif // _WIN32
-    return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                               "pipe failed");
-
-  return SocketListener{std::move(sockets), pipeFD};
-}
-
-SocketListener::~SocketListener() {
-  shutdown();
-  if (m_pipe[0] != -1)
-    close(m_pipe[0]);
-  if (m_pipe[1] != -1)
-    close(m_pipe[1]);
-}
-
-llvm::Expected<std::vector<std::string>> SocketListener::addresses() const {
-  std::vector<std::string> addrs;
-  std::error_code EC;
-  for (auto &s : m_sockets) {
-    auto addr = SocketAddress::addressOf(s, EC);
-    if (EC)
-      return llvm::make_error<llvm::StringError>(EC);
-
-    addrs.push_back(addr.getName());
-  }
-  return addrs;
-}
-
-void SocketListener::shutdown() {
-  bool listening = m_listening;
-
-  if (!listening)
-    return;
-
-  if (!m_listening.compare_exchange_strong(listening, false))
-    return;
-
-  for (auto &s : m_sockets) {
-    // If the socket has a path (e.g. a unix:// socket), remove it after
-    // closing.
-    std::error_code _; // ignoring failures during shutdown.
-    std::string path = SocketAddress::addressOf(s, _).getPath();
-    s.close();
-    if (!path.empty())
-      unlink(path.c_str());
-  }
-
-  // Write to the pipe to indiciate that accept() should exit immediately.
-  char byte = 'A';
-  ssize_t written = ::write(m_pipe[1], &byte, 1);
-
-  // Ignore any write() error
-  (void)written;
-}
-
-llvm::Expected<Socket> SocketListener::accept() {
-  if (m_sockets.empty()) {
-    return llvm::createStringError(
-        std::make_error_code(std::errc::bad_file_descriptor), "no open socket");
-  }
-
-  std::vector<struct pollfd> pollfds;
-
-  for (auto &s : m_sockets)
-    pollfds.emplace_back(pollfd{s.fd, POLLIN, 0});
-
-  pollfds.emplace_back(pollfd{m_pipe[0], POLLIN, 0});
-
-  while (m_listening.load()) {
-    int status;
-#ifdef _WIN32
-    status = WSAPoll(pollfds.data(), pollfds.size(), -1);
-#else
-    status = ::poll(pollfds.data(), pollfds.size(), -1);
-#endif
-
-    if (status == -1) {
-      return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                                 "poll() failed");
-    }
-
-    for (auto &pfd : pollfds) {
-      if (pfd.revents & POLLIN) {
-        // Check if shutdown was requested.
-        if (pfd.fd == m_pipe[0]) {
-          break;
-        }
-
-        struct sockaddr client;
-        socklen_t len;
-        int fd =
-            llvm::sys::RetryAfterSignal(-1, ::accept, pfd.fd, &client, &len);
-        if (fd == -1) {
-          return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
-                                                     "accept() failed");
-        }
-
-        return Socket{fd};
-      }
-    }
-  }
-
-  return llvm::make_error<llvm::StringError>(
-      std::make_error_code(std::errc::connection_aborted), "socket shutdown");
-}
-
-} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Socket.h b/lldb/tools/lldb-dap/Socket.h
deleted file mode 100644
index 98923032f91417..00000000000000
--- a/lldb/tools/lldb-dap/Socket.h
+++ /dev/null
@@ -1,182 +0,0 @@
-//===-- Socket.h ------------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TOOLS_LLDB_DAP_SOCKET_H
-#define LLDB_TOOLS_LLDB_DAP_SOCKET_H
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#ifdef _WIN32
-#include "lldb/Host/windows/windows.h"
-#include <winsock2.h>
-#include <ws2tcpip.h>
-typedef ADDRESS_FAMILY sa_family_t;
-typedef SOCKET NativeSocket;
-#else
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-typedef int NativeSocket;
-#endif
-
-namespace lldb_dap {
-
-struct Socket;
-
-/// Manages socket addresses for socket based communication.
-struct SocketAddress {
-  SocketAddress();
-  explicit SocketAddress(const struct addrinfo *);
-  explicit SocketAddress(const struct sockaddr &);
-  explicit SocketAddress(const struct sockaddr_in &);
-  explicit SocketAddress(const struct sockaddr_in6 &);
-  explicit SocketAddress(const struct sockaddr_un &);
-
-  static SocketAddress addressOf(const Socket &s, std::error_code &EC);
-
-  sa_family_t getFamily() const { return m_address.sa.sa_family; }
-  uint16_t getPort() const {
-    switch (getFamily()) {
-    case AF_INET:
-      return ntohs(m_address.sa_ipv4.sin_port);
-    case AF_INET6:
-      return ntohs(m_address.sa_ipv6.sin6_port);
-    }
-    return 0;
-  }
-  std::string getName() const;
-  /// Returns the path for AF_UNIX sockets, otherwise "".
-  std::string getPath() const;
-  /// Get the length for the current socket address family
-  socklen_t getLength() const { return m_address.sa.sa_len; };
-
-  bool isLocalhost() const;
-  bool isAnyAddr() const;
-
-  void setFamily(sa_family_t family);
-  bool setToAnyAddress(sa_family_t family, uint16_t port);
-  bool setPort(uint16_t port) {
-    switch (getFamily()) {
-    case AF_INET:
-      m_address.sa_ipv4.sin_port = htons(port);
-      return true;
-    case AF_INET6:
-      m_address.sa_ipv6.sin6_port = htons(port);
-      return true;
-    }
-    return false;
-  }
-
-  void clear() { bzero(&m_address, sizeof(sockaddr_t)); }
-
-  struct sockaddr &sockaddr() { return m_address.sa; }
-  const struct sockaddr &sockaddr() const { return m_address.sa; }
-
-private:
-  typedef union sockaddr_tag {
-    struct sockaddr sa;
-    struct sockaddr_in sa_ipv4;
-    struct sockaddr_in6 sa_ipv6;
-    struct sockaddr_un su;
-    struct sockaddr_storage ss;
-  } sockaddr_t;
-  sockaddr_t m_address;
-};
-
-/// Manages the lifetime of a socket.
-struct Socket {
-  NativeSocket fd;
-
-  /// Close the socket.
-  void close();
-
-  explicit Socket(NativeSocket fd);
-  ~Socket();
-  Socket(Socket &&S);
-  Socket(const Socket &S) = delete;
-  Socket &operator=(const Socket &S) = delete;
-};
-
-#ifdef _WIN32
-/// Ensures proper initialization and cleanup of winsock resources
-///
-/// Make sure that calls to WSAStartup and WSACleanup are balanced.
-class WSABalancer {
-public:
-  WSABalancer();
-  ~WSABalancer();
-};
-#endif // _WIN32
-
-/// Manages listening for socket connections.
-///
-/// SocketListener handles listening for both unix and tcp based connections.
-struct SocketListener {
-  SocketListener(const SocketListener &S) = delete;
-  SocketListener &operator=(const SocketListener &S) = delete;
-  SocketListener(SocketListener &&SL);
-  ~SocketListener();
-
-  /// Creates a listening socket bound to the specified name.
-  ///
-  /// Handles the socket creation, binding, and immediately starts listening for
-  /// incoming connections.
-  ///
-  /// \param[in] name
-  ///     Socket names formatted like `protocol:name`.
-  ///
-  ///     Supported protocols include tcp and unix sockets.
-  ///
-  ///     Names must follow the following formats:
-  ///
-  ///     * tcp://host:port
-  ///     * tcp:host:port
-  ///     * tcp:port (host will be assumed 0.0.0.0)
-  ///     * :port (implicit tcp)
-  ///     * unix:///path
-  ///     * /path (implicit unix)
-  static llvm::Expected<SocketListener> createListener(llvm::StringRef name);
-
-  /// Returns an array of active listening sockets.
-  llvm::Expected<std::vector<std::string>> addresses() const;
-
-  /// Shutdown the socket listener.
-  void shutdown();
-
-  /// Returns true if listening, otherwise false.
-  bool isListening() const { return m_listening; }
-
-  /// Accept returns the next client connection, blocking until a connection is
-  /// made or Shutdown is called.
-  llvm::Expected<Socket> accept();
-
-private:
-  SocketListener(std::vector<Socket> sockets, int fds[2]);
-
-  std::atomic<bool> m_listening;
-  std::vector<Socket> m_sockets;
-
-  /// If a separate thread calls shutdown, the listening file descriptor
-  /// could be closed while ::poll is waiting for it to be ready to perform a
-  /// I/O operations. ::poll will continue to block even after FD is closed so
-  /// use a self-pipe mechanism to get ::poll to return
-  int m_pipe[2] = {};
-
-#ifdef _WIN32
-  WSABalancer _;
-#endif // _WIN32
-};
-
-} // namespace lldb_dap
-
-#endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 3821704236919e..230cf69752c877 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -11,7 +11,6 @@
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
 #include "RunInTerminal.h"
-#include "Socket.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
 #include "lldb/API/SBFile.h"
@@ -21,11 +20,18 @@
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
 #include "lldb/Host/Config.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/DomainSocket.h"
+#include "lldb/Utility/Status.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
@@ -46,13 +52,16 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <fstream>
 #include <iostream>
 #include <map>
 #include <memory>
 #include <optional>
+#include <ostream>
 #include <set>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <system_error>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -86,6 +95,13 @@ typedef int socklen_t;
 #endif
 
 using namespace lldb_dap;
+using lldb_private::DomainSocket;
+using lldb_private::MainLoop;
+using lldb_private::MainLoopBase;
+using lldb_private::NativeSocket;
+using lldb_private::Socket;
+using lldb_private::Status;
+using lldb_private::TCPSocket;
 
 namespace {
 using namespace llvm::opt;
@@ -1994,15 +2010,16 @@ llvm::Error request_runInTerminal(DAP &dap,
 #endif
   llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
       launch_request, dap.debug_adaptor_path, comm_file.m_path, debugger_pid);
-  dap.SendReverseRequest("runInTerminal", std::move(reverse_request),
-                         [](llvm::Expected<llvm::json::Value> value) {
-                           if (!value) {
-                             llvm::Error err = value.takeError();
-                             llvm::errs()
-                                 << "runInTerminal request failed: "
-                                 << llvm::toString(std::move(err)) << "\n";
-                           }
-                         });
+  dap.SendReverseRequest(
+      "runInTerminal", std::move(reverse_request), [](auto dap, auto value) {
+        if (!value) {
+          dap.SendOutput(
+              OutputType::Stderr,
+              llvm::formatv("runInTerminal request failed: {}",
+                            llvm::toString(std::move(value.takeError())))
+                  .str());
+        }
+      });
 
   if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
     attach_info.SetProcessID(*pid);
@@ -4889,7 +4906,28 @@ static void redirection_test() {
   fflush(stderr);
 }
 
-static SocketListener *g_listener = nullptr;
+static llvm::Expected<std::pair<Socket::SocketProtocol, std::string>>
+parseConnection(llvm::StringRef conn) {
+  if (conn.contains("://")) {
+    llvm::StringRef scheme, rest;
+    std::tie(scheme, rest) = conn.split("://");
+
+    if (scheme == "unix" || scheme == "unix-connect") {
+      return std::make_pair(Socket::ProtocolUnixDomain, rest.str());
+    }
+    if (scheme == "tcp" || scheme == "connect") {
+      return std::make_pair(Socket::ProtocolTcp, rest.str());
+    }
+  } else if (conn.starts_with("/")) {
+    return std::make_pair(Socket::ProtocolUnixDomain, conn.str());
+  } else if (conn.contains(":")) {
+    return std::make_pair(Socket::ProtocolTcp, conn.str());
+  }
+
+  return llvm::createStringError(
+      "expected '[unix://]/path' or '[tcp://][host]:port', got '%s'.",
+      conn.str().c_str());
+}
 
 int main(int argc, char *argv[]) {
   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
@@ -4968,16 +5006,9 @@ int main(int argc, char *argv[]) {
   }
 
   const char *log_file_path = getenv("LLDBDAP_LOG");
-  llvm::raw_ostream *log;
-  if (log_file_path) {
-    std::error_code EC;
-    log = new llvm::raw_fd_ostream(log_file_path, EC);
-    if (EC) {
-      llvm::errs() << "Could not open log file: " << EC.message() << ", "
-                   << log_file_path << '\n';
-      return EXIT_FAILURE;
-    }
-  }
+  std::ofstream *log = nullptr;
+  if (log_file_path)
+    log = new std::ofstream(log_file_path);
 
   const auto pre_init_commands =
       input_args.getAllArgValues(OPT_pre_init_command);
@@ -5001,72 +5032,95 @@ int main(int argc, char *argv[]) {
       llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
 
   if (!connection.empty()) {
-    auto maybeListener = SocketListener::createListener(connection);
-    if (auto Err = maybeListener.takeError()) {
-      llvm::errs() << "Failed to listen for connections: "
-                   << llvm::toString(std::move(Err)) << "\n";
+    auto maybeProtoclAndName = parseConnection(connection);
+    if (auto Err = maybeProtoclAndName.takeError()) {
+      std::cerr << "Invalid connection specification "
+                << llvm::toString(std::move(Err)) << std::endl;
       return EXIT_FAILURE;
     }
 
-    g_listener = &*maybeListener;
-
-    llvm::sys::SetInterruptFunction([]() {
-      if (g_listener)
-        g_listener->shutdown();
-    });
+    Socket::SocketProtocol protocol;
+    std::string name;
+    std::tie(protocol, name) = *maybeProtoclAndName;
+
+    Status error;
+    std::unique_ptr<Socket> listener = Socket::Create(protocol, false, error);
+    if (error.Fail()) {
+      std::cerr << "Failed to create listener for protocol "
+                << Socket::FindSchemeByProtocol(protocol)
+                << ", error: " << llvm::toString(error.takeError())
+                << std::endl;
+      return EXIT_FAILURE;
+    }
 
-    auto maybeListenerAddresses = g_listener->addresses();
-    if (auto Err = maybeListenerAddresses.takeError()) {
-      llvm::errs() << "listener failed to retrieve listening socket address: "
-                   << Err << "\n";
+    error = listener->Listen(name, /* backlog */ 5);
+    if (error.Fail()) {
+      std::cerr << "Failed to listen, error: "
+                << llvm::toString(error.takeError()) << std::endl;
       return EXIT_FAILURE;
     }
 
+    std::string address = listener->GetListeningConnectionURI();
+
     if (log)
-      *log << "started with connection listeners "
-           << llvm::join(*maybeListenerAddresses, ", ") << "\n";
+      *log << "started with connection listeners " << address << "\n";
 
-    llvm::outs() << "Listening for: "
-                 << llvm::join(*maybeListenerAddresses, ", ") << "\n";
+    std::cout << "Listening for: " << address << std::endl;
     // Ensure listening address are flushed for calles to retrieve the resolve
     // address.
-    llvm::outs().flush();
+    std::flush(std::cout);
+
+    MainLoop mainloop;
+    mainloop.RegisterSignal(
+        SIGHUP, [](auto &RL) { RL.RequestTermination(); }, error);
 
-    while (g_listener && g_listener->isListening()) {
-      llvm::Expected<Socket> maybeClient = g_listener->accept();
-      if (auto Err = maybeClient.takeError()) {
+    auto start_dap = [=](std::unique_ptr<Socket> client) {
+      DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
+      NativeSocket sock = client->GetNativeSocket();
+
+      if (auto Err = dap.ConfigureIO()) {
         if (log)
-          *log << "client accept failed: " << Err << "\n";
-        llvm::errs() << "client connection failed: " << Err << "\n";
-        continue;
+          *log << "client[" << sock << "] configureIO failed: "
+               << llvm::toStringWithoutConsuming(Err) << "\n";
+        std::cerr << "failed to configure client connect: "
+                  << llvm::toString(std::move(Err)) << std::endl;
+        return;
       }
 
-      Socket client = std::move(*maybeClient);
-      std::thread t([=, client = std::move(client)]() mutable {
-        DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
+      RegisterRequestCallbacks(dap);
+      dap.input.descriptor = StreamDescriptor::from_socket(sock, false);
+      dap.output.descriptor = StreamDescriptor::from_socket(sock, false);
+      if (auto Err = dap.Loop()) {
+        if (log)
+          *log << "client[" << sock
+               << "] Transport Error: " << llvm::toStringWithoutConsuming(Err)
+               << "\n";
+        std::cerr << "client transport error: "
+                  << llvm::toString(std::move(Err)) << std::endl;
+      }
 
-        if (auto Err = dap.ConfigureIO()) {
-          llvm::errs() << "failed to configure client connect: "
-                       << llvm::toString(std::move(Err)) << "\n";
-          return;
-        }
+      if (log)
+        *log << "client[" << sock << "] connection closed\n";
+    };
 
-        RegisterRequestCallbacks(dap);
-        dap.input.descriptor = StreamDescriptor::from_socket(client.fd, false);
-        dap.output.descriptor = StreamDescriptor::from_socket(client.fd, false);
-        if (auto Err = dap.Loop()) {
-          if (log)
-            *log << "client[" << client.fd
-                 << "] Transport Error: " << llvm::toString(std::move(Err))
-                 << "\n";
-        }
+    auto on_accept = [=](std::unique_ptr<Socket> client) {
+      llvm::errs() << "Client connected...\n";
+      // Start a thread for each connection, unblocking the listening thread.
+      std::thread(start_dap, std::move(client)).detach();
+    };
 
-        if (log)
-          *log << "client[" << client.fd << "] connection closed\n";
+    auto handles = listener->Accept(mainloop, on_accept);
+    if (auto Err = handles.takeError()) {
+      std::cerr << "failed to register accept() with the main loop: "
+                << llvm::toString(std::move(Err)) << std::endl;
+      return EXIT_FAILURE;
+    }
 
-        client.close();
-      });
-      t.detach();
+    error = mainloop.Run();
+    if (error.Fail()) {
+      std::cerr << "failed to accept()" << llvm::toString(error.takeError())
+                << std::endl;
+      return EXIT_FAILURE;
     }
 
     return EXIT_SUCCESS;
@@ -5076,7 +5130,8 @@ int main(int argc, char *argv[]) {
   int new_stdout_fd = dup(fileno(stdout));
   DAP dap = DAP(program_path.str(), log, default_repl_mode, pre_init_commands);
   if (auto Err = dap.ConfigureIO(fileno(stdout), fileno(stderr))) {
-    llvm::errs() << "Failed to create lldb-dap instance: " << Err << "\n";
+    std::cout << "Failed to create lldb-dap instance: "
+              << llvm::toString(std::move(Err)) << "\n";
     return EXIT_FAILURE;
   }
   RegisterRequestCallbacks(dap);
@@ -5102,7 +5157,10 @@ int main(int argc, char *argv[]) {
   bool CleanExit = true;
   if (auto Err = dap.Loop()) {
     if (log)
-      *log << "Transport Error: " << Err << "\n";
+      *log << "Transport Error: " << llvm::toStringWithoutConsuming(Err)
+           << "\n";
+    std::cerr << "Transport Error: " << llvm::toString(std::move(Err))
+              << std::endl;
     CleanExit = false;
   }
 

>From d1ff196124bb38dcc15ef0b7086534c53158abc3 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 26 Nov 2024 15:03:57 -0800
Subject: [PATCH 5/5] Refactoring both stdin/stdout mode and the server mode to
 use the same callback to handle a DAP connection.

This should help ensure both have a uniform behavior.
---
 lldb/tools/lldb-dap/IOStream.cpp |  22 +-----
 lldb/tools/lldb-dap/IOStream.h   |   7 +-
 lldb/tools/lldb-dap/lldb-dap.cpp | 118 +++++++++++++------------------
 3 files changed, 55 insertions(+), 92 deletions(-)

diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
index 7d0f363440f53d..d07410af7b2319 100644
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ b/lldb/tools/lldb-dap/IOStream.cpp
@@ -26,23 +26,9 @@ StreamDescriptor::StreamDescriptor(StreamDescriptor &&other) {
   *this = std::move(other);
 }
 
-StreamDescriptor::~StreamDescriptor() {
-  if (!m_close)
-    return;
-
-  if (m_is_socket)
-#if defined(_WIN32)
-    ::closesocket(m_socket);
-#else
-    ::close(m_socket);
-#endif
-  else
-    ::close(m_fd);
-}
+StreamDescriptor::~StreamDescriptor() = default;
 
 StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) {
-  m_close = other.m_close;
-  other.m_close = false;
   m_is_socket = other.m_is_socket;
   if (m_is_socket)
     m_socket = other.m_socket;
@@ -51,19 +37,17 @@ StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) {
   return *this;
 }
 
-StreamDescriptor StreamDescriptor::from_socket(SOCKET s, bool close) {
+StreamDescriptor StreamDescriptor::from_socket(SOCKET s) {
   StreamDescriptor sd;
   sd.m_is_socket = true;
   sd.m_socket = s;
-  sd.m_close = close;
   return sd;
 }
 
-StreamDescriptor StreamDescriptor::from_file(int fd, bool close) {
+StreamDescriptor StreamDescriptor::from_file(int fd) {
   StreamDescriptor sd;
   sd.m_is_socket = false;
   sd.m_fd = fd;
-  sd.m_close = close;
   return sd;
 }
 
diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h
index 3284b0899397ce..7296c1dba213e3 100644
--- a/lldb/tools/lldb-dap/IOStream.h
+++ b/lldb/tools/lldb-dap/IOStream.h
@@ -31,17 +31,16 @@ typedef int SOCKET;
 // treat them identically.
 namespace lldb_dap {
 struct StreamDescriptor {
-  StreamDescriptor();
+  explicit StreamDescriptor();
   ~StreamDescriptor();
   StreamDescriptor(StreamDescriptor &&other);
 
   StreamDescriptor &operator=(StreamDescriptor &&other);
 
-  static StreamDescriptor from_socket(SOCKET s, bool close);
-  static StreamDescriptor from_file(int fd, bool close);
+  static StreamDescriptor from_socket(SOCKET s);
+  static StreamDescriptor from_file(int fd);
 
   bool m_is_socket = false;
-  bool m_close = false;
   union {
     int m_fd;
     SOCKET m_socket;
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 230cf69752c877..87dcf4a088da48 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -5031,6 +5031,43 @@ int main(int argc, char *argv[]) {
   auto terminate_debugger =
       llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
 
+  auto HandleClient = [=](int out_fd, int err_fd, StreamDescriptor input,
+                          StreamDescriptor output) {
+    DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
+    if (auto Err = dap.ConfigureIO(out_fd, err_fd)) {
+      if (log)
+        *log << "configureIO failed: " << llvm::toStringWithoutConsuming(Err)
+             << "\n";
+      std::cerr << "failed to configureIO: " << llvm::toString(std::move(Err))
+                << std::endl;
+      return false;
+    }
+
+    RegisterRequestCallbacks(dap);
+    dap.input.descriptor = std::move(input);
+    dap.output.descriptor = std::move(output);
+
+    // used only by TestVSCode_redirection_to_console.py
+    if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
+      redirection_test();
+
+    if (auto Err = dap.Loop()) {
+      if (log)
+        *log << "Transport Error: " << llvm::toStringWithoutConsuming(Err)
+             << "\n";
+
+      std::cerr << "Transpot Error: " << llvm::toString(std::move(Err))
+                << std::endl;
+
+      return false;
+    }
+
+    if (log)
+      *log << "connection closed\n";
+
+    return true;
+  };
+
   if (!connection.empty()) {
     auto maybeProtoclAndName = parseConnection(connection);
     if (auto Err = maybeProtoclAndName.takeError()) {
@@ -5074,42 +5111,17 @@ int main(int argc, char *argv[]) {
     mainloop.RegisterSignal(
         SIGHUP, [](auto &RL) { RL.RequestTermination(); }, error);
 
-    auto start_dap = [=](std::unique_ptr<Socket> client) {
-      DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands);
-      NativeSocket sock = client->GetNativeSocket();
-
-      if (auto Err = dap.ConfigureIO()) {
-        if (log)
-          *log << "client[" << sock << "] configureIO failed: "
-               << llvm::toStringWithoutConsuming(Err) << "\n";
-        std::cerr << "failed to configure client connect: "
-                  << llvm::toString(std::move(Err)) << std::endl;
-        return;
-      }
-
-      RegisterRequestCallbacks(dap);
-      dap.input.descriptor = StreamDescriptor::from_socket(sock, false);
-      dap.output.descriptor = StreamDescriptor::from_socket(sock, false);
-      if (auto Err = dap.Loop()) {
-        if (log)
-          *log << "client[" << sock
-               << "] Transport Error: " << llvm::toStringWithoutConsuming(Err)
-               << "\n";
-        std::cerr << "client transport error: "
-                  << llvm::toString(std::move(Err)) << std::endl;
-      }
-
-      if (log)
-        *log << "client[" << sock << "] connection closed\n";
-    };
-
-    auto on_accept = [=](std::unique_ptr<Socket> client) {
-      llvm::errs() << "Client connected...\n";
+    auto OnAccept = [=](std::unique_ptr<Socket> client) {
       // Start a thread for each connection, unblocking the listening thread.
-      std::thread(start_dap, std::move(client)).detach();
+      std::thread([=, client = std::move(client)]() {
+        HandleClient(
+            /*out_fd=*/-1, /*err_fd=*/-1,
+            StreamDescriptor::from_socket(client->GetNativeSocket()),
+            StreamDescriptor::from_socket(client->GetNativeSocket()));
+      }).detach();
     };
 
-    auto handles = listener->Accept(mainloop, on_accept);
+    auto handles = listener->Accept(mainloop, OnAccept);
     if (auto Err = handles.takeError()) {
       std::cerr << "failed to register accept() with the main loop: "
                 << llvm::toString(std::move(Err)) << std::endl;
@@ -5128,41 +5140,9 @@ int main(int argc, char *argv[]) {
 
   // stdout/stderr redirection to the IDE's console
   int new_stdout_fd = dup(fileno(stdout));
-  DAP dap = DAP(program_path.str(), log, default_repl_mode, pre_init_commands);
-  if (auto Err = dap.ConfigureIO(fileno(stdout), fileno(stderr))) {
-    std::cout << "Failed to create lldb-dap instance: "
-              << llvm::toString(std::move(Err)) << "\n";
-    return EXIT_FAILURE;
-  }
-  RegisterRequestCallbacks(dap);
-
-#if defined(_WIN32)
-  // Windows opens stdout and stdin in text mode which converts \n to 13,10
-  // while the value is just 10 on Darwin/Linux. Setting the file mode to
-  // binary fixes this.
-  int result = _setmode(fileno(stdout), _O_BINARY);
-  assert(result);
-  result = _setmode(fileno(stdin), _O_BINARY);
-  UNUSED_IF_ASSERT_DISABLED(result);
-  assert(result);
-#endif
-
-  dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
-  dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
-
-  /// used only by TestVSCode_redirection_to_console.py
-  if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
-    redirection_test();
-
-  bool CleanExit = true;
-  if (auto Err = dap.Loop()) {
-    if (log)
-      *log << "Transport Error: " << llvm::toStringWithoutConsuming(Err)
-           << "\n";
-    std::cerr << "Transport Error: " << llvm::toString(std::move(Err))
-              << std::endl;
-    CleanExit = false;
-  }
+  bool clean_exit = HandleClient(fileno(stdout), fileno(stderr),
+                                 StreamDescriptor::from_file(fileno(stdin)),
+                                 StreamDescriptor::from_file(new_stdout_fd));
 
-  return CleanExit ? EXIT_SUCCESS : EXIT_FAILURE;
+  return clean_exit ? EXIT_SUCCESS : EXIT_FAILURE;
 }



More information about the lldb-commits mailing list