Skip to content

Commit 0fb3b5d

Browse files
committed
[lldb-dap] Correctly report breakpoints as resolved on macOS (llvm#129589)
On macOS, breakpoints are briefly unresolved between process launch and when the dynamic loader has informed us about the loaded libraries. This information was being forwarded by lldb-dap, but only partially. In the event handler, we were listening for the `LocationsAdded` and `LocationsRemoved` breakpoint events. For the scenario described above, the latter would trigger and we'd send an event reporting the breakpoint as unresolved. The problem is that when the breakpoint location is resolved again, you receive a `LocationsResolved` event, not a `LocationsAdded` event. As a result, the breakpoint would continue to show up as unresolved in the DAP client. I found a test that tried to test part of this behavior, but the test was broken and disabled. I revived the test and added coverage for the situation described above. Fixes llvm#112629 rdar://137968318 (cherry picked from commit d654d37)
1 parent 3a02857 commit 0fb3b5d

File tree

2 files changed

+57
-56
lines changed

2 files changed

+57
-56
lines changed

lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Test lldb-dap setBreakpoints request
33
"""
44

5-
65
import dap_server
76
from lldbsuite.test.decorators import *
87
from lldbsuite.test.lldbtest import *
@@ -12,9 +11,7 @@
1211

1312

1413
class TestDAP_breakpointEvents(lldbdap_testcase.DAPTestCaseBase):
15-
@skipIfWindows
1614
@skipUnlessDarwin
17-
@expectedFailureAll(macos_version=[">=", "13.0"])
1815
def test_breakpoint_events(self):
1916
"""
2017
This test sets a breakpoint in a shared library and runs and stops
@@ -63,70 +60,73 @@ def test_breakpoint_events(self):
6360
response = self.dap_server.request_setBreakpoints(
6461
main_source_path, [main_bp_line]
6562
)
66-
if response:
67-
breakpoints = response["body"]["breakpoints"]
68-
for breakpoint in breakpoints:
69-
main_bp_id = breakpoint["id"]
70-
dap_breakpoint_ids.append("%i" % (main_bp_id))
71-
# line = breakpoint['line']
72-
self.assertTrue(
73-
breakpoint["verified"], "expect main breakpoint to be verified"
74-
)
63+
self.assertTrue(response)
64+
breakpoints = response["body"]["breakpoints"]
65+
for breakpoint in breakpoints:
66+
main_bp_id = breakpoint["id"]
67+
dap_breakpoint_ids.append("%i" % (main_bp_id))
68+
self.assertTrue(
69+
breakpoint["verified"], "expect main breakpoint to be verified"
70+
)
7571

7672
response = self.dap_server.request_setBreakpoints(
7773
foo_source_path, [foo_bp1_line]
7874
)
79-
if response:
80-
breakpoints = response["body"]["breakpoints"]
81-
for breakpoint in breakpoints:
82-
foo_bp_id = breakpoint["id"]
83-
dap_breakpoint_ids.append("%i" % (foo_bp_id))
84-
self.assertFalse(
85-
breakpoint["verified"], "expect foo breakpoint to not be verified"
86-
)
75+
self.assertTrue(response)
76+
breakpoints = response["body"]["breakpoints"]
77+
for breakpoint in breakpoints:
78+
foo_bp_id = breakpoint["id"]
79+
dap_breakpoint_ids.append("%i" % (foo_bp_id))
80+
self.assertFalse(
81+
breakpoint["verified"], "expect foo breakpoint to not be verified"
82+
)
8783

8884
# Get the stop at the entry point
8985
self.continue_to_next_stop()
9086

9187
# We are now stopped at the entry point to the program. Shared
92-
# libraries are not loaded yet (at least on macOS they aren't) and any
93-
# breakpoints set in foo.cpp should not be resolved.
88+
# libraries are not loaded yet (at least on macOS they aren't) and only
89+
# the breakpoint in the main executable should be resolved.
90+
self.assertEqual(len(self.dap_server.breakpoint_events), 1)
91+
event = self.dap_server.breakpoint_events[0]
92+
body = event["body"]
9493
self.assertEqual(
95-
len(self.dap_server.breakpoint_events),
96-
0,
97-
"no breakpoint events when stopped at entry point",
94+
body["reason"], "changed", "breakpoint event should say changed"
9895
)
96+
breakpoint = body["breakpoint"]
97+
self.assertEqual(breakpoint["id"], main_bp_id)
98+
self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved")
99+
100+
# Clear the list of breakpoint events so we don't see this one again.
101+
self.dap_server.breakpoint_events.clear()
99102

100103
# Continue to the breakpoint
101104
self.continue_to_breakpoints(dap_breakpoint_ids)
102105

103-
# Make sure we only get an event for the breakpoint we set via a call
104-
# to self.dap_server.request_setBreakpoints(...), not the breakpoint
105-
# we set with with a LLDB command in preRunCommands.
106-
self.assertEqual(
107-
len(self.dap_server.breakpoint_events),
108-
1,
109-
"make sure we got a breakpoint event",
110-
)
111-
event = self.dap_server.breakpoint_events[0]
112-
# Verify the details of the breakpoint changed notification.
113-
body = event["body"]
114-
self.assertEqual(
115-
body["reason"], "changed", "breakpoint event is says breakpoint is changed"
116-
)
117-
breakpoint = body["breakpoint"]
118-
self.assertTrue(
119-
breakpoint["verified"], "breakpoint event is says it is verified"
120-
)
121-
self.assertEqual(
122-
breakpoint["id"],
123-
foo_bp_id,
124-
"breakpoint event is for breakpoint %i" % (foo_bp_id),
125-
)
126-
self.assertTrue(
127-
"line" in breakpoint and breakpoint["line"] > 0,
128-
"breakpoint event is has a line number",
129-
)
130-
self.assertNotIn(
131-
"source", breakpoint, "breakpoint event should not return a source object"
132-
)
106+
# When the process launches, we first expect to see both the main and
107+
# foo breakpoint as unresolved.
108+
for event in self.dap_server.breakpoint_events[:2]:
109+
body = event["body"]
110+
self.assertEqual(
111+
body["reason"], "changed", "breakpoint event should say changed"
112+
)
113+
breakpoint = body["breakpoint"]
114+
self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids)
115+
self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved")
116+
117+
# Then, once the dynamic loader has given us a load address, they
118+
# should show up as resolved again.
119+
for event in self.dap_server.breakpoint_events[3:]:
120+
body = event["body"]
121+
self.assertEqual(
122+
body["reason"], "changed", "breakpoint event should say changed"
123+
)
124+
breakpoint = body["breakpoint"]
125+
self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids)
126+
self.assertTrue(breakpoint["verified"], "breakpoint should be resolved")
127+
self.assertNotIn(
128+
"source",
129+
breakpoint,
130+
"breakpoint event should not return a source object",
131+
)
132+
self.assertIn("line", breakpoint, "breakpoint event should have line")

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,8 @@ void EventThreadFunction(DAP &dap) {
530530
// of wether the locations were added or removed, the breakpoint
531531
// ins't going away, so we the reason is always "changed".
532532
if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
533-
event_type & lldb::eBreakpointEventTypeLocationsRemoved) &&
533+
event_type & lldb::eBreakpointEventTypeLocationsRemoved ||
534+
event_type & lldb::eBreakpointEventTypeLocationsResolved) &&
534535
bp.MatchesName(BreakpointBase::GetBreakpointLabel())) {
535536
auto bp_event = CreateEventObject("breakpoint");
536537
llvm::json::Object body;

0 commit comments

Comments
 (0)