Skip to content

Commit 8160276

Browse files
authored
[lldb-dap] Synchronously wait for breakpoints resolves in tests (#140470)
Attempt to improve tests by synchronously waiting for breakpoints to resolve. Not sure if it will fix all the tests but I think it should make the tests more stable
1 parent 79da1c4 commit 8160276

File tree

6 files changed

+72
-10
lines changed

6 files changed

+72
-10
lines changed

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ def __init__(
166166
self.initialized = False
167167
self.frame_scopes = {}
168168
self.init_commands = init_commands
169+
self.resolved_breakpoints = {}
169170

170171
@classmethod
171172
def encode_content(cls, s: str) -> bytes:
@@ -296,6 +297,9 @@ def _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool:
296297
# and 'progressEnd' events. Keep these around in case test
297298
# cases want to verify them.
298299
self.progress_events.append(packet)
300+
elif event == "breakpoint":
301+
# Breakpoint events are sent when a breakpoint is resolved
302+
self._update_verified_breakpoints([body["breakpoint"]])
299303

300304
elif packet_type == "response":
301305
if packet["command"] == "disconnect":
@@ -309,6 +313,13 @@ def _process_continued(self, all_threads_continued: bool):
309313
if all_threads_continued:
310314
self.thread_stop_reasons = {}
311315

316+
def _update_verified_breakpoints(self, breakpoints: list[Event]):
317+
for breakpoint in breakpoints:
318+
if "id" in breakpoint:
319+
self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get(
320+
"verified", False
321+
)
322+
312323
def send_packet(self, command_dict: Request, set_sequence=True):
313324
"""Take the "command_dict" python dictionary and encode it as a JSON
314325
string and send the contents as a packet to the VSCode debug
@@ -454,6 +465,17 @@ def wait_for_breakpoint_events(self, timeout: Optional[float] = None):
454465
breakpoint_events.append(event)
455466
return breakpoint_events
456467

468+
def wait_for_breakpoints_to_be_verified(
469+
self, breakpoint_ids: list[str], timeout: Optional[float] = None
470+
):
471+
"""Wait for all breakpoints to be verified. Return all unverified breakpoints."""
472+
while any(id not in self.resolved_breakpoints for id in breakpoint_ids):
473+
breakpoint_event = self.wait_for_event("breakpoint", timeout=timeout)
474+
if breakpoint_event is None:
475+
break
476+
477+
return [id for id in breakpoint_ids if id not in self.resolved_breakpoints]
478+
457479
def wait_for_exited(self, timeout: Optional[float] = None):
458480
event_dict = self.wait_for_event("exited", timeout=timeout)
459481
if event_dict is None:
@@ -1013,7 +1035,10 @@ def request_setBreakpoints(self, source: Source, line_array, data=None):
10131035
"type": "request",
10141036
"arguments": args_dict,
10151037
}
1016-
return self.send_recv(command_dict)
1038+
response = self.send_recv(command_dict)
1039+
if response["success"]:
1040+
self._update_verified_breakpoints(response["body"]["breakpoints"])
1041+
return response
10171042

10181043
def request_setExceptionBreakpoints(self, filters):
10191044
args_dict = {"filters": filters}
@@ -1039,7 +1064,10 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non
10391064
"type": "request",
10401065
"arguments": args_dict,
10411066
}
1042-
return self.send_recv(command_dict)
1067+
response = self.send_recv(command_dict)
1068+
if response["success"]:
1069+
self._update_verified_breakpoints(response["body"]["breakpoints"])
1070+
return response
10431071

10441072
def request_dataBreakpointInfo(
10451073
self, variablesReference, name, frameIndex=0, threadId=None

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ def build_and_create_debug_adapter_for_attach(self):
4949
self.build_and_create_debug_adapter(dictionary={"EXE": unique_name})
5050
return self.getBuildArtifact(unique_name)
5151

52-
def set_source_breakpoints(self, source_path, lines, data=None):
52+
def set_source_breakpoints(
53+
self, source_path, lines, data=None, wait_for_resolve=True
54+
):
5355
"""Sets source breakpoints and returns an array of strings containing
5456
the breakpoint IDs ("1", "2") for each breakpoint that was set.
5557
Parameter data is array of data objects for breakpoints.
@@ -65,9 +67,13 @@ def set_source_breakpoints(self, source_path, lines, data=None):
6567
breakpoint_ids = []
6668
for breakpoint in breakpoints:
6769
breakpoint_ids.append("%i" % (breakpoint["id"]))
70+
if wait_for_resolve:
71+
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
6872
return breakpoint_ids
6973

70-
def set_source_breakpoints_assembly(self, source_reference, lines, data=None):
74+
def set_source_breakpoints_assembly(
75+
self, source_reference, lines, data=None, wait_for_resolve=True
76+
):
7177
response = self.dap_server.request_setBreakpoints(
7278
Source(source_reference=source_reference),
7379
lines,
@@ -79,9 +85,13 @@ def set_source_breakpoints_assembly(self, source_reference, lines, data=None):
7985
breakpoint_ids = []
8086
for breakpoint in breakpoints:
8187
breakpoint_ids.append("%i" % (breakpoint["id"]))
88+
if wait_for_resolve:
89+
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
8290
return breakpoint_ids
8391

84-
def set_function_breakpoints(self, functions, condition=None, hitCondition=None):
92+
def set_function_breakpoints(
93+
self, functions, condition=None, hitCondition=None, wait_for_resolve=True
94+
):
8595
"""Sets breakpoints by function name given an array of function names
8696
and returns an array of strings containing the breakpoint IDs
8797
("1", "2") for each breakpoint that was set.
@@ -95,8 +105,22 @@ def set_function_breakpoints(self, functions, condition=None, hitCondition=None)
95105
breakpoint_ids = []
96106
for breakpoint in breakpoints:
97107
breakpoint_ids.append("%i" % (breakpoint["id"]))
108+
if wait_for_resolve:
109+
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
98110
return breakpoint_ids
99111

112+
def wait_for_breakpoints_to_resolve(
113+
self, breakpoint_ids: list[str], timeout: Optional[float] = DEFAULT_TIMEOUT
114+
):
115+
unresolved_breakpoints = self.dap_server.wait_for_breakpoints_to_be_verified(
116+
breakpoint_ids, timeout
117+
)
118+
self.assertEqual(
119+
len(unresolved_breakpoints),
120+
0,
121+
f"Expected to resolve all breakpoints. Unresolved breakpoint ids: {unresolved_breakpoints}",
122+
)
123+
100124
def waitUntil(self, condition_callback):
101125
for _ in range(20):
102126
if condition_callback():

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import os
1313

1414

15-
@skip("Temporarily disable the breakpoint tests")
1615
class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
1716
def setUp(self):
1817
lldbdap_testcase.DAPTestCaseBase.setUp(self)

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import lldbdap_testcase
1111

1212

13-
@skip("Temporarily disable the breakpoint tests")
1413
class TestDAP_setFunctionBreakpoints(lldbdap_testcase.DAPTestCaseBase):
1514
@skipIfWindows
1615
def test_set_and_clear(self):

lldb/test/API/tools/lldb-dap/module/TestDAP_module.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ def run_test(self, symbol_basename, expect_debug_info_size):
1616
program = self.getBuildArtifact(program_basename)
1717
self.build_and_launch(program)
1818
functions = ["foo"]
19-
breakpoint_ids = self.set_function_breakpoints(functions)
19+
20+
# This breakpoint will be resolved only when the libfoo module is loaded
21+
breakpoint_ids = self.set_function_breakpoints(
22+
functions, wait_for_resolve=False
23+
)
2024
self.assertEqual(len(breakpoint_ids), len(functions), "expect one breakpoint")
2125
self.continue_to_breakpoints(breakpoint_ids)
2226
active_modules = self.dap_server.get_modules()

lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,18 @@ def test_terminated_event(self):
3535
self.build_and_launch(program)
3636
# Set breakpoints
3737
functions = ["foo"]
38-
breakpoint_ids = self.set_function_breakpoints(functions)
38+
39+
# This breakpoint will be resolved only when the libfoo module is loaded
40+
breakpoint_ids = self.set_function_breakpoints(
41+
functions, wait_for_resolve=False
42+
)
3943
self.assertEqual(len(breakpoint_ids), len(functions), "expect one breakpoint")
4044
main_bp_line = line_number("main.cpp", "// main breakpoint 1")
41-
breakpoint_ids.append(self.set_source_breakpoints("main.cpp", [main_bp_line]))
45+
breakpoint_ids.append(
46+
self.set_source_breakpoints(
47+
"main.cpp", [main_bp_line], wait_for_resolve=False
48+
)
49+
)
4250

4351
self.continue_to_breakpoints(breakpoint_ids)
4452
self.continue_to_exit()

0 commit comments

Comments
 (0)