[Lldb-commits] [lldb] [lldb-dap] Add data breakpoints for bytes (PR #167237)
Sergei Druzhkov via lldb-commits
lldb-commits at lists.llvm.org
Sun Nov 9 10:43:02 PST 2025
https://github.com/DrSergei created https://github.com/llvm/llvm-project/pull/167237
This patch adds support for `dataBreakpointInfoBytes` capability from DAP. You can test this feature in VSCode (`Add data breakpoint at address` button in breakpoints tab).
>From 482c89dcee876a9d50b0ecc0a9e90482a2d9c94a Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Sat, 8 Nov 2025 22:41:39 +0300
Subject: [PATCH] [lldb-dap] Add data breakpoints for bytes
---
.../test/tools/lldb-dap/dap_server.py | 20 ++++---
.../TestDAP_setDataBreakpoints.py | 52 +++++++++++++++++++
.../DataBreakpointInfoRequestHandler.cpp | 44 +++++++++++-----
lldb/tools/lldb-dap/Handler/RequestHandler.h | 3 ++
4 files changed, 100 insertions(+), 19 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 ac550962cfb85..aaa6f76001aa3 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
@@ -1234,16 +1234,24 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
return response
def request_dataBreakpointInfo(
- self, variablesReference, name, frameIndex=0, threadId=None
+ self, variablesReference, name, size=None, frameIndex=0, threadId=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 = (
+ {
+ "variablesReference": variablesReference,
+ "name": name,
+ "frameId": stackFrame["id"],
+ }
+ if size is None
+ else {
+ "bytes": size,
+ "name": name,
+ "asAddress": True,
+ }
+ )
command_dict = {
"command": "dataBreakpointInfo",
"type": "request",
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..7afc42f89ce55 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
@@ -171,3 +171,55 @@ def test_functionality(self):
self.continue_to_next_stop()
x_val = self.dap_server.get_local_variable_value("x")
self.assertEqual(x_val, "10")
+
+ @skipIfWindows
+ def test_bytes(self):
+ """Tests setting data breakpoints on memory range."""
+ 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()
+ # Test write watchpoints on x, arr[2]
+ x = self.dap_server.get_local_variable("x")
+ response_x = self.dap_server.request_dataBreakpointInfo(
+ 0, x["memoryReference"], 4
+ )
+ arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
+ response_arr_2 = self.dap_server.request_dataBreakpointInfo(
+ 0, arr_2["memoryReference"], 4
+ )
+
+ # Test response from dataBreakpointInfo request.
+ self.assertEqual(
+ response_x["body"]["dataId"].split("/"), [x["memoryReference"][2:], "4"]
+ )
+ self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes)
+ self.assertEqual(
+ response_arr_2["body"]["dataId"].split("/"),
+ [arr_2["memoryReference"][2:], "4"],
+ )
+ self.assertEqual(response_arr_2["body"]["accessTypes"], self.accessTypes)
+ dataBreakpoints = [
+ {"dataId": response_x["body"]["dataId"], "accessType": "write"},
+ {"dataId": response_arr_2["body"]["dataId"], "accessType": "write"},
+ ]
+ set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
+ self.assertEqual(
+ set_response["body"]["breakpoints"],
+ [{"verified": True}, {"verified": True}],
+ )
+
+ self.continue_to_next_stop()
+ x_val = self.dap_server.get_local_variable_value("x")
+ i_val = self.dap_server.get_local_variable_value("i")
+ self.assertEqual(x_val, "2")
+ self.assertEqual(i_val, "1")
+
+ self.continue_to_next_stop()
+ arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
+ i_val = self.dap_server.get_local_variable_value("i")
+ self.assertEqual(arr_2["value"], "42")
+ self.assertEqual(i_val, "2")
+ self.dap_server.request_setDataBreakpoint([])
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 87b93fc999ecd..c55b165c6b783 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -16,6 +16,20 @@
namespace lldb_dap {
+static bool CheckAddress(DAP &dap, lldb::addr_t load_addr) {
+ 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()) {
+ if (!(region.IsReadable() || region.IsWritable())) {
+ return false;
+ }
+ }
+ return true;
+}
+
/// 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.
@@ -23,7 +37,6 @@ llvm::Expected<protocol::DataBreakpointInfoResponseBody>
DataBreakpointInfoRequestHandler::Run(
const protocol::DataBreakpointInfoArguments &args) const {
protocol::DataBreakpointInfoResponseBody response;
- lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
lldb::SBValue variable = dap.variables.FindVariable(
args.variablesReference.value_or(0), args.name);
std::string addr, size;
@@ -43,7 +56,8 @@ DataBreakpointInfoRequestHandler::Run(
addr = llvm::utohexstr(load_addr);
size = llvm::utostr(byte_size);
}
- } else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
+ } else if (lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
+ args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
lldb::SBValue value = frame.EvaluateExpression(args.name.c_str());
if (value.GetError().Fail()) {
lldb::SBError error = value.GetError();
@@ -58,17 +72,10 @@ DataBreakpointInfoRequestHandler::Run(
if (data.IsValid()) {
size = llvm::utostr(data.GetByteSize());
addr = llvm::utohexstr(load_addr);
- 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()) {
- if (!(region.IsReadable() || region.IsWritable())) {
- is_data_ok = false;
- response.description = "memory region for address " + addr +
- " has no read or write permissions";
- }
+ if (!CheckAddress(dap, load_addr)) {
+ is_data_ok = false;
+ response.description = "memory region for address " + addr +
+ " has no read or write permissions";
}
} else {
is_data_ok = false;
@@ -76,6 +83,17 @@ DataBreakpointInfoRequestHandler::Run(
"unable to get byte size for expression: " + args.name;
}
}
+ } else if (args.asAddress) {
+ size = llvm::utostr(args.bytes.value_or(1));
+ if (llvm::StringRef(args.name).starts_with("0x"))
+ addr = args.name.substr(2);
+ else
+ addr = llvm::utohexstr(std::stoull(args.name));
+ if (!CheckAddress(dap, std::stoull(addr, 0, 16))) {
+ is_data_ok = false;
+ response.description = "memory region for address " + addr +
+ " has no read or write permissions";
+ }
} else {
is_data_ok = false;
response.description = "variable not found: " + args.name;
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index bc22133d92453..c9cafd33327a8 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -432,6 +432,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;
};
More information about the lldb-commits
mailing list