[Lldb-commits] [lldb] [lldb-dap] Enabling instruction breakpoint support to lldb-dap. (PR #105278)
Santhosh Kumar Ellendula via lldb-commits
lldb-commits at lists.llvm.org
Thu Aug 22 14:07:44 PDT 2024
https://github.com/santhoshe447 updated https://github.com/llvm/llvm-project/pull/105278
>From 960351c9abf51f42d92604ac6297aa5b76ddfba5 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-hyd.qualcomm.com>
Date: Fri, 17 Nov 2023 15:09:10 +0530
Subject: [PATCH 01/28] [lldb][test] Add the ability to extract the variable
value out of the summary.
---
.../Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py | 3 +++
1 file changed, 3 insertions(+)
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 9d79872b029a33..0cf9d4fde49488 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
@@ -195,6 +195,9 @@ def collect_console(self, duration):
def get_local_as_int(self, name, threadId=None):
value = self.dap_server.get_local_variable_value(name, threadId=threadId)
+ # 'value' may have the variable value and summary.
+ # Extract the variable value since summary can have nonnumeric characters.
+ value = value.split(" ")[0]
if value.startswith("0x"):
return int(value, 16)
elif value.startswith("0"):
>From ab44a6991c5bc8ac5764c3f71cbe3acc747b3776 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Fri, 3 May 2024 02:47:05 -0700
Subject: [PATCH 02/28] [lldb-dap] Added "port" property to vscode "attach"
command.
Adding a "port" property to the VsCode "attach" command likely extends the functionality of the debugger configuratiuon 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 <port>".
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",
"port":1234,
"program": "${workspaceFolder}/a.out",
"args": [],
"stopOnEntry": false,
"cwd": "${workspaceFolder}",
"env": [],
}
]
}
---
lldb/include/lldb/lldb-defines.h | 1 +
.../Python/lldbsuite/test/lldbtest.py | 9 ++
.../test/tools/lldb-dap/dap_server.py | 6 +
.../test/tools/lldb-dap/lldbdap_testcase.py | 20 +++
.../attach/TestDAP_attachByPortNum.py | 120 ++++++++++++++++++
lldb/tools/lldb-dap/lldb-dap.cpp | 36 +++++-
lldb/tools/lldb-dap/package.json | 11 ++
7 files changed, 199 insertions(+), 4 deletions(-)
create mode 100644 lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h
index c7bd019c5c90eb..a1e6ee2ce468cb 100644
--- a/lldb/include/lldb/lldb-defines.h
+++ b/lldb/include/lldb/lldb-defines.h
@@ -96,6 +96,7 @@
#define LLDB_INVALID_QUEUE_ID 0
#define LLDB_INVALID_CPU_ID UINT32_MAX
#define LLDB_INVALID_WATCHPOINT_RESOURCE_ID UINT32_MAX
+#define LLDB_INVALID_PORT_NUMBER 0
/// CPU Type definitions
#define LLDB_ARCH_DEFAULT "systemArch"
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 5fd686c143e9f9..fb3cd22959df25 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1572,6 +1572,15 @@ def findBuiltClang(self):
return os.environ["CC"]
+ def getBuiltServerTool(self, server_tool):
+ # Tries to find simulation/lldb-server/gdbserver tool at the same folder as the lldb.
+ lldb_dir = os.path.dirname(lldbtest_config.lldbExec)
+ path = shutil.which(server_tool, path=lldb_dir)
+ if path is not None:
+ return path
+
+ return ""
+
def yaml2obj(self, yaml_path, obj_path, max_size=None):
"""
Create an object file at the given path from a yaml file.
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 5838281bcb1a10..96d312565f953e 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
@@ -568,6 +568,8 @@ def request_attach(
coreFile=None,
postRunCommands=None,
sourceMap=None,
+ port=None,
+ hostname=None
):
args_dict = {}
if pid is not None:
@@ -597,6 +599,10 @@ def request_attach(
args_dict["postRunCommands"] = postRunCommands
if sourceMap:
args_dict["sourceMap"] = sourceMap
+ if port is not None:
+ args_dict['port'] = port
+ if hostname is not None:
+ args_dict['hostname'] = hostname
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 d56ea5dca14beb..ba58a3075c6a2f 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
@@ -335,6 +335,26 @@ def cleanup():
response["success"], "attach failed (%s)" % (response["message"])
)
+ def attach_by_port(self, program=None, pid=None, disconnectAutomatically=True, waitFor=None, sourceInitFile=False, port=None, hostname=None):
+ '''Build the default Makefile target, create the VSCode debug adaptor,
+ and attach to the process.
+ '''
+ # This overloaded function helps to request attach by port number
+ # Make sure we disconnect and terminate the VSCode debug adaptor even
+ # if we throw an exception during the test case.
+ def cleanup():
+ if disconnectAutomatically:
+ self.dap_server.request_disconnect(terminateDebuggee=True)
+ self.dap_server.terminate()
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+ # Initialize and launch the program
+ self.dap_server.request_initialize(sourceInitFile)
+ response = self.dap_server.request_attach(
+ program=program, pid=pid, waitFor=waitFor, port=port, hostname=hostname)
+ return response
+
def launch(
self,
program=None,
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 00000000000000..0c0ea6ca436166
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -0,0 +1,120 @@
+"""
+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
+import lldbdap_testcase
+import os
+import shutil
+import subprocess
+import tempfile
+import threading
+import time
+import sys
+import re
+
+class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
+
+ def runTargetProgramOnPort(self, port=None, program=None):
+ # sim_tool="hexagon-sim"
+ # if isIUTarget():
+ # sim_tool="iu-sim"
+ target_sim_path=self.getBuiltServerTool("lldb-server")
+ if target_sim_path:
+ target_sim_path +=' g localhost:' + port + ' '
+
+ self.process = subprocess.Popen([target_sim_path + program], shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ return self.process
+
+ 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()
+
+ @skipIfWindows
+ @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfRemote
+ def test_by_port(self):
+ '''
+ Tests attaching to a process by port.
+ '''
+ self.build_and_create_debug_adaptor()
+ program = self.getBuildArtifact("a.out")
+
+ port = '2345'
+ self.process = self.runTargetProgramOnPort(port=port, program=program)
+ pid=self.process.pid
+ response = self.attach_by_port(program=program, port=int(port), sourceInitFile=True)
+ if not (response and response['success']):
+ self.assertTrue(response['success'],
+ 'attach failed (%s)' % (response['message']))
+ self.set_and_hit_breakpoint(continueToExit=True)
+ self.process.kill()
+ os.system('killall hexagon-sim')
+
+ @skipIfWindows
+ @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfRemote
+ 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")
+
+ port = '2345'
+ self.process = self.runTargetProgramOnPort(port=port, program=program)
+ response = self.attach_by_port(program=program,pid=1234, port=int(port), sourceInitFile=True)
+ if not (response and response['success']):
+ self.assertFalse(response['success'], "The user can't specify both pid and port")
+ self.process.kill()
+
+ @skipIfWindows
+ @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfRemote
+ 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'
+ self.process = self.runTargetProgramOnPort(port=port, program=program)
+ response = self.attach_by_port(program=program, port=int(port), sourceInitFile=True)
+ if not (response and response['success']):
+ self.assertFalse(response['success'], "The user can't attach with invalid port (%s)" % port)
+ self.process.kill()
+
+ @skipIfWindows
+ @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfRemote
+ 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'
+ self.process = self.runTargetProgramOnPort(port=port, program=program)
+ response = self.attach_by_port(program=program, port=int(port), sourceInitFile=True)
+ if not (response and response['success']):
+ self.assertFalse(response['success'], "The user can't attach with illegal port (%s)" % port)
+ self.process.kill()
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 8000d68dea7e36..2280e7217eaaae 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -688,6 +688,8 @@ void request_attach(const llvm::json::Object &request) {
auto arguments = request.getObject("arguments");
const lldb::pid_t pid =
GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
+ const auto port = GetUnsigned(arguments, "port", LLDB_INVALID_PORT_NUMBER);
+ llvm::StringRef hostname = GetString(arguments, "hostname");
if (pid != LLDB_INVALID_PROCESS_ID)
attach_info.SetProcessID(pid);
const auto wait_for = GetBoolean(arguments, "waitFor", false);
@@ -749,7 +751,7 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if (pid == LLDB_INVALID_PROCESS_ID && wait_for) {
+ if ((pid == LLDB_INVALID_PROCESS_ID || port == LLDB_INVALID_PORT_NUMBER) && wait_for) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
"Waiting to attach to \"%s\"...",
@@ -762,9 +764,35 @@ 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) &&
+ (port != LLDB_INVALID_PORT_NUMBER)) {
+ // If both pid and port numbers are specified.
+ error.SetErrorString("The user can't specify both pid and port");
+ } else if ((pid != LLDB_INVALID_PROCESS_ID) &&
+ (port == LLDB_INVALID_PORT_NUMBER)) {
+ // If pid is specified and port is not.
+ g_dap.target.Attach(attach_info, error);
+ } else if ((port != LLDB_INVALID_PORT_NUMBER) && (port < UINT16_MAX) &&
+ (pid == LLDB_INVALID_PROCESS_ID)) {
+ // 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("connect://localhost:");
+
+ // If the user has provided hostname other than localhost.
+ if (!hostname.empty() && !hostname.starts_with("localhost")) {
+ connect_url = llvm::formatv("connect://{0}:", hostname.data());
+ }
+ connect_url += std::to_string(port);
+ g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
+ error);
+ } else {
+ error.SetErrorString("Invalid pid/port number specified");
+ }
+ } 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 2e8ad074256bf5..624b5710f50fac 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -347,6 +347,17 @@
"type": "string",
"description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds."
},
+ "port": {
+ "type": [
+ "number",
+ "string"
+ ],
+ "description": "TCP/IP port to attach to. Specifying both pid and port is an error."
+ },
+ "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.",
>From 4e2e524f7d468cbd3b8c283635e766bb6de35376 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Wed, 8 May 2024 12:41:43 -0700
Subject: [PATCH 03/28] [lldb-dap] Added "port" property to vscode "attach"
command.
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",
"port":1234,
"program": "${workspaceFolder}/a.out",
"args": [],
"stopOnEntry": false,
"cwd": "${workspaceFolder}",
"env": [],
}
]
}
In this patch we have resolved code formatting issues and fixed "test_by_name" failure.
---
.../Python/lldbsuite/test/lldbtest.py | 2 +-
.../test/tools/lldb-dap/dap_server.py | 6 +-
.../test/tools/lldb-dap/lldbdap_testcase.py | 21 +++-
.../attach/TestDAP_attachByPortNum.py | 109 ++++++++++--------
lldb/tools/lldb-dap/lldb-dap.cpp | 13 +--
lldb/tools/lldb-dap/package.json | 12 +-
6 files changed, 94 insertions(+), 69 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index fb3cd22959df25..dde3efd8f5f871 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1572,7 +1572,7 @@ def findBuiltClang(self):
return os.environ["CC"]
- def getBuiltServerTool(self, server_tool):
+ def getBuiltinServerTool(self, server_tool):
# Tries to find simulation/lldb-server/gdbserver tool at the same folder as the lldb.
lldb_dir = os.path.dirname(lldbtest_config.lldbExec)
path = shutil.which(server_tool, path=lldb_dir)
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 613180bc99af6c..bab16479d26c37 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
@@ -573,7 +573,7 @@ def request_attach(
postRunCommands=None,
sourceMap=None,
port=None,
- hostname=None
+ hostname=None,
):
args_dict = {}
if pid is not None:
@@ -604,9 +604,9 @@ def request_attach(
if sourceMap:
args_dict["sourceMap"] = sourceMap
if port is not None:
- args_dict['port'] = port
+ args_dict["port"] = port
if hostname is not None:
- args_dict['hostname'] = hostname
+ args_dict["hostname"] = hostname
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 ba58a3075c6a2f..922fb1d5784e67 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
@@ -335,10 +335,20 @@ def cleanup():
response["success"], "attach failed (%s)" % (response["message"])
)
- def attach_by_port(self, program=None, pid=None, disconnectAutomatically=True, waitFor=None, sourceInitFile=False, port=None, hostname=None):
- '''Build the default Makefile target, create the VSCode debug adaptor,
- and attach to the process.
- '''
+ def attach_by_port(
+ self,
+ program=None,
+ pid=None,
+ disconnectAutomatically=True,
+ waitFor=None,
+ sourceInitFile=False,
+ port=None,
+ hostname=None,
+ ):
+ """Build the default Makefile target, create the VSCode debug adaptor,
+ and attach to the process.
+ """
+
# This overloaded function helps to request attach by port number
# Make sure we disconnect and terminate the VSCode debug adaptor even
# if we throw an exception during the test case.
@@ -352,7 +362,8 @@ def cleanup():
# Initialize and launch the program
self.dap_server.request_initialize(sourceInitFile)
response = self.dap_server.request_attach(
- program=program, pid=pid, waitFor=waitFor, port=port, hostname=hostname)
+ program=program, pid=pid, waitFor=waitFor, port=port, hostname=hostname
+ )
return response
def launch(
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 0c0ea6ca436166..4b9965cbb55717 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -15,34 +15,35 @@
import threading
import time
import sys
-import re
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
def runTargetProgramOnPort(self, port=None, program=None):
- # sim_tool="hexagon-sim"
- # if isIUTarget():
- # sim_tool="iu-sim"
- target_sim_path=self.getBuiltServerTool("lldb-server")
- if target_sim_path:
- target_sim_path +=' g localhost:' + port + ' '
+ server_tool = "lldb-server"
+ server_path = self.getBuiltinServerTool(server_tool)
+ if server_path:
+ server_path +=" g localhost:" + port + " "
- self.process = subprocess.Popen([target_sim_path + program], shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ self.process = subprocess.Popen(
+ [server_path + program],
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
return self.process
def set_and_hit_breakpoint(self, continueToExit=True):
- source = 'main.c'
+ source = "main.c"
main_source_path = os.path.join(os.getcwd(), source)
- breakpoint1_line = line_number(main_source_path, '// breakpoint 1')
+ 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.assertEqual(
+ len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
+ )
self.continue_to_breakpoints(breakpoint_ids)
if continueToExit:
self.continue_to_exit()
@@ -51,70 +52,86 @@ def set_and_hit_breakpoint(self, continueToExit=True):
@skipIfNetBSD # Hangs on NetBSD as well
@skipIfRemote
def test_by_port(self):
- '''
- Tests attaching to a process by port.
- '''
+ """
+ Tests attaching to a process by port.
+ """
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = '2345'
+ port = "2345"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- pid=self.process.pid
- response = self.attach_by_port(program=program, port=int(port), sourceInitFile=True)
- if not (response and response['success']):
- self.assertTrue(response['success'],
- 'attach failed (%s)' % (response['message']))
+ pid = self.process.pid
+ response = self.attach_by_port(
+ program=program, port=int(port), sourceInitFile=True
+ )
+ if not (response and response["success"]):
+ self.assertTrue(
+ response["success"], "attach failed (%s)" % (response["message"])
+ )
self.set_and_hit_breakpoint(continueToExit=True)
self.process.kill()
- os.system('killall hexagon-sim')
@skipIfWindows
@skipIfNetBSD # Hangs on NetBSD as well
@skipIfRemote
def test_by_port_and_pid(self):
- '''
- Tests attaching to a process by process ID and port number.
- '''
+ """
+ Tests attaching to a process by process ID and port number.
+ """
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = '2345'
+ port = "2345"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- response = self.attach_by_port(program=program,pid=1234, port=int(port), sourceInitFile=True)
- if not (response and response['success']):
- self.assertFalse(response['success'], "The user can't specify both pid and port")
+ response = self.attach_by_port(
+ program=program, pid=1234, port=int(port), sourceInitFile=True
+ )
+ if not (response and response["success"]):
+ self.assertFalse(
+ response["success"], "The user can't specify both pid and port"
+ )
self.process.kill()
@skipIfWindows
@skipIfNetBSD # Hangs on NetBSD as well
@skipIfRemote
def test_by_invalid_port(self):
- '''
- Tests attaching to a process by invalid port number 0.
- '''
+ """
+ Tests attaching to a process by invalid port number 0.
+ """
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = '0'
+ port = "0"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- response = self.attach_by_port(program=program, port=int(port), sourceInitFile=True)
- if not (response and response['success']):
- self.assertFalse(response['success'], "The user can't attach with invalid port (%s)" % port)
+ response = self.attach_by_port(
+ program=program, port=int(port), sourceInitFile=True
+ )
+ if not (response and response["success"]):
+ self.assertFalse(
+ response["success"],
+ "The user can't attach with invalid port (%s)" % port,
+ )
self.process.kill()
@skipIfWindows
@skipIfNetBSD # Hangs on NetBSD as well
@skipIfRemote
def test_by_illegal_port(self):
- '''
- Tests attaching to a process by illegal/greater port number 65536
- '''
+ """
+ Tests attaching to a process by illegal/greater port number 65536
+ """
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = '65536'
+ port = "65536"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- response = self.attach_by_port(program=program, port=int(port), sourceInitFile=True)
- if not (response and response['success']):
- self.assertFalse(response['success'], "The user can't attach with illegal port (%s)" % port)
+ response = self.attach_by_port(
+ program=program, port=int(port), sourceInitFile=True
+ )
+ if not (response and response["success"]):
+ self.assertFalse(
+ response["success"],
+ "The user can't attach with illegal port (%s)" % port,
+ )
self.process.kill()
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 787a732c184102..2b123a01da657a 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -751,7 +751,8 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if ((pid == LLDB_INVALID_PROCESS_ID || port == LLDB_INVALID_PORT_NUMBER) && wait_for) {
+ if ((pid == LLDB_INVALID_PROCESS_ID || port == LLDB_INVALID_PORT_NUMBER) &&
+ wait_for) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
"Waiting to attach to \"%s\"...",
@@ -769,12 +770,7 @@ void request_attach(const llvm::json::Object &request) {
(port != LLDB_INVALID_PORT_NUMBER)) {
// If both pid and port numbers are specified.
error.SetErrorString("The user can't specify both pid and port");
- } else if ((pid != LLDB_INVALID_PROCESS_ID) &&
- (port == LLDB_INVALID_PORT_NUMBER)) {
- // If pid is specified and port is not.
- g_dap.target.Attach(attach_info, error);
- } else if ((port != LLDB_INVALID_PORT_NUMBER) && (port < UINT16_MAX) &&
- (pid == LLDB_INVALID_PROCESS_ID)) {
+ } else if ((port != LLDB_INVALID_PORT_NUMBER) && (port < UINT16_MAX)) {
// If port is specified and pid is not.
lldb::SBListener listener = g_dap.debugger.GetListener();
@@ -790,7 +786,8 @@ void request_attach(const llvm::json::Object &request) {
g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
error);
} else {
- error.SetErrorString("Invalid pid/port number specified");
+ // Attach by process name or id.
+ g_dap.target.Attach(attach_info, error);
}
} else
g_dap.target.LoadCore(core_file.data(), error);
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index 624b5710f50fac..224115b8ddffbb 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -349,15 +349,15 @@
},
"port": {
"type": [
- "number",
- "string"
- ],
+ "number",
+ "string"
+ ],
"description": "TCP/IP port to attach to. Specifying both pid and port is an error."
},
"hostname": {
- "type": "string",
- "description": "The hostname to connect to a remote system. The default hostname being used localhost."
- },
+ "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.",
>From 175f1d32c446473252f4ed87632372a607c22579 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Thu, 9 May 2024 01:46:51 -0700
Subject: [PATCH 04/28] [lldb-dap] Added "port" property to vscode "attach"
command. Resolved code format issues.
---
.../API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 4b9965cbb55717..c1b0d57cffb94a 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -22,7 +22,7 @@ def runTargetProgramOnPort(self, port=None, program=None):
server_tool = "lldb-server"
server_path = self.getBuiltinServerTool(server_tool)
if server_path:
- server_path +=" g localhost:" + port + " "
+ server_path += " g localhost:" + port + " "
self.process = subprocess.Popen(
[server_path + program],
@@ -31,7 +31,7 @@ def runTargetProgramOnPort(self, port=None, program=None):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
-
+
return self.process
def set_and_hit_breakpoint(self, continueToExit=True):
@@ -43,7 +43,7 @@ def set_and_hit_breakpoint(self, continueToExit=True):
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()
>From e5c0aed39eb5903d1d98355bb2af4af134b98546 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Thu, 9 May 2024 01:58:17 -0700
Subject: [PATCH 05/28] Resolved code format issue.
---
lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index c1b0d57cffb94a..8962e887cd8b73 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -22,7 +22,7 @@ def runTargetProgramOnPort(self, port=None, program=None):
server_tool = "lldb-server"
server_path = self.getBuiltinServerTool(server_tool)
if server_path:
- server_path += " g localhost:" + port + " "
+ server_path += " g localhost:" + port + " "
self.process = subprocess.Popen(
[server_path + program],
>From c502203c6b129ba569e36a1d922bb13c322f3706 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Thu, 9 May 2024 02:04:18 -0700
Subject: [PATCH 06/28] Resolved code format errors.
---
lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 8962e887cd8b73..aee92d94d1ae0b 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -16,8 +16,8 @@
import time
import sys
-class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
def runTargetProgramOnPort(self, port=None, program=None):
server_tool = "lldb-server"
server_path = self.getBuiltinServerTool(server_tool)
>From c1bf2bdaa33353913b8529750d41da482ce18161 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Wed, 15 May 2024 15:16:44 -0700
Subject: [PATCH 07/28] [lldb-dap] Added "port" property to vscode "attach"
command - Addressed review comments Addressed all review comments.
---
.../test/tools/lldb-dap/lldbdap_testcase.py | 36 +++--------------
.../attach/TestDAP_attachByPortNum.py | 39 +++++++++++--------
lldb/tools/lldb-dap/lldb-dap.cpp | 2 +-
3 files changed, 29 insertions(+), 48 deletions(-)
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 922fb1d5784e67..d95a80482a60b1 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
@@ -297,6 +297,9 @@ def attach(
sourceMap=None,
sourceInitFile=False,
expectFailure=False,
+ port=None,
+ hostname=None,
+
):
"""Build the default Makefile target, create the DAP debug adaptor,
and attach to the process.
@@ -327,6 +330,8 @@ def cleanup():
coreFile=coreFile,
postRunCommands=postRunCommands,
sourceMap=sourceMap,
+ port=port,
+ hostname=hostname,
)
if expectFailure:
return response
@@ -335,37 +340,6 @@ def cleanup():
response["success"], "attach failed (%s)" % (response["message"])
)
- def attach_by_port(
- self,
- program=None,
- pid=None,
- disconnectAutomatically=True,
- waitFor=None,
- sourceInitFile=False,
- port=None,
- hostname=None,
- ):
- """Build the default Makefile target, create the VSCode debug adaptor,
- and attach to the process.
- """
-
- # This overloaded function helps to request attach by port number
- # Make sure we disconnect and terminate the VSCode debug adaptor even
- # if we throw an exception during the test case.
- def cleanup():
- if disconnectAutomatically:
- self.dap_server.request_disconnect(terminateDebuggee=True)
- self.dap_server.terminate()
-
- # Execute the cleanup function during test case tear down.
- self.addTearDownHook(cleanup)
- # Initialize and launch the program
- self.dap_server.request_initialize(sourceInitFile)
- response = self.dap_server.request_attach(
- program=program, pid=pid, waitFor=waitFor, port=port, hostname=hostname
- )
- return response
-
def launch(
self,
program=None,
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index aee92d94d1ae0b..cfa20260fed397 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -7,6 +7,8 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test import lldbplatformutil
+import lldbgdbserverutils
import lldbdap_testcase
import os
import shutil
@@ -19,13 +21,22 @@
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
def runTargetProgramOnPort(self, port=None, program=None):
- server_tool = "lldb-server"
- server_path = self.getBuiltinServerTool(server_tool)
- if server_path:
- server_path += " g localhost:" + port + " "
+ 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.")
+ server_tool += " g localhost:" + port + " "
+ 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.")
+ server_tool += " --listen localhost:" + port + " "
self.process = subprocess.Popen(
- [server_path + program],
+ [server_tool + program],
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -61,13 +72,9 @@ def test_by_port(self):
port = "2345"
self.process = self.runTargetProgramOnPort(port=port, program=program)
pid = self.process.pid
- response = self.attach_by_port(
+ response = self.attach(
program=program, port=int(port), sourceInitFile=True
)
- if not (response and response["success"]):
- self.assertTrue(
- response["success"], "attach failed (%s)" % (response["message"])
- )
self.set_and_hit_breakpoint(continueToExit=True)
self.process.kill()
@@ -83,8 +90,8 @@ def test_by_port_and_pid(self):
port = "2345"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- response = self.attach_by_port(
- program=program, pid=1234, port=int(port), sourceInitFile=True
+ response = self.attach(
+ program=program, pid=1234, port=int(port), sourceInitFile=True, expectFailure=True
)
if not (response and response["success"]):
self.assertFalse(
@@ -104,8 +111,8 @@ def test_by_invalid_port(self):
port = "0"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- response = self.attach_by_port(
- program=program, port=int(port), sourceInitFile=True
+ response = self.attach(
+ program=program, port=int(port), sourceInitFile=True, expectFailure=True
)
if not (response and response["success"]):
self.assertFalse(
@@ -126,8 +133,8 @@ def test_by_illegal_port(self):
port = "65536"
self.process = self.runTargetProgramOnPort(port=port, program=program)
- response = self.attach_by_port(
- program=program, port=int(port), sourceInitFile=True
+ response = self.attach(
+ program=program, port=int(port), sourceInitFile=True, expectFailure=True
)
if not (response and response["success"]):
self.assertFalse(
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index a403e7ae26bfd2..c9cd6ffe1db433 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -758,7 +758,7 @@ void request_attach(const llvm::json::Object &request) {
(port != LLDB_INVALID_PORT_NUMBER)) {
// If both pid and port numbers are specified.
error.SetErrorString("The user can't specify both pid and port");
- } else if ((port != LLDB_INVALID_PORT_NUMBER) && (port < UINT16_MAX)) {
+ } else if (port != LLDB_INVALID_PORT_NUMBER) {
// If port is specified and pid is not.
lldb::SBListener listener = g_dap.debugger.GetListener();
>From 5590afc46aff9765f314139bbf4a446ca0f21b0c Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Wed, 15 May 2024 15:32:29 -0700
Subject: [PATCH 08/28] [lldb-dap] Added "port" property to vscode "attach"
command - fix code format resolved code format issues.
---
.../test/tools/lldb-dap/lldbdap_testcase.py | 1 -
.../lldb-dap/attach/TestDAP_attachByPortNum.py | 18 ++++++++++--------
2 files changed, 10 insertions(+), 9 deletions(-)
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 d95a80482a60b1..a2bef31ceb0ce1 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
@@ -299,7 +299,6 @@ def attach(
expectFailure=False,
port=None,
hostname=None,
-
):
"""Build the default Makefile target, create the DAP debug adaptor,
and attach to the process.
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index cfa20260fed397..e5cdc256a7abc1 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -22,18 +22,18 @@
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
def runTargetProgramOnPort(self, port=None, program=None):
server_tool = None
- if (lldbplatformutil.getPlatform() == "linux"):
+ 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.")
- server_tool += " g localhost:" + port + " "
- elif (lldbplatformutil.getPlatform() == "macosx"):
+ server_tool += " g localhost:" + port + " "
+ 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.")
- server_tool += " --listen localhost:" + port + " "
+ server_tool += " --listen localhost:" + port + " "
self.process = subprocess.Popen(
[server_tool + program],
@@ -72,9 +72,7 @@ def test_by_port(self):
port = "2345"
self.process = self.runTargetProgramOnPort(port=port, program=program)
pid = self.process.pid
- response = self.attach(
- program=program, port=int(port), sourceInitFile=True
- )
+ response = self.attach(program=program, port=int(port), sourceInitFile=True)
self.set_and_hit_breakpoint(continueToExit=True)
self.process.kill()
@@ -91,7 +89,11 @@ def test_by_port_and_pid(self):
port = "2345"
self.process = self.runTargetProgramOnPort(port=port, program=program)
response = self.attach(
- program=program, pid=1234, port=int(port), sourceInitFile=True, expectFailure=True
+ program=program,
+ pid=1234,
+ port=int(port),
+ sourceInitFile=True,
+ expectFailure=True,
)
if not (response and response["success"]):
self.assertFalse(
>From 023b51e61e3e5127dc2289ea47e2e4d3e7a6db26 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Tue, 28 May 2024 11:11:33 -0700
Subject: [PATCH 09/28] [lldb-dap] Added "port" property to vscode "attach"
command - resolved all review comments. All review comments have been
addressed, and the attach-by-port tests have been verified on Linux machine.
Although the functionality is intended to support both Linux and macOS, we
were unable to verify it on macOS due to lack of access.
---
lldb/include/lldb/lldb-defines.h | 1 -
.../Python/lldbsuite/test/lldbtest.py | 9 ----
.../test/tools/lldb-dap/lldbdap_testcase.py | 20 ++++++++
.../attach/TestDAP_attachByPortNum.py | 50 +++++++++----------
lldb/tools/lldb-dap/lldb-dap.cpp | 11 ++--
5 files changed, 49 insertions(+), 42 deletions(-)
diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h
index a1e6ee2ce468cb..c7bd019c5c90eb 100644
--- a/lldb/include/lldb/lldb-defines.h
+++ b/lldb/include/lldb/lldb-defines.h
@@ -96,7 +96,6 @@
#define LLDB_INVALID_QUEUE_ID 0
#define LLDB_INVALID_CPU_ID UINT32_MAX
#define LLDB_INVALID_WATCHPOINT_RESOURCE_ID UINT32_MAX
-#define LLDB_INVALID_PORT_NUMBER 0
/// CPU Type definitions
#define LLDB_ARCH_DEFAULT "systemArch"
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 5a93dc93c3eee9..1ad8ab6e6e462d 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1637,15 +1637,6 @@ def findBuiltClang(self):
return os.environ["CC"]
- def getBuiltinServerTool(self, server_tool):
- # Tries to find simulation/lldb-server/gdbserver tool at the same folder as the lldb.
- lldb_dir = os.path.dirname(lldbtest_config.lldbExec)
- path = shutil.which(server_tool, path=lldb_dir)
- if path is not None:
- return path
-
- return ""
-
def yaml2obj(self, yaml_path, obj_path, max_size=None):
"""
Create an object file at the given path from a yaml file.
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 a2bef31ceb0ce1..15d8a94a1f7149 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):
@@ -487,3 +489,21 @@ def build_and_launch(
launchCommands=launchCommands,
expectFailure=expectFailure,
)
+
+ def getBuiltinServerToolWithPortArg(self, port):
+ # Tries to find simulation/lldb-server/gdbserver tool and
+ # configure the server arguments to attach with given port numeber.
+ 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.")
+ server_tool += " g localhost:" + str(port) + " "
+ 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.")
+ server_tool += " --listen localhost:" + str(port) + " "
+ return server_tool
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index e5cdc256a7abc1..6933bd04fdda47 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -17,24 +17,19 @@
import threading
import time
import sys
+import socket
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
- def runTargetProgramOnPort(self, port=None, program=None):
- 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.")
- server_tool += " g localhost:" + port + " "
- 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.")
- server_tool += " --listen localhost:" + port + " "
+ def get_free_port(self):
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(('', 0))
+ port = s.getsockname()[1]
+ s.close()
+ return port
+ def runTargetProgramOnPort(self, port=None, program=None):
+ server_tool = self.getBuiltinServerToolWithPortArg(port)
self.process = subprocess.Popen(
[server_tool + program],
shell=True,
@@ -60,7 +55,7 @@ def set_and_hit_breakpoint(self, continueToExit=True):
self.continue_to_exit()
@skipIfWindows
- @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfNetBSD
@skipIfRemote
def test_by_port(self):
"""
@@ -69,15 +64,15 @@ def test_by_port(self):
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = "2345"
+ port = self.get_free_port()
self.process = self.runTargetProgramOnPort(port=port, program=program)
pid = self.process.pid
- response = self.attach(program=program, port=int(port), sourceInitFile=True)
+ response = self.attach(program=program, port=port, sourceInitFile=True)
self.set_and_hit_breakpoint(continueToExit=True)
self.process.kill()
@skipIfWindows
- @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfNetBSD
@skipIfRemote
def test_by_port_and_pid(self):
"""
@@ -86,12 +81,13 @@ def test_by_port_and_pid(self):
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = "2345"
+ port = self.get_free_port()
self.process = self.runTargetProgramOnPort(port=port, program=program)
+ pid = self.process.pid
response = self.attach(
program=program,
- pid=1234,
- port=int(port),
+ pid=pid,
+ port=port,
sourceInitFile=True,
expectFailure=True,
)
@@ -102,7 +98,7 @@ def test_by_port_and_pid(self):
self.process.kill()
@skipIfWindows
- @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfNetBSD
@skipIfRemote
def test_by_invalid_port(self):
"""
@@ -111,10 +107,10 @@ def test_by_invalid_port(self):
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = "0"
+ port = 0
self.process = self.runTargetProgramOnPort(port=port, program=program)
response = self.attach(
- program=program, port=int(port), sourceInitFile=True, expectFailure=True
+ program=program, port=port, sourceInitFile=True, expectFailure=True
)
if not (response and response["success"]):
self.assertFalse(
@@ -124,7 +120,7 @@ def test_by_invalid_port(self):
self.process.kill()
@skipIfWindows
- @skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfNetBSD
@skipIfRemote
def test_by_illegal_port(self):
"""
@@ -133,10 +129,10 @@ def test_by_illegal_port(self):
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = "65536"
+ port = 65536
self.process = self.runTargetProgramOnPort(port=port, program=program)
response = self.attach(
- program=program, port=int(port), sourceInitFile=True, expectFailure=True
+ program=program, port=port, sourceInitFile=True, expectFailure=True
)
if not (response and response["success"]):
self.assertFalse(
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 1a45ae3d7adbdc..9fedad8a41482a 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -672,11 +672,12 @@ 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 port = GetUnsigned(arguments, "port", LLDB_INVALID_PORT_NUMBER);
- llvm::StringRef hostname = GetString(arguments, "hostname");
+ const auto port = GetUnsigned(arguments, "port", invalid_port);
+ llvm::StringRef hostname = GetString(arguments, "hostname", "localhost");
if (pid != LLDB_INVALID_PROCESS_ID)
attach_info.SetProcessID(pid);
const auto wait_for = GetBoolean(arguments, "waitFor", false);
@@ -738,7 +739,7 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if ((pid == LLDB_INVALID_PROCESS_ID || port == LLDB_INVALID_PORT_NUMBER) &&
+ if ((pid == LLDB_INVALID_PROCESS_ID || port == invalid_port) &&
wait_for) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
@@ -754,10 +755,10 @@ void request_attach(const llvm::json::Object &request) {
g_dap.debugger.SetAsync(false);
if (core_file.empty()) {
if ((pid != LLDB_INVALID_PROCESS_ID) &&
- (port != LLDB_INVALID_PORT_NUMBER)) {
+ (port != invalid_port)) {
// If both pid and port numbers are specified.
error.SetErrorString("The user can't specify both pid and port");
- } else if (port != LLDB_INVALID_PORT_NUMBER) {
+ } else if (port != invalid_port) {
// If port is specified and pid is not.
lldb::SBListener listener = g_dap.debugger.GetListener();
>From d19eb88231cb1ca3d7519061811cbacf34402194 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Tue, 28 May 2024 11:27:04 -0700
Subject: [PATCH 10/28] [lldb-dap] Added "port" property to vscode "attach"
command -resolved format check issues.
---
.../API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py | 2 +-
lldb/tools/lldb-dap/lldb-dap.cpp | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 6933bd04fdda47..f220b454883018 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -23,7 +23,7 @@
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
def get_free_port(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.bind(('', 0))
+ s.bind(("", 0))
port = s.getsockname()[1]
s.close()
return port
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 9fedad8a41482a..e5165459dd9148 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -739,8 +739,7 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if ((pid == LLDB_INVALID_PROCESS_ID || port == invalid_port) &&
- wait_for) {
+ if ((pid == LLDB_INVALID_PROCESS_ID || port == invalid_port) && wait_for) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
"Waiting to attach to \"%s\"...",
@@ -754,8 +753,7 @@ void request_attach(const llvm::json::Object &request) {
// the launch call and the launch will happen synchronously
g_dap.debugger.SetAsync(false);
if (core_file.empty()) {
- if ((pid != LLDB_INVALID_PROCESS_ID) &&
- (port != invalid_port)) {
+ if ((pid != LLDB_INVALID_PROCESS_ID) && (port != invalid_port)) {
// If both pid and port numbers are specified.
error.SetErrorString("The user can't specify both pid and port");
} else if (port != invalid_port) {
>From fe9a4b685eb089cadcc914ba1583bcbc085ada5a Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Thu, 30 May 2024 04:24:09 -0700
Subject: [PATCH 11/28] [lldb-dap] Added "port" property to vscode "attach"
command. #91570 Resolved few review commennts.
---
.../API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py | 4 ----
lldb/tools/lldb-dap/lldb-dap.cpp | 7 +------
2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index f220b454883018..2198fc77b8326e 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -56,7 +56,6 @@ def set_and_hit_breakpoint(self, continueToExit=True):
@skipIfWindows
@skipIfNetBSD
- @skipIfRemote
def test_by_port(self):
"""
Tests attaching to a process by port.
@@ -73,7 +72,6 @@ def test_by_port(self):
@skipIfWindows
@skipIfNetBSD
- @skipIfRemote
def test_by_port_and_pid(self):
"""
Tests attaching to a process by process ID and port number.
@@ -99,7 +97,6 @@ def test_by_port_and_pid(self):
@skipIfWindows
@skipIfNetBSD
- @skipIfRemote
def test_by_invalid_port(self):
"""
Tests attaching to a process by invalid port number 0.
@@ -121,7 +118,6 @@ def test_by_invalid_port(self):
@skipIfWindows
@skipIfNetBSD
- @skipIfRemote
def test_by_illegal_port(self):
"""
Tests attaching to a process by illegal/greater port number 65536
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e5165459dd9148..985652f4801ebb 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -762,12 +762,7 @@ void request_attach(const llvm::json::Object &request) {
// If the user hasn't provided the hostname property, default localhost
// being used.
- std::string connect_url("connect://localhost:");
-
- // If the user has provided hostname other than localhost.
- if (!hostname.empty() && !hostname.starts_with("localhost")) {
- connect_url = llvm::formatv("connect://{0}:", hostname.data());
- }
+ std::string connect_url = llvm::formatv("connect://{0}:", hostname.data());
connect_url += std::to_string(port);
g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
error);
>From c91b02de2896cc6ce5a9ab9493dedb44244c329c Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Thu, 30 May 2024 05:24:23 -0700
Subject: [PATCH 12/28] [lldb-dap] Added "port" property to vscode "attach"
command. Resolved code format issues.
---
lldb/tools/lldb-dap/lldb-dap.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 985652f4801ebb..ac862dc4ea1d28 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -762,7 +762,8 @@ void request_attach(const llvm::json::Object &request) {
// If the user hasn't provided the hostname property, default localhost
// being used.
- std::string connect_url = llvm::formatv("connect://{0}:", hostname.data());
+ std::string connect_url =
+ llvm::formatv("connect://{0}:", hostname.data());
connect_url += std::to_string(port);
g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
error);
>From de29728f0ccf7c82035eba751062608b85833d5a Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Tue, 4 Jun 2024 10:36:02 -0700
Subject: [PATCH 13/28] [lldb-dap] Added "port" property to vscode "attach"
command - addressed review comments. Used named pipe to read debug server
listening port. Since we are only using "gdb-remote" process plugin, changed
to "gdb-remote-port" and "gdb-remote-hostname" from "port" and "hostname"
---
.../test/tools/lldb-dap/dap_server.py | 12 +-
.../test/tools/lldb-dap/lldbdap_testcase.py | 15 +-
.../attach/TestDAP_attachByPortNum.py | 129 +++++++++++++-----
lldb/tools/lldb-dap/lldb-dap.cpp | 14 +-
lldb/tools/lldb-dap/package.json | 4 +-
5 files changed, 115 insertions(+), 59 deletions(-)
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 bab16479d26c37..fbfa851c1356c6 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
@@ -572,8 +572,8 @@ def request_attach(
coreFile=None,
postRunCommands=None,
sourceMap=None,
- port=None,
- hostname=None,
+ gdbRemotePort=None,
+ gdbRemoteHostname=None,
):
args_dict = {}
if pid is not None:
@@ -603,10 +603,10 @@ def request_attach(
args_dict["postRunCommands"] = postRunCommands
if sourceMap:
args_dict["sourceMap"] = sourceMap
- if port is not None:
- args_dict["port"] = port
- if hostname is not None:
- args_dict["hostname"] = hostname
+ 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 15d8a94a1f7149..086c6c5f906558 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
@@ -299,8 +299,8 @@ def attach(
sourceMap=None,
sourceInitFile=False,
expectFailure=False,
- port=None,
- hostname=None,
+ gdbRemotePort=None,
+ gdbRemoteHostname=None,
):
"""Build the default Makefile target, create the DAP debug adaptor,
and attach to the process.
@@ -331,8 +331,8 @@ def cleanup():
coreFile=coreFile,
postRunCommands=postRunCommands,
sourceMap=sourceMap,
- port=port,
- hostname=hostname,
+ gdbRemotePort=gdbRemotePort,
+ gdbRemoteHostname=gdbRemoteHostname,
)
if expectFailure:
return response
@@ -490,20 +490,17 @@ def build_and_launch(
expectFailure=expectFailure,
)
- def getBuiltinServerToolWithPortArg(self, port):
- # Tries to find simulation/lldb-server/gdbserver tool and
- # configure the server arguments to attach with given port numeber.
+ 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.")
- server_tool += " g localhost:" + str(port) + " "
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.")
- server_tool += " --listen localhost:" + str(port) + " "
return server_tool
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 2198fc77b8326e..5c247462350c6e 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -8,38 +8,39 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
from lldbsuite.test import lldbplatformutil
-import lldbgdbserverutils
import lldbdap_testcase
import os
import shutil
import subprocess
import tempfile
import threading
-import time
import sys
import socket
+import select
-class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
- def get_free_port(self):
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.bind(("", 0))
- port = s.getsockname()[1]
- s.close()
- return port
-
- def runTargetProgramOnPort(self, port=None, program=None):
- server_tool = self.getBuiltinServerToolWithPortArg(port)
- self.process = subprocess.Popen(
- [server_tool + program],
- shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
+# 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.
+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)
- return self.process
+ def close(self):
+ os.close(self._fd)
+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)
@@ -54,21 +55,54 @@ def set_and_hit_breakpoint(self, continueToExit=True):
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()
- port = self.get_free_port()
- self.process = self.runTargetProgramOnPort(port=port, program=program)
- pid = self.process.pid
- response = self.attach(program=program, port=port, sourceInitFile=True)
+ 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, 30)
+ # 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.kill()
+ self.process.terminate()
@skipIfWindows
@skipIfNetBSD
@@ -79,13 +113,28 @@ def test_by_port_and_pid(self):
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
- port = self.get_free_port()
- self.process = self.runTargetProgramOnPort(port=port, program=program)
+ 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, 30)
+ # Trim null byte, convert to int
+ port = int(port[:-1])
+ self.assertIsNotNone(port, " Failed to read the port number from debug server pipe")
+
pid = self.process.pid
+
response = self.attach(
program=program,
pid=pid,
- port=port,
+ gdbRemotePort=port,
sourceInitFile=True,
expectFailure=True,
)
@@ -93,7 +142,7 @@ def test_by_port_and_pid(self):
self.assertFalse(
response["success"], "The user can't specify both pid and port"
)
- self.process.kill()
+ self.process.terminate()
@skipIfWindows
@skipIfNetBSD
@@ -105,16 +154,21 @@ def test_by_invalid_port(self):
program = self.getBuildArtifact("a.out")
port = 0
- self.process = self.runTargetProgramOnPort(port=port, program=program)
+ args = [program]
+ debug_server_tool = self.getBuiltinDebugServerTool()
+ self.process = self.spawnSubprocess(
+ debug_server_tool, args, install_remote=False
+ )
+
response = self.attach(
- program=program, port=port, sourceInitFile=True, expectFailure=True
+ 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,
)
- self.process.kill()
+ self.process.terminate()
@skipIfWindows
@skipIfNetBSD
@@ -126,13 +180,18 @@ def test_by_illegal_port(self):
program = self.getBuildArtifact("a.out")
port = 65536
- self.process = self.runTargetProgramOnPort(port=port, program=program)
+ args = [program]
+ debug_server_tool = self.getBuiltinDebugServerTool()
+ self.process = self.spawnSubprocess(
+ debug_server_tool, args, install_remote=False
+ )
+
response = self.attach(
- program=program, port=port, sourceInitFile=True, expectFailure=True
+ 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.kill()
+ self.process.terminate()
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index ac862dc4ea1d28..b852a470f6d4bd 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -676,8 +676,8 @@ void request_attach(const llvm::json::Object &request) {
auto arguments = request.getObject("arguments");
const lldb::pid_t pid =
GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
- const auto port = GetUnsigned(arguments, "port", invalid_port);
- llvm::StringRef hostname = GetString(arguments, "hostname", "localhost");
+ const auto gdb_remote_port = GetUnsigned(arguments, "gdb-remote-port", invalid_port);
+ llvm::StringRef 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);
@@ -739,7 +739,7 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if ((pid == LLDB_INVALID_PROCESS_ID || port == invalid_port) && 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\"...",
@@ -753,18 +753,18 @@ void request_attach(const llvm::json::Object &request) {
// the launch call and the launch will happen synchronously
g_dap.debugger.SetAsync(false);
if (core_file.empty()) {
- if ((pid != LLDB_INVALID_PROCESS_ID) && (port != invalid_port)) {
+ 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 (port != invalid_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}:", hostname.data());
- connect_url += std::to_string(port);
+ llvm::formatv("connect://{0}:", gdb_remote_hostname.data());
+ connect_url += std::to_string(gdb_remote_port);
g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
error);
} else {
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index c9f05b7c572b4d..62cf69b3597286 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -348,14 +348,14 @@
"type": "string",
"description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds."
},
- "port": {
+ "gdb-remote-port": {
"type": [
"number",
"string"
],
"description": "TCP/IP port to attach to. Specifying both pid and port is an error."
},
- "hostname": {
+ "gdb-remote-hostname": {
"type": "string",
"description": "The hostname to connect to a remote system. The default hostname being used localhost."
},
>From ac84b5334435e976489037a2779785db84f48bf9 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Tue, 4 Jun 2024 11:03:38 -0700
Subject: [PATCH 14/28] [lldb-dap] Added "port" property to vscode "attach"
command - resolved code format issues.
---
.../attach/TestDAP_attachByPortNum.py | 33 +++++++++++--------
lldb/tools/lldb-dap/lldb-dap.cpp | 12 ++++---
2 files changed, 27 insertions(+), 18 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 5c247462350c6e..e49c2eabeb50d9 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -39,8 +39,10 @@ def read(self, size, timeout):
def close(self):
os.close(self._fd)
+
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)
@@ -58,7 +60,7 @@ def set_and_hit_breakpoint(self, continueToExit=True):
def get_debug_server_command_line_args(self):
args = []
if lldbplatformutil.getPlatform() == "linux":
- args = ["gdbserver"]
+ args = ["gdbserver"]
elif lldbplatformutil.getPlatform() == "macosx":
args = ["--listen"]
if lldb.remote_platform:
@@ -72,33 +74,34 @@ def get_debug_server_pipe(self):
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, 30)
+ 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.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)
@@ -118,19 +121,21 @@ def test_by_port_and_pid(self):
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, 30)
+ 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.assertIsNotNone(
+ port, " Failed to read the port number from debug server pipe"
+ )
+
pid = self.process.pid
-
+
response = self.attach(
program=program,
pid=pid,
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index b852a470f6d4bd..ab2b9ced1f33b4 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -676,8 +676,10 @@ void request_attach(const llvm::json::Object &request) {
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);
- llvm::StringRef gdb_remote_hostname = GetString(arguments, "gdb-remote-hostname", "localhost");
+ const auto gdb_remote_port =
+ GetUnsigned(arguments, "gdb-remote-port", invalid_port);
+ llvm::StringRef 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);
@@ -739,7 +741,8 @@ void request_attach(const llvm::json::Object &request) {
return;
}
- if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && 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\"...",
@@ -753,7 +756,8 @@ void request_attach(const llvm::json::Object &request) {
// the launch call and the launch will happen synchronously
g_dap.debugger.SetAsync(false);
if (core_file.empty()) {
- if ((pid != LLDB_INVALID_PROCESS_ID) && (gdb_remote_port != invalid_port)) {
+ 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) {
>From 08f40aaa5975fd2cab6e7fd8c69b66c0ab97dd59 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Mon, 10 Jun 2024 05:27:50 -0700
Subject: [PATCH 15/28] [lldb-dap] Added "port" property to vscode "attach"
command - fixed review comments.
---
.../API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py | 7 -------
lldb/tools/lldb-dap/lldb-dap.cpp | 4 ++--
2 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index e49c2eabeb50d9..12c41be3bc6e07 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -159,12 +159,6 @@ def test_by_invalid_port(self):
program = self.getBuildArtifact("a.out")
port = 0
- 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
)
@@ -173,7 +167,6 @@ def test_by_invalid_port(self):
response["success"],
"The user can't attach with invalid port (%s)" % port,
)
- self.process.terminate()
@skipIfWindows
@skipIfNetBSD
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index ab2b9ced1f33b4..408356350dc21f 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -678,7 +678,7 @@ void request_attach(const llvm::json::Object &request) {
GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
const auto gdb_remote_port =
GetUnsigned(arguments, "gdb-remote-port", invalid_port);
- llvm::StringRef gdb_remote_hostname =
+ const auto gdb_remote_hostname =
GetString(arguments, "gdb-remote-hostname", "localhost");
if (pid != LLDB_INVALID_PROCESS_ID)
attach_info.SetProcessID(pid);
@@ -767,7 +767,7 @@ void request_attach(const llvm::json::Object &request) {
// If the user hasn't provided the hostname property, default localhost
// being used.
std::string connect_url =
- llvm::formatv("connect://{0}:", gdb_remote_hostname.data());
+ 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);
>From 2a0bc63c222ce14602c5e9ebfff1283b84b2aa46 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Mon, 10 Jun 2024 05:54:29 -0700
Subject: [PATCH 16/28] [lldb-dap] Added "port" property to vscode "attach"
command - resolved review comments.
---
.../attach/TestDAP_attachByPortNum.py | 25 ++++---------------
1 file changed, 5 insertions(+), 20 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 12c41be3bc6e07..7e8068af100b4d 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -116,25 +116,11 @@ def test_by_port_and_pid(self):
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"
- )
-
- pid = self.process.pid
+ # 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,
@@ -147,7 +133,6 @@ def test_by_port_and_pid(self):
self.assertFalse(
response["success"], "The user can't specify both pid and port"
)
- self.process.terminate()
@skipIfWindows
@skipIfNetBSD
>From 656c34a5e28bde2e75629553bda17690eef30896 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Mon, 10 Jun 2024 09:49:14 -0700
Subject: [PATCH 17/28] [lldb-dap] Added "port" property to vscode "attach"
command. I have shifted "Pipe" class to common location
"lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py"
---
.../tools/lldb-server/lldbgdbserverutils.py | 146 ++++++++++++++++++
.../attach/TestDAP_attachByPortNum.py | 26 +---
.../commandline/TestGdbRemoteConnection.py | 144 +----------------
3 files changed, 150 insertions(+), 166 deletions(-)
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 d1a4119bac7815..5253839c7405dd 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
index 7e8068af100b4d..fbabc857bd0e0b 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -8,6 +8,7 @@
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
@@ -16,28 +17,6 @@
import threading
import sys
import socket
-import select
-
-
-# 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.
-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)
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
@@ -119,7 +98,8 @@ def test_by_port_and_pid(self):
# 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
+
+ pid = 1354
port = 1234
response = self.attach(
diff --git a/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py b/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py
index 853b7ad5ef2901..ed600d396fad44 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):
>From fe36e25552d79a91c0a1dd0d93fcf23fdcd0ae28 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Mon, 22 Jul 2024 12:40:54 -0700
Subject: [PATCH 18/28] [lldb-dap] Updated README.md for newly added attach
properties.
Added "gdb-remote-port" and "gdb-remote-hostname" attach properties usage in
README.md.
This was missed in PR https://github.com/llvm/llvm-project/pull/91570
---
lldb/tools/lldb-dap/README.md | 31 +++++++++++++++++++++++++++++++
lldb/tools/lldb-dap/package.json | 2 +-
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md
index 8ecbaf7ce98163..11a14d29ab51e2 100644
--- a/lldb/tools/lldb-dap/README.md
+++ b/lldb/tools/lldb-dap/README.md
@@ -157,6 +157,20 @@ locally on port `2345`.
}
```
+You can also use the `gdb-remote-port` parameter to send an attach request
+to a debug server running on the current machine,
+instead of using the custom command `attachCommands`.
+
+```javascript
+{
+ "name": "Local Debug Server",
+ "type": "lldb-dap",
+ "request": "attach",
+ "program": "/tmp/a.out",
+ "gdb-remote-port": 2345,
+}
+```
+
#### Connect to a Debug Server on Another Machine
This connects to a debug server running on another machine with hostname
@@ -173,6 +187,23 @@ port `5678` of that other machine.
}
```
+You can also use the `gdb-remote-hostname` and `gdb-remote-port` parameters
+to send an attach request to a debug server running on a different machine,
+instead of custom command `attachCommands`.
+The default hostname being used `localhost`.
+
+
+```javascript
+{
+ "name": "Local Debug Server",
+ "type": "lldb-dap",
+ "request": "attach",
+ "program": "/tmp/a.out",
+ "gdb-remote-port": 5678,
+ "gdb-remote-hostname": "hostname",
+}
+```
+
## Custom debugger commands
The `lldb-dap` tool includes additional custom commands to support the Debug
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index fd5de30ed28dd7..66d9efb37d9810 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -353,7 +353,7 @@
"number",
"string"
],
- "description": "TCP/IP port to attach to. Specifying both pid and port is an error."
+ "description": "TCP/IP port to attach to a remote systerm. Specifying both pid and port is an error."
},
"gdb-remote-hostname": {
"type": "string",
>From 10d16b8b2109c3152e1f5d9cee628e92ef3b5e29 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Tue, 23 Jul 2024 23:34:00 -0700
Subject: [PATCH 19/28] [lldb-dap] Updated README.md for newly added attach
properties. #99926 Corrected spelling mistakes.
---
lldb/tools/lldb-dap/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index 66d9efb37d9810..4ff9c2287ac7b2 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -353,7 +353,7 @@
"number",
"string"
],
- "description": "TCP/IP port to attach to a remote systerm. Specifying both pid and port is an error."
+ "description": "TCP/IP port to attach to a remote system. Specifying both pid and port is an error."
},
"gdb-remote-hostname": {
"type": "string",
>From 58affe702b485d160d97be05051bce067a3972a8 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <sellendu at hu-sellendu-lv.qualcomm.com>
Date: Wed, 24 Jul 2024 23:48:02 -0700
Subject: [PATCH 20/28] [lldb-dap] Bump the version to 0.2.3
Bump the lldb-dap version to 0.2.3.
---
lldb/tools/lldb-dap/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index 4ff9c2287ac7b2..97e4efe7bac19d 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -1,7 +1,7 @@
{
"name": "lldb-dap",
"displayName": "LLDB DAP",
- "version": "0.2.2",
+ "version": "0.2.3",
"publisher": "llvm-vs-code-extensions",
"homepage": "https://lldb.llvm.org",
"description": "LLDB debugging from VSCode",
>From 7e221825b8537f69e19cf366c446d9a09a4768fe Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Tue, 20 Aug 2024 12:21:12 -0700
Subject: [PATCH 21/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Added support for "supportsInstructionBreakpoints" capability and
now it this command is triggered when we set instruction breakpoint. We need
this support as part of enabling disassembly view debugging. Following
features should work as part of this feature enablement:
1. Settings breakpoints in disassembly view: Unsetting the breakpoint is not happening from the disassembly view. Currently we need to unset breakpoint manually from the breakpoint List. Multiple breakpoints are getting set for the same $
2. Step over, step into, continue in the disassembly view
The format for DisassembleRequest and DisassembleResponse at https://raw.githubusercontent.com/microsoft/vscode/master/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts .
---
.../test/tools/lldb-dap/dap_server.py | 14 ++
.../lldb-dap/instruction-breakpoint/Makefile | 6 +
.../TestDAP_instruction_breakpoint.py | 76 ++++++++
.../lldb-dap/instruction-breakpoint/main.cpp | 18 ++
lldb/tools/lldb-dap/CMakeLists.txt | 1 +
lldb/tools/lldb-dap/DAP.h | 2 +
lldb/tools/lldb-dap/DAPForward.h | 1 +
lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 27 +++
lldb/tools/lldb-dap/InstructionBreakpoint.h | 36 ++++
lldb/tools/lldb-dap/JSONUtils.cpp | 96 +++++++++
lldb/tools/lldb-dap/JSONUtils.h | 11 ++
lldb/tools/lldb-dap/lldb-dap.cpp | 184 +++++++++++++++++-
12 files changed, 470 insertions(+), 2 deletions(-)
create mode 100644 lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile
create mode 100644 lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
create mode 100644 lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
create mode 100644 lldb/tools/lldb-dap/InstructionBreakpoint.cpp
create mode 100644 lldb/tools/lldb-dap/InstructionBreakpoint.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 a324af57b61df3..98b599608946c0 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
@@ -1095,6 +1095,20 @@ def terminate(self):
self.send.close()
# self.recv.close()
+ def request_setInstructionBreakpoints(self, memory_reference=[]):
+ breakpoints = []
+ for i in memory_reference:
+ args_dict = {
+ "instructionReference": i,
+ }
+ breakpoints.append(args_dict)
+ args_dict = {"breakpoints": breakpoints}
+ command_dict = {
+ "command": "setInstructionBreakpoints",
+ "type": "request",
+ "arguments": args_dict,
+ }
+ return self.send_recv(command_dict)
class DebugAdaptorServer(DebugCommunication):
def __init__(
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile
new file mode 100644
index 00000000000000..714cd70b0bd35c
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile
@@ -0,0 +1,6 @@
+CXX_SOURCES := main-copy.cpp
+CXXFLAGS_EXTRAS := -O1 -g
+include Makefile.rules
+
+main-copy.cpp: main.cpp
+ cp -f $< $@
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
new file mode 100644
index 00000000000000..06a56e3861d4a4
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
@@ -0,0 +1,76 @@
+import dap_server
+import shutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbdap_testcase
+import os
+import lldb
+
+
+class TestDAP_InstructionBreakpointTestCase(lldbdap_testcase.DAPTestCaseBase):
+
+ NO_DEBUG_INFO_TESTCASE = True
+ def setUp(self):
+ lldbdap_testcase.DAPTestCaseBase.setUp(self)
+
+ self.main_basename = 'main-copy.cpp'
+ self.main_path = os.path.realpath(self.getBuildArtifact(self.main_basename))
+
+ def test_instruction_breakpoint(self):
+ self.build()
+ self.instruction_breakpoint_test()
+
+ def instruction_breakpoint_test(self):
+ """Sample test to ensure SBFrame::Disassemble produces SOME output"""
+ # Create a target by the debugger.
+ target = self.createTestTarget()
+
+ #build target and create DAP
+ #self.build_and_create_debug_adaptor()
+
+ main_line = line_number('main.cpp', 'breakpoint 1')
+
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+
+ # Set source breakpoint 1
+ response = self.dap_server.request_setBreakpoints(self.main_path, [main_line])
+ breakpoints = response['body']['breakpoints']
+ self.assertEquals(len(breakpoints), 1)
+ breakpoint = breakpoints[0]
+ self.assertEqual(breakpoint['line'], main_line, "incorrect breakpoint source line")
+ self.assertTrue(breakpoint['verified'], "breakpoint is not verified")
+ self.assertEqual(self.main_basename, breakpoint['source']['name'], "incorrect source name")
+ self.assertEqual(self.main_path, breakpoint['source']['path'], "incorrect source file path")
+ other_breakpoint_id = breakpoint['id']
+
+ # Continue and then verifiy the breakpoint
+ self.dap_server.request_continue()
+ self.verify_breakpoint_hit([other_breakpoint_id])
+
+ # now we check the stack trace making sure that we got mapped source paths
+ frames = self.dap_server.request_stackTrace()['body']['stackFrames']
+ intstructionPointerReference = []
+ setIntstructionBreakpoints = []
+ intstructionPointerReference.append(frames[0]['instructionPointerReference'])
+ self.assertEqual(frames[0]['source']['name'], self.main_basename, "incorrect source name")
+ self.assertEqual(frames[0]['source']['path'], self.main_path, "incorrect source file path")
+
+ # Check disassembly view
+ instruction = self.disassemble(frameIndex=0)
+ self.assertEqual(instruction["address"], intstructionPointerReference[0], "current breakpoint reference is not in the disaasembly view")
+
+ # Get next instruction address to set instruction breakpoint
+ disassembled_instruction_list = self.dap_server.disassembled_instructions
+ instruction_addr_list = list(disassembled_instruction_list.keys())
+ index = instruction_addr_list.index(intstructionPointerReference[0])
+ if len(instruction_addr_list) >= (index + 1):
+ next_inst_addr = int(instruction_addr_list[index + 1], 16)
+ if next_inst_addr is not 0:
+ setIntstructionBreakpoints.append(hex(next_inst_addr))
+ instruction_breakpoint_response = self.dap_server.request_setInstructionBreakpoints(setIntstructionBreakpoints)
+ inst_breakpoints = instruction_breakpoint_response['body']['breakpoints']
+ self.assertEqual(inst_breakpoints[0]['instructionReference'], hex(next_inst_addr), "Instruction breakpoint has not been resolved or failed to relocate the instruction breakpoint")
+ self.dap_server.request_continue()
+ self.verify_breakpoint_hit([inst_breakpoints[0]['id']])
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
new file mode 100644
index 00000000000000..c4b72144b7110b
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int function(int x) {
+
+ if (x == 0)// breakpoint 1
+ return x;
+
+ if ((x % 2) != 0)
+ return x;
+ else
+ return function(x-1) + x;
+}
+
+int main(int argc, char const *argv[]) {
+ int n = function(2);
+ return n;
+}
\ No newline at end of file
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index f8f0d86453f585..d68098bf7b3266 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -38,6 +38,7 @@ add_lldb_tool(lldb-dap
SourceBreakpoint.cpp
DAP.cpp
Watchpoint.cpp
+ InstructionBreakpoint.cpp
LINK_LIBS
liblldb
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 7828272aa15a7d..8be5a5a95aa38a 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -68,6 +68,8 @@ namespace lldb_dap {
typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
+typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>
+ InstructionBreakpointMap;
enum class OutputType { Console, Stdout, Stderr, Telemetry };
enum DAPBroadcasterBits {
diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h
index 8c79488fae8dbf..159d999a63c820 100644
--- a/lldb/tools/lldb-dap/DAPForward.h
+++ b/lldb/tools/lldb-dap/DAPForward.h
@@ -15,6 +15,7 @@ struct ExceptionBreakpoint;
struct FunctionBreakpoint;
struct SourceBreakpoint;
struct Watchpoint;
+struct InstructionBreakpoint;
} // namespace lldb_dap
namespace lldb {
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
new file mode 100644
index 00000000000000..d4d8db5c51ec15
--- /dev/null
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
@@ -0,0 +1,27 @@
+//===-- InstructionBreakpoint.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 "InstructionBreakpoint.h"
+#include "DAP.h"
+
+namespace lldb_dap {
+
+// Instruction Breakpoint
+InstructionBreakpoint::InstructionBreakpoint(const llvm::json::Object &obj)
+ : Breakpoint(obj), instructionReference(LLDB_INVALID_ADDRESS), id(0),
+ offset(GetSigned(obj, "offset", 0)) {
+ GetString(obj, "instructionReference").getAsInteger(0, instructionReference);
+ instructionReference += offset;
+}
+
+void InstructionBreakpoint::SetInstructionBreakpoint() {
+ bp = g_dap.target.BreakpointCreateByAddress(instructionReference);
+ id = bp.GetID();
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h
new file mode 100644
index 00000000000000..a9b94f648aeab7
--- /dev/null
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h
@@ -0,0 +1,36 @@
+//===-- InstructionBreakpoint.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_INSTRUCTIONBREAKPOINT_H
+#define LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H
+
+#include "Breakpoint.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_dap {
+
+// Instruction Breakpoint
+struct InstructionBreakpoint : public Breakpoint {
+
+ lldb::addr_t instructionReference;
+ int32_t id;
+ int32_t offset;
+
+ InstructionBreakpoint()
+ : Breakpoint(), instructionReference(LLDB_INVALID_ADDRESS), id(0),
+ offset(0) {}
+ InstructionBreakpoint(const llvm::json::Object &obj);
+
+ // Set instruction breakpoint in LLDB as a new breakpoint
+ void SetInstructionBreakpoint();
+};
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index a8b85f55939e17..5a2a6c044ea4b1 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -766,6 +766,102 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
return llvm::json::Value(std::move(object));
}
+// Response to `setInstructionBreakpoints` request.
+// "Breakpoint": {
+// "type": "object",
+// "description": "Response to `setInstructionBreakpoints` request.",
+// "properties": {
+// "id": {
+// "type": "number",
+// "description": "The identifier for the breakpoint. It is needed if
+// breakpoint events are used to update or remove breakpoints."
+// },
+// "verified": {
+// "type": "boolean",
+// "description": "If true, the breakpoint could be set (but not
+// necessarily at the desired location."
+// },
+// "message": {
+// "type": "string",
+// "description": "A message about the state of the breakpoint.
+// This is shown to the user and can be used to explain why a breakpoint
+// could not be verified."
+// },
+// "source": {
+// "type": "Source",
+// "description": "The source where the breakpoint is located."
+// },
+// "line": {
+// "type": "number",
+// "description": "The start line of the actual range covered by the
+// breakpoint."
+// },
+// "column": {
+// "type": "number",
+// "description": "The start column of the actual range covered by the
+// breakpoint."
+// },
+// "endLine": {
+// "type": "number",
+// "description": "The end line of the actual range covered by the
+// breakpoint."
+// },
+// "endColumn": {
+// "type": "number",
+// "description": "The end column of the actual range covered by the
+// breakpoint. If no end line is given, then the end column is assumed to
+// be in the start line."
+// },
+// "instructionReference": {
+// "type": "string",
+// "description": "A memory reference to where the breakpoint is set."
+// },
+// "offset": {
+// "type": "number",
+// "description": "The offset from the instruction reference.
+// This can be negative."
+// },
+// },
+// "required": [ "id", "verified", "line"]
+// }
+llvm::json::Value CreateInstructionBreakpoint(lldb::SBBreakpoint &bp) {
+ llvm::json::Object object;
+ if (!bp.IsValid()) {
+ return llvm::json::Value(std::move(object));
+ }
+ object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
+ object.try_emplace("id", bp.GetID());
+
+ lldb::SBBreakpointLocation bp_loc;
+ const auto num_locs = bp.GetNumLocations();
+ for (size_t i = 0; i < num_locs; ++i) {
+ bp_loc = bp.GetLocationAtIndex(i);
+ if (bp_loc.IsResolved())
+ break;
+ }
+ // If not locations are resolved, use the first location.
+ if (!bp_loc.IsResolved())
+ bp_loc = bp.GetLocationAtIndex(0);
+ auto bp_addr = bp_loc.GetAddress();
+
+ lldb::addr_t address_load = bp_addr.GetLoadAddress(g_dap.target);
+ std::string address_hex;
+ llvm::raw_string_ostream addr_strm(address_hex);
+ if (address_load != LLDB_INVALID_ADDRESS) {
+ addr_strm << llvm::format_hex(address_load, 0);
+ addr_strm.flush();
+ object.try_emplace("instructionReference", address_hex);
+ }
+
+ if (bp_addr.IsValid()) {
+ auto line_entry = bp_addr.GetLineEntry();
+ const auto line = line_entry.GetLine();
+ if (line != UINT32_MAX)
+ object.try_emplace("line", line);
+ }
+ return llvm::json::Value(std::move(object));
+}
+
// "Thread": {
// "type": "object",
// "description": "A Thread",
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 1515f5ba2e5f4d..693f35ee07e5f6 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -322,6 +322,17 @@ llvm::json::Value CreateSource(llvm::StringRef source_path);
/// definition outlined by Microsoft.
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame);
+/// Create a "instruction" object for a LLDB disassemble object as described in
+/// the Visual Studio Code debug adaptor definition.
+///
+/// \param[in] bp
+/// The LLDB instruction object used to populate the disassembly
+/// instruction.
+/// \return
+/// A "Scope" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateInstructionBreakpoint(lldb::SBBreakpoint &bp);
+
/// Create a "Thread" object for a LLDB thread object.
///
/// This function will fill in the following keys in the returned
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index f50a6c17310739..8a0c99393a0838 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1627,12 +1627,12 @@ void request_initialize(const llvm::json::Object &request) {
"lldb-dap", "Commands for managing lldb-dap.");
if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
cmd.AddCommand(
- "startDebugging", new StartDebuggingRequestHandler(),
+ "startDebugging", &g_dap.start_debugging_request_handler,
"Sends a startDebugging request from the debug adapter to the client "
"to start a child debug session of the same type as the caller.");
}
cmd.AddCommand(
- "repl-mode", new ReplModeRequestHandler(),
+ "repl-mode", &g_dap.repl_mode_request_handler,
"Get or set the repl behavior of lldb-dap evaluation requests.");
g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction);
@@ -1718,6 +1718,8 @@ void request_initialize(const llvm::json::Object &request) {
body.try_emplace("supportsLogPoints", true);
// The debug adapter supports data watchpoints.
body.try_emplace("supportsDataBreakpoints", true);
+ // The debug adapter support for instruction breakpoint.
+ body.try_emplace("supportsInstructionBreakpoints", true);
// Put in non-DAP specification lldb specific information.
llvm::json::Object lldb_json;
@@ -4046,6 +4048,181 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
g_dap.SendJSON(llvm::json::Value(std::move(response)));
}
+// SetInstructionBreakpoints request; value of command field is
+// 'setInstructionBreakpoints'. Replaces all existing instruction breakpoints.
+// Typically, instruction breakpoints would be set from a disassembly window. To
+// clear all instruction breakpoints, specify an empty array. When an
+// instruction breakpoint is hit, a `stopped` event (with reason `instruction
+// breakpoint`) is generated. Clients should only call this request if the
+// corresponding capability `supportsInstructionBreakpoints` is true. interface
+// SetInstructionBreakpointsRequest extends Request {
+// command: 'setInstructionBreakpoints';
+// arguments: SetInstructionBreakpointsArguments;
+// }
+// interface SetInstructionBreakpointsArguments {
+// The instruction references of the breakpoints
+// breakpoints: InstructionBreakpoint[];
+// }
+// "InstructionBreakpoint ": {
+// "type": "object",
+// "description": "Properties of a breakpoint passed to the
+// setInstructionBreakpoints request.", "properties": {
+// "instructionReference": {
+// "type": "string",
+// "description": "The instruction reference of the breakpoint.
+// This should be a memory or instruction pointer reference from an
+// EvaluateResponse, Variable, StackFrame, GotoTarget, or Breakpoint."
+// },
+// "offset": {
+// "type": "number",
+// "description": "The offset from the instruction reference.
+// This can be negative."
+// },
+// "condition": {
+// "type": "string",
+// "description": "An expression for conditional breakpoints.
+// It is only honored by a debug adapter if the corresponding capability
+// supportsConditionalBreakpoints` is true."
+// },
+// "hitCondition": {
+// "type": "string",
+// "description": "An expression that controls how many hits of the
+// breakpoint are ignored. The debug adapter is expected to interpret the
+// expression as needed. The attribute is only honored by a debug adapter
+// if the corresponding capability `supportsHitConditionalBreakpoints` is
+// true."
+// },
+// }
+// interface SetInstructionBreakpointsResponse extends Response {
+// body: {
+// Information about the breakpoints. The array elements correspond to the
+// elements of the `breakpoints` array.
+// breakpoints: Breakpoint[];
+// };
+// }
+// Response to `setInstructionBreakpoints` request.
+// "Breakpoint": {
+// "type": "object",
+// "description": "Response to `setInstructionBreakpoints` request.",
+// "properties": {
+// "id": {
+// "type": "number",
+// "description": "The identifier for the breakpoint. It is needed if
+// breakpoint events are used to update or remove breakpoints."
+// },
+// "verified": {
+// "type": "boolean",
+// "description": "If true, the breakpoint could be set (but not
+// necessarily at the desired location."
+// },
+// "message": {
+// "type": "string",
+// "description": "A message about the state of the breakpoint.
+// This is shown to the user and can be used to explain why a breakpoint
+// could not be verified."
+// },
+// "source": {
+// "type": "Source",
+// "description": "The source where the breakpoint is located."
+// },
+// "line": {
+// "type": "number",
+// "description": "The start line of the actual range covered by the
+// breakpoint."
+// },
+// "column": {
+// "type": "number",
+// "description": "The start column of the actual range covered by the
+// breakpoint."
+// },
+// "endLine": {
+// "type": "number",
+// "description": "The end line of the actual range covered by the
+// breakpoint."
+// },
+// "endColumn": {
+// "type": "number",
+// "description": "The end column of the actual range covered by the
+// breakpoint. If no end line is given, then the end column is assumed to
+// be in the start line."
+// },
+// "instructionReference": {
+// "type": "string",
+// "description": "A memory reference to where the breakpoint is set."
+// },
+// "offset": {
+// "type": "number",
+// "description": "The offset from the instruction reference.
+// This can be negative."
+// },
+// },
+// "required": [ "id", "verified", "line"]
+// }
+void request_setInstructionBreakpoints(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ llvm::json::Array response_breakpoints;
+ llvm::json::Object body;
+ FillResponse(request, response);
+
+ auto arguments = request.getObject("arguments");
+ auto breakpoints = arguments->getArray("breakpoints");
+
+ // It holds active instruction brealpoint list received from DAP.
+ InstructionBreakpointMap request_ibp;
+ if (breakpoints) {
+ for (const auto &bp : *breakpoints) {
+ auto bp_obj = bp.getAsObject();
+ if (bp_obj) {
+ // Read instruction breakpoint request.
+ InstructionBreakpoint inst_bp(*bp_obj);
+ // Store them into map for reference.
+ request_ibp[inst_bp.instructionReference] = std::move(inst_bp);
+ }
+ }
+
+ // Store removed instruction breakpoint list to delete them further.
+ std::vector<lldb::addr_t> removed_ibp;
+
+ // Iterate previouse active instruction breakpoint list.
+ for (auto &prev_ibp : g_dap.instruction_breakpoints) {
+ // Find previouse instruction breakpoint reference address in newly
+ // received instruction breakpoint list.
+ auto inst_reference = request_ibp.find(prev_ibp.first);
+ // Request for remove and delete the breakpoint, if the prev instruction
+ // breakpoint ID is not available in active instrcation breakpoint list.
+ // Means delete removed breakpoint instance.
+ if (inst_reference == request_ibp.end()) {
+ g_dap.target.BreakpointDelete(prev_ibp.second.id);
+ removed_ibp.push_back(prev_ibp.first);
+ } else {
+ // Instead of recreating breakpoint instance, update the breakpoint if
+ // there are any conditional changes.
+ prev_ibp.second.UpdateBreakpoint(inst_reference->second);
+ request_ibp.erase(inst_reference);
+ response_breakpoints.emplace_back(
+ CreateInstructionBreakpoint(prev_ibp.second.bp));
+ }
+ }
+ // Update Prev instruction breakpoint list.
+ for (const auto &name : removed_ibp) {
+ g_dap.instruction_breakpoints.erase(name);
+ }
+
+ for (auto &req_bpi : request_ibp) {
+ // Add this breakpoint info to the response
+ g_dap.instruction_breakpoints[req_bpi.first] = std::move(req_bpi.second);
+ InstructionBreakpoint &new_bp =
+ g_dap.instruction_breakpoints[req_bpi.first];
+ new_bp.SetInstructionBreakpoint();
+ response_breakpoints.emplace_back(CreateInstructionBreakpoint(new_bp.bp));
+ }
+ }
+
+ body.try_emplace("breakpoints", std::move(response_breakpoints));
+ response.try_emplace("body", std::move(body));
+ g_dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
void RegisterRequestCallbacks() {
g_dap.RegisterRequestCallback("attach", request_attach);
g_dap.RegisterRequestCallback("completions", request_completions);
@@ -4078,6 +4255,9 @@ void RegisterRequestCallbacks() {
g_dap.RegisterRequestCallback("threads", request_threads);
g_dap.RegisterRequestCallback("variables", request_variables);
g_dap.RegisterRequestCallback("disassemble", request_disassemble);
+ // Instruction breapoint request
+ g_dap.RegisterRequestCallback("setInstructionBreakpoints",
+ request_setInstructionBreakpoints);
// Custom requests
g_dap.RegisterRequestCallback("compileUnits", request_compileUnits);
g_dap.RegisterRequestCallback("modules", request_modules);
>From ffe8dbcbaf6fec2602f4d1330833e6540c493314 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Tue, 20 Aug 2024 12:42:57 -0700
Subject: [PATCH 22/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Resolved code format issues.
---
.../TestDAP_instruction_breakpoint.py | 64 +++++++++++++------
.../lldb-dap/instruction-breakpoint/main.cpp | 4 +-
2 files changed, 45 insertions(+), 23 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
index 06a56e3861d4a4..24d55404778c9d 100644
--- a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
@@ -9,12 +9,12 @@
class TestDAP_InstructionBreakpointTestCase(lldbdap_testcase.DAPTestCaseBase):
-
NO_DEBUG_INFO_TESTCASE = True
+
def setUp(self):
lldbdap_testcase.DAPTestCaseBase.setUp(self)
- self.main_basename = 'main-copy.cpp'
+ self.main_basename = "main-copy.cpp"
self.main_path = os.path.realpath(self.getBuildArtifact(self.main_basename))
def test_instruction_breakpoint(self):
@@ -26,40 +26,52 @@ def instruction_breakpoint_test(self):
# Create a target by the debugger.
target = self.createTestTarget()
- #build target and create DAP
- #self.build_and_create_debug_adaptor()
-
- main_line = line_number('main.cpp', 'breakpoint 1')
+ main_line = line_number("main.cpp", "breakpoint 1")
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
# Set source breakpoint 1
response = self.dap_server.request_setBreakpoints(self.main_path, [main_line])
- breakpoints = response['body']['breakpoints']
+ breakpoints = response["body"]["breakpoints"]
self.assertEquals(len(breakpoints), 1)
breakpoint = breakpoints[0]
- self.assertEqual(breakpoint['line'], main_line, "incorrect breakpoint source line")
- self.assertTrue(breakpoint['verified'], "breakpoint is not verified")
- self.assertEqual(self.main_basename, breakpoint['source']['name'], "incorrect source name")
- self.assertEqual(self.main_path, breakpoint['source']['path'], "incorrect source file path")
- other_breakpoint_id = breakpoint['id']
+ self.assertEqual(
+ breakpoint["line"], main_line, "incorrect breakpoint source line"
+ )
+ self.assertTrue(breakpoint["verified"], "breakpoint is not verified")
+ self.assertEqual(
+ self.main_basename, breakpoint["source"]["name"], "incorrect source name"
+ )
+ self.assertEqual(
+ self.main_path, breakpoint["source"]["path"], "incorrect source file path"
+ )
+ other_breakpoint_id = breakpoint["id"]
# Continue and then verifiy the breakpoint
self.dap_server.request_continue()
self.verify_breakpoint_hit([other_breakpoint_id])
# now we check the stack trace making sure that we got mapped source paths
- frames = self.dap_server.request_stackTrace()['body']['stackFrames']
+ frames = self.dap_server.request_stackTrace()["body"]["stackFrames"]
intstructionPointerReference = []
setIntstructionBreakpoints = []
- intstructionPointerReference.append(frames[0]['instructionPointerReference'])
- self.assertEqual(frames[0]['source']['name'], self.main_basename, "incorrect source name")
- self.assertEqual(frames[0]['source']['path'], self.main_path, "incorrect source file path")
+ intstructionPointerReference.append(frames[0]["instructionPointerReference"])
+ self.assertEqual(
+ frames[0]["source"]["name"], self.main_basename, "incorrect source name"
+ )
+ self.assertEqual(
+ frames[0]["source"]["path"], self.main_path, "incorrect source file path"
+ )
+
# Check disassembly view
instruction = self.disassemble(frameIndex=0)
- self.assertEqual(instruction["address"], intstructionPointerReference[0], "current breakpoint reference is not in the disaasembly view")
+ self.assertEqual(
+ instruction["address"],
+ intstructionPointerReference[0],
+ "current breakpoint reference is not in the disaasembly view",
+ )
# Get next instruction address to set instruction breakpoint
disassembled_instruction_list = self.dap_server.disassembled_instructions
@@ -69,8 +81,18 @@ def instruction_breakpoint_test(self):
next_inst_addr = int(instruction_addr_list[index + 1], 16)
if next_inst_addr is not 0:
setIntstructionBreakpoints.append(hex(next_inst_addr))
- instruction_breakpoint_response = self.dap_server.request_setInstructionBreakpoints(setIntstructionBreakpoints)
- inst_breakpoints = instruction_breakpoint_response['body']['breakpoints']
- self.assertEqual(inst_breakpoints[0]['instructionReference'], hex(next_inst_addr), "Instruction breakpoint has not been resolved or failed to relocate the instruction breakpoint")
+ instruction_breakpoint_response = (
+ self.dap_server.request_setInstructionBreakpoints(
+ setIntstructionBreakpoints
+ )
+ )
+ inst_breakpoints = instruction_breakpoint_response["body"][
+ "breakpoints"
+ ]
+ self.assertEqual(
+ inst_breakpoints[0]["instructionReference"],
+ hex(next_inst_addr),
+ "Instruction breakpoint has not been resolved or failed to relocate the instruction breakpoint",
+ )
self.dap_server.request_continue()
- self.verify_breakpoint_hit([inst_breakpoints[0]['id']])
+ self.verify_breakpoint_hit([inst_breakpoints[0]["id"]])
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
index c4b72144b7110b..3c710d64171570 100644
--- a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
@@ -3,13 +3,13 @@
int function(int x) {
- if (x == 0)// breakpoint 1
+ if (x == 0) // breakpoint 1
return x;
if ((x % 2) != 0)
return x;
else
- return function(x-1) + x;
+ return function(x - 1) + x;
}
int main(int argc, char const *argv[]) {
>From 4d87a93cb74806e80798a1083a841246b3c53804 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Tue, 20 Aug 2024 12:53:32 -0700
Subject: [PATCH 23/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Resolved code format issues.
---
.../instruction-breakpoint/TestDAP_instruction_breakpoint.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
index 24d55404778c9d..ed1a6bf250c761 100644
--- a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
@@ -64,7 +64,6 @@ def instruction_breakpoint_test(self):
frames[0]["source"]["path"], self.main_path, "incorrect source file path"
)
-
# Check disassembly view
instruction = self.disassemble(frameIndex=0)
self.assertEqual(
>From 18bcb037a91270825926cdd3a611c7b88117e80e Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Tue, 20 Aug 2024 15:05:50 -0700
Subject: [PATCH 24/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. The following changes were committed by mistake, so they have been
reverted. "startDebugging", &g_dap.start_debugging_request_handler,
"repl-mode", &g_dap.repl_mode_request_handler,
---
lldb/tools/lldb-dap/lldb-dap.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 8a0c99393a0838..7c0799eaadfb6d 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1627,12 +1627,12 @@ void request_initialize(const llvm::json::Object &request) {
"lldb-dap", "Commands for managing lldb-dap.");
if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
cmd.AddCommand(
- "startDebugging", &g_dap.start_debugging_request_handler,
+ "startDebugging", new StartDebuggingRequestHandler(),
"Sends a startDebugging request from the debug adapter to the client "
"to start a child debug session of the same type as the caller.");
}
cmd.AddCommand(
- "repl-mode", &g_dap.repl_mode_request_handler,
+ "repl-mode", new ReplModeRequestHandler(),
"Get or set the repl behavior of lldb-dap evaluation requests.");
g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction);
>From 9893985131ff336327dd431bdfdeaf62fcda5565 Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Tue, 20 Aug 2024 21:01:08 -0700
Subject: [PATCH 25/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Few changes were missed in lldb/tools/lldb-dap/DAP.h, so added in
this version.
---
lldb/tools/lldb-dap/DAP.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 8be5a5a95aa38a..189301e28cf74d 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -54,6 +54,7 @@
#include "ExceptionBreakpoint.h"
#include "FunctionBreakpoint.h"
#include "IOStream.h"
+#include "InstructionBreakpoint.h"
#include "ProgressEvent.h"
#include "RunInTerminal.h"
#include "SourceBreakpoint.h"
@@ -158,6 +159,7 @@ struct DAP {
std::unique_ptr<std::ofstream> log;
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
FunctionBreakpointMap function_breakpoints;
+ InstructionBreakpointMap instruction_breakpoints;
std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
llvm::once_flag init_exception_breakpoints_flag;
std::vector<std::string> pre_init_commands;
>From e0101c12bb6f11f06a00efff9849f276903f623e Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Thu, 22 Aug 2024 09:10:42 -0700
Subject: [PATCH 26/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Resolved review comments.
---
.../lldb-dap/instruction-breakpoint/Makefile | 2 +-
lldb/tools/lldb-dap/lldb-dap.cpp | 287 +++++++++++-------
2 files changed, 184 insertions(+), 105 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile
index 714cd70b0bd35c..697527c4e55223 100644
--- a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile
@@ -1,5 +1,5 @@
CXX_SOURCES := main-copy.cpp
-CXXFLAGS_EXTRAS := -O1 -g
+CXXFLAGS_EXTRAS := -O0 -g
include Makefile.rules
main-copy.cpp: main.cpp
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 7c0799eaadfb6d..61f06e70df28fe 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -4048,116 +4048,195 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
g_dap.SendJSON(llvm::json::Value(std::move(response)));
}
-// SetInstructionBreakpoints request; value of command field is
-// 'setInstructionBreakpoints'. Replaces all existing instruction breakpoints.
-// Typically, instruction breakpoints would be set from a disassembly window. To
-// clear all instruction breakpoints, specify an empty array. When an
-// instruction breakpoint is hit, a `stopped` event (with reason `instruction
-// breakpoint`) is generated. Clients should only call this request if the
-// corresponding capability `supportsInstructionBreakpoints` is true. interface
-// SetInstructionBreakpointsRequest extends Request {
-// command: 'setInstructionBreakpoints';
-// arguments: SetInstructionBreakpointsArguments;
-// }
-// interface SetInstructionBreakpointsArguments {
-// The instruction references of the breakpoints
-// breakpoints: InstructionBreakpoint[];
-// }
-// "InstructionBreakpoint ": {
-// "type": "object",
-// "description": "Properties of a breakpoint passed to the
-// setInstructionBreakpoints request.", "properties": {
-// "instructionReference": {
-// "type": "string",
-// "description": "The instruction reference of the breakpoint.
-// This should be a memory or instruction pointer reference from an
-// EvaluateResponse, Variable, StackFrame, GotoTarget, or Breakpoint."
-// },
-// "offset": {
-// "type": "number",
-// "description": "The offset from the instruction reference.
-// This can be negative."
-// },
-// "condition": {
-// "type": "string",
-// "description": "An expression for conditional breakpoints.
-// It is only honored by a debug adapter if the corresponding capability
-// supportsConditionalBreakpoints` is true."
-// },
-// "hitCondition": {
-// "type": "string",
-// "description": "An expression that controls how many hits of the
-// breakpoint are ignored. The debug adapter is expected to interpret the
-// expression as needed. The attribute is only honored by a debug adapter
-// if the corresponding capability `supportsHitConditionalBreakpoints` is
-// true."
-// },
-// }
-// interface SetInstructionBreakpointsResponse extends Response {
-// body: {
-// Information about the breakpoints. The array elements correspond to the
-// elements of the `breakpoints` array.
-// breakpoints: Breakpoint[];
-// };
-// }
-// Response to `setInstructionBreakpoints` request.
-// "Breakpoint": {
-// "type": "object",
-// "description": "Response to `setInstructionBreakpoints` request.",
-// "properties": {
-// "id": {
-// "type": "number",
-// "description": "The identifier for the breakpoint. It is needed if
-// breakpoint events are used to update or remove breakpoints."
-// },
-// "verified": {
-// "type": "boolean",
-// "description": "If true, the breakpoint could be set (but not
-// necessarily at the desired location."
-// },
-// "message": {
-// "type": "string",
-// "description": "A message about the state of the breakpoint.
-// This is shown to the user and can be used to explain why a breakpoint
-// could not be verified."
-// },
-// "source": {
-// "type": "Source",
-// "description": "The source where the breakpoint is located."
-// },
-// "line": {
-// "type": "number",
-// "description": "The start line of the actual range covered by the
-// breakpoint."
+// "SetInstructionBreakpointsRequest" : {
+// "allOf" : [
+// {"$ref" : "#/definitions/Request"}, {
+// "type" : "object",
+// "description" :
+// "Replaces all existing instruction breakpoints. Typically, "
+// "instruction breakpoints would be set from a disassembly window. "
+// "\nTo clear all instruction breakpoints, specify an empty "
+// "array.\nWhen an instruction breakpoint is hit, a `stopped` event "
+// "(with reason `instruction breakpoint`) is generated.\nClients "
+// "should only call this request if the corresponding capability "
+// "`supportsInstructionBreakpoints` is true.",
+// "properties" : {
+// "command" : {"type" : "string", "enum" :
+// ["setInstructionBreakpoints"]}, "arguments" :
+// {"$ref" : "#/definitions/SetInstructionBreakpointsArguments"}
+// },
+// "required" : [ "command", "arguments" ]
+// }
+// ]
+// },
+// "SetInstructionBreakpointsArguments"
+// : {
+// "type" : "object",
+// "description" : "Arguments for `setInstructionBreakpoints` request",
+// "properties" : {
+// "breakpoints" : {
+// "type" : "array",
+// "items" : {"$ref" : "#/definitions/InstructionBreakpoint"},
+// "description" : "The instruction references of the breakpoints"
+// }
+// },
+// "required" : ["breakpoints"]
// },
-// "column": {
-// "type": "number",
-// "description": "The start column of the actual range covered by the
-// breakpoint."
+// "SetInstructionBreakpointsResponse"
+// : {
+// "allOf" : [
+// {"$ref" : "#/definitions/Response"}, {
+// "type" : "object",
+// "description" : "Response to `setInstructionBreakpoints` request",
+// "properties" : {
+// "body" : {
+// "type" : "object",
+// "properties" : {
+// "breakpoints" : {
+// "type" : "array",
+// "items" : {"$ref" : "#/definitions/Breakpoint"},
+// "description" :
+// "Information about the breakpoints. The array elements
+// " "correspond to the elements of the `breakpoints`
+// array."
+// }
+// },
+// "required" : ["breakpoints"]
+// }
+// },
+// "required" : ["body"]
+// }
+// ]
// },
-// "endLine": {
-// "type": "number",
-// "description": "The end line of the actual range covered by the
-// breakpoint."
+// "InstructionBreakpoint" : {
+// "type" : "object",
+// "description" : "Properties of a breakpoint passed to the "
+// "`setInstructionBreakpoints` request",
+// "properties" : {
+// "instructionReference" : {
+// "type" : "string",
+// "description" :
+// "The instruction reference of the breakpoint.\nThis should be a "
+// "memory or instruction pointer reference from an
+// `EvaluateResponse`, "
+// "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`."
// },
-// "endColumn": {
-// "type": "number",
-// "description": "The end column of the actual range covered by the
-// breakpoint. If no end line is given, then the end column is assumed to
-// be in the start line."
+// "offset" : {
+// "type" : "integer",
+// "description" : "The offset from the instruction reference in "
+// "bytes.\nThis can be negative."
// },
-// "instructionReference": {
-// "type": "string",
-// "description": "A memory reference to where the breakpoint is set."
+// "condition" : {
+// "type" : "string",
+// "description" : "An expression for conditional breakpoints.\nIt is only
+// "
+// "honored by a debug adapter if the corresponding "
+// "capability `supportsConditionalBreakpoints` is true."
// },
-// "offset": {
-// "type": "number",
-// "description": "The offset from the instruction reference.
-// This can be negative."
+// "hitCondition" : {
+// "type" : "string",
+// "description" : "An expression that controls how many hits of the "
+// "breakpoint are ignored.\nThe debug adapter is expected
+// " "to interpret the expression as needed.\nThe
+// attribute " "is only honored by a debug adapter if the
+// corresponding " "capability
+// `supportsHitConditionalBreakpoints` is true."
// },
+// "mode" : {
+// "type" : "string",
+// "description" : "The mode of this breakpoint. If defined, this must be
+// "
+// "one of the `breakpointModes` the debug adapter "
+// "advertised in its `Capabilities`."
+// }
// },
-// "required": [ "id", "verified", "line"]
-// }
+// "required" : ["instructionReference"]
+// },
+// "Breakpoint"
+// : {
+// "type" : "object",
+// "description" :
+// "Information about a breakpoint created in `setBreakpoints`, "
+// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or "
+// "`setDataBreakpoints` requests.",
+// "properties" : {
+// "id" : {
+// "type" : "integer",
+// "description" :
+// "The identifier for the breakpoint. It is needed if breakpoint
+// " "events are used to update or remove breakpoints."
+// },
+// "verified" : {
+// "type" : "boolean",
+// "description" : "If true, the breakpoint could be set (but not "
+// "necessarily at the desired location)."
+// },
+// "message" : {
+// "type" : "string",
+// "description" : "A message about the state of the breakpoint.\nThis
+// "
+// "is shown to the user and can be used to explain
+// why " "a breakpoint could not be verified."
+// },
+// "source" : {
+// "$ref" : "#/definitions/Source",
+// "description" : "The source where the breakpoint is located."
+// },
+// "line" : {
+// "type" : "integer",
+// "description" :
+// "The start line of the actual range covered by the breakpoint."
+// },
+// "column" : {
+// "type" : "integer",
+// "description" :
+// "Start position of the source range covered by the breakpoint.
+// " "It is measured in UTF-16 code units and the client
+// capability "
+// "`columnsStartAt1` determines whether it is 0- or 1-based."
+// },
+// "endLine" : {
+// "type" : "integer",
+// "description" :
+// "The end line of the actual range covered by the breakpoint."
+// },
+// "endColumn" : {
+// "type" : "integer",
+// "description" :
+// "End position of the source range covered by the breakpoint. It
+// " "is measured in UTF-16 code units and the client capability "
+// "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf
+// " "no end line is given, then the end column is assumed to be
+// in " "the start line."
+// },
+// "instructionReference" : {
+// "type" : "string",
+// "description" : "A memory reference to where the breakpoint is
+// set."
+// },
+// "offset" : {
+// "type" : "integer",
+// "description" : "The offset from the instruction reference.\nThis "
+// "can be negative."
+// },
+// "reason" : {
+// "type" : "string",
+// "description" :
+// "A machine-readable explanation of why a breakpoint may not be
+// " "verified. If a breakpoint is verified or a specific reason
+// is " "not known, the adapter should omit this property.
+// Possible " "values include:\n\n- `pending`: Indicates a
+// breakpoint might be " "verified in the future, but the adapter
+// cannot verify it in the " "current state.\n - `failed`:
+// Indicates a breakpoint was not " "able to be verified, and the
+// adapter does not believe it can be " "verified without
+// intervention.",
+// "enum" : [ "pending", "failed" ]
+// }
+// },
+// "required" : ["verified"]
+// },
+
void request_setInstructionBreakpoints(const llvm::json::Object &request) {
llvm::json::Object response;
llvm::json::Array response_breakpoints;
@@ -4167,7 +4246,7 @@ void request_setInstructionBreakpoints(const llvm::json::Object &request) {
auto arguments = request.getObject("arguments");
auto breakpoints = arguments->getArray("breakpoints");
- // It holds active instruction brealpoint list received from DAP.
+ // It holds active instruction breakpoint list received from DAP.
InstructionBreakpointMap request_ibp;
if (breakpoints) {
for (const auto &bp : *breakpoints) {
>From 17f8c6d704588e6befbb1fd5101b88c13520872f Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Thu, 22 Aug 2024 14:03:34 -0700
Subject: [PATCH 27/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Added stop reason for instruction breakpoint.
---
lldb/tools/lldb-dap/DAP.cpp | 31 ++++++++++++++++++++++++++++++-
lldb/tools/lldb-dap/DAP.h | 4 ++++
lldb/tools/lldb-dap/JSONUtils.cpp | 8 +++++++-
3 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index c3c70e9d739846..1186c65ec351d3 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -67,7 +67,7 @@ static std::string capitalize(llvm::StringRef str) {
void DAP::PopulateExceptionBreakpoints() {
llvm::call_once(init_exception_breakpoints_flag, [this]() {
- exception_breakpoints = std::vector<ExceptionBreakpoint> {};
+ exception_breakpoints = std::vector<ExceptionBreakpoint>{};
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
exception_breakpoints->emplace_back("cpp_catch", "C++ Catch",
@@ -985,4 +985,33 @@ void DAP::SetThreadFormat(llvm::StringRef format) {
}
}
+InstructionBreakpoint *
+DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {
+
+ for (auto &bp : instruction_breakpoints) {
+ if (bp.second.id == bp_id)
+ return &bp.second;
+ }
+ return nullptr;
+}
+
+InstructionBreakpoint *
+DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
+ const auto num = thread.GetStopReasonDataCount();
+ InstructionBreakpoint *inst_bp = nullptr;
+ for (size_t i = 0; i < num; i += 2) {
+ // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
+ // thread.GetStopReasonDataAtIndex(i+1) will return the location
+ // within that breakpoint. We only care about the bp ID so we can
+ // see if this is an instruction breakpoint that is getting hit.
+ lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
+ inst_bp = GetInstructionBreakpoint(bp_id);
+ // If any breakpoint is not an exception breakpoint, then stop and
+ // report this as a normal breakpoint
+ if (inst_bp == nullptr)
+ return nullptr;
+ }
+ return inst_bp;
+}
+
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 189301e28cf74d..f1aad113d1894c 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -333,6 +333,10 @@ struct DAP {
void SetThreadFormat(llvm::StringRef format);
+ InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);
+
+ InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
+
private:
// Send the JSON in "json_str" to the "out" stream. Correctly send the
// "Content-Length:" field followed by the length, followed by the raw
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 5a2a6c044ea4b1..e9a59cbb5fd85a 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -986,7 +986,13 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
body.try_emplace("reason", "exception");
EmplaceSafeString(body, "description", exc_bp->label);
} else {
- body.try_emplace("reason", "breakpoint");
+ InstructionBreakpoint *inst_bp =
+ g_dap.GetInstructionBPFromStopReason(thread);
+ if (inst_bp) {
+ body.try_emplace("reason", "instruction breakpoint");
+ } else {
+ body.try_emplace("reason", "breakpoint");
+ }
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
std::string desc_str =
>From 8c1949857cec8fb30bdd2607a7a14fc6a0e9aeea Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Ellendula <quic_sellendu at quicinc.com>
Date: Thu, 22 Aug 2024 14:07:01 -0700
Subject: [PATCH 28/28] [lldb-dap] Enabling instruction breakpoint support to
lldb-dap. Added few comments
---
lldb/tools/lldb-dap/DAP.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 1186c65ec351d3..d14f0494d96f4a 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -1006,7 +1006,7 @@ DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
// see if this is an instruction breakpoint that is getting hit.
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
inst_bp = GetInstructionBreakpoint(bp_id);
- // If any breakpoint is not an exception breakpoint, then stop and
+ // If any breakpoint is not an instruction breakpoint, then stop and
// report this as a normal breakpoint
if (inst_bp == nullptr)
return nullptr;
More information about the lldb-commits
mailing list