Author: Ely Ronnen
Date: 2025-05-31T16:59:46+02:00
New Revision: 81602769d830e6791200e8cc7dd10a3afc32570b

URL: 
https://github.com/llvm/llvm-project/commit/81602769d830e6791200e8cc7dd10a3afc32570b
DIFF: 
https://github.com/llvm/llvm-project/commit/81602769d830e6791200e8cc7dd10a3afc32570b.diff

LOG: [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

Added: 
    

Modified: 
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
    lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
    lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py
    lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
    lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py

Removed: 
    


################################################################################
diff  --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 4c8c51905e1d0..6b41aef2bb5b8 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -166,6 +166,7 @@ def __init__(
         self.initialized = False
         self.frame_scopes = {}
         self.init_commands = init_commands
+        self.resolved_breakpoints = {}
 
     @classmethod
     def encode_content(cls, s: str) -> bytes:
@@ -296,6 +297,9 @@ def _handle_recv_packet(self, packet: 
Optional[ProtocolMessage]) -> bool:
                 # and 'progressEnd' events. Keep these around in case test
                 # cases want to verify them.
                 self.progress_events.append(packet)
+            elif event == "breakpoint":
+                # Breakpoint events are sent when a breakpoint is resolved
+                self._update_verified_breakpoints([body["breakpoint"]])
 
         elif packet_type == "response":
             if packet["command"] == "disconnect":
@@ -309,6 +313,13 @@ def _process_continued(self, all_threads_continued: bool):
         if all_threads_continued:
             self.thread_stop_reasons = {}
 
+    def _update_verified_breakpoints(self, breakpoints: list[Event]):
+        for breakpoint in breakpoints:
+            if "id" in breakpoint:
+                self.resolved_breakpoints[str(breakpoint["id"])] = 
breakpoint.get(
+                    "verified", False
+                )
+
     def send_packet(self, command_dict: Request, set_sequence=True):
         """Take the "command_dict" python dictionary and encode it as a JSON
         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):
             breakpoint_events.append(event)
         return breakpoint_events
 
+    def wait_for_breakpoints_to_be_verified(
+        self, breakpoint_ids: list[str], timeout: Optional[float] = None
+    ):
+        """Wait for all breakpoints to be verified. Return all unverified 
breakpoints."""
+        while any(id not in self.resolved_breakpoints for id in 
breakpoint_ids):
+            breakpoint_event = self.wait_for_event("breakpoint", 
timeout=timeout)
+            if breakpoint_event is None:
+                break
+
+        return [id for id in breakpoint_ids if id not in 
self.resolved_breakpoints]
+
     def wait_for_exited(self, timeout: Optional[float] = None):
         event_dict = self.wait_for_event("exited", timeout=timeout)
         if event_dict is None:
@@ -1013,7 +1035,10 @@ def request_setBreakpoints(self, source: Source, 
line_array, data=None):
             "type": "request",
             "arguments": args_dict,
         }
-        return self.send_recv(command_dict)
+        response = self.send_recv(command_dict)
+        if response["success"]:
+            self._update_verified_breakpoints(response["body"]["breakpoints"])
+        return response
 
     def request_setExceptionBreakpoints(self, filters):
         args_dict = {"filters": filters}
@@ -1039,7 +1064,10 @@ def request_setFunctionBreakpoints(self, names, 
condition=None, hitCondition=Non
             "type": "request",
             "arguments": args_dict,
         }
-        return self.send_recv(command_dict)
+        response = self.send_recv(command_dict)
+        if response["success"]:
+            self._update_verified_breakpoints(response["body"]["breakpoints"])
+        return response
 
     def request_dataBreakpointInfo(
         self, variablesReference, name, frameIndex=0, threadId=None

diff  --git 
a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 91ae55977046b..e26dbf6ae514f 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -49,7 +49,9 @@ def build_and_create_debug_adapter_for_attach(self):
         self.build_and_create_debug_adapter(dictionary={"EXE": unique_name})
         return self.getBuildArtifact(unique_name)
 
-    def set_source_breakpoints(self, source_path, lines, data=None):
+    def set_source_breakpoints(
+        self, source_path, lines, data=None, wait_for_resolve=True
+    ):
         """Sets source breakpoints and returns an array of strings containing
         the breakpoint IDs ("1", "2") for each breakpoint that was set.
         Parameter data is array of data objects for breakpoints.
@@ -65,9 +67,13 @@ def set_source_breakpoints(self, source_path, lines, 
data=None):
         breakpoint_ids = []
         for breakpoint in breakpoints:
             breakpoint_ids.append("%i" % (breakpoint["id"]))
+        if wait_for_resolve:
+            self.wait_for_breakpoints_to_resolve(breakpoint_ids)
         return breakpoint_ids
 
-    def set_source_breakpoints_assembly(self, source_reference, lines, 
data=None):
+    def set_source_breakpoints_assembly(
+        self, source_reference, lines, data=None, wait_for_resolve=True
+    ):
         response = self.dap_server.request_setBreakpoints(
             Source(source_reference=source_reference),
             lines,
@@ -79,9 +85,13 @@ def set_source_breakpoints_assembly(self, source_reference, 
lines, data=None):
         breakpoint_ids = []
         for breakpoint in breakpoints:
             breakpoint_ids.append("%i" % (breakpoint["id"]))
+        if wait_for_resolve:
+            self.wait_for_breakpoints_to_resolve(breakpoint_ids)
         return breakpoint_ids
 
-    def set_function_breakpoints(self, functions, condition=None, 
hitCondition=None):
+    def set_function_breakpoints(
+        self, functions, condition=None, hitCondition=None, 
wait_for_resolve=True
+    ):
         """Sets breakpoints by function name given an array of function names
         and returns an array of strings containing the breakpoint IDs
         ("1", "2") for each breakpoint that was set.
@@ -95,8 +105,22 @@ def set_function_breakpoints(self, functions, 
condition=None, hitCondition=None)
         breakpoint_ids = []
         for breakpoint in breakpoints:
             breakpoint_ids.append("%i" % (breakpoint["id"]))
+        if wait_for_resolve:
+            self.wait_for_breakpoints_to_resolve(breakpoint_ids)
         return breakpoint_ids
 
+    def wait_for_breakpoints_to_resolve(
+        self, breakpoint_ids: list[str], timeout: Optional[float] = 
DEFAULT_TIMEOUT
+    ):
+        unresolved_breakpoints = 
self.dap_server.wait_for_breakpoints_to_be_verified(
+            breakpoint_ids, timeout
+        )
+        self.assertEqual(
+            len(unresolved_breakpoints),
+            0,
+            f"Expected to resolve all breakpoints. Unresolved breakpoint ids: 
{unresolved_breakpoints}",
+        )
+
     def waitUntil(self, condition_callback):
         for _ in range(20):
             if condition_callback():

diff  --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py 
b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
index 22470daac2887..831edd6494c1e 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
@@ -12,7 +12,6 @@
 import os
 
 
-@skip("Temporarily disable the breakpoint tests")
 class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
     def setUp(self):
         lldbdap_testcase.DAPTestCaseBase.setUp(self)

diff  --git 
a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py 
b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py
index baaca4d974d5d..946595f639edc 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py
@@ -10,7 +10,6 @@
 import lldbdap_testcase
 
 
-@skip("Temporarily disable the breakpoint tests")
 class TestDAP_setFunctionBreakpoints(lldbdap_testcase.DAPTestCaseBase):
     @skipIfWindows
     def test_set_and_clear(self):

diff  --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py 
b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
index 3fc0f752ee39e..4fc221668a8ee 100644
--- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
+++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
@@ -16,7 +16,11 @@ def run_test(self, symbol_basename, expect_debug_info_size):
         program = self.getBuildArtifact(program_basename)
         self.build_and_launch(program)
         functions = ["foo"]
-        breakpoint_ids = self.set_function_breakpoints(functions)
+
+        # This breakpoint will be resolved only when the libfoo module is 
loaded
+        breakpoint_ids = self.set_function_breakpoints(
+            functions, wait_for_resolve=False
+        )
         self.assertEqual(len(breakpoint_ids), len(functions), "expect one 
breakpoint")
         self.continue_to_breakpoints(breakpoint_ids)
         active_modules = self.dap_server.get_modules()

diff  --git 
a/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py 
b/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py
index b0abe2a38dac4..7de85bd1589cd 100644
--- a/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py
+++ b/lldb/test/API/tools/lldb-dap/terminated-event/TestDAP_terminatedEvent.py
@@ -35,10 +35,18 @@ def test_terminated_event(self):
         self.build_and_launch(program)
         # Set breakpoints
         functions = ["foo"]
-        breakpoint_ids = self.set_function_breakpoints(functions)
+
+        # This breakpoint will be resolved only when the libfoo module is 
loaded
+        breakpoint_ids = self.set_function_breakpoints(
+            functions, wait_for_resolve=False
+        )
         self.assertEqual(len(breakpoint_ids), len(functions), "expect one 
breakpoint")
         main_bp_line = line_number("main.cpp", "// main breakpoint 1")
-        breakpoint_ids.append(self.set_source_breakpoints("main.cpp", 
[main_bp_line]))
+        breakpoint_ids.append(
+            self.set_source_breakpoints(
+                "main.cpp", [main_bp_line], wait_for_resolve=False
+            )
+        )
 
         self.continue_to_breakpoints(breakpoint_ids)
         self.continue_to_exit()


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to