From 47de54e9a2cb2b8433f53e751e1c3413fcd7aa78 Mon Sep 17 00:00:00 2001 From: Wanyi Date: Mon, 28 Apr 2025 18:32:51 -0400 Subject: [PATCH] [lldb-dap] Fix TestDap_attach.py flakiness (#137278) # Summary This patch makes the `request_attach` wait for events `process` and `initialized` just like `request_launch`. This ensure the DAP session can move forward somewhat correctly. Recently `TestDap_attach.test_terminate_commands` became flaky. It's hitting: ``` lldbsuite/test/tools/lldb-dap/dap_server.py", line 350, in send_recv raise ValueError(desc) ValueError: no response for "disconnect" ``` I took a look at the DAP msg from that test case and noticed: - It's not using the regular attaching, instead it's using the `attachCommands` to launch debug the binary and it will stop at entry. - The `initialized` event returned after the `disconnect` request. Which means lldb-dap didn't really get ready yet. ### NOTE The `dap_server.py` is doing things to mimic the VSCode (or other dap clients) but it had some assumptions. For example, it's still missing the `configurationDone` request and response because it relies on a continue action to trigger the `configurationDone` request. # Test Plan ``` ./bin/llvm-lit -va /Users/wanyi/llvm-upstream/llvm-project/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py ./bin/llvm-lit -va /Users/wanyi/llvm-upstream/llvm-project/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py ``` To test the `wait_for_events` timeout case ``` events = self.wait_for_events(["process", "initialized", "fake", "event"], 1) if events: raise ValueError(f'no events {",".join(events)} found for within timeout 1') ``` Observed image ### Also Looks like some test cases should be re-enabled in https://github.com/llvm/llvm-project/commit/0b8dfb5762fdc350c5071c9eeffd4f7e4d495e98 But only comments was removed. The skip statements survived the change. --- .../test/tools/lldb-dap/dap_server.py | 21 +++++++++++++++---- .../tools/lldb-dap/attach/TestDAP_attach.py | 3 --- 2 files changed, 17 insertions(+), 7 deletions(-) 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 99fcc423894b..6d9ab770684f 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 @@ -380,6 +380,17 @@ class DebugCommunication(object): ) return None + def wait_for_events(self, events, timeout=None): + """Wait for a list of events in `events` in any order. + Return the events not hit before the timeout expired""" + events = events[:] # Make a copy to avoid modifying the input + while events: + event_dict = self.wait_for_event(filter=events, timeout=timeout) + if event_dict is None: + break + events.remove(event_dict["event"]) + return events + def wait_for_stopped(self, timeout=None): stopped_events = [] stopped_event = self.wait_for_event( @@ -618,7 +629,11 @@ class DebugCommunication(object): if gdbRemoteHostname is not None: args_dict["gdb-remote-hostname"] = gdbRemoteHostname command_dict = {"command": "attach", "type": "request", "arguments": args_dict} - return self.send_recv(command_dict) + response = self.send_recv(command_dict) + + if response["success"]: + self.wait_for_events(["process", "initialized"]) + return response def request_breakpointLocations( self, file_path, line, end_line=None, column=None, end_column=None @@ -872,9 +887,7 @@ class DebugCommunication(object): response = self.send_recv(command_dict) if response["success"]: - # Wait for a 'process' and 'initialized' event in any order - self.wait_for_event(filter=["process", "initialized"]) - self.wait_for_event(filter=["process", "initialized"]) + self.wait_for_events(["process", "initialized"]) return response def request_next(self, threadId, granularity="statement"): diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index b9fbf2c8d14f..dcdfada2ff4c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -93,7 +93,6 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): self.set_and_hit_breakpoint(continueToExit=True) @skipUnlessDarwin - @skipIfDarwin @skipIfNetBSD # Hangs on NetBSD as well def test_by_name_waitFor(self): """ @@ -114,7 +113,6 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): self.attach(program=program, waitFor=True) self.set_and_hit_breakpoint(continueToExit=True) - @skipIfDarwin @skipIfNetBSD # Hangs on NetBSD as well def test_commands(self): """ @@ -201,7 +199,6 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): self.verify_commands("exitCommands", output, exitCommands) self.verify_commands("terminateCommands", output, terminateCommands) - @skipIfDarwin @skipIfNetBSD # Hangs on NetBSD as well @skipIf( archs=["arm", "aarch64"]