Skip to content

Commit 2cacc7a

Browse files
authored
[lldb-dap] Deduplicate watchpoints starting at the same address on SetDataBreakpointsRequest. (#83192)
If a SetDataBreakpointsRequest contains a list data breakpoints which have duplicate starting addresses, the current behaviour is returning `{verified: true}` to both watchpoints with duplicated starting addresses. This confuses the client and what actually happens in lldb is the second one overwrite the first one. This fixes it by letting the last watchpoint at given address have `{verified: true}` and all previous watchpoints at the same address should have `{verfied: false}` at response.
1 parent b81bb0e commit 2cacc7a

File tree

4 files changed

+78
-11
lines changed

4 files changed

+78
-11
lines changed

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,51 @@ def setUp(self):
1212
lldbdap_testcase.DAPTestCaseBase.setUp(self)
1313
self.accessTypes = ["read", "write", "readWrite"]
1414

15+
@skipIfWindows
16+
@skipIfRemote
17+
def test_duplicate_start_addresses(self):
18+
"""Test setDataBreakpoints with multiple watchpoints starting at the same addresses."""
19+
program = self.getBuildArtifact("a.out")
20+
self.build_and_launch(program)
21+
source = "main.cpp"
22+
first_loop_break_line = line_number(source, "// first loop breakpoint")
23+
self.set_source_breakpoints(source, [first_loop_break_line])
24+
self.continue_to_next_stop()
25+
self.dap_server.get_stackFrame()
26+
# Test setting write watchpoint using expressions: &x, arr+2
27+
response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
28+
response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
29+
# Test response from dataBreakpointInfo request.
30+
self.assertEquals(response_x["body"]["dataId"].split("/")[1], "4")
31+
self.assertEquals(response_x["body"]["accessTypes"], self.accessTypes)
32+
self.assertEquals(response_arr_2["body"]["dataId"].split("/")[1], "4")
33+
self.assertEquals(response_arr_2["body"]["accessTypes"], self.accessTypes)
34+
# The first one should be overwritten by the third one as they start at
35+
# the same address. This is indicated by returning {verified: False} for
36+
# the first one.
37+
dataBreakpoints = [
38+
{"dataId": response_x["body"]["dataId"], "accessType": "read"},
39+
{"dataId": response_arr_2["body"]["dataId"], "accessType": "write"},
40+
{"dataId": response_x["body"]["dataId"], "accessType": "write"},
41+
]
42+
set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints)
43+
self.assertEquals(
44+
set_response["body"]["breakpoints"],
45+
[{"verified": False}, {"verified": True}, {"verified": True}],
46+
)
47+
48+
self.continue_to_next_stop()
49+
x_val = self.dap_server.get_local_variable_value("x")
50+
i_val = self.dap_server.get_local_variable_value("i")
51+
self.assertEquals(x_val, "2")
52+
self.assertEquals(i_val, "1")
53+
54+
self.continue_to_next_stop()
55+
arr_2 = self.dap_server.get_local_variable_child("arr", "[2]")
56+
i_val = self.dap_server.get_local_variable_value("i")
57+
self.assertEquals(arr_2["value"], "42")
58+
self.assertEquals(i_val, "2")
59+
1560
@skipIfWindows
1661
@skipIfRemote
1762
def test_expression(self):

lldb/tools/lldb-dap/Watchpoint.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,11 @@ Watchpoint::Watchpoint(const llvm::json::Object &obj) : BreakpointBase(obj) {
1616
llvm::StringRef dataId = GetString(obj, "dataId");
1717
std::string accessType = GetString(obj, "accessType").str();
1818
auto [addr_str, size_str] = dataId.split('/');
19-
lldb::addr_t addr;
20-
size_t size;
2119
llvm::to_integer(addr_str, addr, 16);
2220
llvm::to_integer(size_str, size);
23-
lldb::SBWatchpointOptions options;
2421
options.SetWatchpointTypeRead(accessType != "write");
2522
if (accessType != "read")
2623
options.SetWatchpointTypeWrite(lldb::eWatchpointWriteTypeOnModify);
27-
wp = g_dap.target.WatchpointCreateByAddress(addr, size, options, error);
28-
SetCondition();
29-
SetHitCondition();
3024
}
3125

3226
void Watchpoint::SetCondition() { wp.SetCondition(condition.c_str()); }
@@ -38,11 +32,20 @@ void Watchpoint::SetHitCondition() {
3832
}
3933

4034
void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
41-
if (error.Success()) {
42-
object.try_emplace("verified", true);
43-
} else {
35+
if (!error.IsValid() || error.Fail()) {
4436
object.try_emplace("verified", false);
45-
EmplaceSafeString(object, "message", error.GetCString());
37+
if (error.Fail())
38+
EmplaceSafeString(object, "message", error.GetCString());
39+
} else {
40+
object.try_emplace("verified", true);
4641
}
4742
}
43+
44+
void Watchpoint::SetWatchpoint() {
45+
wp = g_dap.target.WatchpointCreateByAddress(addr, size, options, error);
46+
if (!condition.empty())
47+
SetCondition();
48+
if (!hitCondition.empty())
49+
SetHitCondition();
50+
}
4851
} // namespace lldb_dap

lldb/tools/lldb-dap/Watchpoint.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
namespace lldb_dap {
1818

1919
struct Watchpoint : public BreakpointBase {
20+
lldb::addr_t addr;
21+
size_t size;
22+
lldb::SBWatchpointOptions options;
2023
// The LLDB breakpoint associated wit this watchpoint.
2124
lldb::SBWatchpoint wp;
2225
lldb::SBError error;
@@ -28,6 +31,8 @@ struct Watchpoint : public BreakpointBase {
2831
void SetCondition() override;
2932
void SetHitCondition() override;
3033
void CreateJsonObject(llvm::json::Object &object) override;
34+
35+
void SetWatchpoint();
3136
};
3237
} // namespace lldb_dap
3338

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2880,15 +2880,29 @@ void request_setDataBreakpoints(const llvm::json::Object &request) {
28802880
const auto *breakpoints = arguments->getArray("breakpoints");
28812881
llvm::json::Array response_breakpoints;
28822882
g_dap.target.DeleteAllWatchpoints();
2883+
std::vector<Watchpoint> watchpoints;
28832884
if (breakpoints) {
28842885
for (const auto &bp : *breakpoints) {
28852886
const auto *bp_obj = bp.getAsObject();
28862887
if (bp_obj) {
28872888
Watchpoint wp(*bp_obj);
2888-
AppendBreakpoint(&wp, response_breakpoints);
2889+
watchpoints.push_back(wp);
28892890
}
28902891
}
28912892
}
2893+
// If two watchpoints start at the same address, the latter overwrite the
2894+
// former. So, we only enable those at first-seen addresses when iterating
2895+
// backward.
2896+
std::set<lldb::addr_t> addresses;
2897+
for (auto iter = watchpoints.rbegin(); iter != watchpoints.rend(); ++iter) {
2898+
if (addresses.count(iter->addr) == 0) {
2899+
iter->SetWatchpoint();
2900+
addresses.insert(iter->addr);
2901+
}
2902+
}
2903+
for (auto wp : watchpoints)
2904+
AppendBreakpoint(&wp, response_breakpoints);
2905+
28922906
llvm::json::Object body;
28932907
body.try_emplace("breakpoints", std::move(response_breakpoints));
28942908
response.try_emplace("body", std::move(body));

0 commit comments

Comments
 (0)