From: AdrianF <adrian.freihofer@siemens.com>
To: openembedded-core@lists.openembedded.org
Cc: Adrian Freihofer <adrian.freihofer@siemens.com>
Subject: [PATCH 13/15] devtool: ide-sdk: support LLDB ATTACH mode
Date: Wed, 9 Sep 2026 23:53:13 +0200 [thread overview]
Message-ID: <20260909215337.89106-14-adrian.freihofer@siemens.com> (raw)
In-Reply-To: <20260909215337.89106-1-adrian.freihofer@siemens.com>
From: Adrian Freihofer <adrian.freihofer@siemens.com>
Bring support for LLDB ATTACH mode to the IDE SDK as it is already
supported for GDB.
lldb-server platform mode has no launch/attach distinction on the
target: it just offers a platform connection, and the CodeLLDB client
decides client-side whether to launch or attach. ATTACH was previously
rejected outright because gdbserver's PID-based attach model doesn't
apply, but lldb-server can serve both modes identically.
Give each server_modes() entry (MULTI and ATTACH, for service binaries)
its own gdbserver_ports entry instead of the single gdbserver_port, so
both get their own slirp-forwarded port. _vscode_launch_bin_dbg_lldb()
now emits "request": "attach" with a postDebugTask to stop the
lldb-server instance afterwards, and drops the launch-only
stopOnEntry/cwd keys, which are invalid in the attach schema.
ide_none.py's lldbinit generation and LldbServerConfigNone.initialize()
are updated the same way to also generate the ATTACH-mode start script
for service binaries.
Also update the tests accordingly to verify the new ATTACH mode
configurations.
Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
meta/lib/oeqa/selftest/cases/devtool.py | 19 +++++--
scripts/lib/devtool/ide_plugins/__init__.py | 60 ++++++++++-----------
scripts/lib/devtool/ide_plugins/ide_code.py | 23 +++++---
scripts/lib/devtool/ide_plugins/ide_none.py | 15 +++---
4 files changed, 66 insertions(+), 51 deletions(-)
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 1cc9843a72..ec257f9859 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -4640,10 +4640,21 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests):
f"Configuration '{config_name}' should not have MIMode (CodeLLDB)")
self.assertNotIn("miDebuggerPath", config,
f"Configuration '{config_name}' should not have miDebuggerPath")
- self.assertEqual(config["request"], "launch",
- f"Configuration '{config_name}' should be launch type")
- self.assertEqual(config["cwd"], "/tmp",
- f"Configuration '{config_name}' cwd should be /tmp (writable on target)")
+
+ is_attach = "_attach" in config_name
+ if is_attach:
+ self.assertEqual(config["request"], "attach",
+ f"Configuration '{config_name}' should be attach type")
+ self.assertNotIn("cwd", config,
+ f"Configuration '{config_name}' should not set cwd in attach mode")
+ self.assertIn("postDebugTask", config,
+ f"attach configuration '{config_name}' should have postDebugTask "
+ "to stop the lldb-server platform instance afterwards")
+ else:
+ self.assertEqual(config["request"], "launch",
+ f"Configuration '{config_name}' should be launch type")
+ self.assertEqual(config["cwd"], "/tmp",
+ f"Configuration '{config_name}' cwd should be /tmp (writable on target)")
# Verify initCommands contain the platform connect sequence
init_commands = config.get("initCommands", [])
diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py
index bb9f100652..bc0b2ca9b7 100644
--- a/scripts/lib/devtool/ide_plugins/__init__.py
+++ b/scripts/lib/devtool/ide_plugins/__init__.py
@@ -267,9 +267,6 @@ class LldbServerConfig(DebuggerCrossConfig):
Unlike gdbserver, lldb-server platform mode is architecture-agnostic on the host
side: a single lldb-native binary handles all target architectures via the
LLDB platform protocol that CodeLLDB speaks natively.
-
- 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"
TARGET_START_RETRIES = 600
@@ -281,10 +278,12 @@ class LldbServerConfig(DebuggerCrossConfig):
# lldb-server platform spawns a separate gdb-remote-protocol
# "gdbserver" instance per debug session; without --gdbserver-port it
# picks a random port, which cannot be forwarded through slirp NAT.
- # Pin it to a fixed, dedicated port that gets slirp-forwarded too.
- self.gdbserver_port = DebuggerCrossConfig._port_next
- DebuggerCrossConfig._port_next += 1
- self.extra_ports.append(self.gdbserver_port)
+ # Pin a fixed, dedicated port per mode, each gets slirp-forwarded too.
+ self.gdbserver_ports = {}
+ for mode in self.server_modes():
+ self.gdbserver_ports[mode] = DebuggerCrossConfig._port_next
+ DebuggerCrossConfig._port_next += 1
+ self.extra_ports.append(self.gdbserver_ports[mode])
def _lldb_server_tmp_dir(self, mode):
return os.path.join('/tmp', 'lldb_server_%s' % self.id_pretty_mode(mode))
@@ -296,35 +295,38 @@ class LldbServerConfig(DebuggerCrossConfig):
return os.path.join(self._lldb_server_tmp_dir(mode), 'lldb_server.log')
def _target_start_cmd(self, mode):
- """SSH command to start lldb-server in platform mode on the target."""
+ """SSH command to start lldb-server in platform mode on the target.
+
+ Used identically for MULTI and ATTACH: in both cases lldb-server just
+ offers a platform connection, it does not care whether the client that
+ connects to it goes on to launch a new process or attach to an
+ existing one.
+ """
+ if mode not in (DebuggerServerModes.MULTI, DebuggerServerModes.ATTACH):
+ raise DevtoolError("Unsupported lldb-server mode: %s" % mode)
lldb_server = self.debugger_cross.debug_server_path
# Use '*:<port>' so lldb-server binds on all interfaces (0.0.0.0), not
# just loopback. The bare ':<port>' form only binds to 127.0.0.1 in
# 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.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)
- 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 --gdbserver-port %s > %s 2>&1 & _lldb_server_pid=\\$!; " % (
- lldb_server, self.debug_server_port, self.gdbserver_port, log_file)
- cmd += "echo \\$_lldb_server_pid > %s; " % pid_file
- cmd += self._target_wait_for_tcp_port_cmd(
- "lldb_server_pid", log_file)
- else:
- raise DevtoolError(
- "lldb-server only supports MULTI mode; "
- "ATTACH is handled client-side with 'process attach': %s" % mode)
+ 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 = self._target_tcp_port_check_cmd(mode) + " && exit 0; "
+ cmd += "mkdir -p %s; " % tmp_dir
+ cmd += "cd %s; " % tmp_dir
+ cmd += "%s platform --server --listen *:%s --gdbserver-port %s > %s 2>&1 & _lldb_server_pid=\\$!; " % (
+ lldb_server, self.port(mode), self.gdbserver_ports[mode], log_file)
+ cmd += "echo \\$_lldb_server_pid > %s; " % pid_file
+ cmd += self._target_wait_for_tcp_port_cmd(
+ "lldb_server_pid", log_file, mode)
return "\"/bin/sh -c '" + cmd + "'\""
def _target_stop_cmd(self, server_mode):
- """SSH command to stop a MULTI-mode lldb-server on the target."""
- pid_file = self._lldb_server_pid_file(DebuggerServerModes.MULTI)
- tmp_dir = self._lldb_server_tmp_dir(DebuggerServerModes.MULTI)
+ """SSH command to stop the lldb-server platform instance for the given mode."""
+ 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})
return "\"/bin/sh -c '" + cmd + "'\""
@@ -335,10 +337,6 @@ class LldbServerConfig(DebuggerCrossConfig):
def server_script(self, mode):
return os.path.join(self.script_dir, self.server_script_file(mode))
- def server_modes(self):
- """ATTACH mode is not applicable for lldb-server platform."""
- return [self.default_mode]
-
class IdeBase:
"""Base class defining the interface for IDE plugins"""
diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py
index f8036f15de..081dc08657 100644
--- a/scripts/lib/devtool/ide_plugins/ide_code.py
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -508,11 +508,12 @@ class IdeVSCode(IdeBase):
return launch_config
def _vscode_launch_bin_dbg_lldb(self, lldb_config, server_mode):
- """Generate a CodeLLDB (type: lldb) launch configuration entry for launch.json.
+ """Generate a CodeLLDB (type: lldb) launch/attach configuration entry for launch.json.
CodeLLDB connects to lldb-server via the LLDB platform protocol. The
initCommands select the remote platform and open the connection before
- the process is launched, so CodeLLDB can inspect and control it.
+ the process is launched or attached to, so CodeLLDB can inspect and
+ control it.
Using targetCreateCommands instead of "program" so we can pass both the
local host binary (for debug symbols) and the remote target path (where
@@ -520,14 +521,16 @@ class IdeVSCode(IdeBase):
"target create --remote-file". This prevents LLDB from uploading the
binary from its module cache to a temporary directory and ensures the
process starts from its installed location where the dynamic linker can
- find shared libraries via the standard search paths.
+ find shared libraries via the standard search paths. In ATTACH mode the
+ same lldb-server platform connection is used.
"""
modified_recipe = lldb_config.modified_recipe
debugger_cross = modified_recipe.debugger_cross
+ is_attach = server_mode == DebuggerServerModes.ATTACH
init_commands = [
"platform select remote-linux",
- "platform connect connect://%s:%d" % (debugger_cross.host, lldb_config.debug_server_port),
+ "platform connect connect://%s:%d" % (debugger_cross.host, lldb_config.port(server_mode)),
# Clear the default step-avoid-regexp so std:: and other library
# namespaces are not silently skipped on step-in. (default is "std::" in LLDB 15+)
"settings set target.process.thread.step-avoid-regexp \"\"",
@@ -592,15 +595,21 @@ class IdeVSCode(IdeBase):
launch_config = {
"name": lldb_config.id_pretty_mode(server_mode),
"type": "lldb",
- "request": "launch",
+ "request": "attach" if is_attach else "launch",
# Use targetCreateCommands instead of "program" to control both
# the local binary (for debug symbols) and the remote path.
"targetCreateCommands": [target_create_cmd],
- "stopOnEntry": False,
- "cwd": "/tmp",
"preLaunchTask": lldb_config.id_pretty_mode(server_mode),
"initCommands": init_commands,
}
+ if is_attach:
+ launch_config["postDebugTask"] = self._stop_task_label(
+ lldb_config, server_mode)
+ else:
+ # cwd configures the process the debugger launches, it is not
+ # part of the attach schema.
+ launch_config["stopOnEntry"] = False
+ launch_config["cwd"] = "/tmp"
if source_map:
launch_config["sourceMap"] = source_map
if modified_recipe.b:
diff --git a/scripts/lib/devtool/ide_plugins/ide_none.py b/scripts/lib/devtool/ide_plugins/ide_none.py
index 559940fda2..eba576bfdb 100644
--- a/scripts/lib/devtool/ide_plugins/ide_none.py
+++ b/scripts/lib/devtool/ide_plugins/ide_none.py
@@ -205,20 +205,15 @@ class LldbServerConfigNone(LldbServerConfig):
if server_mode is None:
server_mode = self.default_mode
lines = ['# This file is generated by devtool ide-sdk']
- if server_mode == DebuggerServerModes.MULTI:
- lines.append('# On the remote target:')
- lines.append('# lldb-server platform --server --listen *:%d --gdbserver-port %d' % (
- self.debug_server_port, self.gdbserver_port))
- else:
- lines.append('# On the remote target:')
- lines.append('# lldb-server platform --one-shot --server --listen *:%d --gdbserver-port %d' % (
- self.debug_server_port, self.gdbserver_port))
+ lines.append('# On the remote target:')
+ lines.append('# lldb-server platform --server --listen *:%d --gdbserver-port %d' % (
+ self.port(server_mode), self.gdbserver_ports[server_mode]))
lines.append('# On the build machine:')
lines.append('# cd ' + self.modified_recipe.real_srctree)
lines.append('# ' + self.debugger_cross.lldb + ' -s ' + self.lldbinit)
lines.append('platform select remote-linux')
lines.append('platform connect connect://%s:%d' % (
- self.debugger_cross.host, self.debug_server_port))
+ self.debugger_cross.host, self.port(server_mode)))
lines.append('settings set target.process.thread.step-avoid-regexp ""')
if self.image_recipe.rootfs_dbg:
@@ -267,6 +262,8 @@ class LldbServerConfigNone(LldbServerConfig):
def initialize(self):
self._gen_lldb_server_start_script()
+ if self.binary.runs_as_service and self.default_mode != DebuggerServerModes.ATTACH:
+ self._gen_lldb_server_start_script(DebuggerServerModes.ATTACH)
self._gen_lldbinit_config()
self._gen_lldb_start_script()
--
2.55.0
next prev parent reply other threads:[~2026-09-09 21:53 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 21:53 [PATCH 00/15] devtool ide-sdk: support booting from NFS, VSCode clangd AdrianF
2026-09-09 21:53 ` [PATCH 01/15] devtool: deploy: split ssh deployment into a separate function AdrianF
2026-09-09 21:53 ` [PATCH 02/15] devtool: deploy: allow deploying directly into a local rootfs directory AdrianF
2026-09-09 21:53 ` [PATCH 03/15] oe-selftest: devtool deploy-target: test deploying into a local rootfs path AdrianF
2026-09-09 21:53 ` [PATCH 04/15] devtool: ide-sdk: support NFS rootfs AdrianF
2026-09-09 21:53 ` [PATCH 05/15] oe-selftest: devtool ide-sdk: test NFS debug rootfs AdrianF
2026-09-09 21:53 ` [PATCH 06/15] runqemu-extract-sdk: refactor in Python AdrianF
2026-09-10 17:45 ` [OE-core] " Richard Purdie
2026-09-09 21:53 ` [PATCH 07/15] devtool: ide-sdk: reuse pseudo_rootfs_utils for SDK rootfs extraction AdrianF
2026-09-09 21:53 ` [PATCH 08/15] cpp-example: update formatting and code style AdrianF
2026-09-09 21:53 ` [PATCH 09/15] cpp-example: fix clang-tidy warnings AdrianF
2026-09-09 21:53 ` [PATCH 10/15] devtool: ide-sdk: add clangd support for VSCode IntelliSense AdrianF
2026-09-09 21:53 ` [PATCH 11/15] devtool: ide-sdk: support clangd for non-clang toolchain recipes AdrianF
2026-09-09 21:53 ` [PATCH 12/15] devtool: ide-sdk: format C/C++ with clangd when .clang-format is present AdrianF
2026-09-09 21:53 ` AdrianF [this message]
2026-09-09 21:53 ` [PATCH 14/15] devtool: ide-sdk: pin BBPATH in generated do_install script AdrianF
2026-09-09 21:53 ` [PATCH 15/15] buildstats: close proc/disk log files via weakref.finalize safety net AdrianF
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260909215337.89106-14-adrian.freihofer@siemens.com \
--to=adrian.freihofer@siemens.com \
--cc=openembedded-core@lists.openembedded.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox