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

>From 88a8522f1b29b2ff392974322acdb722b7e00b70 Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Tue, 28 Jan 2025 12:39:38 -0800
Subject: [PATCH 1/2] [lldb-dap] Refactoring lldb-dap port listening mode to
 allow multiple connections.

This adjusts the lldb-dap listening mode to accept multiple clients.
Each client initializes a new instance of DAP and an associated 
`lldb::SBDebugger` instance.

The listening mode is configured with the `--connection` option and supports 
listening on a port or a unix socket on supported platforms.
---
 .../test/tools/lldb-dap/dap_server.py         |  74 ++++-
 .../test/tools/lldb-dap/lldbdap_testcase.py   |   6 +-
 lldb/test/API/tools/lldb-dap/server/Makefile  |   3 +
 .../tools/lldb-dap/server/TestDAP_server.py   |  77 +++++
 lldb/test/API/tools/lldb-dap/server/main.c    |  10 +
 lldb/test/Shell/DAP/TestOptions.test          |   4 +-
 lldb/tools/lldb-dap/DAP.cpp                   |  18 +-
 lldb/tools/lldb-dap/DAP.h                     |   6 +-
 lldb/tools/lldb-dap/DAPForward.h              |   2 +
 lldb/tools/lldb-dap/Options.td                |  22 +-
 lldb/tools/lldb-dap/lldb-dap.cpp              | 283 +++++++++++-------
 11 files changed, 358 insertions(+), 147 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/tools/lldb-dap/dap_server.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index c29992ce9c7848..6d765e10236733 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
@@ -1150,11 +1150,12 @@ def request_setInstructionBreakpoints(self, 
memory_reference=[]):
         }
         return self.send_recv(command_dict)
 
+
 class DebugAdaptorServer(DebugCommunication):
     def __init__(
         self,
         executable=None,
-        port=None,
+        connection=None,
         init_commands=[],
         log_file=None,
         env=None,
@@ -1167,21 +1168,62 @@ def __init__(
 
             if log_file:
                 adaptor_env["LLDBDAP_LOG"] = log_file
+            args = [executable]
+
+            if connection is not None:
+                args.append("--connection")
+                args.append(connection)
+
             self.process = subprocess.Popen(
-                [executable],
+                args,
                 stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 env=adaptor_env,
             )
+
+        if connection is not None:
+            # If the process was also launched, parse the connection from the
+            # resolved connection. For example, if the connection
+            # `connection://localhost:0` was specified then the OS would pick a
+            # random port for listening and lldb-dap would print the listening
+            # port to stdout.
+            if self.process is not None:
+                # lldb-dap will print the listening address once the listener 
is
+                # made to stdout. The listener is formatted like
+                # `connection://host:port` or `unix-connection:///path`.
+                expected_prefix = "Listening for: "
+                out = self.process.stdout.readline().decode()
+                if not out.startswith(expected_prefix):
+                    self.process.kill()
+                    raise ValueError(
+                        "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").split(",", 1)[0]
+                )
+
+            scheme, address = connection.split("://")
+            if scheme == "unix-connect":  # unix-connect:///path
+                s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+                s.connect(address)
+            elif scheme == "connection":  # connection://[host]:port
+                host, port = address.rsplit(":", 1)
+                # create_connection with try both ipv4 and ipv6.
+                s = socket.create_connection((host.strip("[]"), int(port)))
+            else:
+                raise ValueError("invalid connection: {}".format(connection))
             DebugCommunication.__init__(
-                self, self.process.stdout, self.process.stdin, init_commands, 
log_file
+                self, s.makefile("rb"), s.makefile("wb"), init_commands, 
log_file
             )
-        elif port is not None:
-            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            s.connect(("127.0.0.1", port))
+            self.connection = connection
+        else:
             DebugCommunication.__init__(
-                self, s.makefile("r"), s.makefile("w"), init_commands
+                self, self.process.stdout, self.process.stdin, init_commands, 
log_file
             )
 
     def get_pid(self):
@@ -1349,10 +1391,9 @@ def main():
     )
 
     parser.add_option(
-        "--port",
-        type="int",
-        dest="port",
-        help="Attach a socket to a port instead of using STDIN for VSCode",
+        "--connection",
+        dest="connection",
+        help="Attach a socket connection of using STDIN for VSCode",
         default=None,
     )
 
@@ -1498,15 +1539,16 @@ def main():
 
     (options, args) = parser.parse_args(sys.argv[1:])
 
-    if options.vscode_path is None and options.port is None:
+    if options.vscode_path is None and options.connection is None:
         print(
             "error: must either specify a path to a Visual Studio Code "
             "Debug Adaptor vscode executable path using the --vscode "
-            "option, or a port to attach to for an existing lldb-dap "
-            "using the --port option"
+            "option, or using the --connection option"
         )
         return
-    dbg = DebugAdaptorServer(executable=options.vscode_path, port=options.port)
+    dbg = DebugAdaptorServer(
+        executable=options.vscode_path, connection=options.connection
+    )
     if options.debug:
         raw_input('Waiting for debugger to attach pid "%i"' % (dbg.get_pid()))
     if options.replay:
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..015c613956d86b 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
@@ -1,5 +1,6 @@
 import os
 import time
+import subprocess
 
 import dap_server
 from lldbsuite.test.lldbtest import *
@@ -10,10 +11,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, lldbDAPEnv=None):
+    def create_debug_adaptor(self, lldbDAPEnv=None, connection=None):
         """Create the Visual Studio Code debug adaptor"""
         self.assertTrue(
             is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
@@ -21,6 +22,7 @@ def create_debug_adaptor(self, lldbDAPEnv=None):
         log_file_path = self.getBuildArtifact("dap.txt")
         self.dap_server = dap_server.DebugAdaptorServer(
             executable=self.lldbDAPExec,
+            connection=connection,
             init_commands=self.setUpCommands(),
             log_file=log_file_path,
             env=lldbDAPEnv,
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..451278a0946ef2
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
\ No newline at end of file
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..9597f73b0659be
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -0,0 +1,77 @@
+"""
+Test lldb-dap server integration.
+"""
+
+import os
+import signal
+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 start_server(self, connection):
+        log_file_path = self.getBuildArtifact("dap.txt")
+        server = dap_server.DebugAdaptorServer(
+            executable=self.lldbDAPExec,
+            connection=connection,
+            init_commands=self.setUpCommands(),
+            log_file=log_file_path,
+        )
+
+        def cleanup():
+            server.terminate()
+
+        self.addTearDownHook(cleanup)
+
+        return server
+
+    def run_debug_session(self, connection, name):
+        self.dap_server = dap_server.DebugAdaptorServer(
+            connection=connection,
+        )
+        program = self.getBuildArtifact("a.out")
+        source = "main.c"
+        breakpoint_line = line_number(source, "// breakpoint")
+
+        self.launch(
+            program,
+            args=[name],
+            disconnectAutomatically=False,
+        )
+        self.set_source_breakpoints(source, [breakpoint_line])
+        self.continue_to_next_stop()
+        self.continue_to_exit()
+        output = self.get_stdout()
+        self.assertEqual(output, f"Hello {name}!\r\n")
+        self.dap_server.request_disconnect()
+
+    def test_server_port(self):
+        """
+        Test launching a binary with a lldb-dap in server mode on a specific 
port.
+        """
+        self.build()
+        server = self.start_server(connection="tcp://localhost:0")
+        self.run_debug_session(server.connection, "Alice")
+        self.run_debug_session(server.connection, "Bob")
+
+    @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()
+        name = dir + "/dap-connection-" + str(os.getpid())
+
+        def cleanup():
+            os.unlink(name)
+
+        self.addTearDownHook(cleanup)
+
+        self.build()
+        server = self.start_server(connection="unix://" + name)
+        self.run_debug_session(server.connection, "Alice")
+        self.run_debug_session(server.connection, "Bob")
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..446ae82532af57
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[]) {
+  if (argc == 2) { // breakpoint 1
+    printf("Hello %s!\n", argv[1]);
+  } else {
+    printf("Hello World!\n");
+  }
+  return 0;
+}
diff --git a/lldb/test/Shell/DAP/TestOptions.test 
b/lldb/test/Shell/DAP/TestOptions.test
index e37e9116e3cddb..d290cdae590fd6 100644
--- a/lldb/test/Shell/DAP/TestOptions.test
+++ b/lldb/test/Shell/DAP/TestOptions.test
@@ -1,8 +1,8 @@
 # RUN: lldb-dap --help | FileCheck %s
+# CHECK: --connection
 # CHECK: -g
 # CHECK: --help
 # CHECK: -h
-# CHECK: --port
-# CHECK: -p
+# CHECK: --repl-mode
 # CHECK: --wait-for-debugger
 
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index a67abe582abd40..11a4db5c7f0f48 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -17,7 +17,6 @@
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
-#include "lldb/Host/FileSystem.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
@@ -58,12 +57,13 @@ const char DEV_NULL[] = "/dev/null";
 
 namespace lldb_dap {
 
-DAP::DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
-         StreamDescriptor input, StreamDescriptor output)
-    : debug_adaptor_path(path), log(log), input(std::move(input)),
+DAP::DAP(std::string name, llvm::StringRef path, std::ofstream *log,
+         StreamDescriptor input, StreamDescriptor output, ReplMode repl_mode,
+         std::vector<std::string> pre_init_commands)
+    : name(name), debug_adaptor_path(path), log(log), input(std::move(input)),
       output(std::move(output)), broadcaster("lldb-dap"),
-      exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
-      stop_at_entry(false), is_attach(false),
+      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),
@@ -249,7 +249,8 @@ 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} {1} <-- ", now.count(), name).str()
+         << std::endl
          << "Content-Length: " << json_str.size() << "\r\n\r\n"
          << llvm::formatv("{0:2}", json).str() << std::endl;
   }
@@ -279,7 +280,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
+    *log << llvm::formatv("{0:f9} {1} --> ", now.count(), name).str()
+         << std::endl
          << "Content-Length: " << length << "\r\n\r\n";
   }
   return json_str;
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 846300cb945b0d..7c28dbf801179f 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -139,6 +139,7 @@ struct SendEventRequestHandler : public 
lldb::SBCommandPluginInterface {
 };
 
 struct DAP {
+  std::string name;
   llvm::StringRef debug_adaptor_path;
   std::ofstream *log;
   InputStream input;
@@ -203,8 +204,9 @@ struct DAP {
   // will contain that expression.
   std::string last_nonempty_var_expression;
 
-  DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
-      StreamDescriptor input, StreamDescriptor output);
+  DAP(std::string name, llvm::StringRef path, std::ofstream *log,
+      StreamDescriptor input, StreamDescriptor output, ReplMode repl_mode,
+      std::vector<std::string> pre_init_commands);
   ~DAP();
   DAP(const DAP &rhs) = delete;
   void operator=(const DAP &rhs) = delete;
diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h
index 0196d83dcd6a91..15e286f3d08dc1 100644
--- a/lldb/tools/lldb-dap/DAPForward.h
+++ b/lldb/tools/lldb-dap/DAPForward.h
@@ -11,6 +11,8 @@
 
 // IWYU pragma: begin_exports
 
+#include "lldb/lldb-forward.h"
+
 namespace lldb_dap {
 struct BreakpointBase;
 struct ExceptionBreakpoint;
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index d7b4a065abec01..97a6ec118c47b5 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -17,12 +17,13 @@ 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>,
-  HelpText<"Alias for --port">;
+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>">,
@@ -40,9 +41,12 @@ def debugger_pid: S<"debugger-pid">,
   HelpText<"The PID of the lldb-dap instance that sent the launchInTerminal "
     "request when using --launch-target.">;
 
-def repl_mode: S<"repl-mode">,
-  MetaVarName<"<mode>">,
-  HelpText<"The mode for handling repl evaluation requests, supported modes: 
variable, command, auto.">;
+def repl_mode
+    : S<"repl-mode">,
+      MetaVarName<"<mode>">,
+      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/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 9e0e7f21ce4fc7..2c7fdb0f0d3cfb 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -14,45 +14,55 @@
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
 #include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFile.h"
 #include "lldb/API/SBInstruction.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
 #include "lldb/Host/Config.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UriParser.h"
+#include "lldb/lldb-forward.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"
 #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/ThreadPool.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <array>
-#include <cassert>
-#include <climits>
-#include <cstdarg>
 #include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <fcntl.h>
+#include <fstream>
+#include <iostream>
 #include <map>
 #include <memory>
 #include <optional>
+#include <ostream>
 #include <set>
+#include <string>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <thread>
+#include <utility>
 #include <vector>
 
 #if defined(_WIN32)
@@ -68,6 +78,7 @@
 #else
 #include <netinet/in.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 #endif
 
@@ -83,6 +94,9 @@ typedef int socklen_t;
 #endif
 
 using namespace lldb_dap;
+using lldb_private::NativeSocket;
+using lldb_private::Socket;
+using lldb_private::Status;
 
 namespace {
 using namespace llvm::opt;
@@ -142,43 +156,6 @@ lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t 
variablesReference) {
   }
 }
 
-SOCKET AcceptConnection(std::ofstream *log, int portno) {
-  // Accept a socket connection from any host on "portno".
-  SOCKET newsockfd = -1;
-  struct sockaddr_in serv_addr, cli_addr;
-  SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
-  if (sockfd < 0) {
-    if (log)
-      *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 (log)
-        *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 (log)
-          *log << "error: accept (" << strerror(errno) << ")" << std::endl;
-    }
-#if defined(_WIN32)
-    closesocket(sockfd);
-#else
-    close(sockfd);
-#endif
-  }
-  return newsockfd;
-}
-
 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
@@ -4857,10 +4834,10 @@ static void printHelp(LLDBDAPOptTable &table, 
llvm::StringRef tool_name) {
   The debug adapter can be started in two modes.
 
   Running lldb-dap without any arguments will start communicating with the
-  parent over stdio. Passing a port number causes lldb-dap to start listening
-  for connections on that port.
+  parent over stdio. Passing a --connection URI will cause lldb-dap to listen 
+  for a connection in the specified mode.
 
-    lldb-dap -p <port>
+    lldb-dap --connection connection://localhost:<port>
 
   Passing --wait-for-debugger will pause the process at startup and wait for a
   debugger to attach to the process.
@@ -4952,6 +4929,29 @@ static int DuplicateFileDescriptor(int fd) {
 #endif
 }
 
+static llvm::Expected<std::pair<Socket::SocketProtocol, std::string>>
+validateConnection(llvm::StringRef conn) {
+  auto uri = lldb_private::URI::Parse(conn);
+
+  if (uri && (uri->scheme == "tcp" || uri->scheme == "connect" ||
+              !uri->hostname.empty() || uri->port)) {
+    return std::make_pair(
+        Socket::ProtocolTcp,
+        formatv("[{0}]:{1}", uri->hostname.empty() ? "0.0.0.0" : uri->hostname,
+                uri->port.value_or(0)));
+  }
+
+  if (uri && (uri->scheme == "unix" || uri->scheme == "unix-connect" ||
+              uri->path != "/")) {
+    return std::make_pair(Socket::ProtocolUnixDomain, uri->path.str());
+  }
+
+  return llvm::createStringError(
+      "Unsupported connection specifier, expected 'unix-connect:///path' or "
+      "'connect://[host]:port', got '%s'.",
+      conn.str().c_str());
+}
+
 int main(int argc, char *argv[]) {
   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
 #if !defined(__APPLE__)
@@ -4987,9 +4987,9 @@ int main(int argc, char *argv[]) {
     } else if (repl_mode_value == "command") {
       default_repl_mode = ReplMode::Command;
     } else {
-      llvm::errs()
-          << "'" << repl_mode_value
-          << "' is not a valid option, use 'variable', 'command' or 'auto'.\n";
+      llvm::errs() << "'" << repl_mode_value
+                   << "' is not a valid option, use 'variable', 'command' or "
+                      "'auto'.\n";
       return EXIT_FAILURE;
     }
   }
@@ -5022,15 +5022,10 @@ 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 connection;
+  if (auto *arg = input_args.getLastArg(OPT_connection)) {
+    const auto *path = arg->getValue();
+    connection.assign(path);
   }
 
 #if !defined(_WIN32)
@@ -5058,72 +5053,144 @@ int main(int argc, char *argv[]) {
   auto terminate_debugger =
       llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
 
-  StreamDescriptor input;
-  StreamDescriptor output;
-  std::FILE *redirectOut = nullptr;
-  std::FILE *redirectErr = nullptr;
-  if (portno != -1) {
-    printf("Listening on port %i...\n", portno);
-    SOCKET socket_fd = AcceptConnection(log.get(), portno);
-    if (socket_fd < 0)
-      return EXIT_FAILURE;
+  std::vector<std::string> pre_init_commands;
+  for (const std::string &arg :
+       input_args.getAllArgValues(OPT_pre_init_command)) {
+    pre_init_commands.push_back(arg);
+  }
 
-    input = StreamDescriptor::from_socket(socket_fd, true);
-    output = StreamDescriptor::from_socket(socket_fd, false);
-  } else {
-#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
+  auto RunDAP = [](llvm::StringRef program_path, ReplMode repl_mode,
+                   std::vector<std::string> pre_init_commands,
+                   std::ofstream *log, std::string name, StreamDescriptor 
input,
+                   StreamDescriptor output, std::FILE *redirectOut = nullptr,
+                   std::FILE *redirectErr = nullptr) -> bool {
+    DAP dap = DAP(name, program_path, log, std::move(input), std::move(output),
+                  repl_mode, pre_init_commands);
 
-    int stdout_fd = DuplicateFileDescriptor(fileno(stdout));
-    if (stdout_fd == -1) {
+    // stdout/stderr redirection to the IDE's console
+    if (auto Err = dap.ConfigureIO(redirectOut, redirectErr)) {
       llvm::logAllUnhandledErrors(
-          llvm::errorCodeToError(llvm::errnoAsErrorCode()), llvm::errs(),
-          "Failed to configure stdout redirect: ");
+          std::move(Err), llvm::errs(),
+          "Failed to configure lldb-dap IO operations: ");
+      return false;
+    }
+
+    RegisterRequestCallbacks(dap);
+
+    // used only by TestVSCode_redirection_to_console.py
+    if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
+      redirection_test();
+
+    if (auto Err = dap.Loop()) {
+      std::string errorMessage = llvm::toString(std::move(Err));
+      if (log)
+        *log << "Transport Error: " << errorMessage << "\n";
+      return false;
+    }
+    return true;
+  };
+
+  if (!connection.empty()) {
+    auto maybeProtoclAndName = validateConnection(connection);
+    if (auto Err = maybeProtoclAndName.takeError()) {
+      llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
+                                  "Invalid connection: ");
       return EXIT_FAILURE;
     }
 
-    redirectOut = stdout;
-    redirectErr = stderr;
+    Socket::SocketProtocol protocol;
+    std::string name;
+    std::tie(protocol, name) = *maybeProtoclAndName;
 
-    input = StreamDescriptor::from_file(fileno(stdin), false);
-    output = StreamDescriptor::from_file(stdout_fd, false);
-  }
+    Status error;
+    std::unique_ptr<Socket> listener = Socket::Create(protocol, error);
+    if (error.Fail()) {
+      llvm::logAllUnhandledErrors(error.takeError(), llvm::errs(),
+                                  "Failed to create socket listener: ");
+      return EXIT_FAILURE;
+    }
 
-  DAP dap = DAP(program_path.str(), log.get(), default_repl_mode,
-                std::move(input), std::move(output));
+    error = listener->Listen(name, /* backlog */ 5);
+    if (error.Fail()) {
+      llvm::logAllUnhandledErrors(error.takeError(), llvm::errs(),
+                                  "Failed to listen for connections: ");
+      return EXIT_FAILURE;
+    }
 
-  // stdout/stderr redirection to the IDE's console
-  if (auto Err = dap.ConfigureIO(redirectOut, redirectErr)) {
-    llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
-                                "Failed to configure lldb-dap IO operations: 
");
-    return EXIT_FAILURE;
-  }
+    std::string address =
+        llvm::join(listener->GetListeningConnectionURI(), ", ");
+    if (log)
+      *log << "started with connection listeners " << address << "\n";
+
+    llvm::outs() << "Listening for: " << address << "\n";
+    // Ensure listening address are flushed for calles to retrieve the resolve
+    // address.
+    llvm::outs().flush();
+
+    llvm::DefaultThreadPool pool(llvm::optimal_concurrency());
+    unsigned int clientCount = 0;
+
+    while (true) {
+      Socket *accepted_socket;
+      error = listener->Accept(/*timeout=*/std::nullopt, accepted_socket);
+      if (error.Fail()) {
+        llvm::logAllUnhandledErrors(error.takeError(), llvm::errs(),
+                                    "accept failed: ");
+        return EXIT_FAILURE;
+      }
 
-  RegisterRequestCallbacks(dap);
+      std::string name = llvm::formatv("client_{0}", clientCount++).str();
+      if (log) {
+        auto now = std::chrono::duration<double>(
+            std::chrono::system_clock::now().time_since_epoch());
+        *log << llvm::formatv("{0:f9}", now.count()).str()
+             << " client connected: " << name << "\n";
+      }
 
-  for (const std::string &arg :
-       input_args.getAllArgValues(OPT_pre_init_command)) {
-    dap.pre_init_commands.push_back(arg);
+      // Move the client into the connection pool to unblock accepting the next
+      // client.
+      pool.async(
+          [=](Socket *accepted_socket, std::ofstream *log) {
+            StreamDescriptor input = StreamDescriptor::from_socket(
+                accepted_socket->GetNativeSocket(), false);
+            // Close the output last for the best chance at error reporting.
+            StreamDescriptor output = StreamDescriptor::from_socket(
+                accepted_socket->GetNativeSocket(), true);
+            RunDAP(program_path, default_repl_mode, pre_init_commands, log,
+                   name, std::move(input), std::move(output));
+          },
+          accepted_socket, log.get());
+    }
+
+    pool.wait();
+
+    return EXIT_SUCCESS;
   }
 
-  // used only by TestVSCode_redirection_to_console.py
-  if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
-    redirection_test();
+#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
 
-  bool CleanExit = true;
-  if (auto Err = dap.Loop()) {
-    if (log)
-      *log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
-    CleanExit = false;
+  int stdout_fd = DuplicateFileDescriptor(fileno(stdout));
+  if (stdout_fd == -1) {
+    llvm::logAllUnhandledErrors(
+        llvm::errorCodeToError(llvm::errnoAsErrorCode()), llvm::errs(),
+        "Failed to configure stdout redirect: ");
+    return EXIT_FAILURE;
   }
 
-  return CleanExit ? EXIT_SUCCESS : EXIT_FAILURE;
+  StreamDescriptor input = StreamDescriptor::from_file(fileno(stdin), false);
+  StreamDescriptor output = StreamDescriptor::from_file(stdout_fd, false);
+
+  bool status = RunDAP(program_path, default_repl_mode, pre_init_commands,
+                       log.get(), "stdin/stdout", std::move(input),
+                       std::move(output), stdout, stderr);
+  return status ? EXIT_SUCCESS : EXIT_FAILURE;
 }

>From 2fe4f85f7620be29c28452783a3d2cffafb91c31 Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Tue, 4 Feb 2025 10:57:32 -0800
Subject: [PATCH 2/2] Applying review comments.

---
 lldb/test/API/tools/lldb-dap/server/Makefile | 2 +-
 lldb/tools/lldb-dap/DAP.cpp                  | 2 +-
 lldb/tools/lldb-dap/DAPForward.h             | 2 --
 lldb/tools/lldb-dap/lldb-dap.cpp             | 5 ++---
 4 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/server/Makefile 
b/lldb/test/API/tools/lldb-dap/server/Makefile
index 451278a0946ef2..10495940055b63 100644
--- a/lldb/test/API/tools/lldb-dap/server/Makefile
+++ b/lldb/test/API/tools/lldb-dap/server/Makefile
@@ -1,3 +1,3 @@
 C_SOURCES := main.c
 
-include Makefile.rules
\ No newline at end of file
+include Makefile.rules
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 11a4db5c7f0f48..cfaa8ad53c6340 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -62,7 +62,7 @@ DAP::DAP(std::string name, llvm::StringRef path, 
std::ofstream *log,
          std::vector<std::string> pre_init_commands)
     : name(name), debug_adaptor_path(path), log(log), input(std::move(input)),
       output(std::move(output)), broadcaster("lldb-dap"),
-      exception_breakpoints(), pre_init_commands(pre_init_commands),
+      exception_breakpoints(), pre_init_commands(std::move(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),
diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h
index 15e286f3d08dc1..0196d83dcd6a91 100644
--- a/lldb/tools/lldb-dap/DAPForward.h
+++ b/lldb/tools/lldb-dap/DAPForward.h
@@ -11,8 +11,6 @@
 
 // IWYU pragma: begin_exports
 
-#include "lldb/lldb-forward.h"
-
 namespace lldb_dap {
 struct BreakpointBase;
 struct ExceptionBreakpoint;
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 2c7fdb0f0d3cfb..fd86c8d8b3a9b5 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -5110,7 +5110,7 @@ int main(int argc, char *argv[]) {
       return EXIT_FAILURE;
     }
 
-    error = listener->Listen(name, /* backlog */ 5);
+    error = listener->Listen(name, /*backlog=*/5);
     if (error.Fail()) {
       llvm::logAllUnhandledErrors(error.takeError(), llvm::errs(),
                                   "Failed to listen for connections: ");
@@ -5127,9 +5127,8 @@ int main(int argc, char *argv[]) {
     // address.
     llvm::outs().flush();
 
-    llvm::DefaultThreadPool pool(llvm::optimal_concurrency());
+    llvm::DefaultThreadPool pool;
     unsigned int clientCount = 0;
-
     while (true) {
       Socket *accepted_socket;
       error = listener->Accept(/*timeout=*/std::nullopt, accepted_socket);

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to