diff mbox series

[v2,2/8] oe-selftest: devtool ide-sdk: wait for lldb-server readiness

Message ID 20260809093629.3457107-3-adrian.freihofer@siemens.com
State New
Headers show
Series oe-selftest: devtool ide-sdk: fix lldb connect race in _lldb_server_debugging_once | expand

Commit Message

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

The generated MULTI preLaunchTask starts lldb-server on the target and waits
for its TCP port to appear before its SSH command completes. The selftest
bypassed that contract by starting SSH in the background, sleeping for one
second, and only checking that lldb-server appeared in ps. On a loaded
autobuilder this allowed lldb --batch to connect before lldb-server had bound
its listening socket.

Run the generated preLaunchTask SSH command synchronously before lldb --batch.
This follows VS Code's foreground-task behavior and leaves listener readiness
and timeout handling in _target_start_cmd(), where the server is started.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 78 ++++++++++++-------------
 1 file changed, 36 insertions(+), 42 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..d84a18e8b6 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -4175,8 +4175,6 @@  class DevtoolIdeSdkTests(DevtoolBase):
         (see _lldb_debug_cpp_example_batch_commands), and checks that the
         expected magic string and variable values are visible.
         """
-        sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
-
         with open(os.path.join(tempdir, '.vscode', 'launch.json')) as f:
             launch_d = json.load(f)
         with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as f:
@@ -4208,7 +4206,9 @@  class DevtoolIdeSdkTests(DevtoolBase):
             # to SSH via subprocess without an intermediate shell.
             ssh_cmd[-1] = ssh_cmd[-1][1:-1].replace('\\$', '$')
 
-        # Extract connection details from initCommands
+        # The generated foreground preLaunchTask does not complete until its
+        # target-side command has observed lldb-server listening.  Run it
+        # synchronously so this selftest follows the same ordering as VS Code.
         init_commands = lldb_config["initCommands"]
         connect_cmd = next((c for c in init_commands if "platform connect" in c), None)
         self.assertIsNotNone(connect_cmd, "initCommands should contain a platform connect command")
@@ -4218,47 +4218,41 @@  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)
+        self.logger.debug("Starting lldb-server via SSH: %s", " ".join(ssh_cmd))
+        runCmd(ssh_cmd, output_log=self._cmd_logger)
 
-            # 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")
+        # Run lldb --batch: connect to platform, create target with remote-file,
+        # set a source-level breakpoint, and run.
+        # targetCreateCommands replaces the "program" field; each entry is
+        # passed as a separate -o command in batch mode.
+        target_create_commands = lldb_config.get("targetCreateCommands", [])
+        source_map = lldb_config.get("sourceMap", {})
 
-            # Run lldb --batch: connect to platform, create target with remote-file,
-            # set a source-level breakpoint, and run.
-            # targetCreateCommands replaces the "program" field; each entry is
-            # passed as a separate -o command in batch mode.
-            target_create_commands = lldb_config.get("targetCreateCommands", [])
-            source_map = lldb_config.get("sourceMap", {})
-
-            lldb_batch = [lldb_binary, "--batch"]
-            for cmd in init_commands:
-                lldb_batch += ["-o", cmd]
-            for cmd in target_create_commands:
-                lldb_batch += ["-o", cmd]
-            if source_map:
-                # "settings set target.source-map" replaces the *entire*
-                # mapping list rather than appending to it. Issuing one
-                # "-o settings set target.source-map ..." per entry (as done
-                # previously) silently discards all but the last mapping, so
-                # LLDB ends up resolving source files (and verifying their
-                # DWARF MD5 checksum) against the wrong location, e.g. a
-                # stale rootfs-dbg copy of a devtool-modified recipe's own
-                # sources instead of the freshly edited workspace srctree.
-                # All pairs must therefore be set together in a single
-                # command, exactly like CodeLLDB itself does.
-                source_map_args = []
-                for k, v in source_map.items():
-                    v_resolved = v.replace("${workspaceFolder}", tempdir)
-                    source_map_args += [k, v_resolved]
-                lldb_batch += ["-o", "settings set target.source-map %s" % " ".join(source_map_args)]
-            lldb_batch += self._lldb_debug_cpp_example_batch_commands(tempdir)
-            r = runCmd(lldb_batch, output_log=self._cmd_logger)
-            self.assertEqual(r.status, 0, "lldb batch session failed: %s" % r.output)
-            self._lldb_debug_cpp_example_check(r.output, magic_string)
+        lldb_batch = [lldb_binary, "--batch"]
+        for cmd in init_commands:
+            lldb_batch += ["-o", cmd]
+        for cmd in target_create_commands:
+            lldb_batch += ["-o", cmd]
+        if source_map:
+            # "settings set target.source-map" replaces the *entire*
+            # mapping list rather than appending to it. Issuing one
+            # "-o settings set target.source-map ..." per entry (as done
+            # previously) silently discards all but the last mapping, so
+            # LLDB ends up resolving source files (and verifying their
+            # DWARF MD5 checksum) against the wrong location, e.g. a
+            # stale rootfs-dbg copy of a devtool-modified recipe's own
+            # sources instead of the freshly edited workspace srctree.
+            # All pairs must therefore be set together in a single
+            # command, exactly like CodeLLDB itself does.
+            source_map_args = []
+            for k, v in source_map.items():
+                v_resolved = v.replace("${workspaceFolder}", tempdir)
+                source_map_args += [k, v_resolved]
+            lldb_batch += ["-o", "settings set target.source-map %s" % " ".join(source_map_args)]
+        lldb_batch += self._lldb_debug_cpp_example_batch_commands(tempdir)
+        r = runCmd(lldb_batch, output_log=self._cmd_logger)
+        self.assertEqual(r.status, 0, "lldb batch session failed: %s" % r.output)
+        self._lldb_debug_cpp_example_check(r.output, magic_string)
 
     @OETestTag("runqemu")
     def test_devtool_ide_sdk_code_cmake_clang(self):