Skip to content

Conversation

@piyushjaiswal98
Copy link
Contributor

  • Fixed critical race condition in TestDAP_attach.py by adding proper thread synchronization and making unique binary files for attachment
  • Enhanced error handling for DAP server cleanup to prevent cascading failures.
  • Improved Error Message handling in lldbdap_testcase.py

@github-actions
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the lldb label Aug 20, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 20, 2025

@llvm/pr-subscribers-lldb

Author: Piyush Jaiswal (piyushjaiswal98)

Changes
  • Fixed critical race condition in TestDAP_attach.py by adding proper thread synchronization and making unique binary files for attachment
  • Enhanced error handling for DAP server cleanup to prevent cascading failures.
  • Improved Error Message handling in lldbdap_testcase.py

Full diff: https://github.com/llvm/llvm-project/pull/154632.diff

2 Files Affected:

  • (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py (+37-8)
  • (modified) lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py (+47-2)
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 c51b4b1892951..36471501ea666 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
@@ -1,15 +1,16 @@
 import os
 import time
-from typing import Optional
 import uuid
+from typing import Optional
 
 import dap_server
 from dap_server import Source
 from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbplatformutil
-import lldbgdbserverutils
 import base64
 
+import lldbgdbserverutils
+from lldbsuite.test import lldbplatformutil
+
 
 class DAPTestCaseBase(TestBase):
     # set timeout based on whether ASAN was enabled or not. Increase
@@ -451,8 +452,25 @@ def attach(
         # if we throw an exception during the test case.
         def cleanup():
             if disconnectAutomatically:
-                self.dap_server.request_disconnect(terminateDebuggee=True)
-            self.dap_server.terminate()
+                try:
+                    self.dap_server.request_disconnect(terminateDebuggee=True)
+                except (
+                    ValueError,
+                    TimeoutError,
+                    BrokenPipeError,
+                    ConnectionError,
+                    Exception,
+                ) as e:
+                    # DAP server might not be responsive, skip disconnect and terminate directly
+                    print(
+                        f"Warning: disconnect failed ({e}), skipping and terminating directly"
+                    )
+            try:
+                self.dap_server.terminate()
+            except Exception as e:
+                print(
+                    f"Warning: terminate failed ({e}), DAP server may have already died"
+                )
 
         # Execute the cleanup function during test case tear down.
         self.addTearDownHook(cleanup)
@@ -462,9 +480,20 @@ def cleanup():
         if expectFailure:
             return response
         if not (response and response["success"]):
-            self.assertTrue(
-                response["success"], "attach failed (%s)" % (response["message"])
-            )
+            error_msg = "attach failed"
+            if response:
+                if "message" in response:
+                    error_msg += " (%s)" % response["message"]
+                elif "body" in response and "error" in response["body"]:
+                    if "format" in response["body"]["error"]:
+                        error_msg += " (%s)" % response["body"]["error"]["format"]
+                    else:
+                        error_msg += " (error in body)"
+                else:
+                    error_msg += " (no error details available)"
+            else:
+                error_msg += " (no response)"
+            self.assertTrue(response and response["success"], error_msg)
 
     def launch(
         self,
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 55557e6e0030e..ade90dba5eabe 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -4,12 +4,13 @@
 
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-import lldbdap_testcase
 import subprocess
 import threading
 import time
 
+import lldbdap_testcase
+from lldbsuite.test import lldbutil
+
 
 def spawn_and_wait(program, delay):
     if delay:
@@ -227,3 +228,47 @@ def test_terminate_commands(self):
             pattern=terminateCommands[0],
         )
         self.verify_commands("terminateCommands", output, terminateCommands)
+
+    def test_session_id_update(self):
+        program = self.build_and_create_debug_adapter_for_attach()
+        self.process = subprocess.Popen(
+            [program],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+        )
+
+        postRunCommands = [
+            "script print('Actual_Session_ID: ' + str(os.getenv('VSCODE_DEBUG_SESSION_ID')))"
+        ]
+        self.attach(
+            pid=self.process.pid,
+            vscode_session_id="test_session_id",
+            postRunCommands=postRunCommands,
+        )
+        output = self.get_console()
+        lines = filter(lambda x: "Actual_Session_ID" in x, output.splitlines())
+        self.assertTrue(
+            any("test_session_id" in l for l in lines),
+            "expect session id in console output",
+        )
+
+    def test_session_id_update_empty(self):
+        program = self.build_and_create_debug_adapter_for_attach()
+
+        self.spawn_thread = threading.Thread(
+            target=spawn_and_wait,
+            args=(program, 0.1),
+        )
+        self.spawn_thread.start()
+
+        postRunCommands = [
+            "script print('Actual_Session_ID: ' + str(os.getenv('VSCODE_DEBUG_SESSION_ID', 'None')))"
+        ]
+        self.attach(program=program, postRunCommands=postRunCommands)
+        output = self.get_console()
+        lines = filter(lambda x: "Actual_Session_ID" in x, output.splitlines())
+        self.assertTrue(
+            any("Actual_Session_ID: None" in l for l in lines),
+            "expect session id in console output",
+        )

stderr=subprocess.PIPE,
)

postRunCommands = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the sake of readability, can you add a comment here about the env var VSCODE_DEBUG_SESSION_ID and how you are using it here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants