Skip to content

Commit 482c89d

Browse files
committed
[lldb-dap] Add data breakpoints for bytes
1 parent af456df commit 482c89d

File tree

4 files changed

+100
-19
lines changed

4 files changed

+100
-19
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,16 +1234,24 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
12341234
return response
12351235

12361236
def request_dataBreakpointInfo(
1237-
self, variablesReference, name, frameIndex=0, threadId=None
1237+
self, variablesReference, name, size=None, frameIndex=0, threadId=None
12381238
):
12391239
stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
12401240
if stackFrame is None:
12411241
return []
1242-
args_dict = {
1243-
"variablesReference": variablesReference,
1244-
"name": name,
1245-
"frameId": stackFrame["id"],
1246-
}
1242+
args_dict = (
1243+
{
1244+
"variablesReference": variablesReference,
1245+
"name": name,
1246+
"frameId": stackFrame["id"],
1247+
}
1248+
if size is None
1249+
else {
1250+
"bytes": size,
1251+
"name": name,
1252+
"asAddress": True,
1253+
}
1254+
)
12471255
command_dict = {
12481256
"command": "dataBreakpointInfo",
12491257
"type": "request",

lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,55 @@ def test_functionality(self):
171171
self.continue_to_next_stop()
172172
x_val = self.dap_server.get_local_variable_value("x")
173173
self.assertEqual(x_val, "10")
174+
175+
@skipIfWindows
176+
def test_bytes(self):
177+
"""Tests setting data breakpoints on memory range."""
178+
program = self.getBuildArtifact("a.out")
179+
self.build_and_launch(program)
180+
source = "main.cpp"
181+
first_loop_break_line = line_number(source, "// first loop breakpoint")
182+
self.set_source_breakpoints(source, [first_loop_break_line])
183+
self.continue_to_next_stop()
184+
# Test write watchpoints on x, arr[2]
185+
x = self.dap_server.get_local_variable("x")
186+
response_x = self.dap_server.request_dataBreakpointInfo(
187+
0, x["memoryReference"], 4
188+
)
189+
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
190+
response_arr_2 = self.dap_server.request_dataBreakpointInfo(
191+
0, arr_2["memoryReference"], 4
192+
)
193+
194+
# Test response from dataBreakpointInfo request.
195+
self.assertEqual(
196+
response_x["body"]["dataId"].split("/"), [x["memoryReference"][2:], "4"]
197+
)
198+
self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes)
199+
self.assertEqual(
200+
response_arr_2["body"]["dataId"].split("/"),
201+
[arr_2["memoryReference"][2:], "4"],
202+
)
203+
self.assertEqual(response_arr_2["body"]["accessTypes"], self.accessTypes)
204+
dataBreakpoints = [
205+
{"dataId": response_x["body"]["dataId"], "accessType": "write"},
206+
{"dataId": response_arr_2["body"]["dataId"], "accessType": "write"},
207+
]
208+
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
209+
self.assertEqual(
210+
set_response["body"]["breakpoints"],
211+
[{"verified": True}, {"verified": True}],
212+
)
213+
214+
self.continue_to_next_stop()
215+
x_val = self.dap_server.get_local_variable_value("x")
216+
i_val = self.dap_server.get_local_variable_value("i")
217+
self.assertEqual(x_val, "2")
218+
self.assertEqual(i_val, "1")
219+
220+
self.continue_to_next_stop()
221+
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
222+
i_val = self.dap_server.get_local_variable_value("i")
223+
self.assertEqual(arr_2["value"], "42")
224+
self.assertEqual(i_val, "2")
225+
self.dap_server.request_setDataBreakpoint([])

lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,27 @@
1616

1717
namespace lldb_dap {
1818

19+
static bool CheckAddress(DAP &dap, lldb::addr_t load_addr) {
20+
lldb::SBMemoryRegionInfo region;
21+
lldb::SBError err =
22+
dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
23+
// Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
24+
// request if SBProcess::GetMemoryRegionInfo returns error.
25+
if (err.Success()) {
26+
if (!(region.IsReadable() || region.IsWritable())) {
27+
return false;
28+
}
29+
}
30+
return true;
31+
}
32+
1933
/// Obtains information on a possible data breakpoint that could be set on an
2034
/// expression or variable. Clients should only call this request if the
2135
/// corresponding capability supportsDataBreakpoints is true.
2236
llvm::Expected<protocol::DataBreakpointInfoResponseBody>
2337
DataBreakpointInfoRequestHandler::Run(
2438
const protocol::DataBreakpointInfoArguments &args) const {
2539
protocol::DataBreakpointInfoResponseBody response;
26-
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
2740
lldb::SBValue variable = dap.variables.FindVariable(
2841
args.variablesReference.value_or(0), args.name);
2942
std::string addr, size;
@@ -43,7 +56,8 @@ DataBreakpointInfoRequestHandler::Run(
4356
addr = llvm::utohexstr(load_addr);
4457
size = llvm::utostr(byte_size);
4558
}
46-
} else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
59+
} else if (lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
60+
args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
4761
lldb::SBValue value = frame.EvaluateExpression(args.name.c_str());
4862
if (value.GetError().Fail()) {
4963
lldb::SBError error = value.GetError();
@@ -58,24 +72,28 @@ DataBreakpointInfoRequestHandler::Run(
5872
if (data.IsValid()) {
5973
size = llvm::utostr(data.GetByteSize());
6074
addr = llvm::utohexstr(load_addr);
61-
lldb::SBMemoryRegionInfo region;
62-
lldb::SBError err =
63-
dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
64-
// Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
65-
// request if SBProcess::GetMemoryRegionInfo returns error.
66-
if (err.Success()) {
67-
if (!(region.IsReadable() || region.IsWritable())) {
68-
is_data_ok = false;
69-
response.description = "memory region for address " + addr +
70-
" has no read or write permissions";
71-
}
75+
if (!CheckAddress(dap, load_addr)) {
76+
is_data_ok = false;
77+
response.description = "memory region for address " + addr +
78+
" has no read or write permissions";
7279
}
7380
} else {
7481
is_data_ok = false;
7582
response.description =
7683
"unable to get byte size for expression: " + args.name;
7784
}
7885
}
86+
} else if (args.asAddress) {
87+
size = llvm::utostr(args.bytes.value_or(1));
88+
if (llvm::StringRef(args.name).starts_with("0x"))
89+
addr = args.name.substr(2);
90+
else
91+
addr = llvm::utohexstr(std::stoull(args.name));
92+
if (!CheckAddress(dap, std::stoull(addr, 0, 16))) {
93+
is_data_ok = false;
94+
response.description = "memory region for address " + addr +
95+
" has no read or write permissions";
96+
}
7997
} else {
8098
is_data_ok = false;
8199
response.description = "variable not found: " + args.name;

lldb/tools/lldb-dap/Handler/RequestHandler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,9 @@ class DataBreakpointInfoRequestHandler
432432
public:
433433
using RequestHandler::RequestHandler;
434434
static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; }
435+
FeatureSet GetSupportedFeatures() const override {
436+
return {protocol::eAdapterFeatureDataBreakpointBytes};
437+
}
435438
llvm::Expected<protocol::DataBreakpointInfoResponseBody>
436439
Run(const protocol::DataBreakpointInfoArguments &args) const override;
437440
};

0 commit comments

Comments
 (0)