All of lore.kernel.org
 help / color / mirror / Atom feed
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



  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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.