From: Adrian Freihofer Date: Tue, 4 Aug 2026 11:59:33 +0000 (+0200) Subject: devtool: ide-sdk: wait for gdbserver port before returning X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=292a35234e95d7a1dc43809bd57ee2976538c8eb;p=thirdparty%2Fopenembedded%2Fopenembedded-core.git devtool: ide-sdk: wait for gdbserver port before returning In MULTI mode, gdbserver is started as a background process and the SSH command returned immediately, leaving a race between the caller connecting to gdbserver and gdbserver finishing its bind()/listen() sequence. The race condition was observed with lldb-server not with gdbserver, but it is likely to affect both. It might be a fix for gdbserver as well, but at least it is a preparatory step for adding LLDB support, which is the next planned item. There are two possible synchronisation points: - The pid file: written by the shell immediately after fork(), before gdbserver has called bind() or listen() — not useful as a readiness signal. - /proc/net/tcp: the port entry appears after remote_prepare() completes socket()+bind()+listen(), which is the earliest point at which gdbserver will accept a connection. Replace the pid-file idempotency check with a /proc/net/tcp port check so that: - the SSH command doubles as a readiness probe (exits only when gdbserver is actually listening, or after a 10 s timeout with exit 1) - re-running the start command while the server is already up is still a no-op The VSCode task for MULTI mode is changed accordingly: since the SSH command now exits as soon as the server is ready, VSCode no longer needs isBackground + a pattern matcher — a plain task with an empty problemMatcher suffices. The pid file is still written so that the stop script can kill the server by PID. Signed-off-by: Adrian Freihofer Signed-off-by: Mathieu Dubois-Briand --- diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index 4a1686a034..cfb067548d 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -170,11 +170,14 @@ class GdbCrossConfig(DebuggerCrossConfig): else: raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path) elif server_mode == DebuggerServerModes.MULTI: - gdbserver_cmd_start = "test -f %s && exit 0; " % self._gdbserver_pid_file(server_mode) + 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 += "mkdir -p %s; " % self._gdbserver_tmp_dir(server_mode) gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & " % ( 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 += "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;" else: raise DevtoolError("Unsupported gdbserver mode: %s" % server_mode) return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\"" diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py index dfaba3cff6..d237ab8f66 100644 --- a/scripts/lib/devtool/ide_plugins/ide_code.py +++ b/scripts/lib/devtool/ide_plugins/ide_code.py @@ -456,30 +456,45 @@ class IdeVSCode(IdeBase): if cross_debug_config.modified_recipe is not modified_recipe: continue for server_mode in cross_debug_config.server_modes(): - new_task = { - "label": cross_debug_config.id_pretty_mode(server_mode), - "type": "shell", - "isBackground": True, - "command": cross_debug_config.debugger_cross.target_device.ssh_sshexec, - "args": cross_debug_config.target_ssh_gdbserver_start_args(server_mode), - "problemMatcher": [ - { - "pattern": [ - { - "regexp": ".", - "file": 1, - "location": 2, - "message": 3 + if server_mode == DebuggerServerModes.MULTI: + # MULTI mode: the SSH command blocks until the port is ready + # (wait loop in _target_start_cmd), so VSCode treats this as + # a regular non-background task. + new_task = { + "label": cross_debug_config.id_pretty_mode(server_mode), + "type": "shell", + "command": cross_debug_config.debugger_cross.target_device.ssh_sshexec, + "args": cross_debug_config.target_ssh_gdbserver_start_args(server_mode), + "problemMatcher": [] + } + else: + # 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. + new_task = { + "label": cross_debug_config.id_pretty_mode(server_mode), + "type": "shell", + "isBackground": True, + "command": cross_debug_config.debugger_cross.target_device.ssh_sshexec, + "args": cross_debug_config.target_ssh_gdbserver_start_args(server_mode), + "problemMatcher": [ + { + "pattern": [ + { + "regexp": ".", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": True, + "beginsPattern": ".", + "endsPattern": ".", } - ], - "background": { - "activeOnStart": True, - "beginsPattern": ".", - "endsPattern": ".", } - } - ] - } + ] + } # Deploy the artifacts to the target before starting gdbserver if not already running if server_mode != DebuggerServerModes.ATTACH: new_task['dependsOn'] = [