[Lldb-commits] [lldb] a52be0c - [lldb-dap] Added "port" property to vscode "attach" command. (#91570)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Jun 28 08:20:53 PDT 2024
Author: Santhosh Kumar Ellendula
Date: 2024-06-28T10:20:50-05:00
New Revision: a52be0cc114cc58a35bee65c517adaeb66ee6d89
URL: https://github.com/llvm/llvm-project/commit/a52be0cc114cc58a35bee65c517adaeb66ee6d89
DIFF: https://github.com/llvm/llvm-project/commit/a52be0cc114cc58a35bee65c517adaeb66ee6d89.diff
LOG: [lldb-dap] Added "port" property to vscode "attach" command. (#91570)
Adding a "port" property to the VsCode "attach" command likely extends
the functionality of the debugger configuration to allow attaching to a
process using PID or PORT number.
Currently, the "Attach" configuration lets the user specify a pid. We
tell the user to use the attachCommands property to run "gdb-remote ".
Followed the below conditions for "attach" command with "port" and "pid"
We should add a "port" property. If port is specified and pid is not,
use that port to attach. If both port and pid are specified, return an
error saying that the user can't specify both pid and port.
Ex - launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "lldb-dap Debug",
"type": "lldb-dap",
"request": "attach",
"gdb-remote-port":1234,
"program": "${workspaceFolder}/a.out",
"args": [],
"stopOnEntry": false,
"cwd": "${workspaceFolder}",
"env": [],
}
]
}
---------
Co-authored-by: Santhosh Kumar Ellendula <sellendu at hu-sellendu-hyd.qualcomm.com>
Co-authored-by: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Added:
lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
Modified:
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py
lldb/tools/lldb-dap/lldb-dap.cpp
lldb/tools/lldb-dap/package.json
Removed:
################################################################################
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 a9eeec1840990..a324af57b61df 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
@@ -574,6 +574,8 @@ def request_attach(
coreFile=None,
postRunCommands=None,
sourceMap=None,
+ gdbRemotePort=None,
+ gdbRemoteHostname=None,
):
args_dict = {}
if pid is not None:
@@ -603,6 +605,10 @@ def request_attach(
args_dict["postRunCommands"] = postRunCommands
if sourceMap:
args_dict["sourceMap"] = sourceMap
+ if gdbRemotePort is not None:
+ args_dict["gdb-remote-port"] = gdbRemotePort
+ if gdbRemoteHostname is not None:
+ args_dict["gdb-remote-hostname"] = gdbRemoteHostname
command_dict = {"command": "attach", "type": "request", "arguments": args_dict}
return self.send_recv(command_dict)
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 600dc2b5fe9bb..a312a88ebd7e5 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
@@ -3,6 +3,8 @@
import dap_server
from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbplatformutil
+import lldbgdbserverutils
class DAPTestCaseBase(TestBase):
@@ -299,6 +301,8 @@ def attach(
sourceMap=None,
sourceInitFile=False,
expectFailure=False,
+ gdbRemotePort=None,
+ gdbRemoteHostname=None,
):
"""Build the default Makefile target, create the DAP debug adaptor,
and attach to the process.
@@ -329,6 +333,8 @@ def cleanup():
coreFile=coreFile,
postRunCommands=postRunCommands,
sourceMap=sourceMap,
+ gdbRemotePort=gdbRemotePort,
+ gdbRemoteHostname=gdbRemoteHostname,
)
if expectFailure:
return response
@@ -485,3 +491,18 @@ def build_and_launch(
launchCommands=launchCommands,
expectFailure=expectFailure,
)
+
+ def getBuiltinDebugServerTool(self):
+ # Tries to find simulation/lldb-server/gdbserver tool path.
+ server_tool = None
+ if lldbplatformutil.getPlatform() == "linux":
+ server_tool = lldbgdbserverutils.get_lldb_server_exe()
+ if server_tool is None:
+ self.dap_server.request_disconnect(terminateDebuggee=True)
+ self.assertIsNotNone(server_tool, "lldb-server not found.")
+ elif lldbplatformutil.getPlatform() == "macosx":
+ server_tool = lldbgdbserverutils.get_debugserver_exe()
+ if server_tool is None:
+ self.dap_server.request_disconnect(terminateDebuggee=True)
+ self.assertIsNotNone(server_tool, "debugserver not found.")
+ return server_tool
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
index d1a4119bac781..5253839c7405d 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
@@ -13,6 +13,7 @@
from lldbsuite.test import configuration
from textwrap import dedent
import shutil
+import select
def _get_support_exe(basename):
@@ -969,3 +970,148 @@ def __str__(self):
self._output_queue,
self._accumulated_output,
)
+
+
+# A class representing a pipe for communicating with debug server.
+# This class includes menthods to open the pipe and read the port number from it.
+if lldbplatformutil.getHostPlatform() == "windows":
+ import ctypes
+ import ctypes.wintypes
+ from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID
+
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
+
+ PIPE_ACCESS_INBOUND = 1
+ FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
+ FILE_FLAG_OVERLAPPED = 0x40000000
+ PIPE_TYPE_BYTE = 0
+ PIPE_REJECT_REMOTE_CLIENTS = 8
+ INVALID_HANDLE_VALUE = -1
+ ERROR_ACCESS_DENIED = 5
+ ERROR_IO_PENDING = 997
+
+ class OVERLAPPED(ctypes.Structure):
+ _fields_ = [
+ ("Internal", LPVOID),
+ ("InternalHigh", LPVOID),
+ ("Offset", DWORD),
+ ("OffsetHigh", DWORD),
+ ("hEvent", HANDLE),
+ ]
+
+ def __init__(self):
+ super(OVERLAPPED, self).__init__(
+ Internal=0, InternalHigh=0, Offset=0, OffsetHigh=0, hEvent=None
+ )
+
+ LPOVERLAPPED = ctypes.POINTER(OVERLAPPED)
+
+ CreateNamedPipe = kernel32.CreateNamedPipeW
+ CreateNamedPipe.restype = HANDLE
+ CreateNamedPipe.argtypes = (
+ LPCWSTR,
+ DWORD,
+ DWORD,
+ DWORD,
+ DWORD,
+ DWORD,
+ DWORD,
+ LPVOID,
+ )
+
+ ConnectNamedPipe = kernel32.ConnectNamedPipe
+ ConnectNamedPipe.restype = BOOL
+ ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED)
+
+ CreateEvent = kernel32.CreateEventW
+ CreateEvent.restype = HANDLE
+ CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR)
+
+ GetOverlappedResultEx = kernel32.GetOverlappedResultEx
+ GetOverlappedResultEx.restype = BOOL
+ GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL)
+
+ ReadFile = kernel32.ReadFile
+ ReadFile.restype = BOOL
+ ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)
+
+ CloseHandle = kernel32.CloseHandle
+ CloseHandle.restype = BOOL
+ CloseHandle.argtypes = (HANDLE,)
+
+ class Pipe(object):
+ def __init__(self, prefix):
+ while True:
+ self.name = "lldb-" + str(random.randrange(1e10))
+ full_name = "\\\\.\\pipe\\" + self.name
+ self._handle = CreateNamedPipe(
+ full_name,
+ PIPE_ACCESS_INBOUND
+ | FILE_FLAG_FIRST_PIPE_INSTANCE
+ | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
+ 1,
+ 4096,
+ 4096,
+ 0,
+ None,
+ )
+ if self._handle != INVALID_HANDLE_VALUE:
+ break
+ if ctypes.get_last_error() != ERROR_ACCESS_DENIED:
+ raise ctypes.WinError(ctypes.get_last_error())
+
+ self._overlapped = OVERLAPPED()
+ self._overlapped.hEvent = CreateEvent(None, True, False, None)
+ result = ConnectNamedPipe(self._handle, self._overlapped)
+ assert result == 0
+ if ctypes.get_last_error() != ERROR_IO_PENDING:
+ raise ctypes.WinError(ctypes.get_last_error())
+
+ def finish_connection(self, timeout):
+ if not GetOverlappedResultEx(
+ self._handle,
+ self._overlapped,
+ ctypes.byref(DWORD(0)),
+ timeout * 1000,
+ True,
+ ):
+ raise ctypes.WinError(ctypes.get_last_error())
+
+ def read(self, size, timeout):
+ buf = ctypes.create_string_buffer(size)
+ if not ReadFile(
+ self._handle, ctypes.byref(buf), size, None, self._overlapped
+ ):
+ if ctypes.get_last_error() != ERROR_IO_PENDING:
+ raise ctypes.WinError(ctypes.get_last_error())
+ read = DWORD(0)
+ if not GetOverlappedResultEx(
+ self._handle, self._overlapped, ctypes.byref(read), timeout * 1000, True
+ ):
+ raise ctypes.WinError(ctypes.get_last_error())
+ return buf.raw[0 : read.value]
+
+ def close(self):
+ CloseHandle(self._overlapped.hEvent)
+ CloseHandle(self._handle)
+
+else:
+
+ class Pipe(object):
+ def __init__(self, prefix):
+ self.name = os.path.join(prefix, "stub_port_number")
+ os.mkfifo(self.name)
+ self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)
+
+ def finish_connection(self, timeout):
+ pass
+
+ def read(self, size, timeout):
+ (readers, _, _) = select.select([self._fd], [], [], timeout)
+ if self._fd not in readers:
+ raise TimeoutError
+ return os.read(self._fd, size)
+
+ def close(self):
+ os.close(self._fd)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
new file mode 100644
index 0000000000000..fbabc857bd0e0
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -0,0 +1,160 @@
+"""
+Test lldb-dap "port" configuration to "attach" request
+"""
+
+
+import dap_server
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test import lldbplatformutil
+from lldbgdbserverutils import Pipe
+import lldbdap_testcase
+import os
+import shutil
+import subprocess
+import tempfile
+import threading
+import sys
+import socket
+
+
+class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
+ default_timeout = 20
+
+ def set_and_hit_breakpoint(self, continueToExit=True):
+ source = "main.c"
+ main_source_path = os.path.join(os.getcwd(), source)
+ breakpoint1_line = line_number(main_source_path, "// breakpoint 1")
+ lines = [breakpoint1_line]
+ # Set breakpoint in the thread function so we can step the threads
+ breakpoint_ids = self.set_source_breakpoints(main_source_path, lines)
+ self.assertEqual(
+ len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
+ )
+ self.continue_to_breakpoints(breakpoint_ids)
+ if continueToExit:
+ self.continue_to_exit()
+
+ def get_debug_server_command_line_args(self):
+ args = []
+ if lldbplatformutil.getPlatform() == "linux":
+ args = ["gdbserver"]
+ elif lldbplatformutil.getPlatform() == "macosx":
+ args = ["--listen"]
+ if lldb.remote_platform:
+ args += ["*:0"]
+ else:
+ args += ["localhost:0"]
+ return args
+
+ def get_debug_server_pipe(self):
+ pipe = Pipe(self.getBuildDir())
+ self.addTearDownHook(lambda: pipe.close())
+ pipe.finish_connection(self.default_timeout)
+ return pipe
+
+ @skipIfWindows
+ @skipIfNetBSD
+ def test_by_port(self):
+ """
+ Tests attaching to a process by port.
+ """
+ self.build_and_create_debug_adaptor()
+ program = self.getBuildArtifact("a.out")
+
+ debug_server_tool = self.getBuiltinDebugServerTool()
+
+ pipe = self.get_debug_server_pipe()
+ args = self.get_debug_server_command_line_args()
+ args += [program]
+ args += ["--named-pipe", pipe.name]
+
+ self.process = self.spawnSubprocess(
+ debug_server_tool, args, install_remote=False
+ )
+
+ # Read the port number from the debug server pipe.
+ port = pipe.read(10, self.default_timeout)
+ # Trim null byte, convert to int
+ port = int(port[:-1])
+ self.assertIsNotNone(
+ port, " Failed to read the port number from debug server pipe"
+ )
+
+ self.attach(program=program, gdbRemotePort=port, sourceInitFile=True)
+ self.set_and_hit_breakpoint(continueToExit=True)
+ self.process.terminate()
+
+ @skipIfWindows
+ @skipIfNetBSD
+ def test_by_port_and_pid(self):
+ """
+ Tests attaching to a process by process ID and port number.
+ """
+ self.build_and_create_debug_adaptor()
+ program = self.getBuildArtifact("a.out")
+
+ # It is not necessary to launch "lldb-server" to obtain the actual port and pid for attaching.
+ # However, when providing the port number and pid directly, "lldb-dap" throws an error message, which is expected.
+ # So, used random pid and port numbers here.
+
+ pid = 1354
+ port = 1234
+
+ response = self.attach(
+ program=program,
+ pid=pid,
+ gdbRemotePort=port,
+ sourceInitFile=True,
+ expectFailure=True,
+ )
+ if not (response and response["success"]):
+ self.assertFalse(
+ response["success"], "The user can't specify both pid and port"
+ )
+
+ @skipIfWindows
+ @skipIfNetBSD
+ def test_by_invalid_port(self):
+ """
+ Tests attaching to a process by invalid port number 0.
+ """
+ self.build_and_create_debug_adaptor()
+ program = self.getBuildArtifact("a.out")
+
+ port = 0
+ response = self.attach(
+ program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True
+ )
+ if not (response and response["success"]):
+ self.assertFalse(
+ response["success"],
+ "The user can't attach with invalid port (%s)" % port,
+ )
+
+ @skipIfWindows
+ @skipIfNetBSD
+ def test_by_illegal_port(self):
+ """
+ Tests attaching to a process by illegal/greater port number 65536
+ """
+ self.build_and_create_debug_adaptor()
+ program = self.getBuildArtifact("a.out")
+
+ port = 65536
+ args = [program]
+ debug_server_tool = self.getBuiltinDebugServerTool()
+ self.process = self.spawnSubprocess(
+ debug_server_tool, args, install_remote=False
+ )
+
+ response = self.attach(
+ program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True
+ )
+ if not (response and response["success"]):
+ self.assertFalse(
+ response["success"],
+ "The user can't attach with illegal port (%s)" % port,
+ )
+ self.process.terminate()
diff --git a/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py b/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py
index 853b7ad5ef290..ed600d396fad4 100644
--- a/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py
+++ b/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py
@@ -1,153 +1,11 @@
import gdbremote_testcase
import random
-import select
import socket
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbgdbserverutils import Server
import lldbsuite.test.lldbplatformutil
-
-if lldbplatformutil.getHostPlatform() == "windows":
- import ctypes
- import ctypes.wintypes
- from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID
-
- kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
-
- PIPE_ACCESS_INBOUND = 1
- FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
- FILE_FLAG_OVERLAPPED = 0x40000000
- PIPE_TYPE_BYTE = 0
- PIPE_REJECT_REMOTE_CLIENTS = 8
- INVALID_HANDLE_VALUE = -1
- ERROR_ACCESS_DENIED = 5
- ERROR_IO_PENDING = 997
-
- class OVERLAPPED(ctypes.Structure):
- _fields_ = [
- ("Internal", LPVOID),
- ("InternalHigh", LPVOID),
- ("Offset", DWORD),
- ("OffsetHigh", DWORD),
- ("hEvent", HANDLE),
- ]
-
- def __init__(self):
- super(OVERLAPPED, self).__init__(
- Internal=0, InternalHigh=0, Offset=0, OffsetHigh=0, hEvent=None
- )
-
- LPOVERLAPPED = ctypes.POINTER(OVERLAPPED)
-
- CreateNamedPipe = kernel32.CreateNamedPipeW
- CreateNamedPipe.restype = HANDLE
- CreateNamedPipe.argtypes = (
- LPCWSTR,
- DWORD,
- DWORD,
- DWORD,
- DWORD,
- DWORD,
- DWORD,
- LPVOID,
- )
-
- ConnectNamedPipe = kernel32.ConnectNamedPipe
- ConnectNamedPipe.restype = BOOL
- ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED)
-
- CreateEvent = kernel32.CreateEventW
- CreateEvent.restype = HANDLE
- CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR)
-
- GetOverlappedResultEx = kernel32.GetOverlappedResultEx
- GetOverlappedResultEx.restype = BOOL
- GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL)
-
- ReadFile = kernel32.ReadFile
- ReadFile.restype = BOOL
- ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)
-
- CloseHandle = kernel32.CloseHandle
- CloseHandle.restype = BOOL
- CloseHandle.argtypes = (HANDLE,)
-
- class Pipe(object):
- def __init__(self, prefix):
- while True:
- self.name = "lldb-" + str(random.randrange(1e10))
- full_name = "\\\\.\\pipe\\" + self.name
- self._handle = CreateNamedPipe(
- full_name,
- PIPE_ACCESS_INBOUND
- | FILE_FLAG_FIRST_PIPE_INSTANCE
- | FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
- 1,
- 4096,
- 4096,
- 0,
- None,
- )
- if self._handle != INVALID_HANDLE_VALUE:
- break
- if ctypes.get_last_error() != ERROR_ACCESS_DENIED:
- raise ctypes.WinError(ctypes.get_last_error())
-
- self._overlapped = OVERLAPPED()
- self._overlapped.hEvent = CreateEvent(None, True, False, None)
- result = ConnectNamedPipe(self._handle, self._overlapped)
- assert result == 0
- if ctypes.get_last_error() != ERROR_IO_PENDING:
- raise ctypes.WinError(ctypes.get_last_error())
-
- def finish_connection(self, timeout):
- if not GetOverlappedResultEx(
- self._handle,
- self._overlapped,
- ctypes.byref(DWORD(0)),
- timeout * 1000,
- True,
- ):
- raise ctypes.WinError(ctypes.get_last_error())
-
- def read(self, size, timeout):
- buf = ctypes.create_string_buffer(size)
- if not ReadFile(
- self._handle, ctypes.byref(buf), size, None, self._overlapped
- ):
- if ctypes.get_last_error() != ERROR_IO_PENDING:
- raise ctypes.WinError(ctypes.get_last_error())
- read = DWORD(0)
- if not GetOverlappedResultEx(
- self._handle, self._overlapped, ctypes.byref(read), timeout * 1000, True
- ):
- raise ctypes.WinError(ctypes.get_last_error())
- return buf.raw[0 : read.value]
-
- def close(self):
- CloseHandle(self._overlapped.hEvent)
- CloseHandle(self._handle)
-
-else:
-
- class Pipe(object):
- def __init__(self, prefix):
- self.name = os.path.join(prefix, "stub_port_number")
- os.mkfifo(self.name)
- self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)
-
- def finish_connection(self, timeout):
- pass
-
- def read(self, size, timeout):
- (readers, _, _) = select.select([self._fd], [], [], timeout)
- if self._fd not in readers:
- raise TimeoutError
- return os.read(self._fd, size)
-
- def close(self):
- os.close(self._fd)
+from lldbgdbserverutils import Pipe
class TestGdbRemoteConnection(gdbremote_testcase.GdbRemoteTestCaseBase):
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 470c9f84c6a20..b74474b9d383c 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -673,9 +673,14 @@ void request_attach(const llvm::json::Object &request) {
lldb::SBError error;
FillResponse(request, response);
lldb::SBAttachInfo attach_info;
+ const int invalid_port = 0;
auto arguments = request.getObject("arguments");
const lldb::pid_t pid =
GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
+ const auto gdb_remote_port =
+ GetUnsigned(arguments, "gdb-remote-port", invalid_port);
+ const auto gdb_remote_hostname =
+ GetString(arguments, "gdb-remote-hostname", "localhost");
if (pid != LLDB_INVALID_PROCESS_ID)
attach_info.SetProcessID(pid);
const auto wait_for = GetBoolean(arguments, "waitFor", false);
@@ -737,7 +742,8 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if (pid == LLDB_INVALID_PROCESS_ID && wait_for) {
+ if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
+ wait_for) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
"Waiting to attach to \"%s\"...",
@@ -750,9 +756,27 @@ void request_attach(const llvm::json::Object &request) {
// Disable async events so the attach will be successful when we return from
// the launch call and the launch will happen synchronously
g_dap.debugger.SetAsync(false);
- if (core_file.empty())
- g_dap.target.Attach(attach_info, error);
- else
+ if (core_file.empty()) {
+ if ((pid != LLDB_INVALID_PROCESS_ID) &&
+ (gdb_remote_port != invalid_port)) {
+ // If both pid and port numbers are specified.
+ error.SetErrorString("The user can't specify both pid and port");
+ } else if (gdb_remote_port != invalid_port) {
+ // If port is specified and pid is not.
+ lldb::SBListener listener = g_dap.debugger.GetListener();
+
+ // If the user hasn't provided the hostname property, default localhost
+ // being used.
+ std::string connect_url =
+ llvm::formatv("connect://{0}:", gdb_remote_hostname);
+ connect_url += std::to_string(gdb_remote_port);
+ g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
+ error);
+ } else {
+ // Attach by process name or id.
+ g_dap.target.Attach(attach_info, error);
+ }
+ } else
g_dap.target.LoadCore(core_file.data(), error);
// Reenable async events
g_dap.debugger.SetAsync(true);
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index adffc3f0a2c58..62cf69b359728 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -348,6 +348,17 @@
"type": "string",
"description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds."
},
+ "gdb-remote-port": {
+ "type": [
+ "number",
+ "string"
+ ],
+ "description": "TCP/IP port to attach to. Specifying both pid and port is an error."
+ },
+ "gdb-remote-hostname": {
+ "type": "string",
+ "description": "The hostname to connect to a remote system. The default hostname being used localhost."
+ },
"enableAutoVariableSummaries": {
"type": "boolean",
"description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.",
More information about the lldb-commits
mailing list