[Lldb-commits] [lldb] [lldb-dap] Move requests into their own object/file (PR #128262)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Fri Feb 21 17:21:03 PST 2025
https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/128262
None
>From 03bf2fafb0b8b7e1dc08f6510802b73b5fe4dde7 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 21 Feb 2025 19:20:36 -0600
Subject: [PATCH] [lldb-dap] Move requests into their own object/file
---
lldb/tools/lldb-dap/CMakeLists.txt | 5 +
lldb/tools/lldb-dap/DAP.h | 8 +
lldb/tools/lldb-dap/Request/AttachRequest.cpp | 211 ++++++++++++++++++
lldb/tools/lldb-dap/Request/Request.cpp | 92 ++++++++
lldb/tools/lldb-dap/Request/Request.h | 45 ++++
lldb/tools/lldb-dap/lldb-dap.cpp | 4 +
6 files changed, 365 insertions(+)
create mode 100644 lldb/tools/lldb-dap/Request/AttachRequest.cpp
create mode 100644 lldb/tools/lldb-dap/Request/Request.cpp
create mode 100644 lldb/tools/lldb-dap/Request/Request.h
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 43fc18873feb3..e97f7ecdbd31b 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -36,6 +36,9 @@ add_lldb_tool(lldb-dap
SourceBreakpoint.cpp
Watchpoint.cpp
+ Request/Request.cpp
+ Request/AttachRequest.cpp
+
LINK_LIBS
liblldb
lldbHost
@@ -46,6 +49,8 @@ add_lldb_tool(lldb-dap
Support
)
+target_include_directories(lldb-dap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+
if(LLDB_DAP_WELCOME_MESSAGE)
target_compile_definitions(lldb-dap
PRIVATE
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index b23be68ea002f..33507061e0750 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -16,6 +16,7 @@
#include "InstructionBreakpoint.h"
#include "OutputRedirector.h"
#include "ProgressEvent.h"
+#include "Request/Request.h"
#include "SourceBreakpoint.h"
#include "lldb/API/SBBroadcaster.h"
#include "lldb/API/SBCommandInterpreter.h"
@@ -37,6 +38,7 @@
#include "llvm/Support/JSON.h"
#include "llvm/Support/Threading.h"
#include <map>
+#include <memory>
#include <mutex>
#include <optional>
#include <thread>
@@ -184,6 +186,7 @@ struct DAP {
lldb::pid_t restarting_process_id;
bool configuration_done_sent;
std::map<std::string, RequestCallback, std::less<>> request_handlers;
+ llvm::StringMap<std::unique_ptr<Request>> new_request_handlers;
bool waiting_for_run_in_terminal;
ProgressEventReporter progress_event_reporter;
// Keep track of the last stop thread index IDs as threads won't go away
@@ -330,6 +333,11 @@ struct DAP {
/// IDE.
void RegisterRequestCallback(std::string request, RequestCallback callback);
+ /// Registers a request handler.
+ template <typename Request> void RegisterRequest() {
+ new_request_handlers[Request::getName()] = std::make_unique<Request>(*this);
+ }
+
/// Debuggee will continue from stopped state.
void WillContinue() { variables.Clear(); }
diff --git a/lldb/tools/lldb-dap/Request/AttachRequest.cpp b/lldb/tools/lldb-dap/Request/AttachRequest.cpp
new file mode 100644
index 0000000000000..f1b3bfc878427
--- /dev/null
+++ b/lldb/tools/lldb-dap/Request/AttachRequest.cpp
@@ -0,0 +1,211 @@
+//===-- AttachRequest.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "Request.h"
+#include "lldb/API/SBListener.h"
+#include "llvm/Support/FileSystem.h"
+
+namespace lldb_dap {
+/// Prints a welcome message on the editor if the preprocessor variable
+/// LLDB_DAP_WELCOME_MESSAGE is defined.
+static void PrintWelcomeMessage(DAP &dap) {
+#ifdef LLDB_DAP_WELCOME_MESSAGE
+ dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
+#endif
+}
+
+// "AttachRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Attach request; value of command field is 'attach'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "attach" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/AttachRequestArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "AttachRequestArguments": {
+// "type": "object",
+// "description": "Arguments for 'attach' request.\nThe attach request has no
+// standardized attributes."
+// },
+// "AttachResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'attach' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+
+void AttachRequest::operator()(const llvm::json::Object &request) {
+ dap.is_attach = true;
+ dap.last_launch_or_attach_request = request;
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ lldb::SBAttachInfo attach_info;
+ const int invalid_port = 0;
+ const auto *arguments = request.getObject("arguments");
+ const lldb::pid_t pid =
+ GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
+ const auto gdb_remote_port =
+ GetUnsigned(arguments, "gdb-remote-port", invalid_port);
+ const auto gdb_remote_hostname =
+ GetString(arguments, "gdb-remote-hostname", "localhost");
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ attach_info.SetProcessID(pid);
+ const auto wait_for = GetBoolean(arguments, "waitFor", false);
+ attach_info.SetWaitForLaunch(wait_for, false /*async*/);
+ dap.init_commands = GetStrings(arguments, "initCommands");
+ dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
+ dap.stop_commands = GetStrings(arguments, "stopCommands");
+ dap.exit_commands = GetStrings(arguments, "exitCommands");
+ dap.terminate_commands = GetStrings(arguments, "terminateCommands");
+ auto attachCommands = GetStrings(arguments, "attachCommands");
+ llvm::StringRef core_file = GetString(arguments, "coreFile");
+ const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
+ dap.stop_at_entry =
+ core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
+ dap.post_run_commands = GetStrings(arguments, "postRunCommands");
+ const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
+ dap.enable_auto_variable_summaries =
+ GetBoolean(arguments, "enableAutoVariableSummaries", false);
+ dap.enable_synthetic_child_debugging =
+ GetBoolean(arguments, "enableSyntheticChildDebugging", false);
+ dap.display_extended_backtrace =
+ GetBoolean(arguments, "displayExtendedBacktrace", false);
+ dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`");
+ dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
+ dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
+
+ PrintWelcomeMessage(dap);
+
+ // This is a hack for loading DWARF in .o files on Mac where the .o files
+ // in the debug map of the main executable have relative paths which require
+ // the lldb-dap binary to have its working directory set to that relative
+ // root for the .o files in order to be able to load debug info.
+ if (!debuggerRoot.empty())
+ llvm::sys::fs::set_current_path(debuggerRoot);
+
+ // Run any initialize LLDB commands the user specified in the launch.json
+ if (llvm::Error err = dap.RunInitCommands()) {
+ response["success"] = false;
+ EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+
+ SetSourceMapFromArguments(*arguments);
+
+ lldb::SBError status;
+ dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
+ if (status.Fail()) {
+ response["success"] = llvm::json::Value(false);
+ EmplaceSafeString(response, "message", status.GetCString());
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+
+ // Run any pre run LLDB commands the user specified in the launch.json
+ if (llvm::Error err = dap.RunPreRunCommands()) {
+ response["success"] = false;
+ EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+
+ if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
+ wait_for) {
+ char attach_msg[256];
+ auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
+ "Waiting to attach to \"%s\"...",
+ dap.target.GetExecutable().GetFilename());
+ dap.SendOutput(OutputType::Console,
+ llvm::StringRef(attach_msg, attach_msg_len));
+ }
+ if (attachCommands.empty()) {
+ // No "attachCommands", just attach normally.
+ // Disable async events so the attach will be successful when we return from
+ // the launch call and the launch will happen synchronously
+ dap.debugger.SetAsync(false);
+ if (core_file.empty()) {
+ if ((pid != LLDB_INVALID_PROCESS_ID) &&
+ (gdb_remote_port != invalid_port)) {
+ // If both pid and port numbers are specified.
+ error.SetErrorString("The user can't specify both pid and port");
+ } else if (gdb_remote_port != invalid_port) {
+ // If port is specified and pid is not.
+ lldb::SBListener listener = dap.debugger.GetListener();
+
+ // If the user hasn't provided the hostname property, default localhost
+ // being used.
+ std::string connect_url =
+ llvm::formatv("connect://{0}:", gdb_remote_hostname);
+ connect_url += std::to_string(gdb_remote_port);
+ dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
+ error);
+ } else {
+ // Attach by process name or id.
+ dap.target.Attach(attach_info, error);
+ }
+ } else
+ dap.target.LoadCore(core_file.data(), error);
+ // Reenable async events
+ dap.debugger.SetAsync(true);
+ } else {
+ // We have "attachCommands" that are a set of commands that are expected
+ // to execute the commands after which a process should be created. If there
+ // is no valid process after running these commands, we have failed.
+ if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
+ response["success"] = false;
+ EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+ // The custom commands might have created a new target so we should use the
+ // selected target after these commands are run.
+ dap.target = dap.debugger.GetSelectedTarget();
+
+ // Make sure the process is attached and stopped before proceeding as the
+ // the launch commands are not run using the synchronous mode.
+ error = dap.WaitForProcessToStop(timeout_seconds);
+ }
+
+ if (error.Success() && core_file.empty()) {
+ auto attached_pid = dap.target.GetProcess().GetProcessID();
+ if (attached_pid == LLDB_INVALID_PROCESS_ID) {
+ if (attachCommands.empty())
+ error.SetErrorString("failed to attach to a process");
+ else
+ error.SetErrorString("attachCommands failed to attach to a process");
+ }
+ }
+
+ if (error.Fail()) {
+ response["success"] = llvm::json::Value(false);
+ EmplaceSafeString(response, "message", std::string(error.GetCString()));
+ } else {
+ dap.RunPostRunCommands();
+ }
+
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ if (error.Success()) {
+ SendProcessEvent(Attach);
+ dap.SendJSON(CreateEventObject("initialized"));
+ }
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Request/Request.cpp b/lldb/tools/lldb-dap/Request/Request.cpp
new file mode 100644
index 0000000000000..9fe2445be9d32
--- /dev/null
+++ b/lldb/tools/lldb-dap/Request/Request.cpp
@@ -0,0 +1,92 @@
+//===-- Request.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Request.h"
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "lldb/API/SBFileSpec.h"
+
+namespace lldb_dap {
+
+void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
+ lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
+ char exe_path[PATH_MAX];
+ exe_fspec.GetPath(exe_path, sizeof(exe_path));
+ llvm::json::Object event(CreateEventObject("process"));
+ llvm::json::Object body;
+ EmplaceSafeString(body, "name", std::string(exe_path));
+ const auto pid = dap.target.GetProcess().GetProcessID();
+ body.try_emplace("systemProcessId", (int64_t)pid);
+ body.try_emplace("isLocalProcess", true);
+ const char *startMethod = nullptr;
+ switch (launch_method) {
+ case Launch:
+ startMethod = "launch";
+ break;
+ case Attach:
+ startMethod = "attach";
+ break;
+ case AttachForSuspendedLaunch:
+ startMethod = "attachForSuspendedLaunch";
+ break;
+ }
+ body.try_emplace("startMethod", startMethod);
+ event.try_emplace("body", std::move(body));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// Both attach and launch take a either a sourcePath or sourceMap
+// argument (or neither), from which we need to set the target.source-map.
+void Request::SetSourceMapFromArguments(const llvm::json::Object &arguments) {
+ const char *sourceMapHelp =
+ "source must be be an array of two-element arrays, "
+ "each containing a source and replacement path string.\n";
+
+ std::string sourceMapCommand;
+ llvm::raw_string_ostream strm(sourceMapCommand);
+ strm << "settings set target.source-map ";
+ const auto sourcePath = GetString(arguments, "sourcePath");
+
+ // sourceMap is the new, more general form of sourcePath and overrides it.
+ constexpr llvm::StringRef sourceMapKey = "sourceMap";
+
+ if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) {
+ for (const auto &value : *sourceMapArray) {
+ const auto *mapping = value.getAsArray();
+ if (mapping == nullptr || mapping->size() != 2 ||
+ (*mapping)[0].kind() != llvm::json::Value::String ||
+ (*mapping)[1].kind() != llvm::json::Value::String) {
+ dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+ return;
+ }
+ const auto mapFrom = GetAsString((*mapping)[0]);
+ const auto mapTo = GetAsString((*mapping)[1]);
+ strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
+ }
+ } else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) {
+ for (const auto &[key, value] : *sourceMapObj) {
+ if (value.kind() == llvm::json::Value::String) {
+ strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" ";
+ }
+ }
+ } else {
+ if (ObjectContainsKey(arguments, sourceMapKey)) {
+ dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+ return;
+ }
+ if (sourcePath.empty())
+ return;
+ // Do any source remapping needed before we create our targets
+ strm << "\".\" \"" << sourcePath << "\"";
+ }
+ if (!sourceMapCommand.empty()) {
+ dap.RunLLDBCommands("Setting source map:", {sourceMapCommand});
+ }
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Request/Request.h b/lldb/tools/lldb-dap/Request/Request.h
new file mode 100644
index 0000000000000..339c3b1fb323f
--- /dev/null
+++ b/lldb/tools/lldb-dap/Request/Request.h
@@ -0,0 +1,45 @@
+//===-- Request.h ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
+#define LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+
+namespace lldb_dap {
+struct DAP;
+
+class Request {
+public:
+ Request(DAP &dap) : dap(dap) {}
+ virtual ~Request() = default;
+
+ virtual void operator()(const llvm::json::Object &request) = 0;
+
+ static llvm::StringLiteral getName() { return "invalid"; };
+
+ enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
+
+ void SendProcessEvent(LaunchMethod launch_method);
+ void SetSourceMapFromArguments(const llvm::json::Object &arguments);
+
+protected:
+ DAP &dap;
+};
+
+class AttachRequest : public Request {
+public:
+ using Request::Request;
+ static llvm::StringLiteral getName() { return "attach"; }
+ void operator()(const llvm::json::Object &request) override;
+};
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e323990d8b6ed..b8e950b2980fa 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -10,6 +10,7 @@
#include "FifoFiles.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"
+#include "Request/Request.h"
#include "RunInTerminal.h"
#include "Watchpoint.h"
#include "lldb/API/SBDeclaration.h"
@@ -4998,7 +4999,10 @@ void request_setInstructionBreakpoints(DAP &dap,
}
void RegisterRequestCallbacks(DAP &dap) {
+ dap.RegisterRequest<AttachRequest>();
+
dap.RegisterRequestCallback("attach", request_attach);
+
dap.RegisterRequestCallback("breakpointLocations",
request_breakpointLocations);
dap.RegisterRequestCallback("completions", request_completions);
More information about the lldb-commits
mailing list