diff mbox series

oe-selftest: devtool ide-sdk: fix lldb connect race in _lldb_server_debugging_once

Message ID 20260807093724.1449937-1-adrian.freihofer@siemens.com
State Under Review
Headers show
Series oe-selftest: devtool ide-sdk: fix lldb connect race in _lldb_server_debugging_once | expand

Commit Message

Freihofer, Adrian Aug. 7, 2026, 9:37 a.m. UTC
From: Adrian Freihofer <adrian.freihofer@siemens.com>

Replace the fixed time.sleep(1) + "ps" check with a socket-based polling
loop that probes the lldb-server TCP port directly. The old approach would
declare lldb-server "ready" as soon as the process appeared in ps, but the
listening socket may not yet be bound at that point. On a loaded autobuilder
with several concurrent QEMU instances the resulting race caused sporadic
"Failed to connect" failures in test_devtool_ide_sdk_code_meson_clang (and
the other lldb-based variants).

The new loop attempts socket.create_connection() every 0.5 s for up to 30 s.
A successful TCP handshake guarantees the port is open before lldb --batch
is invoked, eliminating the race.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 44 +++++++++++++++++++++----
 1 file changed, 37 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 7145755fed..ac176d6f26 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -8,6 +8,7 @@  import errno
 import os
 import re
 import shutil
+import socket
 import tempfile
 import time
 import glob
@@ -4218,14 +4219,43 @@  class DevtoolIdeSdkTests(DevtoolBase):
         lldb_binary = os.path.join(lldb_native_sysroot, 'usr', 'bin', 'lldb')
         self.assertExists(lldb_binary, "lldb binary should exist in lldb-native sysroot")
 
-        with RunCmdBackground(ssh_cmd, output_log=self._cmd_logger):
-            time.sleep(1)
+        # Parse host and port from the "platform connect connect://host:port" command
+        connect_match = re.search(r'connect://([^:]+):(\d+)', connect_cmd)
+        self.assertIsNotNone(connect_match, "Could not parse host:port from: %s" % connect_cmd)
+        lldb_server_host = connect_match.group(1)
+        lldb_server_port = int(connect_match.group(2))
 
-            # Verify lldb-server is running on the target
-            r = runCmd('ssh %s root@%s ps' % (sshargs, qemu.ip),
-                       output_log=self._cmd_logger)
-            self.assertIn("lldb-server", r.output,
-                          "lldb-server should be running on target")
+        self.logger.debug("Starting lldb-server via SSH: %s", " ".join(ssh_cmd))
+        with RunCmdBackground(ssh_cmd, output_log=self._cmd_logger):
+            # Poll the lldb-server port instead of sleeping a fixed amount.
+            # On a loaded autobuilder the process may take several seconds to
+            # bind its listening socket even after appearing in "ps".
+            t_start = time.monotonic()
+            deadline = t_start + 30
+            while True:
+                try:
+                    with socket.create_connection((lldb_server_host, lldb_server_port), timeout=1):
+                        break
+                except OSError as e:
+                    elapsed = time.monotonic() - t_start
+                    if time.monotonic() >= deadline:
+                        diag = ""
+                        try:
+                            r_ps = runCmd(
+                                'ssh %s root@%s "ps && echo --- && cat /proc/net/tcp /proc/net/tcp6 2>/dev/null"'
+                                % (sshargs, lldb_server_host))
+                            diag = "\nTarget diagnostics:\n" + r_ps.output
+                        except Exception:
+                            pass
+                        self.fail(
+                            "lldb-server did not start listening on %s:%d within 30s "
+                            "(last error: %s)%s" % (lldb_server_host, lldb_server_port, e, diag))
+                    self.logger.debug(
+                        "Waiting for lldb-server on %s:%d (%.1fs elapsed, error: %s)",
+                        lldb_server_host, lldb_server_port, elapsed, e)
+                    time.sleep(0.5)
+            self.logger.debug("lldb-server ready on %s:%d after %.1fs",
+                              lldb_server_host, lldb_server_port, time.monotonic() - t_start)
 
             # Run lldb --batch: connect to platform, create target with remote-file,
             # set a source-level breakpoint, and run.