[Lldb-commits] [lldb] [lldb][lldb-dap] Support breakpoint info bytes (PR #141122)
Ebuka Ezike via lldb-commits
lldb-commits at lists.llvm.org
Fri May 30 06:03:29 PDT 2025
https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/141122
>From 9408d17b1acf308c7a299bdc6c413a3505d1a039 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Wed, 21 May 2025 23:26:14 +0100
Subject: [PATCH 01/10] [lldb][lldb-dap] support DataBreakpointBytes capability
---
.../DataBreakpointInfoRequestHandler.cpp | 48 ++++++++++++++++++-
lldb/tools/lldb-dap/Handler/RequestHandler.h | 3 ++
.../lldb-dap/Protocol/ProtocolRequests.h | 2 +-
3 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 8cb25d0603449..9b969560d7973 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "DAP.h"
-#include "EventHelper.h"
#include "Protocol/ProtocolTypes.h"
#include "RequestHandler.h"
#include "lldb/API/SBMemoryRegionInfo.h"
@@ -16,12 +15,59 @@
namespace lldb_dap {
+static llvm::Expected<protocol::DataBreakpointInfoResponseBody>
+HandleDataBreakpointBytes(DAP &dap,
+ const protocol::DataBreakpointInfoArguments &args) {
+ llvm::StringRef address = args.name;
+
+ unsigned long long load_addr = LLDB_INVALID_ADDRESS;
+ if (llvm::getAsUnsignedInteger(address, 0, load_addr)) {
+ return llvm::make_error<DAPError>(llvm::formatv("invalid address"),
+ llvm::inconvertibleErrorCode(), false);
+ }
+
+ lldb::SBAddress sb_addr(load_addr, dap.target);
+ if (!sb_addr.IsValid()) {
+ return llvm::make_error<DAPError>(
+ llvm::formatv("address {:x} does not exist in the debuggee", load_addr),
+ llvm::inconvertibleErrorCode(), false);
+ }
+
+ const uint32_t byte_size =
+ args.bytes.value_or(dap.target.GetAddressByteSize());
+
+ protocol::DataBreakpointInfoResponseBody response;
+ response.dataId = llvm::formatv("{:x}/{}", load_addr, byte_size);
+
+ lldb::SBMemoryRegionInfo region;
+ lldb::SBError err =
+ dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
+ // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
+ // request if SBProcess::GetMemoryRegionInfo returns error.
+ if (err.Success() && !(region.IsReadable() || region.IsWritable())) {
+ response.description = llvm::formatv(
+ "memory region for address {} has no read or write permissions",
+ load_addr);
+ } else {
+ response.description = llvm::formatv("{} bytes at {:x}", load_addr);
+ response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
+ protocol::eDataBreakpointAccessTypeWrite,
+ protocol::eDataBreakpointAccessTypeReadWrite};
+ }
+
+ return response;
+}
+
/// Obtains information on a possible data breakpoint that could be set on an
/// expression or variable. Clients should only call this request if the
/// corresponding capability supportsDataBreakpoints is true.
llvm::Expected<protocol::DataBreakpointInfoResponseBody>
DataBreakpointInfoRequestHandler::Run(
const protocol::DataBreakpointInfoArguments &args) const {
+
+ if (args.asAddress.value_or(false))
+ return HandleDataBreakpointBytes(dap, args);
+
protocol::DataBreakpointInfoResponseBody response;
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX));
lldb::SBValue variable = dap.variables.FindVariable(
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 3a965bcc87a5e..dec68683fee65 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -420,6 +420,9 @@ class DataBreakpointInfoRequestHandler
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; }
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureDataBreakpointBytes};
+ }
llvm::Expected<protocol::DataBreakpointInfoResponseBody>
Run(const protocol::DataBreakpointInfoArguments &args) const override;
};
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 7c774e50d6e56..cde441351fc88 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -668,7 +668,7 @@ struct DataBreakpointInfoArguments {
/// pause on data access anywhere within that range.
/// Clients may set this property only if the `supportsDataBreakpointBytes`
/// capability is true.
- std::optional<int64_t> bytes;
+ std::optional<uint64_t> bytes;
/// If `true`, the `name` is a memory address and the debugger should
/// interpret it as a decimal value, or hex value if it is prefixed with `0x`.
>From e407716994a4ed0b054984c21a4824a06adebd7a Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 22 May 2025 15:18:57 +0100
Subject: [PATCH 02/10] [lldb][lldb-dap] support DataBreakpointBytes capability
---
.../Handler/DataBreakpointInfoRequestHandler.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 9b969560d7973..2e429c045cdc0 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -37,7 +37,7 @@ HandleDataBreakpointBytes(DAP &dap,
args.bytes.value_or(dap.target.GetAddressByteSize());
protocol::DataBreakpointInfoResponseBody response;
- response.dataId = llvm::formatv("{:x}/{}", load_addr, byte_size);
+ response.dataId = llvm::formatv("{:x-}/{}", load_addr, byte_size);
lldb::SBMemoryRegionInfo region;
lldb::SBError err =
@@ -48,8 +48,10 @@ HandleDataBreakpointBytes(DAP &dap,
response.description = llvm::formatv(
"memory region for address {} has no read or write permissions",
load_addr);
+
} else {
- response.description = llvm::formatv("{} bytes at {:x}", load_addr);
+ response.description =
+ llvm::formatv("{} bytes at {:x}", byte_size, load_addr);
response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
protocol::eDataBreakpointAccessTypeWrite,
protocol::eDataBreakpointAccessTypeReadWrite};
@@ -86,7 +88,7 @@ DataBreakpointInfoRequestHandler::Run(
is_data_ok = false;
response.description = "variable size is 0";
} else {
- addr = llvm::utohexstr(load_addr);
+ addr = llvm::utohexstr(load_addr, /*lowerCase=*/true);
size = llvm::utostr(byte_size);
}
} else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
@@ -103,7 +105,7 @@ DataBreakpointInfoRequestHandler::Run(
lldb::SBData data = value.GetPointeeData();
if (data.IsValid()) {
size = llvm::utostr(data.GetByteSize());
- addr = llvm::utohexstr(load_addr);
+ addr = llvm::utohexstr(load_addr, /*lowerCase=*/true);
lldb::SBMemoryRegionInfo region;
lldb::SBError err =
dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
@@ -132,7 +134,7 @@ DataBreakpointInfoRequestHandler::Run(
response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
protocol::eDataBreakpointAccessTypeWrite,
protocol::eDataBreakpointAccessTypeReadWrite};
- response.description = size + " bytes at " + addr + " " + args.name;
+ response.description = size + " bytes at 0x" + addr + " " + args.name;
}
return response;
>From 4c3aebe9882eb9afc0b354e0168294865541c226 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 22 May 2025 19:19:07 +0100
Subject: [PATCH 03/10] [lldb][lldb-dap] add DataBreakpointBytes test case
---
.../test/tools/lldb-dap/dap_server.py | 33 ++++++++----
.../test/tools/lldb-dap/lldbdap_testcase.py | 19 ++++---
.../TestDAP_setDataBreakpoints.py | 53 ++++++++++++++++---
.../tools/lldb-dap/databreakpoint/main.cpp | 3 ++
4 files changed, 87 insertions(+), 21 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 a028381a0a4f9..2468ca7e37f14 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
@@ -1042,16 +1042,31 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
return self.send_recv(command_dict)
def request_dataBreakpointInfo(
- self, variablesReference, name, frameIndex=0, threadId=None
+ self,
+ name: str,
+ variablesReference: int = None,
+ frameIndex: int = 0,
+ bytes_: int = None,
+ asAddress: bool = None,
):
- stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
- if stackFrame is None:
- return []
- args_dict = {
- "variablesReference": variablesReference,
- "name": name,
- "frameId": stackFrame["id"],
- }
+
+ args_dict = {}
+ if asAddress is not None:
+ args_dict = {
+ "name": name,
+ "asAddress": asAddress,
+ "bytes": bytes_,
+ }
+ else:
+ stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=None)
+ if stackFrame is None:
+ return []
+ args_dict = {
+ "variablesReference": variablesReference,
+ "name": name,
+ "frameId": stackFrame["id"],
+ }
+
command_dict = {
"command": "dataBreakpointInfo",
"type": "request",
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 91ae55977046b..996c85ea69622 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
@@ -104,7 +104,9 @@ def waitUntil(self, condition_callback):
time.sleep(0.5)
return False
- def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+ def verify_breakpoint_hit(
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ ):
"""Wait for the process we are debugging to stop, and verify we hit
any breakpoint location in the "breakpoint_ids" array.
"breakpoint_ids" should be a list of breakpoint ID strings
@@ -131,9 +133,10 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
# So when looking at the description we just want to make sure
# the right breakpoint matches and not worry about the actual
# location.
+ type_name = "watchpoint" if is_watchpoint else "breakpoint"
description = body["description"]
for breakpoint_id in breakpoint_ids:
- match_desc = f"breakpoint {breakpoint_id}."
+ match_desc = f"{type_name} {breakpoint_id}"
if match_desc in description:
return
self.assertTrue(False, f"breakpoint not hit, stopped_events={stopped_events}")
@@ -329,12 +332,16 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
self.do_continue()
return self.dap_server.wait_for_stopped(timeout)
- def continue_to_breakpoint(self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT):
- self.continue_to_breakpoints((breakpoint_id), timeout)
+ def continue_to_breakpoint(
+ self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ ):
+ self.continue_to_breakpoints([breakpoint_id], timeout, is_watchpoint)
- def continue_to_breakpoints(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+ def continue_to_breakpoints(
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ ):
self.do_continue()
- self.verify_breakpoint_hit(breakpoint_ids, timeout)
+ self.verify_breakpoint_hit(breakpoint_ids, timeout, is_watchpoint)
def continue_to_exception_breakpoint(self, filter_label, timeout=DEFAULT_TIMEOUT):
self.do_continue()
diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
index a542a318050dd..62392f2c49afd 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
@@ -23,8 +23,8 @@ def test_duplicate_start_addresses(self):
self.continue_to_next_stop()
self.dap_server.get_stackFrame()
# Test setting write watchpoint using expressions: &x, arr+2
- response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
- response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
+ response_x = self.dap_server.request_dataBreakpointInfo("&x", 0)
+ response_arr_2 = self.dap_server.request_dataBreakpointInfo("arr+2", 0)
# Test response from dataBreakpointInfo request.
self.assertEqual(response_x["body"]["dataId"].split("/")[1], "4")
self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes)
@@ -56,6 +56,47 @@ def test_duplicate_start_addresses(self):
self.assertEqual(arr_2["value"], "42")
self.assertEqual(i_val, "2")
+ @skipIfWindows
+ def test_breakpoint_info_bytes(self):
+ """Test supportBreakpointInfoBytes
+ Set the watchpoint on `var` variable address + 6 characters.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+ source = "main.cpp"
+ first_loop_break_line = line_number(source, "// first loop breakpoint")
+ self.set_source_breakpoints(source, [first_loop_break_line])
+ self.continue_to_next_stop()
+
+ # get the address of `var` variable
+ eval_response = self.dap_server.request_evaluate("&var", context="watch")
+ self.assertTrue(eval_response["success"])
+ var_address = eval_response["body"]["result"]
+
+ var_byte_watch_size = 5
+ bp_resp = self.dap_server.request_dataBreakpointInfo(
+ var_address, asAddress=True, bytes_=var_byte_watch_size
+ )
+ resp_data_id = bp_resp["body"]["dataId"]
+ self.assertTrue(
+ bp_resp["success"], f"dataBreakpointInfo request failed: {bp_resp}"
+ )
+ self.assertEqual(resp_data_id.split("/")[1], str(var_byte_watch_size))
+
+ data_breakpoints = [{"dataId": resp_data_id, "accessType": "write"}]
+ self.dap_server.request_setDataBreakpoint(data_breakpoints)
+
+ self.continue_to_breakpoint(breakpoint_id=1, is_watchpoint=True)
+ eval_response = self.dap_server.request_evaluate("var", context="watch")
+ self.assertTrue(eval_response["success"])
+ var_value = eval_response["body"]["result"]
+ self.assertEqual(var_value, '"HALLO"')
+
+ # Remove the watchpoint because once it leaves this function scope, the address can be
+ # be used by another variable or register.
+ self.dap_server.request_setDataBreakpoint([])
+ self.continue_to_exit()
+
@skipIfWindows
def test_expression(self):
"""Tests setting data breakpoints on expression."""
@@ -67,8 +108,8 @@ def test_expression(self):
self.continue_to_next_stop()
self.dap_server.get_stackFrame()
# Test setting write watchpoint using expressions: &x, arr+2
- response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
- response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
+ response_x = self.dap_server.request_dataBreakpointInfo("&x", 0)
+ response_arr_2 = self.dap_server.request_dataBreakpointInfo("arr+2", 0)
# Test response from dataBreakpointInfo request.
self.assertEqual(response_x["body"]["dataId"].split("/")[1], "4")
self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes)
@@ -107,10 +148,10 @@ def test_functionality(self):
self.continue_to_next_stop()
self.dap_server.get_local_variables()
# Test write watchpoints on x, arr[2]
- response_x = self.dap_server.request_dataBreakpointInfo(1, "x")
+ response_x = self.dap_server.request_dataBreakpointInfo("x", 1)
arr = self.dap_server.get_local_variable("arr")
response_arr_2 = self.dap_server.request_dataBreakpointInfo(
- arr["variablesReference"], "[2]"
+ "[2]", arr["variablesReference"]
)
# Test response from dataBreakpointInfo request.
diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp b/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp
index bef09c203845e..e4007980d27a8 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp
@@ -1,5 +1,6 @@
int main(int argc, char const *argv[]) {
// Test for data breakpoint
+ char var[6] = "HELLO";
int x = 0;
int arr[4] = {1, 2, 3, 4};
for (int i = 0; i < 5; ++i) { // first loop breakpoint
@@ -10,6 +11,8 @@ int main(int argc, char const *argv[]) {
}
}
+ var[1] = 'A';
+
x = 1;
for (int i = 0; i < 10; ++i) { // second loop breakpoint
++x;
>From 23d775a75ddc867113e27a9b8fbb93e9631e9cd5 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 22 May 2025 20:57:27 +0100
Subject: [PATCH 04/10] [lldb] format files.
---
.../lldbsuite/test/tools/lldb-dap/dap_server.py | 12 ++++++------
.../test/tools/lldb-dap/lldbdap_testcase.py | 6 +++---
2 files changed, 9 insertions(+), 9 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 2468ca7e37f14..a287979492528 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
@@ -1042,12 +1042,12 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
return self.send_recv(command_dict)
def request_dataBreakpointInfo(
- self,
- name: str,
- variablesReference: int = None,
- frameIndex: int = 0,
- bytes_: int = None,
- asAddress: bool = None,
+ self,
+ name: str,
+ variablesReference: int = None,
+ frameIndex: int = 0,
+ bytes_: int = None,
+ asAddress: bool = None,
):
args_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 996c85ea69622..dadaf0fc53145 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
@@ -105,7 +105,7 @@ def waitUntil(self, condition_callback):
return False
def verify_breakpoint_hit(
- self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
):
"""Wait for the process we are debugging to stop, and verify we hit
any breakpoint location in the "breakpoint_ids" array.
@@ -333,12 +333,12 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
return self.dap_server.wait_for_stopped(timeout)
def continue_to_breakpoint(
- self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
):
self.continue_to_breakpoints([breakpoint_id], timeout, is_watchpoint)
def continue_to_breakpoints(
- self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
):
self.do_continue()
self.verify_breakpoint_hit(breakpoint_ids, timeout, is_watchpoint)
>From fdcc1fb1f5df425f8a01090878aba49fff10e7c8 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 22 May 2025 21:03:08 +0100
Subject: [PATCH 05/10] [lldb] format tests.
---
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py | 1 -
1 file changed, 1 deletion(-)
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 a287979492528..535c33f070c5b 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
@@ -1049,7 +1049,6 @@ def request_dataBreakpointInfo(
bytes_: int = None,
asAddress: bool = None,
):
-
args_dict = {}
if asAddress is not None:
args_dict = {
>From 830134959d5325dacab9656c42054ccc7b5916da Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Fri, 23 May 2025 21:10:04 +0100
Subject: [PATCH 06/10] [lldb][lldb-dap] review changes.
[lldb][lldb-dap] add review changes
---
.../test/tools/lldb-dap/dap_server.py | 15 +++++++++------
.../test/tools/lldb-dap/lldbdap_testcase.py | 19 ++++++++++---------
.../TestDAP_setDataBreakpoints.py | 4 ++--
.../DataBreakpointInfoRequestHandler.cpp | 9 ++++-----
lldb/tools/lldb-dap/JSONUtils.cpp | 11 ++++++++++-
5 files changed, 35 insertions(+), 23 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 535c33f070c5b..151f897fb50ee 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
@@ -1044,10 +1044,10 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
def request_dataBreakpointInfo(
self,
name: str,
- variablesReference: int = None,
- frameIndex: int = 0,
- bytes_: int = None,
- asAddress: bool = None,
+ variablesReference: Optional[int] | None = None,
+ frameIndex: Optional[int] = 0,
+ bytes_: Optional[int] = None,
+ asAddress: Optional[bool] = None,
):
args_dict = {}
if asAddress is not None:
@@ -1057,9 +1057,12 @@ def request_dataBreakpointInfo(
"bytes": bytes_,
}
else:
- stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=None)
+ thread_id = self.get_thread_id()
+ stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=thread_id)
if stackFrame is None:
- return []
+ raise ValueError(
+ f"could not get stackframe for frameIndex: {frameIndex} and threadId: {thread_id}"
+ )
args_dict = {
"variablesReference": variablesReference,
"name": name,
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 dadaf0fc53145..9e59cbb037b30 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
@@ -105,7 +105,7 @@ def waitUntil(self, condition_callback):
return False
def verify_breakpoint_hit(
- self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
):
"""Wait for the process we are debugging to stop, and verify we hit
any breakpoint location in the "breakpoint_ids" array.
@@ -118,9 +118,10 @@ def verify_breakpoint_hit(
body = stopped_event["body"]
if "reason" not in body:
continue
- if (
- body["reason"] != "breakpoint"
- and body["reason"] != "instruction breakpoint"
+ if body["reason"] not in (
+ "breakpoint",
+ "instruction breakpoint",
+ "data breakpoint",
):
continue
if "description" not in body:
@@ -133,7 +134,7 @@ def verify_breakpoint_hit(
# So when looking at the description we just want to make sure
# the right breakpoint matches and not worry about the actual
# location.
- type_name = "watchpoint" if is_watchpoint else "breakpoint"
+ type_name = reason or "breakpoint"
description = body["description"]
for breakpoint_id in breakpoint_ids:
match_desc = f"{type_name} {breakpoint_id}"
@@ -333,15 +334,15 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
return self.dap_server.wait_for_stopped(timeout)
def continue_to_breakpoint(
- self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
):
- self.continue_to_breakpoints([breakpoint_id], timeout, is_watchpoint)
+ self.continue_to_breakpoints([breakpoint_id], timeout, reason)
def continue_to_breakpoints(
- self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
):
self.do_continue()
- self.verify_breakpoint_hit(breakpoint_ids, timeout, is_watchpoint)
+ self.verify_breakpoint_hit(breakpoint_ids, timeout, reason)
def continue_to_exception_breakpoint(self, filter_label, timeout=DEFAULT_TIMEOUT):
self.do_continue()
diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
index 62392f2c49afd..0d8d63e680f92 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
@@ -77,16 +77,16 @@ def test_breakpoint_info_bytes(self):
bp_resp = self.dap_server.request_dataBreakpointInfo(
var_address, asAddress=True, bytes_=var_byte_watch_size
)
- resp_data_id = bp_resp["body"]["dataId"]
self.assertTrue(
bp_resp["success"], f"dataBreakpointInfo request failed: {bp_resp}"
)
+ resp_data_id = bp_resp["body"]["dataId"]
self.assertEqual(resp_data_id.split("/")[1], str(var_byte_watch_size))
data_breakpoints = [{"dataId": resp_data_id, "accessType": "write"}]
self.dap_server.request_setDataBreakpoint(data_breakpoints)
- self.continue_to_breakpoint(breakpoint_id=1, is_watchpoint=True)
+ self.continue_to_breakpoint(breakpoint_id=1, reason="data breakpoint")
eval_response = self.dap_server.request_evaluate("var", context="watch")
self.assertTrue(eval_response["success"])
var_value = eval_response["body"]["result"]
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 2e429c045cdc0..8bde7b271fe1f 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -18,16 +18,15 @@ namespace lldb_dap {
static llvm::Expected<protocol::DataBreakpointInfoResponseBody>
HandleDataBreakpointBytes(DAP &dap,
const protocol::DataBreakpointInfoArguments &args) {
- llvm::StringRef address = args.name;
+ llvm::StringRef raw_address = args.name;
- unsigned long long load_addr = LLDB_INVALID_ADDRESS;
- if (llvm::getAsUnsignedInteger(address, 0, load_addr)) {
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ if (raw_address.getAsInteger<lldb::addr_t>(0, load_addr)) {
return llvm::make_error<DAPError>(llvm::formatv("invalid address"),
llvm::inconvertibleErrorCode(), false);
}
- lldb::SBAddress sb_addr(load_addr, dap.target);
- if (!sb_addr.IsValid()) {
+ if (lldb::SBAddress address(load_addr, dap.target); !address.IsValid()) {
return llvm::make_error<DAPError>(
llvm::formatv("address {:x} does not exist in the debuggee", load_addr),
llvm::inconvertibleErrorCode(), false);
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index c9c6f4554c325..adfa72523ca82 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -893,7 +893,16 @@ llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread,
EmplaceSafeString(body, "description", desc_str);
}
} break;
- case lldb::eStopReasonWatchpoint:
+ case lldb::eStopReasonWatchpoint: {
+ lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
+ lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
+ std::string desc_str =
+ llvm::formatv("data breakpoint {0}.{1}", bp_id, bp_loc_id);
+ body.try_emplace("hitBreakpointIds",
+ llvm::json::Array{llvm::json::Value(bp_id)});
+ body.try_emplace("reason", "data breakpoint");
+ EmplaceSafeString(body, "description", desc_str);
+ } break;
case lldb::eStopReasonInstrumentation:
body.try_emplace("reason", "breakpoint");
break;
>From 0c7f1f9a8b4a724f8923c75a33aebc6e3b09b0cb Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Sun, 25 May 2025 12:12:33 +0100
Subject: [PATCH 07/10] [lldb][lldb-dap] format
---
.../lldbsuite/test/tools/lldb-dap/dap_server.py | 8 ++++----
.../test/tools/lldb-dap/lldbdap_testcase.py | 12 ++++++------
2 files changed, 10 insertions(+), 10 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 151f897fb50ee..7b6a3db432452 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
@@ -1044,10 +1044,10 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
def request_dataBreakpointInfo(
self,
name: str,
- variablesReference: Optional[int] | None = None,
- frameIndex: Optional[int] = 0,
- bytes_: Optional[int] = None,
- asAddress: Optional[bool] = None,
+ variablesReference: Optional[int] | None = None,
+ frameIndex: Optional[int] = 0,
+ bytes_: Optional[int] = None,
+ asAddress: Optional[bool] = None,
):
args_dict = {}
if asAddress is not None:
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 9e59cbb037b30..c0c723b94b6bf 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
@@ -105,7 +105,7 @@ def waitUntil(self, condition_callback):
return False
def verify_breakpoint_hit(
- self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
):
"""Wait for the process we are debugging to stop, and verify we hit
any breakpoint location in the "breakpoint_ids" array.
@@ -119,9 +119,9 @@ def verify_breakpoint_hit(
if "reason" not in body:
continue
if body["reason"] not in (
- "breakpoint",
- "instruction breakpoint",
- "data breakpoint",
+ "breakpoint",
+ "instruction breakpoint",
+ "data breakpoint",
):
continue
if "description" not in body:
@@ -334,12 +334,12 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
return self.dap_server.wait_for_stopped(timeout)
def continue_to_breakpoint(
- self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
+ self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
):
self.continue_to_breakpoints([breakpoint_id], timeout, reason)
def continue_to_breakpoints(
- self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
+ self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, reason: Optional[str] = None
):
self.do_continue()
self.verify_breakpoint_hit(breakpoint_ids, timeout, reason)
>From e77bc42e980c2036babfa0777d9d49e57c13b862 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 29 May 2025 13:43:47 +0100
Subject: [PATCH 08/10] [lldb] add the access types from qXfer_memory_map
succeeds.
---
.../Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index c4c2a8aab3c98..29eab8840bcc1 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -1725,6 +1725,8 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
if (region_info.GetRange() == qXfer_region_info.GetRange()) {
region_info.SetFlash(qXfer_region_info.GetFlash());
region_info.SetBlocksize(qXfer_region_info.GetBlocksize());
+ region_info.SetReadable(qXfer_region_info.GetReadable());
+ region_info.SetWritable(qXfer_region_info.GetWritable());
}
}
return error;
>From a6f9c53b8b0a6c63461e9b402b23b75404e50f77 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 29 May 2025 13:46:06 +0100
Subject: [PATCH 09/10] [lldb][lldb-dap] add the access types only if it is
supported.
---
.../DataBreakpointInfoRequestHandler.cpp | 50 +++++++++++++------
1 file changed, 35 insertions(+), 15 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 8bde7b271fe1f..2e15288403800 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -15,10 +15,24 @@
namespace lldb_dap {
-static llvm::Expected<protocol::DataBreakpointInfoResponseBody>
+namespace {
+std::vector<protocol::DataBreakpointAccessType>
+GetBreakpointAccessTypes(lldb::SBMemoryRegionInfo region) {
+ std::vector<protocol::DataBreakpointAccessType> types;
+ if (region.IsReadable())
+ types.emplace_back(protocol::eDataBreakpointAccessTypeRead);
+ if (region.IsWritable())
+ types.emplace_back(protocol::eDataBreakpointAccessTypeWrite);
+ if (region.IsReadable() && region.IsWritable())
+ types.emplace_back(protocol::eDataBreakpointAccessTypeReadWrite);
+
+ return types;
+}
+
+llvm::Expected<protocol::DataBreakpointInfoResponseBody>
HandleDataBreakpointBytes(DAP &dap,
const protocol::DataBreakpointInfoArguments &args) {
- llvm::StringRef raw_address = args.name;
+ const llvm::StringRef raw_address = args.name;
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
if (raw_address.getAsInteger<lldb::addr_t>(0, load_addr)) {
@@ -35,29 +49,35 @@ HandleDataBreakpointBytes(DAP &dap,
const uint32_t byte_size =
args.bytes.value_or(dap.target.GetAddressByteSize());
- protocol::DataBreakpointInfoResponseBody response;
- response.dataId = llvm::formatv("{:x-}/{}", load_addr, byte_size);
-
lldb::SBMemoryRegionInfo region;
lldb::SBError err =
dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
- // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
- // request if SBProcess::GetMemoryRegionInfo returns error.
- if (err.Success() && !(region.IsReadable() || region.IsWritable())) {
+ std::vector<protocol::DataBreakpointAccessType> access_types =
+ GetBreakpointAccessTypes(region);
+
+ protocol::DataBreakpointInfoResponseBody response;
+ if (err.Fail()) {
+ response.dataId = std::nullopt;
+ response.description = err.GetCString();
+ return response;
+ }
+
+ if (access_types.empty()) {
+ response.dataId = std::nullopt;
response.description = llvm::formatv(
"memory region for address {} has no read or write permissions",
load_addr);
-
- } else {
- response.description =
- llvm::formatv("{} bytes at {:x}", byte_size, load_addr);
- response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
- protocol::eDataBreakpointAccessTypeWrite,
- protocol::eDataBreakpointAccessTypeReadWrite};
+ return response;
}
+ response.dataId = llvm::formatv("{:x-}/{}", load_addr, byte_size);
+ response.description =
+ llvm::formatv("{} bytes at {:x}", byte_size, load_addr);
+ response.accessTypes = std::move(access_types);
+
return response;
}
+} // namespace
/// Obtains information on a possible data breakpoint that could be set on an
/// expression or variable. Clients should only call this request if the
>From fed2974d8c836325e5007a29a38a26a7b8eddfac Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Fri, 30 May 2025 14:02:43 +0100
Subject: [PATCH 10/10] [lldb-dap] remove unnecessary type hint
---
.../packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 7b6a3db432452..cf97ac5e03e44 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
@@ -1044,7 +1044,7 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
def request_dataBreakpointInfo(
self,
name: str,
- variablesReference: Optional[int] | None = None,
+ variablesReference: Optional[int] = None,
frameIndex: Optional[int] = 0,
bytes_: Optional[int] = None,
asAddress: Optional[bool] = None,
More information about the lldb-commits
mailing list