Skip to content

Commit 4ca1091

Browse files
SLTozermahesh-attarde
authored andcommitted
[lldb-dap] Allow returning multiple breakpoints in "stopped" event (llvm#149133)
Currently, the "stopped" event returned when a breakpoint is hit will always return only the ID of first breakpoint returned from `GetStopReasonDataAtIndex`. This is slightly different from the behaviour in `lldb`, where multiple breakpoints can exist at a single instruction address and all are returned as part of the stop reason when that address is hit. This patch allows all multiple hit breakpoints to be returned in the "stopped" event, both in the hitBreakpointIds field and in the description, using the same formatting as lldb e.g. "breakpoint 1.1 2.1". I'm not aware of any effect this will have on debugger plugins; as far as I can tell, it makes no difference within the VS Code UI - this just fixes a minor issue encountered while writing an `lldb-dap` backend for Dexter.
1 parent 3067a21 commit 4ca1091

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,28 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
173173
return
174174
self.assertTrue(False, f"breakpoint not hit, stopped_events={stopped_events}")
175175

176+
def verify_all_breakpoints_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
177+
"""Wait for the process we are debugging to stop, and verify we hit
178+
all of the breakpoint locations in the "breakpoint_ids" array.
179+
"breakpoint_ids" should be a list of int breakpoint IDs ([1, 2])."""
180+
stopped_events = self.dap_server.wait_for_stopped(timeout)
181+
for stopped_event in stopped_events:
182+
if "body" in stopped_event:
183+
body = stopped_event["body"]
184+
if "reason" not in body:
185+
continue
186+
if (
187+
body["reason"] != "breakpoint"
188+
and body["reason"] != "instruction breakpoint"
189+
):
190+
continue
191+
if "hitBreakpointIds" not in body:
192+
continue
193+
hit_bps = body["hitBreakpointIds"]
194+
if all(breakpoint_id in hit_bps for breakpoint_id in breakpoint_ids):
195+
return
196+
self.assertTrue(False, f"breakpoints not hit, stopped_events={stopped_events}")
197+
176198
def verify_stop_exception_info(self, expected_description, timeout=DEFAULT_TIMEOUT):
177199
"""Wait for the process we are debugging to stop, and verify the stop
178200
reason is 'exception' and that the description matches

lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,26 @@ def test_column_breakpoints(self):
398398
self.stepIn()
399399
func_name = self.get_stackFrames()[0]["name"]
400400
self.assertEqual(func_name, "a::fourteen(int)")
401+
402+
@skipIfWindows
403+
def test_hit_multiple_breakpoints(self):
404+
"""Test that if we hit multiple breakpoints at the same address, they
405+
all appear in the stop reason."""
406+
breakpoint_lines = [
407+
line_number("main.cpp", "// break non-breakpointable line"),
408+
line_number("main.cpp", "// before loop"),
409+
]
410+
411+
program = self.getBuildArtifact("a.out")
412+
self.build_and_launch(program)
413+
414+
# Set a pair of breakpoints that will both resolve to the same address.
415+
breakpoint_ids = [
416+
int(bp_id)
417+
for bp_id in self.set_source_breakpoints(self.main_path, breakpoint_lines)
418+
]
419+
self.assertEqual(len(breakpoint_ids), 2, "expected two breakpoints")
420+
self.dap_server.request_continue()
421+
print(breakpoint_ids)
422+
# Verify we hit both of the breakpoints we just set
423+
self.verify_all_breakpoints_hit(breakpoint_ids)

lldb/test/API/tools/lldb-dap/breakpoint/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ int main(int argc, char const *argv[]) {
3333
if (foo == nullptr) {
3434
fprintf(stderr, "%s\n", dlerror());
3535
exit(2);
36-
}
36+
} // break non-breakpointable line
3737
foo(12); // before loop
3838

3939
for (int i = 0; i < 10; ++i) {

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -654,12 +654,17 @@ llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread,
654654
} else {
655655
body.try_emplace("reason", "breakpoint");
656656
}
657-
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
658-
lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
659-
std::string desc_str =
660-
llvm::formatv("breakpoint {0}.{1}", bp_id, bp_loc_id);
661-
body.try_emplace("hitBreakpointIds",
662-
llvm::json::Array{llvm::json::Value(bp_id)});
657+
std::vector<lldb::break_id_t> bp_ids;
658+
std::ostringstream desc_sstream;
659+
desc_sstream << "breakpoint";
660+
for (size_t idx = 0; idx < thread.GetStopReasonDataCount(); idx += 2) {
661+
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx);
662+
lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(idx + 1);
663+
bp_ids.push_back(bp_id);
664+
desc_sstream << " " << bp_id << "." << bp_loc_id;
665+
}
666+
std::string desc_str = desc_sstream.str();
667+
body.try_emplace("hitBreakpointIds", llvm::json::Array(bp_ids));
663668
EmplaceSafeString(body, "description", desc_str);
664669
}
665670
} break;

0 commit comments

Comments
 (0)