From patchwork Sun Aug 9 09:35:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94811 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D0DB6C5AD6A for ; Sun, 9 Aug 2026 09:36:49 +0000 (UTC) Received: from mta-64-226.siemens.flowmailer.net (mta-64-226.siemens.flowmailer.net [185.136.64.226]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.6424.1786268196863467459 for ; Sun, 09 Aug 2026 02:36:39 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=dnH1utVM; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-1329275-2026080909363308ec9d11800002073c-4pwry3@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 2026080909363308ec9d11800002073c for ; Sun, 09 Aug 2026 11:36:33 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=/KVFK8zdGoAkNiEcJTSxsKXK8/JXaOhLToBQaEk3T+U=; b=dnH1utVMkPtOiFMSWfMum7qyMu4eWKsa7VxfXFB8U5Z3aoePA26tZkDjqKiz0YsEPb17ZF bU2lzxvVlidxrAj++T7ddiQsxkGihScmMx6fL9B1QQcbpUyQu860WcP19xXenT+N8HYJx6pk OgwJUAYGD9+aPrA1FXcERh6X1mu/TeG2xno+HgkVuy+yClUVgeVUJ6Z2+bfuyQ1sv832jeW+ oITxzw7lAtqQbL+OtD2e9NZIuy6Tm7hm7DIKNAdHXOmlypBy47CWDJBMUM5UvICEtgcpfnzn vIOeGrhsKrFqy9xmZZZPd0XQfiCtcecbRkxeT//EyGkL303JQorQU9Kw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 1/8] devtool: ide-sdk: wait for lldb-server readiness Date: Sun, 9 Aug 2026 11:35:52 +0200 Message-ID: <20260809093629.3457107-2-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243070 From: Adrian Freihofer Start lldb-server in the target background, record its PID, and wait for the listener before the generated MULTI pre-launch task returns. This ensures a client can connect only after lldb-server is accepting connections and preserves timeout cleanup. Move reusable TCP listener checks and bounded wait generation into DebuggerCrossConfig to make it reusable for GDB as well. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_plugins/__init__.py | 26 ++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index d50ba7bc65..deb6050907 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -97,6 +97,20 @@ class DebuggerCrossConfig: modes.append(DebuggerServerModes.ATTACH) return modes + def _target_tcp_port_check_cmd(self): + hex_port = "%04X" % self.debug_server_port + return "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null" % hex_port + + def _target_wait_for_tcp_port_cmd(self, pid_var=None): + cleanup = "" + if pid_var: + cleanup = "kill \\$_%s 2>/dev/null; " % pid_var + return ( + "_w=0; while ! %s; do _w=\\$((_w+1)); [ \\$_w -lt 100 ] || { " + "%secho %s did not start on port %s >&2; exit 1; }; sleep 0.1; done;" + % (self._target_tcp_port_check_cmd(), cleanup, self.DEBUG_SERVER_NAME, + self.debug_server_port)) + def initialize(self): """Called after construction to generate any required config files.""" pass @@ -197,6 +211,7 @@ class LldbServerConfig(DebuggerCrossConfig): The ATTACH mode is not supported because lldb-server platform does not take a PID argument; attaching is done client-side via 'process attach'. """ + DEBUG_SERVER_NAME = "lldb-server" def __init__(self, image_recipe, modified_recipe, binary, default_mode=DebuggerServerModes.MULTI): @@ -224,18 +239,17 @@ class LldbServerConfig(DebuggerCrossConfig): cmd = "cd /tmp && %s platform --one-shot --server --listen *:%s" % ( lldb_server, self.debug_server_port) elif mode == DebuggerServerModes.MULTI: - hex_port = "%04X" % self.debug_server_port pid_file = self._lldb_server_pid_file(mode) tmp_dir = self._lldb_server_tmp_dir(mode) log_file = self._lldb_server_log_file(mode) - cmd = "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null && exit 0; " % hex_port + cmd = self._target_tcp_port_check_cmd() + " && exit 0; " cmd += "mkdir -p %s; " % tmp_dir cmd += "cd %s; " % tmp_dir - cmd += "%s platform --server --listen *:%s > %s 2>&1 & " % ( + cmd += "%s platform --server --listen *:%s > %s 2>&1 & _lldb_server_pid=\\$!; " % ( lldb_server, self.debug_server_port, log_file) - cmd += "echo \\$! > %s; " % pid_file - cmd += "_w=0; while ! grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null; " % hex_port - cmd += "do _w=\\$((_w+1)); [ \\$_w -lt 100 ] || { echo lldb-server did not start on port %s >&2; exit 1; }; sleep 0.1; done;" % self.debug_server_port + cmd += "echo \\$_lldb_server_pid > %s; " % pid_file + cmd += self._target_wait_for_tcp_port_cmd( + "lldb_server_pid") else: raise DevtoolError( "lldb-server does not support mode %s " From patchwork Sun Aug 9 09:35:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94810 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 710ABC5AC80 for ; Sun, 9 Aug 2026 09:36:48 +0000 (UTC) Received: from mta-65-227.siemens.flowmailer.net (mta-65-227.siemens.flowmailer.net [185.136.65.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.6422.1786268196863093921 for ; Sun, 09 Aug 2026 02:36:39 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=Vm+gBXEM; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-1329275-2026080909363315f36ffe9100020748-7nr_tu@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 2026080909363315f36ffe9100020748 for ; Sun, 09 Aug 2026 11:36:33 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=Kum9CQdJuuamVg6rmFywavt0Xv/I1XmIkTkUItQHSsU=; b=Vm+gBXEMt2Kh2+FQqDyeO7E/O96XmfSnM0LSGY3ll0sUmmZvok7/Io9413XPgwDZRbVUPT Gmmm2slSDM5efPNnDGAeHXpmZ8YzZL/jiVnGTrNpJGpyTndszAkLSrEpFxEPxymkofUDBkih DT7+gQIQ3DSxSVqaUii328B+exYo6f4jnlDwz6cOdTxb3h72BJBd/bownBv9LPOqIyC6gLuI i5Joa7o7SO7zzumg3y/FOdrO+YjPNkBk3YojwRYrGWQYRx7p7VRHZK/bqXM8dpdwUeYTgUa7 LF7/LTjPx3jHeQMbCSGrSdPRO5JcXLiNujTJP7hFfuJkwLCPbpND0E5A==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 2/8] oe-selftest: devtool ide-sdk: wait for lldb-server readiness Date: Sun, 9 Aug 2026 11:35:53 +0200 Message-ID: <20260809093629.3457107-3-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243073 From: Adrian Freihofer 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 --- meta/lib/oeqa/selftest/cases/devtool.py | 78 ++++++++++++------------- 1 file changed, 36 insertions(+), 42 deletions(-) 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): From patchwork Sun Aug 9 09:35:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94812 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 10A20C5AD7B for ; Sun, 9 Aug 2026 09:36:50 +0000 (UTC) Received: from mta-65-228.siemens.flowmailer.net (mta-65-228.siemens.flowmailer.net [185.136.65.228]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6562.1786268196863272765 for ; Sun, 09 Aug 2026 02:36:38 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=eBKroHBC; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.228, mailfrom: fm-1329275-2026080909363309e3f3462700020753-vrrs8t@rts-flowmailer.siemens.com) Received: by mta-65-228.siemens.flowmailer.net with ESMTPSA id 2026080909363309e3f3462700020753 for ; Sun, 09 Aug 2026 11:36:33 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=0QCLf/SR6s3v5jfzjMn+UKkes0XSYZ2/St+bM6A+8QM=; b=eBKroHBCBQj+zI3ZNC0QrLxCMmn10amNMc5UuWuqD99qrgCazAZfUCLfEunzT4eFBPWVN4 IDi8uWlU9zmH3AW2d4meLYnWWLOMrIGh9qj8qdMiSL8GfvfTTEzetoD58ANk+al4V/tBq2Nc Rok56CdXV0SZE1Ny1BmjsFSocEqNFv8brsU1KFBEINleyzSbCe+dUtpO1Xx+01RhS+prVhJP l54GTJoVBQvPZhShUiJz38ehI2F0SEO7hYAvHWZumqHtsRDxtyDXCv4n7i7+eoUG4M9krIAg Pzu5xRD2k+Ixr/kJ8kkdLshbL6liXIpTOcKJj6RnwnEm79dFLQ+fRUcw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 3/8] devtool: ide-sdk: synchronize debugger server readiness Date: Sun, 9 Aug 2026 11:35:54 +0200 Message-ID: <20260809093629.3457107-4-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:50 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243066 From: Adrian Freihofer Have GDB ONCE and ATTACH target commands wait for the listening socket, report a generated readiness marker, and remain active for the debug session. Make the VS Code background-task matcher wait for that marker instead of accepting arbitrary output. Centralize TCP readiness checks, timeout cleanup, server names, and marker generation in DebuggerCrossConfig. Reuse the TCP wait for persistent GDB and LLDB servers, and reject the unused LLDB one-shot mode explicitly. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 7 ++-- scripts/lib/devtool/ide_plugins/__init__.py | 41 ++++++++++++++------- scripts/lib/devtool/ide_plugins/ide_code.py | 7 +++- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index d84a18e8b6..04a42b8906 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3508,10 +3508,11 @@ class DevtoolIdeSdkTests(DevtoolBase): # Start gdbserver on target using the task command (keep the ssh connection open while debugging) ssh_gdbserver_cmd = [task_command] + task_args - # Fix shell command escaping - remove extra quotes from the last argument - # The task_args likely contains a quoted shell command that needs to be unquoted + # The tasks.json argument is formatted for an intermediate shell. Strip + # its quotes and restore dollar expansions before passing it directly to + # SSH via subprocess. if len(ssh_gdbserver_cmd) > 0 and ssh_gdbserver_cmd[-1].startswith('"') and ssh_gdbserver_cmd[-1].endswith('"'): - ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1] # Remove surrounding quotes + ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1].replace('\\$', '$') self.logger.debug(f"Starting gdbserver with command: {' '.join(ssh_gdbserver_cmd)}") with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger): # Give gdbserver a moment to start diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index deb6050907..6c88e0e34e 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -101,6 +101,12 @@ class DebuggerCrossConfig: hex_port = "%04X" % self.debug_server_port return "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null" % hex_port + def get_debug_server_ready_marker(self, port): + return "%s ready on port %s" % (self.DEBUG_SERVER_NAME, port) + + def get_debug_server_ready_marker_pattern(self): + return "^%s$" % self.get_debug_server_ready_marker("[0-9]+") + def _target_wait_for_tcp_port_cmd(self, pid_var=None): cleanup = "" if pid_var: @@ -130,6 +136,7 @@ class GdbCrossConfig(DebuggerCrossConfig): gdbinit / gdb wrapper scripts used by ide=none as well as the target-side tmp/pid/log paths consumed by the gdbserver start command. """ + DEBUG_SERVER_NAME = "gdbserver" def __init__(self, image_recipe, modified_recipe, binary, default_mode=DebuggerServerModes.MULTI): @@ -172,26 +179,35 @@ class GdbCrossConfig(DebuggerCrossConfig): "\"/bin/sh -c '/usr/bin/gdbserver --once :1234 /usr/bin/cmake-example'\"" """ if server_mode == DebuggerServerModes.ONCE: - gdbserver_cmd_start = "%s --once :%s %s" % ( + gdbserver_cmd_start = "%s --once :%s %s & " % ( self.debugger_cross.debug_server_path, self.debug_server_port, self.binary.binary_path) + gdbserver_cmd_start += "_gdbserver_pid=\\$!; " + gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd( + "gdbserver_pid") + " " + gdbserver_cmd_start += "echo %s; wait \\$_gdbserver_pid" % ( + self.get_debug_server_ready_marker(self.debug_server_port)) elif server_mode == DebuggerServerModes.ATTACH: pid_command = self.binary.pid_command if pid_command: - gdbserver_cmd_start = "%s --attach :%s \\$(%s)" % ( + gdbserver_cmd_start = "%s --attach :%s \\$(%s) & " % ( self.debugger_cross.debug_server_path, self.debug_server_port, pid_command) + gdbserver_cmd_start += "_gdbserver_pid=\\$!; " + gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd( + "gdbserver_pid") + " " + gdbserver_cmd_start += "echo %s; wait \\$_gdbserver_pid" % ( + self.get_debug_server_ready_marker(self.debug_server_port)) else: raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path) elif server_mode == DebuggerServerModes.MULTI: - hex_port = "%04X" % self.debug_server_port - gdbserver_cmd_start = "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null && exit 0; " % hex_port + gdbserver_cmd_start = self._target_tcp_port_check_cmd() + " && exit 0; " gdbserver_cmd_start += "mkdir -p %s; " % self._gdbserver_tmp_dir(server_mode) - gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & " % ( + gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & _gdbserver_pid=\\$!; " % ( self.debugger_cross.debug_server_path, self.debug_server_port, self._gdbserver_log_file(server_mode)) - gdbserver_cmd_start += "echo \\$! > %s; " % self._gdbserver_pid_file(server_mode) - gdbserver_cmd_start += "_w=0; while ! grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null; " % hex_port - gdbserver_cmd_start += "do _w=\\$((_w+1)); [ \\$_w -lt 100 ] || exit 1; sleep 0.1; done;" + gdbserver_cmd_start += "echo \\$_gdbserver_pid > %s; " % self._gdbserver_pid_file(server_mode) + gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd( + "gdbserver_pid") else: raise DevtoolError("Unsupported gdbserver mode: %s" % server_mode) return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\"" @@ -235,10 +251,7 @@ class LldbServerConfig(DebuggerCrossConfig): # lldb-server 21.x and the remote lldb client connects from the host. # Start from /tmp because lldb-server creates temp files in its cwd and # the SSH default cwd (/home/root) may not exist on a minimal image. - if mode == DebuggerServerModes.ONCE: - cmd = "cd /tmp && %s platform --one-shot --server --listen *:%s" % ( - lldb_server, self.debug_server_port) - elif mode == DebuggerServerModes.MULTI: + if mode == DebuggerServerModes.MULTI: pid_file = self._lldb_server_pid_file(mode) tmp_dir = self._lldb_server_tmp_dir(mode) log_file = self._lldb_server_log_file(mode) @@ -252,8 +265,8 @@ class LldbServerConfig(DebuggerCrossConfig): "lldb_server_pid") else: raise DevtoolError( - "lldb-server does not support mode %s " - "(ATTACH is handled client-side with 'process attach')" % mode) + "lldb-server only supports MULTI mode; " + "ATTACH is handled client-side with 'process attach': %s" % mode) return "\"/bin/sh -c '" + cmd + "'\"" def _target_kill_cmd(self): diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py index 9faba3f2d1..5b66c56e35 100644 --- a/scripts/lib/devtool/ide_plugins/ide_code.py +++ b/scripts/lib/devtool/ide_plugins/ide_code.py @@ -609,6 +609,11 @@ class IdeVSCode(IdeBase): # ONCE / ATTACH: gdbserver runs in the foreground for the # whole session, so VSCode needs isBackground + a pattern # matcher to avoid waiting for the task to exit. + if server_mode in (DebuggerServerModes.ONCE, + DebuggerServerModes.ATTACH): + ends_pattern = cross_debug_config.get_debug_server_ready_marker_pattern() + else: + ends_pattern = "." new_task = { "label": cross_debug_config.id_pretty_mode(server_mode), "type": "shell", @@ -628,7 +633,7 @@ class IdeVSCode(IdeBase): "background": { "activeOnStart": True, "beginsPattern": ".", - "endsPattern": ".", + "endsPattern": ends_pattern, } } ] From patchwork Sun Aug 9 09:35:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94815 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 03D4DC5AD68 for ; Sun, 9 Aug 2026 09:36:50 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.6421.1786268196863023839 for ; Sun, 09 Aug 2026 02:36:39 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=PcgMepvF; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-1329275-20260809093633eef150bfc20002074a-pgfnmb@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 20260809093633eef150bfc20002074a for ; Sun, 09 Aug 2026 11:36:33 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=WXFDCsJgtNrfO2XVI70VkwvnsFP9RpSqjMs9K9ldZGg=; b=PcgMepvFJCuJcWEQWFRWPDOjKRNw4qodlEfilHrEFaRnlJFZKMU4/dwHbt0QxVEuMPUvs/ uAedDYRQkUr2YbUlHXwesTMrAcz/9B1gSlQP0mZDUCbbrUeJMDQIRH4E9TVknKLlzK/3jh8G 9I95hmAiJ6GyVGhWgkB2cGrItdep3duPfx55o0nIzEx9P6Lv6yVDs3+RNwiU++cHyCgJ9gX5 aZBX35vcbuJOYO0boPRu2IgfIt+PafGtJkKWLZlpmsmQ9R6jsLlhGGsia5DqSlv3Kbt+3QLu czGVk6+vKU+zeoQGLNFimOwbbtL1x4+ecvglfwDqc0cSaB+MWnACqC3w==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 4/8] oe-selftest: devtool ide-sdk: wait for gdbserver readiness Date: Sun, 9 Aug 2026 11:35:55 +0200 Message-ID: <20260809093629.3457107-5-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:50 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243069 From: Adrian Freihofer Replace the fixed delay and process-list probe for GDB background tasks with a wait for the readiness marker generated in tasks.json. This verifies the same background-task contract VS Code uses before starting the debugger session. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 30 ++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 04a42b8906..11a26f03bb 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -2747,6 +2747,20 @@ class RunCmdBackground: def __enter__(self): self.cmd.run() + return self + + def wait_for_output(self, pattern, timeout): + pattern = re.compile(pattern, re.MULTILINE) + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + output = b"".join(self.cmd._output_chunks).decode( + "utf-8", errors="replace") + if pattern.search(output): + return True + if self.cmd.process.poll() is not None: + break + time.sleep(0.1) + return False def __exit__(self, exc_type, exc_val, exc_tb): self.cmd.stop() @@ -3514,16 +3528,12 @@ class DevtoolIdeSdkTests(DevtoolBase): if len(ssh_gdbserver_cmd) > 0 and ssh_gdbserver_cmd[-1].startswith('"') and ssh_gdbserver_cmd[-1].endswith('"'): ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1].replace('\\$', '$') self.logger.debug(f"Starting gdbserver with command: {' '.join(ssh_gdbserver_cmd)}") - with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger): - # Give gdbserver a moment to start - time.sleep(1) - - # Verify gdbserver is running on target and listening on expected port - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger) - self.assertEqual(result.status, 0, "Failed to check processes on target") - self.assertIn("gdbserver", result.output, "gdbserver should be running on target") - _, server_port = server_addr.split(':') - self.assertIn(server_port, result.output, f"gdbserver should be listening on port {server_port}") + _, server_port = server_addr.split(':') + with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger) as gdbserver: + ready_pattern = prelaunch_task["problemMatcher"][0]["background"]["endsPattern"] + self.assertTrue( + gdbserver.wait_for_output(ready_pattern, timeout=15), + "gdbserver did not report readiness on port %s" % server_port) if debug_func and debug_check_func: # Do a gdb remote session using the once configuration From patchwork Sun Aug 9 09:35:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94813 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C214FC5AD4E for ; Sun, 9 Aug 2026 09:36:49 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.6423.1786268196863316516 for ; Sun, 09 Aug 2026 02:36:39 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=EoTo/osq; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-1329275-202608090936346da5eaf9f00002076f-n3ixht@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 202608090936346da5eaf9f00002076f for ; Sun, 09 Aug 2026 11:36:34 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=he5Ky0q+sH573P14tRLma9ZoYdRWa7OslcGISsdPw80=; b=EoTo/osqok0l+cZ7AWHJt5pE/F20Xqa9uOqiYfNayeLO1oh9xrEt/+CLEcmqy4lrBg9/3j HkVMTgoQi79ZsIi42IN5z/e1nAWxc7Pk4AEV+DPHvWh+SdH5zFvw0hu6XeNqtmcrR4YW99uy 9l12DzEZhE7g2x1awzdU8zNj9+r1diPXG/NFEoAPiWvPJGwWlrXuH3OHbK28VY39++vWrEqf DF7M636zdwdkIfSA4zQ7AL73W6WH0uJzBjnSvaLgkvBXofgpnD/0pzWmMwTMevNQzYksm1rK tlfowgJi8qL6NYlmgsHQjLad1HuSYxkwm72cY4nUCOPqQUjZW5o+NSHA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 5/8] devtool: ide-sdk: wait for debugger server shutdown Date: Sun, 9 Aug 2026 11:35:56 +0200 Message-ID: <20260809093629.3457107-6-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243071 From: Adrian Freihofer Have the ide=none GDB and LLDB stop scripts wait for their recorded server PID to exit before removing their state. This makes a successful stop command a reliable target-side lifecycle boundary and reports a timeout when the server cannot be stopped. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_plugins/__init__.py | 7 +++++++ scripts/lib/devtool/ide_plugins/ide_none.py | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index 6c88e0e34e..1c4288a8b5 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -117,6 +117,13 @@ class DebuggerCrossConfig: % (self._target_tcp_port_check_cmd(), cleanup, self.DEBUG_SERVER_NAME, self.debug_server_port)) + def _target_wait_for_process_exit_cmd(self, pid_var): + return ( + "_w=0; while kill -0 \\$_%s 2>/dev/null; do _w=\\$((_w+1)); " + "[ \\$_w -lt 100 ] || { echo %s did not stop >&2; exit 1; }; " + "sleep 0.1; done;" + % (pid_var, self.DEBUG_SERVER_NAME)) + def initialize(self): """Called after construction to generate any required config files.""" pass diff --git a/scripts/lib/devtool/ide_plugins/ide_none.py b/scripts/lib/devtool/ide_plugins/ide_none.py index a8ddc3f39f..8e01c2fc41 100644 --- a/scripts/lib/devtool/ide_plugins/ide_none.py +++ b/scripts/lib/devtool/ide_plugins/ide_none.py @@ -24,9 +24,13 @@ class GdbCrossConfigNone(GdbCrossConfig): """Kill a gdbserver process""" # This is the usual behavior: gdbserver is stopped on demand if server_mode == DebuggerServerModes.MULTI: - gdbserver_cmd_stop = "test -f %s && kill \\$(cat %s);" % ( - self._gdbserver_pid_file(server_mode), self._gdbserver_pid_file(server_mode)) - gdbserver_cmd_stop += " rm -rf %s" % self._gdbserver_tmp_dir(server_mode) + pid_file = self._gdbserver_pid_file(server_mode) + gdbserver_cmd_stop = "if test -f %s; then _gdbserver_pid=\\$(cat %s); " % ( + pid_file, pid_file) + gdbserver_cmd_stop += "kill \\$_gdbserver_pid 2>/dev/null; " + gdbserver_cmd_stop += self._target_wait_for_process_exit_cmd( + "gdbserver_pid") + gdbserver_cmd_stop += " fi; rm -rf %s" % self._gdbserver_tmp_dir(server_mode) # This is unexpected since gdbserver should terminate after each debug session # Just kill all gdbserver instances to keep it simple else: @@ -169,8 +173,11 @@ class LldbServerConfigNone(LldbServerConfig): if server_mode == DebuggerServerModes.MULTI: pid_file = self._lldb_server_pid_file(server_mode) tmp_dir = self._lldb_server_tmp_dir(server_mode) - cmd = ("test -f %(pf)s && kill \\$(cat %(pf)s) 2>/dev/null; rm -rf %(td)s" - % {'pf': pid_file, 'td': tmp_dir}) + cmd = "if test -f %s; then _lldb_server_pid=\\$(cat %s); " % ( + pid_file, pid_file) + cmd += "kill \\$_lldb_server_pid 2>/dev/null; " + cmd += self._target_wait_for_process_exit_cmd("lldb_server_pid") + cmd += " fi; rm -rf %s" % tmp_dir else: cmd = "killall lldb-server 2>/dev/null || true" return "\"/bin/sh -c '" + cmd + "'\"" From patchwork Sun Aug 9 09:35:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94816 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 587BDC5B560 for ; Sun, 9 Aug 2026 09:36:50 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6563.1786268196863498907 for ; Sun, 09 Aug 2026 02:36:38 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=aAQ9pUg4; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-20260809093634a1c8874642000207cd-byjmsx@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 20260809093634a1c8874642000207cd for ; Sun, 09 Aug 2026 11:36:34 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=FLuk/Fp1RICuFTSdCz+nPCQeL2R9xp0EjDtD3nk2R2U=; b=aAQ9pUg41mc09rNi2JGWAQszjqvqn8eANp4TiGpky/WNB9RzrFXQq3ZBHyBELkdlZS+UPT iKaBViHbu7ZhhrXg5mzEuSlRXJXiK9VKxzdcGaAPxiNBr4mLKIT+VrLCrKNQQEAVYSmueq0k dTawHS2VAXrY1Mxa+Gy/kbagg5Q2AdIXRqwAHQRNkNh3ixKJAXIbmPe+ksJYVUJqY7gcjlw4 CQ0sk8LkwlxFjMleljTNK8ZOkY6TFwlVa42ZsCYo5bGE9osoQAF9l76VedowVaZpNdz/vNln 0rFj/XK0gTtkv2rr5RDVpfv5dtTDtU4fKCmb4cfxOqESSx9W5NcsVu0w==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 6/8] oe-selftest: devtool ide-sdk: verify debugger shutdown Date: Sun, 9 Aug 2026 11:35:57 +0200 Message-ID: <20260809093629.3457107-7-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:50 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243065 From: Adrian Freihofer Use the QEMU target command interface to inspect the generated ide=none server PID files and process command lines. Verify that the stop helpers remove both the tracked process and its PID file, without relying on broad ps output. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 61 +++++++++++-------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 11a26f03bb..df77c628bb 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3112,7 +3112,6 @@ class DevtoolIdeSdkTests(DevtoolBase): numbers after recompiling, to prove the breakpoints resolve via the freshly rebuilt debug info. """ - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' gdbserver_script = os.path.join(self._workspace_scripts_dir( recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_multi') gdb_script = os.path.join(self._workspace_scripts_dir( @@ -3122,19 +3121,18 @@ class DevtoolIdeSdkTests(DevtoolBase): r = runCmd(gdbserver_script, output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Check there is a gdbserver running - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn("gdbserver ", r.output) + pid_file = '/tmp/gdbserver_1234_usr-bin-%s_multi/gdbserver.pid' % example_exe + status, output = qemu.run('cat %s' % pid_file) + self.assertEqual(status, 0) + gdbserver_pid = output.strip() + self.assertRegex(gdbserver_pid, r'^\d+$') - # Check the pid file is correct - test_cmd = "'cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \ - example_exe + "_multi/gdbserver.pid)/cmdline'" - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd), output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn("gdbserver", r.output) - self.assertIn("--multi", r.output) - self.assertIn("1234", r.output) + # Check the pid file identifies the expected gdbserver process + status, output = qemu.run('cat /proc/%s/cmdline' % gdbserver_pid) + self.assertEqual(status, 0) + self.assertIn("gdbserver", output) + self.assertIn("--multi", output) + self.assertIn("1234", output) # Test remote debugging works gdb_batch_cmd = " --batch " + self._gdb_debug_cpp_example( @@ -3151,10 +3149,10 @@ class DevtoolIdeSdkTests(DevtoolBase): r = runCmd(gdbserver_script + ' stop', output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Check there is no gdbserver running - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertNotIn("gdbserver ", r.output) + # The stop script waits for its recorded PID before it succeeds. + status, _ = qemu.run('test ! -d /proc/%s && test ! -e %s' % ( + gdbserver_pid, pid_file)) + self.assertEqual(status, 0) def _verify_cmake_preset(self, tempdir): """Verify the generated cmake preset works as expected @@ -4396,7 +4394,6 @@ class DevtoolIdeSdkTests(DevtoolBase): lldbinit source map / debug-file-search-paths setup for the library's own debug info specifically. """ - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' scripts_dir = self._workspace_scripts_dir(recipe_name) binary_pretty = 'usr-bin-' + example_exe lldb_server_script = os.path.join( @@ -4410,19 +4407,16 @@ class DevtoolIdeSdkTests(DevtoolBase): r = runCmd(lldb_server_script, output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Verify lldb-server is running on the target - r = runCmd('ssh %s root@%s ps' % (sshargs, qemu.ip), - output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn('lldb-server', r.output) + pid_file = '/tmp/lldb_server_1234_%s_multi/lldb_server.pid' % binary_pretty + status, output = qemu.run('cat %s' % pid_file) + self.assertEqual(status, 0) + lldb_server_pid = output.strip() + self.assertRegex(lldb_server_pid, r'^\d+$') # Verify the pid file points at the running lldb-server process - pid_file = '/tmp/lldb_server_1234_%s_multi/lldb_server.pid' % binary_pretty - test_cmd = "'cat /proc/$(cat %s)/cmdline'" % pid_file - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd), - output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn('lldb-server', r.output) + status, output = qemu.run('cat /proc/%s/cmdline' % lldb_server_pid) + self.assertEqual(status, 0) + self.assertIn('lldb-server', output) # Run an lldb batch session covering the executable, library and # header breakpoints, then continue to completion @@ -4437,11 +4431,10 @@ class DevtoolIdeSdkTests(DevtoolBase): r = runCmd(lldb_server_script + ' stop', output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Verify lldb-server is no longer running - r = runCmd('ssh %s root@%s ps' % (sshargs, qemu.ip), - output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertNotIn('lldb-server', r.output) + # The stop script waits for its recorded PID before it succeeds. + status, _ = qemu.run('test ! -d /proc/%s && test ! -e %s' % ( + lldb_server_pid, pid_file)) + self.assertEqual(status, 0) @OETestTag("runqemu") def test_devtool_ide_sdk_none_cmake_clang(self): From patchwork Sun Aug 9 09:35:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94809 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C05B7C5AD55 for ; Sun, 9 Aug 2026 09:36:49 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6564.1786268196863618279 for ; Sun, 09 Aug 2026 02:36:38 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=Rr8GSOW8; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-202608090936340011cb2fea000207f9-vzszut@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 202608090936340011cb2fea000207f9 for ; Sun, 09 Aug 2026 11:36:34 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=gCz80k2B7e3TlVlYvGqb3t/4a1F1SvIHNfzJV+3kYPU=; b=Rr8GSOW8Ivw0E0tCQ0jHCLE9GJMyYuOC3QYMSjR6uC25FqWzugBKIukXr7ILfglQ3ERlKJ sE+gWVOp9EcahebSU6mlktAOS5SK0LbZy4TmupBuBe5v4C0ebYWra3Q/WwsflNQzdSMbCiLG FziIkqwYukZ5ezZ00v7QCgw0vweJXYGtV1w5H8/ZcyCL7hQ74fjomPWvFVewqv3EqKOvsUtx RF0HXLe/qbjSNgQWa5/IvYHp7l7axjrC8J7/ASSnXa0iyCSyE2F2tRaIoQ2KB/QzEkAiLDsC lDocfk5onkUb98PXU7M/qCoHqgrwihqi950KhYY1aJoU3cH+omWDI6GA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 7/8] oe-selftest: devtool: use QEMU target commands Date: Sun, 9 Aug 2026 11:35:58 +0200 Message-ID: <20260809093629.3457107-8-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243068 From: Adrian Freihofer Use the QEMU target API for target-side verification in devtool selftests instead of invoking SSH directly. Copy the generated file list through the target API before comparing deployed file metadata. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 29 ++++++++++++++----------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index df77c628bb..06be7ef2df 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -2012,8 +2012,8 @@ class DevtoolDeployTargetTests(DevtoolBase): self.logger.debug(deploy_cmd) result = runCmd(deploy_cmd) # Run a test command to see if it was installed properly - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand)) + status, _ = qemu.run(testcommand) + self.assertEqual(status, 0) # Check if it deployed all of the files with the right ownership/perms # First look on the host - need to do this under pseudo to get the correct ownership/perms bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD', 'PATH'], testrecipe) @@ -2032,15 +2032,21 @@ class DevtoolDeployTargetTests(DevtoolBase): for line in filelist1: splitline = line.split() f.write(splitline[-1] + '\n') - result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip)) - filelist2 = self._process_ls_output(result.output) + remotefilelist = '/tmp/%s' % os.path.basename(tmpfilelist) + status, _ = qemu.copy_to(tmpfilelist, remotefilelist) + self.assertEqual(status, 0) + status, output = qemu.run( + 'xargs ls -l < %s; status=$?; rm -f %s; exit $status' % ( + remotefilelist, remotefilelist)) + self.assertEqual(status, 0) + filelist2 = self._process_ls_output(output) filelist1.sort(key=lambda item: item.split()[-1]) filelist2.sort(key=lambda item: item.split()[-1]) self.assertEqual(filelist1, filelist2) # Test undeploy-target result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip)) - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) - self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') + status, _ = qemu.run(testcommand) + self.assertNotEqual(status, 0, 'undeploy-target did not remove command as it should have') class DevtoolBuildImageTests(DevtoolBase): @@ -3510,13 +3516,10 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertEqual(task_command, "ssh", f"Task '{prelaunch_task_name}' should use ssh command") self.assertTrue(len(task_args) >= 2, f"Task '{prelaunch_task_name}' should have at least 2 args (ssh options and remote command)") - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'test -x /usr/bin/gdbserver'), - output_log=self._cmd_logger) - self.assertEqual(result.status, 0, "gdbserver should be installed on target") - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'test -x ' + os.path.join('/usr/bin', example_exe)), - output_log=self._cmd_logger) - self.assertEqual(result.status, 0, "Example binary should be installed on target") + status, _ = qemu.run('test -x /usr/bin/gdbserver') + self.assertEqual(status, 0, "gdbserver should be installed on target") + status, _ = qemu.run('test -x ' + os.path.join('/usr/bin', example_exe)) + self.assertEqual(status, 0, "Example binary should be installed on target") # Start gdbserver on target using the task command (keep the ssh connection open while debugging) ssh_gdbserver_cmd = [task_command] + task_args From patchwork Sun Aug 9 09:35:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 94814 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 25AB7C5AE49 for ; Sun, 9 Aug 2026 09:36:50 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.6420.1786268196862820655 for ; Sun, 09 Aug 2026 02:36:39 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=OWhwGUa8; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-1329275-20260809093634f6624418e7000207dd-eyjg3t@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 20260809093634f6624418e7000207dd for ; Sun, 09 Aug 2026 11:36:34 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=9pbURKpOlQRQWcG1YRXrd/UGgghwHAKuKmbAVeC6bLQ=; b=OWhwGUa8an7dZxC4U1aYiYeKxLFBgh3vwE+9y9enW1OShPc2+1gEDUYmr7beaKGwD7JYSl mi67DYcdqIUfxOVRVaWKdPZBP6KtJ7DJ2lb7G9H9b0MVEq7NqPMIKUJBypEWwAha1iRnNRpm kSrrdH7RF9tcvW3mQe+lVimjA+rzINirODf2qZ6Mq4pg+qzinmEFu3qfdx2jruzdLZZU3r8Z Jgwww7sondh56dnWjkMsFqK3AWJKS50Cvi/kObEbWbEMvt4+dDPWeduqBH1P/77mb6QJKdw0 BM1k8utVWywlGasDeP1q1/OHFrUAwJMnjPJUbSDU/bx0SpdBNO/bo6iQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 8/8] cpp-example: prevent compiler from eliminating debugger test code Date: Sun, 9 Aug 2026 11:35:59 +0200 Message-ID: <20260809093629.3457107-9-adrian.freihofer@siemens.com> In-Reply-To: <20260809093629.3457107-1-adrian.freihofer@siemens.com> References: <20260809093629.3457107-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 09 Aug 2026 09:36:50 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243072 From: Adrian Freihofer Be more paranoid about compiler optimizations that can eliminate code that is only used for debugger inspection. scale_number() in cpp-example-lib.hpp: use 'volatile int scaled' to prevent the compiler from inlining the function body away entirely, which left no out-of-line address for a breakpoint. cpp-example.cpp: initialize the std::vector from volatile variables so the compiler cannot constant-fold the values and eliminate the for-loop body. Pass numbers[0]+numbers[1]+numbers[2] (= 6) as the scale_number argument to keep the vector live at the call site (exe_break_line=63); the test assertion '$4 = 6' is unchanged since 1+2+3 == 6. Update the test accordingly: exe_break_line moves from 56 to 63, the LINE_SHIFT anchor changes to the volatile declaration line, and the list-output assertion matches '{n1, n2, n3}'. Note: the test was originally written to also check that the compiler uses e.g. -O0 to avoid eliminating e.g. the vector, but that should probably be tested separately. Changing this ide-sdk test to use volatile variables is a more robust way to ensure the vector is not eliminated, regardless of compiler flags. Signed-off-by: Adrian Freihofer --- .../recipes-test/cpp/files/cpp-example-lib.hpp | 6 ++++-- .../recipes-test/cpp/files/cpp-example.cpp | 10 +++++----- meta/lib/oeqa/selftest/cases/devtool.py | 17 +++++++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp index d1c9bca416..5af30e2a79 100644 --- a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp +++ b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp @@ -15,10 +15,12 @@ struct CppExample inline static const std::string test_string = "cpp-example-lib Magic: 123456789"; /* Header-only function, to exercise breakpoint resolution against - * header-only debug info. */ + * header-only debug info. volatile prevents compiler optimization from + * eliminating the function body, ensuring a concrete code location exists + * for debugger breakpoints. */ inline static int scale_number(int n) { - int scaled = n * 7; + volatile int scaled = n * 7; std::cout << "scale_number(" << n << ") = " << scaled << std::endl; return scaled; } diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example.cpp b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp index ad1abae257..a376419c13 100644 --- a/meta-selftest/recipes-test/cpp/files/cpp-example.cpp +++ b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp @@ -50,17 +50,17 @@ int main(int argc, char* argv[]) sleep(1); } } while (endless_mode); - + volatile int n1 = 1, n2 = 2, n3 = 3; // Example: Demonstrate std::vector traversal for debugger inspection - std::vector numbers = {1, 2, 3}; + std::vector numbers = {n1, n2, n3}; std::cout << "Traversing std::vector numbers:" << std::endl; for (size_t i = 0; i < numbers.size(); ++i) { std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl; } - // Example: call a header-only function once, to exercise breakpoint - // resolution against header-only debug info. - CppExample::scale_number(6); + // Pass numbers elements as the argument so the compiler cannot eliminate + // the vector; 1+2+3 == 6, so the scale_number(n) check is unchanged. + CppExample::scale_number(numbers[0] + numbers[1] + numbers[2]); return 0; } diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 06be7ef2df..7338b85d7f 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -2975,8 +2975,8 @@ class DevtoolIdeSdkTests(DevtoolBase): with open(cpp_example_cpp, 'r') as file: cpp_code = file.read() cpp_code = cpp_code.replace( - " std::vector numbers = {1, 2, 3};", - extra_lines + " std::vector numbers = {1, 2, 3};") + " volatile int n1 = 1, n2 = 2, n3 = 3;", + extra_lines + " volatile int n1 = 1, n2 = 2, n3 = 3;") with open(cpp_example_cpp, 'w') as file: file.write(cpp_code) @@ -3016,7 +3016,7 @@ class DevtoolIdeSdkTests(DevtoolBase): # the first _gdb_cross_debugging_multi call above. self._gdb_cross_debugging_multi( qemu, recipe_name, example_exe, MAGIC_STRING_NEW, - exe_break_line=56 + LINE_SHIFT, exe_list_line=55 + LINE_SHIFT, + exe_break_line=63 + LINE_SHIFT, exe_list_line=55 + LINE_SHIFT, hpp_break_line=21 + LINE_SHIFT, lib_break_line=31 + LINE_SHIFT) def _gdb_cross(self): @@ -3033,7 +3033,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertIn("GNU gdb", r.output) def _gdb_debug_cpp_example(self, magic_string, gdb_start_cmd="run", - exe_break_line=56, exe_list_line=55, hpp_break_line=21, + exe_break_line=63, exe_list_line=55, hpp_break_line=21, lib_break_line=31): """Get a series of gdb commands to debug the cpp-example-lib example""" gdb_batch_cmd = " -ex 'break main' -ex '%s'" % gdb_start_cmd @@ -3057,8 +3057,9 @@ class DevtoolIdeSdkTests(DevtoolBase): # check if resolving std::vector works with python scripts gdb_batch_cmd += " -ex 'list cpp-example.cpp:%d,%d'" % (exe_list_line, exe_list_line) - # Break on exe_break_line (the std::cout after the declaration) so the - # vector constructor on exe_list_line has already run when GDB stops. + # Break on exe_break_line (the scale_number call) so the vector on + # exe_list_line is both constructed and referenced; the compiler cannot + # eliminate the vector because its elements are passed as the argument. # These line numbers shift after the test inserts extra lines and # recompiles, proving the breakpoint resolves via the freshly rebuilt # debug info rather than a stale, cached line-to-address mapping. @@ -3095,7 +3096,7 @@ class DevtoolIdeSdkTests(DevtoolBase): # check if resolving std::vector works with python scripts self.assertRegex( - gdb_output, r"%d\s+std::vector numbers = \{1, 2, 3\};" % exe_list_line) + gdb_output, r"%d\s+std::vector numbers = \{n1, n2, n3\};" % exe_list_line) self.assertIn("$3 = std::vector of length 3, capacity 3 = {1, 2, 3}", gdb_output) # check that a breakpoint in an inline function defined directly in @@ -3106,7 +3107,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertIn("exited normally", gdb_output) def _gdb_cross_debugging_multi(self, qemu, recipe_name, example_exe, magic_string, - exe_break_line=56, exe_list_line=55, hpp_break_line=21, + exe_break_line=63, exe_list_line=55, hpp_break_line=21, lib_break_line=31): """Verify gdb-cross is working