[Lldb-commits] [lldb] [lldb-dap] Migrate restart request to structured types (PR #172488)
Sergei Druzhkov via lldb-commits
lldb-commits at lists.llvm.org
Tue Dec 16 12:53:57 PST 2025
https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/172488
>From 614c66c30d341eaf560bb6806e5c8c634f239666 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Sun, 14 Dec 2025 23:29:11 +0300
Subject: [PATCH 1/3] [lldb-dap] Migrate restart request to structured types
---
lldb/tools/lldb-dap/Handler/RequestHandler.h | 9 +-
.../Handler/RestartRequestHandler.cpp | 117 ++++--------------
.../lldb-dap/Protocol/ProtocolRequests.cpp | 88 ++++++++++---
.../lldb-dap/Protocol/ProtocolRequests.h | 13 ++
lldb/unittests/DAP/ProtocolRequestsTest.cpp | 40 ++++++
5 files changed, 154 insertions(+), 113 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 0669be50597e9..a18a8049c804d 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -340,11 +340,14 @@ class LaunchRequestHandler
void PostRun() const override;
};
-class RestartRequestHandler : public LegacyRequestHandler {
+class RestartRequestHandler
+ : public RequestHandler<std::optional<protocol::RestartArguments>,
+ protocol::RestartResponse> {
public:
- using LegacyRequestHandler::LegacyRequestHandler;
+ using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "restart"; }
- void operator()(const llvm::json::Object &request) const override;
+ llvm::Error
+ Run(const std::optional<protocol::RestartArguments> &args) const override;
};
class NextRequestHandler
diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index 100173bfc3082..3a2be3f30c7cf 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -7,91 +7,37 @@
//===----------------------------------------------------------------------===//
#include "DAP.h"
+#include "DAPError.h"
#include "EventHelper.h"
-#include "JSONUtils.h"
#include "LLDBUtils.h"
#include "Protocol/ProtocolRequests.h"
#include "RequestHandler.h"
-#include "llvm/Support/JSON.h"
-#include "llvm/Support/raw_ostream.h"
-namespace lldb_dap {
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
-// "RestartRequest": {
-// "allOf": [ { "$ref": "#/definitions/Request" }, {
-// "type": "object",
-// "description": "Restarts a debug session. Clients should only call this
-// request if the corresponding capability `supportsRestartRequest` is
-// true.\nIf the capability is missing or has the value false, a typical
-// client emulates `restart` by terminating the debug adapter first and then
-// launching it anew.",
-// "properties": {
-// "command": {
-// "type": "string",
-// "enum": [ "restart" ]
-// },
-// "arguments": {
-// "$ref": "#/definitions/RestartArguments"
-// }
-// },
-// "required": [ "command" ]
-// }]
-// },
-// "RestartArguments": {
-// "type": "object",
-// "description": "Arguments for `restart` request.",
-// "properties": {
-// "arguments": {
-// "oneOf": [
-// { "$ref": "#/definitions/LaunchRequestArguments" },
-// { "$ref": "#/definitions/AttachRequestArguments" }
-// ],
-// "description": "The latest version of the `launch` or `attach`
-// configuration."
-// }
-// }
-// },
-// "RestartResponse": {
-// "allOf": [ { "$ref": "#/definitions/Response" }, {
-// "type": "object",
-// "description": "Response to `restart` request. This is just an
-// acknowledgement, so no body field is required."
-// }]
-// },
-void RestartRequestHandler::operator()(
- const llvm::json::Object &request) const {
- llvm::json::Object response;
- FillResponse(request, response);
- if (!dap.target.GetProcess().IsValid()) {
- response["success"] = llvm::json::Value(false);
- EmplaceSafeString(response, "message",
- "Restart request received but no process was launched.");
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
+/// Restarts a debug session. Clients should only call this request if the
+/// corresponding capability `supportsRestartRequest` is true.
+/// If the capability is missing or has the value false, a typical client
+/// emulates `restart` by terminating the debug adapter first and then launching
+/// it anew.
+llvm::Error
+RestartRequestHandler::Run(const std::optional<RestartArguments> &args) const {
+ if (!dap.target.GetProcess().IsValid())
+ return llvm::make_error<DAPError>(
+ "Restart request received but no process was launched.");
- const llvm::json::Object *arguments = request.getObject("arguments");
- if (arguments) {
- // The optional `arguments` field in RestartRequest can contain an updated
- // version of the launch arguments. If there's one, use it.
- if (const llvm::json::Value *restart_arguments =
- arguments->get("arguments")) {
- protocol::LaunchRequestArguments updated_arguments;
- llvm::json::Path::Root root;
- if (!fromJSON(*restart_arguments, updated_arguments, root)) {
- response["success"] = llvm::json::Value(false);
- EmplaceSafeString(
- response, "message",
- llvm::formatv("Failed to parse updated launch arguments: {0}",
- llvm::toString(root.getError()))
- .str());
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
- dap.last_launch_request = updated_arguments;
+ if (args) {
+ if (std::holds_alternative<AttachRequestArguments>(args->arguments))
+ return llvm::make_error<DAPError>(
+ "Restarting an AttachRequest is not supported.");
+ if (const auto *arguments =
+ std::get_if<LaunchRequestArguments>(&args->arguments);
+ arguments) {
+ dap.last_launch_request = *arguments;
// Update DAP configuration based on the latest copy of the launch
// arguments.
- dap.SetConfiguration(updated_arguments.configuration, false);
+ dap.SetConfiguration(arguments->configuration, false);
dap.ConfigureSourceMaps();
}
}
@@ -117,12 +63,8 @@ void RestartRequestHandler::operator()(
// FIXME: Should we run 'preRunCommands'?
// FIXME: Should we add a 'preRestartCommands'?
- if (llvm::Error err = LaunchProcess(*dap.last_launch_request)) {
- response["success"] = llvm::json::Value(false);
- EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
+ if (llvm::Error err = LaunchProcess(*dap.last_launch_request))
+ return llvm::make_error<DAPError>(llvm::toString(std::move(err)));
SendProcessEvent(dap, Launch);
@@ -130,16 +72,11 @@ void RestartRequestHandler::operator()(
// Because we're restarting, configuration has already happened so we can
// continue the process right away.
if (dap.stop_at_entry) {
- if (llvm::Error err = SendThreadStoppedEvent(dap, /*on_entry=*/true)) {
- EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
+ if (llvm::Error err = SendThreadStoppedEvent(dap, /*on_entry=*/true))
+ return llvm::make_error<DAPError>(llvm::toString(std::move(err)));
} else {
dap.target.GetProcess().Continue();
}
- dap.SendJSON(llvm::json::Value(std::move(response)));
+ return llvm::Error::success();
}
-
-} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index bf470b78077df..1c2cd158ffd29 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -296,31 +296,53 @@ bool fromJSON(const json::Value &Params, Console &C, json::Path P) {
bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
json::Path P) {
json::ObjectMapper O(Params, P);
- return O && fromJSON(Params, LRA.configuration, P) &&
- O.mapOptional("noDebug", LRA.noDebug) &&
- O.mapOptional("launchCommands", LRA.launchCommands) &&
- O.mapOptional("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) &&
- O.mapOptional("detachOnError", LRA.detachOnError) &&
- O.mapOptional("disableASLR", LRA.disableASLR) &&
- O.mapOptional("disableSTDIO", LRA.disableSTDIO) &&
- O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) &&
- O.mapOptional("runInTerminal", LRA.console) &&
- O.mapOptional("console", LRA.console) &&
- O.mapOptional("stdio", LRA.stdio) && parseEnv(Params, LRA.env, P);
+ bool success =
+ O && fromJSON(Params, LRA.configuration, P) &&
+ O.mapOptional("noDebug", LRA.noDebug) &&
+ O.mapOptional("launchCommands", LRA.launchCommands) &&
+ O.mapOptional("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) &&
+ O.mapOptional("detachOnError", LRA.detachOnError) &&
+ O.mapOptional("disableASLR", LRA.disableASLR) &&
+ O.mapOptional("disableSTDIO", LRA.disableSTDIO) &&
+ O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) &&
+ O.mapOptional("runInTerminal", LRA.console) &&
+ O.mapOptional("console", LRA.console) &&
+ O.mapOptional("stdio", LRA.stdio) && parseEnv(Params, LRA.env, P);
+ if (!success)
+ return false;
+ if (LRA.configuration.program.empty() && LRA.launchCommands.empty()) {
+ P.report("`program` or `launchCommands` should be provided");
+ return false;
+ }
+ return true;
}
bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
json::Path P) {
json::ObjectMapper O(Params, P);
- return O && fromJSON(Params, ARA.configuration, P) &&
- O.mapOptional("attachCommands", ARA.attachCommands) &&
- O.mapOptional("pid", ARA.pid) &&
- O.mapOptional("waitFor", ARA.waitFor) &&
- 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);
+ bool success = O && fromJSON(Params, ARA.configuration, P) &&
+ O.mapOptional("attachCommands", ARA.attachCommands) &&
+ O.mapOptional("pid", ARA.pid) &&
+ O.mapOptional("waitFor", ARA.waitFor) &&
+ 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);
+ if (!success)
+ return false;
+ if (ARA.debuggerId.has_value())
+ return true;
+ if (ARA.targetId.has_value())
+ return true;
+ if ((ARA.pid == LLDB_INVALID_PROCESS_ID) &&
+ ARA.configuration.program.empty() && ARA.attachCommands.empty() &&
+ ARA.coreFile.empty() && (ARA.gdbRemotePort == LLDB_DAP_INVALID_PORT)) {
+ P.report("`pid` or `program` or `attachCommands` or `coreFile` or "
+ "`gdbRemotePort` should be provided");
+ return false;
+ }
+ return true;
}
bool fromJSON(const json::Value &Params, ContinueArguments &CA, json::Path P) {
@@ -737,4 +759,30 @@ llvm::json::Value toJSON(const TestGetTargetBreakpointsResponseBody &Body) {
return result;
}
+bool fromJSON(const llvm::json::Value &Params, RestartArguments &Args,
+ llvm::json::Path Path) {
+ const json::Object *O = Params.getAsObject();
+ if (!O) {
+ Path.report("expected object");
+ return false;
+ }
+ const json::Value *arguments = O->get("arguments");
+ if (arguments == nullptr)
+ return true;
+ LaunchRequestArguments launchArguments;
+ llvm::json::Path::Root root;
+ if (fromJSON(*arguments, launchArguments, root)) {
+ Args.arguments = std::move(launchArguments);
+ return true;
+ }
+ AttachRequestArguments attachArguments;
+ if (fromJSON(*arguments, attachArguments, root)) {
+ Args.arguments = std::move(attachArguments);
+ return true;
+ }
+ Path.report(
+ "failed to parse arguments, expected `launch` or `attach` arguments");
+ return false;
+}
+
} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index cc123943ec0b6..33fcaae1710b5 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -31,6 +31,7 @@
#include <cstdint>
#include <optional>
#include <string>
+#include <variant>
#include <vector>
namespace lldb_dap::protocol {
@@ -1255,6 +1256,18 @@ struct TestGetTargetBreakpointsResponseBody {
};
llvm::json::Value toJSON(const TestGetTargetBreakpointsResponseBody &);
+/// Arguments for `restart` request.
+struct RestartArguments {
+ /// The latest version of the `launch` or `attach` configuration.
+ std::variant<std::monostate, LaunchRequestArguments, AttachRequestArguments>
+ arguments = std::monostate{};
+};
+bool fromJSON(const llvm::json::Value &, RestartArguments &, llvm::json::Path);
+
+/// Response to `restart` request. This is just an acknowledgement, so no body
+/// field is required.
+using RestartResponse = VoidResponse;
+
} // namespace lldb_dap::protocol
#endif
diff --git a/lldb/unittests/DAP/ProtocolRequestsTest.cpp b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
index 710b78960ef09..c639e40453fb0 100644
--- a/lldb/unittests/DAP/ProtocolRequestsTest.cpp
+++ b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
@@ -304,3 +304,43 @@ TEST(ProtocolRequestsTest, TestGetTargetBreakpointsResponseBody) {
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body));
}
+
+TEST(ProtocolRequestsTest, RestartArguments) {
+ llvm::Expected<RestartArguments> expected = parse<RestartArguments>(R"({})");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_TRUE(std::holds_alternative<std::monostate>(expected->arguments));
+
+ // Check missed keys.
+ expected = parse<RestartArguments>(R"({
+ "arguments": {}
+ })");
+ EXPECT_THAT_EXPECTED(expected,
+ FailedWithMessage("failed to parse arguments, expected "
+ "`launch` or `attach` arguments"));
+
+ // Check launch arguments.
+ expected = parse<RestartArguments>(R"({
+ "arguments": {
+ "program": "main.exe",
+ "cwd": "/home/root"
+ }
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ const LaunchRequestArguments *launch_args =
+ std::get_if<LaunchRequestArguments>(&expected->arguments);
+ EXPECT_NE(launch_args, nullptr);
+ EXPECT_EQ(launch_args->configuration.program, "main.exe");
+ EXPECT_EQ(launch_args->cwd, "/home/root");
+
+ // Check attach arguments.
+ expected = parse<RestartArguments>(R"({
+ "arguments": {
+ "pid": 123
+ }
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ const AttachRequestArguments *attach_args =
+ std::get_if<AttachRequestArguments>(&expected->arguments);
+ EXPECT_NE(attach_args, nullptr);
+ EXPECT_EQ(attach_args->pid, 123U);
+}
>From 7253c4840bdae1ee2ff2d5f66a884edb783783f4 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Tue, 16 Dec 2025 21:14:18 +0300
Subject: [PATCH 2/3] Fix review comments
---
.../tools/lldb-dap/attach/TestDAP_attach.py | 2 +-
.../tools/lldb-dap/launch/TestDAP_launch.py | 2 +-
.../lldb-dap/Handler/AttachRequestHandler.cpp | 22 -----------
.../lldb-dap/Handler/LaunchRequestHandler.cpp | 6 ---
.../Handler/RestartRequestHandler.cpp | 4 +-
.../lldb-dap/Protocol/ProtocolRequests.cpp | 37 ++++++++++++++-----
6 files changed, 31 insertions(+), 42 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 d6287397a93b0..e333ff84c100f 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -87,7 +87,7 @@ def test_attach_with_missing_debuggerId_or_targetId(self):
resp = self.attach(targetId=99999, expectFailure=True)
self.assertFalse(resp["success"])
self.assertIn(
- "Both debuggerId and targetId must be specified together",
+ "Both 'debuggerId' and 'targetId' must be specified together",
resp["body"]["error"]["format"],
)
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index ca881f1d817c5..0cab7ed44740f 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -57,7 +57,7 @@ def test_failing_launch_commands_and_console(self):
)
self.assertFalse(response["success"])
self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))
- self.assertEqual(
+ self.assertIn(
"'launchCommands' and non-internal 'console' are mutually exclusive",
self.get_dict_value(response, ["body", "error", "format"]),
)
diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index f0996eb3ff0f4..1c14dcd16e5e6 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -35,33 +35,11 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
std::optional<int> debugger_id = args.debuggerId;
std::optional<lldb::user_id_t> target_id = args.targetId;
- // Validate that both debugger_id and target_id are provided together.
- if (debugger_id.has_value() != target_id.has_value()) {
- return llvm::createStringError(
- "Both debuggerId and targetId must be specified together for debugger "
- "reuse, or both must be omitted to create a new debugger");
- }
-
if (Error err = debugger_id && target_id
? dap.InitializeDebugger(*debugger_id, *target_id)
: dap.InitializeDebugger())
return err;
- // Validate that we have a well formed attach request.
- if (args.attachCommands.empty() && args.coreFile.empty() &&
- args.configuration.program.empty() &&
- args.pid == LLDB_INVALID_PROCESS_ID &&
- args.gdbRemotePort == LLDB_DAP_INVALID_PORT && !target_id.has_value())
- return make_error<DAPError>(
- "expected one of 'pid', 'program', 'attachCommands', "
- "'coreFile', 'gdb-remote-port', or target_id to be specified");
-
- // Check if we have mutually exclusive arguments.
- if ((args.pid != LLDB_INVALID_PROCESS_ID) &&
- (args.gdbRemotePort != LLDB_DAP_INVALID_PORT))
- return make_error<DAPError>(
- "'pid' and 'gdb-remote-port' are mutually exclusive");
-
dap.SetConfiguration(args.configuration, /*is_attach=*/true);
if (!args.coreFile.empty())
dap.stop_at_entry = true;
diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
index 329f0a7bf6453..33563cc7a5cee 100644
--- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
@@ -26,12 +26,6 @@ Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const {
if (Error err = dap.InitializeDebugger())
return err;
- // Validate that we have a well formed launch request.
- if (!arguments.launchCommands.empty() &&
- arguments.console != protocol::eConsoleInternal)
- return make_error<DAPError>(
- "'launchCommands' and non-internal 'console' are mutually exclusive");
-
dap.SetConfiguration(arguments.configuration, /*is_attach=*/false);
dap.last_launch_request = arguments;
diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index 3a2be3f30c7cf..804d982d5d199 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -64,7 +64,7 @@ RestartRequestHandler::Run(const std::optional<RestartArguments> &args) const {
// FIXME: Should we run 'preRunCommands'?
// FIXME: Should we add a 'preRestartCommands'?
if (llvm::Error err = LaunchProcess(*dap.last_launch_request))
- return llvm::make_error<DAPError>(llvm::toString(std::move(err)));
+ return err;
SendProcessEvent(dap, Launch);
@@ -73,7 +73,7 @@ RestartRequestHandler::Run(const std::optional<RestartArguments> &args) const {
// continue the process right away.
if (dap.stop_at_entry) {
if (llvm::Error err = SendThreadStoppedEvent(dap, /*on_entry=*/true))
- return llvm::make_error<DAPError>(llvm::toString(std::move(err)));
+ return err;
} else {
dap.target.GetProcess().Continue();
}
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 1c2cd158ffd29..c87111d8f1b78 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -310,8 +310,15 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
O.mapOptional("stdio", LRA.stdio) && parseEnv(Params, LRA.env, P);
if (!success)
return false;
+ // Validate that we have a well formed launch request.
+ if (!LRA.launchCommands.empty() &&
+ LRA.console != protocol::eConsoleInternal) {
+ P.report(
+ "'launchCommands' and non-internal 'console' are mutually exclusive");
+ return false;
+ }
if (LRA.configuration.program.empty() && LRA.launchCommands.empty()) {
- P.report("`program` or `launchCommands` should be provided");
+ P.report("'program' or 'launchCommands' should be provided");
return false;
}
return true;
@@ -331,15 +338,25 @@ bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
O.mapOptional("debuggerId", ARA.debuggerId);
if (!success)
return false;
- if (ARA.debuggerId.has_value())
- return true;
- if (ARA.targetId.has_value())
- return true;
- if ((ARA.pid == LLDB_INVALID_PROCESS_ID) &&
- ARA.configuration.program.empty() && ARA.attachCommands.empty() &&
- ARA.coreFile.empty() && (ARA.gdbRemotePort == LLDB_DAP_INVALID_PORT)) {
- P.report("`pid` or `program` or `attachCommands` or `coreFile` or "
- "`gdbRemotePort` should be provided");
+ // 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()) {
+ P.report("expected one of 'pid', 'program', 'attachCommands', "
+ "'coreFile', 'gdb-remote-port', or 'targetId' to be specified");
+ return false;
+ }
+ // Check if we have mutually exclusive arguments.
+ if ((ARA.pid != LLDB_INVALID_PROCESS_ID) &&
+ (ARA.gdbRemotePort != LLDB_DAP_INVALID_PORT)) {
+ 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;
>From 03974147a6e0f1ccb5642932eb739b49c4058910 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Tue, 16 Dec 2025 23:53:36 +0300
Subject: [PATCH 3/3] Improve error handling
---
.../lldb-dap/Handler/RestartRequestHandler.cpp | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index 804d982d5d199..2562a8f050c96 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -12,6 +12,7 @@
#include "LLDBUtils.h"
#include "Protocol/ProtocolRequests.h"
#include "RequestHandler.h"
+#include "lldb/API/SBError.h"
using namespace lldb_dap;
using namespace lldb_dap::protocol;
@@ -54,7 +55,8 @@ RestartRequestHandler::Run(const std::optional<RestartArguments> &args) const {
ScopeSyncMode scope_sync_mode(dap.debugger);
lldb::StateType state = process.GetState();
if (state != lldb::eStateConnected) {
- process.Kill();
+ if (lldb::SBError error = process.Kill(); error.Fail())
+ return ToError(error);
}
// Clear the list of thread ids to avoid sending "thread exited" events
// for threads of the process we are terminating.
@@ -63,8 +65,8 @@ RestartRequestHandler::Run(const std::optional<RestartArguments> &args) const {
// FIXME: Should we run 'preRunCommands'?
// FIXME: Should we add a 'preRestartCommands'?
- if (llvm::Error err = LaunchProcess(*dap.last_launch_request))
- return err;
+ if (llvm::Error error = LaunchProcess(*dap.last_launch_request))
+ return error;
SendProcessEvent(dap, Launch);
@@ -72,10 +74,11 @@ RestartRequestHandler::Run(const std::optional<RestartArguments> &args) const {
// Because we're restarting, configuration has already happened so we can
// continue the process right away.
if (dap.stop_at_entry) {
- if (llvm::Error err = SendThreadStoppedEvent(dap, /*on_entry=*/true))
- return err;
+ if (llvm::Error error = SendThreadStoppedEvent(dap, /*on_entry=*/true))
+ return error;
} else {
- dap.target.GetProcess().Continue();
+ if (lldb::SBError error = dap.target.GetProcess().Continue(); error.Fail())
+ return ToError(error);
}
return llvm::Error::success();
More information about the lldb-commits
mailing list