[Lldb-commits] [lldb] [lldb-dap] Move targetId and debuggerId into a session property (PR #175930)
Ebuka Ezike via lldb-commits
lldb-commits at lists.llvm.org
Wed Jan 14 03:47:00 PST 2026
https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/175930
>From b696426f9577059791921fde5ae57f783fe45685 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Wed, 14 Jan 2026 11:34:57 +0000
Subject: [PATCH 1/2] [lldb-dap] Move targetId and debuggerId into a session
property
This makes it clear the fields required for attaching to an
existing debug session.
It also makes it easier to check mutually exclusive fields required
to attach.
---
.../test/tools/lldb-dap/dap_server.py | 9 +++----
.../tools/lldb-dap/attach/TestDAP_attach.py | 13 +++++-----
lldb/tools/lldb-dap/DAP.cpp | 7 +++---
lldb/tools/lldb-dap/DAP.h | 9 +++----
.../lldb-dap/Handler/AttachRequestHandler.cpp | 17 +++++++------
.../lldb-dap/Protocol/ProtocolRequests.cpp | 24 ++++++++++---------
.../lldb-dap/Protocol/ProtocolRequests.h | 16 +++++++++----
lldb/tools/lldb-dap/extension/package.json | 18 +++++++++++---
lldb/unittests/DAP/ProtocolTypesTest.cpp | 9 +++++++
9 files changed, 72 insertions(+), 50 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 875cfb6c6b197..f3f7a168db028 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -874,8 +874,7 @@ def request_attach(
*,
program: Optional[str] = None,
pid: Optional[int] = None,
- debuggerId: Optional[int] = None,
- targetId: Optional[int] = None,
+ session: Optional[dict[str, int]] = None,
waitFor=False,
initCommands: Optional[list[str]] = None,
preRunCommands: Optional[list[str]] = None,
@@ -895,10 +894,8 @@ def request_attach(
args_dict["pid"] = pid
if program is not None:
args_dict["program"] = program
- if debuggerId is not None:
- args_dict["debuggerId"] = debuggerId
- if targetId is not None:
- args_dict["targetId"] = targetId
+ if session is not None:
+ args_dict["session"] = session
if waitFor:
args_dict["waitFor"] = waitFor
args_dict["initCommands"] = self.init_commands
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
index 5810be67a2bf9..629eb657b38f6 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -94,7 +94,7 @@ def test_by_name_waitFor(self):
if self.spawn_thread.is_alive():
self.spawn_thread.join(timeout=10)
- def test_attach_with_missing_debuggerId_or_targetId(self):
+ def test_attach_with_missing_session_debugger(self):
"""
Test that attaching with only one of debuggerId/targetId specified
fails with the expected error message.
@@ -102,14 +102,14 @@ def test_attach_with_missing_debuggerId_or_targetId(self):
self.build_and_create_debug_adapter()
# Test with only targetId specified (no debuggerId)
- resp = self.attach(targetId=99999, waitForResponse=True)
+ session = {"targetId": 99999}
+ resp = self.attach(session=session, waitForResponse=True)
self.assertFalse(resp["success"])
- self.assertIn(
- "Both 'debuggerId' and 'targetId' must be specified together",
+ self.assertIn("missing value at arguments.session.debuggerId",
resp["body"]["error"]["format"],
)
- def test_attach_with_invalid_debuggerId_and_targetId(self):
+ def test_attach_with_invalid_session(self):
"""
Test that attaching with both debuggerId and targetId specified but
invalid fails with an appropriate error message.
@@ -119,7 +119,8 @@ def test_attach_with_invalid_debuggerId_and_targetId(self):
# Attach with both debuggerId=9999 and targetId=99999 (both invalid).
# Since debugger ID 9999 likely doesn't exist in the global registry,
# we expect a validation error.
- resp = self.attach(debuggerId=9999, targetId=99999, waitForResponse=True)
+ session = {"debuggerId": 9999, "targetId": 9999}
+ resp = self.attach(session=session, waitForResponse=True)
self.assertFalse(resp["success"])
error_msg = resp["body"]["error"]["format"]
# Either error is acceptable - both indicate the debugger reuse
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 47468d517d333..c8ba2f73a871a 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -1310,17 +1310,16 @@ void DAP::StartEventThreads() {
StartEventThread();
}
-llvm::Error DAP::InitializeDebugger(int debugger_id,
- lldb::user_id_t target_id) {
+llvm::Error DAP::InitializeDebugger(const DAPSession &session) {
// Find the existing debugger by ID
- debugger = lldb::SBDebugger::FindDebuggerWithID(debugger_id);
+ debugger = lldb::SBDebugger::FindDebuggerWithID(session.debuggerId);
if (!debugger.IsValid()) {
return llvm::createStringError(
"Unable to find existing debugger for debugger ID");
}
// Find the target within the debugger by its globally unique ID
- lldb::SBTarget target = debugger.FindTargetByGloballyUniqueID(target_id);
+ lldb::SBTarget target = debugger.FindTargetByGloballyUniqueID(session.targetId);
if (!target.IsValid()) {
return llvm::createStringError(
"Unable to find existing target for target ID");
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index e1cd5c29a3907..8d56a226dbb28 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -432,12 +432,9 @@ struct DAP final : public DAPTransport::MessageHandler {
/// Perform complete DAP initialization by reusing an existing debugger and
/// target.
///
- /// \param[in] debugger_id
- /// The ID of the existing debugger to reuse.
- ///
- /// \param[in] target_id
- /// The globally unique ID of the existing target to reuse.
- llvm::Error InitializeDebugger(int debugger_id, lldb::user_id_t target_id);
+ /// \param[in] session
+ /// A session consisting of an existing debugger and target.
+ llvm::Error InitializeDebugger(const protocol::DAPSession &session);
/// Start event handling threads based on client capabilities.
void StartEventThreads();
diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index 2261924af9ccb..41e0551373aa7 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -30,12 +30,10 @@ namespace lldb_dap {
Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
// Initialize DAP debugger and related components if not sharing previously
// launched debugger.
- std::optional<int> debugger_id = args.debuggerId;
- std::optional<lldb::user_id_t> target_id = args.targetId;
+ std::optional<DAPSession> session = args.session;
- if (Error err = debugger_id && target_id
- ? dap.InitializeDebugger(*debugger_id, *target_id)
- : dap.InitializeDebugger())
+ if (Error err =
+ session ? dap.InitializeDebugger(*session) : dap.InitializeDebugger())
return err;
dap.SetConfiguration(args.configuration, /*is_attach=*/true);
@@ -59,12 +57,13 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
lldb::SBError error;
lldb::SBTarget target;
- if (target_id) {
+ if (session) {
// Use the unique target ID to get the target.
- target = dap.debugger.FindTargetByGloballyUniqueID(*target_id);
+ target = dap.debugger.FindTargetByGloballyUniqueID(session->targetId);
if (!target.IsValid()) {
error.SetErrorString(
- llvm::formatv("invalid target_id {0} in attach config", *target_id)
+ llvm::formatv("invalid targetId {0} in attach config",
+ session->targetId)
.str()
.c_str());
}
@@ -120,7 +119,7 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
connect_url += std::to_string(args.gdbRemotePort);
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
error);
- } else if (!target_id.has_value()) {
+ } else if (!session) {
// Attach by pid or process name.
lldb::SBAttachInfo attach_info;
if (args.pid != LLDB_INVALID_PROCESS_ID)
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 1f8283eba86fa..23789f6d46f90 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -331,6 +331,15 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
return true;
}
+bool fromJSON(const llvm::json::Value &Params, DAPSession &Ses,
+ llvm::json::Path P) {
+
+ json::ObjectMapper O(Params, P);
+ // Validate that both debuggerID and targetId are provided.
+ return O && O.map("targetId", Ses.targetId) &&
+ O.map("debuggerId", Ses.debuggerId);
+}
+
bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
json::Path P) {
json::ObjectMapper O(Params, P);
@@ -341,16 +350,15 @@ bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
O.mapOptional("gdb-remote-port", ARA.gdbRemotePort) &&
O.mapOptional("gdb-remote-hostname", ARA.gdbRemoteHostname) &&
O.mapOptional("coreFile", ARA.coreFile) &&
- O.mapOptional("targetId", ARA.targetId) &&
- O.mapOptional("debuggerId", ARA.debuggerId);
+ O.mapOptional("session", ARA.session);
if (!success)
return false;
// Validate that we have a well formed attach request.
if (ARA.attachCommands.empty() && ARA.coreFile.empty() &&
ARA.configuration.program.empty() && ARA.pid == LLDB_INVALID_PROCESS_ID &&
- ARA.gdbRemotePort == LLDB_DAP_INVALID_PORT && !ARA.targetId.has_value()) {
+ ARA.gdbRemotePort == LLDB_DAP_INVALID_PORT && !ARA.session.has_value()) {
P.report("expected one of 'pid', 'program', 'attachCommands', "
- "'coreFile', 'gdb-remote-port', or 'targetId' to be specified");
+ "'coreFile', 'gdb-remote-port', or 'session' to be specified");
return false;
}
// Check if we have mutually exclusive arguments.
@@ -359,13 +367,7 @@ bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
P.report("'pid' and 'gdb-remote-port' are mutually exclusive");
return false;
}
- // Validate that both debugger_id and target_id are provided together.
- if (ARA.debuggerId.has_value() != ARA.targetId.has_value()) {
- P.report(
- "Both 'debuggerId' and 'targetId' must be specified together for "
- "debugger reuse, or both must be omitted to create a new debugger");
- return false;
- }
+
return true;
}
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 9a99af9068ef1..fa826563963a5 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -317,6 +317,15 @@ using LaunchResponse = VoidResponse;
/// An invalid 'frameId' default value.
#define LLDB_DAP_INVALID_FRAME_ID UINT64_MAX
+struct DAPSession {
+ /// Unique ID of an existing target to attach to.
+ lldb::user_id_t targetId;
+
+ /// ID of an existing debugger instance to use.
+ int debuggerId;
+};
+bool fromJSON(const llvm::json::Value &, DAPSession &, llvm::json::Path);
+
/// lldb-dap specific attach arguments.
struct AttachRequestArguments {
/// Common lldb-dap configuration values for launching/attaching operations.
@@ -351,11 +360,8 @@ struct AttachRequestArguments {
/// Path to the core file to debug.
std::string coreFile;
- /// Unique ID of an existing target to attach to.
- std::optional<lldb::user_id_t> targetId;
-
- /// ID of an existing debugger instance to use.
- std::optional<int> debuggerId;
+ /// An Existing session that consist of a target and debugger.
+ std::optional<DAPSession> session;
/// @}
};
diff --git a/lldb/tools/lldb-dap/extension/package.json b/lldb/tools/lldb-dap/extension/package.json
index 2a5104fc61dbe..c90609cf7f760 100644
--- a/lldb/tools/lldb-dap/extension/package.json
+++ b/lldb/tools/lldb-dap/extension/package.json
@@ -795,9 +795,21 @@
"description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.",
"default": []
},
- "targetId": {
- "type": "number",
- "description": "The globally unique target id to attach to. Used when a target is dynamically created."
+ "session": {
+ "type": "object",
+ "properties": {
+ "targetId": {
+ "type": "number",
+ "description": "The globally unique target id to attach to. Use when a target is dynamically created."
+ },
+ "debuggerId": {
+ "type": "number",
+ "description": "ID of an existing debugger instance to use."
+ },
+ "additionalProperties": false,
+ "required": ["targetId", "debuggerId"]
+ },
+ "description": "This property is Experimental.\nAttach to an existing debugging session.\nAdded in version 22.0."
},
"initCommands": {
"type": "array",
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 2dd795d28172a..6476492813455 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -1262,3 +1262,12 @@ TEST(ProtocolTypesTest, StackFrame) {
ASSERT_THAT_EXPECTED(expected_frame, llvm::Succeeded());
EXPECT_EQ(pp(*expected_frame), pp(frame));
}
+
+TEST(ProtocolTypesTest, DAPSession) {
+ const DAPSession session{/*targetId*/ 1000, /*debuggerId*/ 300};
+
+ auto expected = parse<DAPSession>(R"({"targetId": 1000, "debuggerId": 300})");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->debuggerId, session.debuggerId);
+ EXPECT_EQ(expected->targetId, session.targetId);
+}
>From 62d0fdd93e0b062d28490e2db3963351dea25bd8 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Wed, 14 Jan 2026 11:46:44 +0000
Subject: [PATCH 2/2] format files
---
lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py | 3 ++-
lldb/tools/lldb-dap/DAP.cpp | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
index 629eb657b38f6..becf793d2dafb 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -105,7 +105,8 @@ def test_attach_with_missing_session_debugger(self):
session = {"targetId": 99999}
resp = self.attach(session=session, waitForResponse=True)
self.assertFalse(resp["success"])
- self.assertIn("missing value at arguments.session.debuggerId",
+ self.assertIn(
+ "missing value at arguments.session.debuggerId",
resp["body"]["error"]["format"],
)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index c8ba2f73a871a..f09575ce8ba42 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -1319,7 +1319,8 @@ llvm::Error DAP::InitializeDebugger(const DAPSession &session) {
}
// Find the target within the debugger by its globally unique ID
- lldb::SBTarget target = debugger.FindTargetByGloballyUniqueID(session.targetId);
+ lldb::SBTarget target =
+ debugger.FindTargetByGloballyUniqueID(session.targetId);
if (!target.IsValid()) {
return llvm::createStringError(
"Unable to find existing target for target ID");
More information about the lldb-commits
mailing list