[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
Sat Feb 22 11:54:38 PST 2025


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/128262

>From e6cbcc3ab7a278c3ef01646db22872589486eb91 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 1/3] [lldb-dap] Move requests into their own object/file

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   5 +
 lldb/tools/lldb-dap/DAP.cpp                   |  23 +-
 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              | 190 +---------------
 7 files changed, 380 insertions(+), 194 deletions(-)
 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.cpp b/lldb/tools/lldb-dap/DAP.cpp
index a67abe582abd4..80f083b1ab592 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -744,16 +744,25 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
   const auto packet_type = GetString(object, "type");
   if (packet_type == "request") {
     const auto command = GetString(object, "command");
+
+    // Try the new request handler first.
+    auto new_handler_pos = new_request_handlers.find(command);
+    if (new_handler_pos != new_request_handlers.end()) {
+      (*new_handler_pos->second)(object);
+      return true; // Success
+    }
+
+    // FIXME: Remove request_handlers once everything has been migrated.
     auto handler_pos = request_handlers.find(command);
-    if (handler_pos == request_handlers.end()) {
-      if (log)
-        *log << "error: unhandled command \"" << command.data() << "\""
-             << std::endl;
-      return false; // Fail
+    if (handler_pos != request_handlers.end()) {
+      handler_pos->second(*this, object);
+      return true; // Success
     }
 
-    handler_pos->second(*this, object);
-    return true; // Success
+    if (log)
+      *log << "error: unhandled command \"" << command.data() << "\""
+           << std::endl;
+    return false; // Fail
   }
 
   if (packet_type == "response") {
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..997b654011b64 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"
@@ -726,192 +727,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "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 request_attach(DAP &dap, 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(dap, *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(dap, Attach);
-    dap.SendJSON(CreateEventObject("initialized"));
-  }
-}
-
 // "BreakpointLocationsRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -4998,7 +4813,8 @@ void request_setInstructionBreakpoints(DAP &dap,
 }
 
 void RegisterRequestCallbacks(DAP &dap) {
-  dap.RegisterRequestCallback("attach", request_attach);
+  dap.RegisterRequest<AttachRequest>();
+
   dap.RegisterRequestCallback("breakpointLocations",
                               request_breakpointLocations);
   dap.RegisterRequestCallback("completions", request_completions);

>From 92322aee828df65ae4ca7523d17c60162e3802b3 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 12:10:00 -0600
Subject: [PATCH 2/3] Address review feedback

---
 lldb/tools/lldb-dap/CMakeLists.txt            |  4 ++--
 lldb/tools/lldb-dap/DAP.h                     |  9 ++++----
 .../AttachHandler.cpp}                        |  6 ++---
 .../RequestHandler.cpp}                       | 10 ++++----
 .../Request.h => Handler/RequestHandler.h}    | 23 ++++++++++---------
 lldb/tools/lldb-dap/lldb-dap.cpp              |  4 ++--
 6 files changed, 30 insertions(+), 26 deletions(-)
 rename lldb/tools/lldb-dap/{Request/AttachRequest.cpp => Handler/AttachHandler.cpp} (98%)
 rename lldb/tools/lldb-dap/{Request/Request.cpp => Handler/RequestHandler.cpp} (92%)
 rename lldb/tools/lldb-dap/{Request/Request.h => Handler/RequestHandler.h} (65%)

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index e97f7ecdbd31b..a3c605bf72391 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -36,8 +36,8 @@ add_lldb_tool(lldb-dap
   SourceBreakpoint.cpp
   Watchpoint.cpp
 
-  Request/Request.cpp
-  Request/AttachRequest.cpp
+  Handler/RequestHandler.cpp
+  Handler/AttachHandler.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 33507061e0750..5abd48d6363cf 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -12,11 +12,11 @@
 #include "DAPForward.h"
 #include "ExceptionBreakpoint.h"
 #include "FunctionBreakpoint.h"
+#include "Handler/RequestHandler.h"
 #include "IOStream.h"
 #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"
@@ -186,7 +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;
+  llvm::StringMap<std::unique_ptr<RequestHandler>> 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
@@ -334,8 +334,9 @@ struct DAP {
   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);
+  template <typename Handler> void RegisterRequest() {
+    new_request_handlers[Handler::getCommand()] =
+        std::make_unique<Handler>(*this);
   }
 
   /// Debuggee will continue from stopped state.
diff --git a/lldb/tools/lldb-dap/Request/AttachRequest.cpp b/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
similarity index 98%
rename from lldb/tools/lldb-dap/Request/AttachRequest.cpp
rename to lldb/tools/lldb-dap/Handler/AttachHandler.cpp
index f1b3bfc878427..3dcde2144246e 100644
--- a/lldb/tools/lldb-dap/Request/AttachRequest.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
@@ -1,4 +1,4 @@
-//===-- AttachRequest.cpp -------------------------------------------------===//
+//===-- AttachHandler.cpp -------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -8,7 +8,7 @@
 
 #include "DAP.h"
 #include "JSONUtils.h"
-#include "Request.h"
+#include "RequestHandler.h"
 #include "lldb/API/SBListener.h"
 #include "llvm/Support/FileSystem.h"
 
@@ -50,7 +50,7 @@ static void PrintWelcomeMessage(DAP &dap) {
 //   }]
 // }
 
-void AttachRequest::operator()(const llvm::json::Object &request) {
+void AttachRequestHandler::operator()(const llvm::json::Object &request) {
   dap.is_attach = true;
   dap.last_launch_or_attach_request = request;
   llvm::json::Object response;
diff --git a/lldb/tools/lldb-dap/Request/Request.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
similarity index 92%
rename from lldb/tools/lldb-dap/Request/Request.cpp
rename to lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 9fe2445be9d32..ef091a805902d 100644
--- a/lldb/tools/lldb-dap/Request/Request.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -1,4 +1,4 @@
-//===-- Request.cpp -------------------------------------------------------===//
+//===-- Handler.cpp -------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,14 +6,15 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Request.h"
+#include "RequestHandler.h"
 #include "DAP.h"
 #include "JSONUtils.h"
 #include "lldb/API/SBFileSpec.h"
 
 namespace lldb_dap {
 
-void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
+void RequestHandler::SendProcessEvent(
+    RequestHandler::LaunchMethod launch_method) {
   lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
   char exe_path[PATH_MAX];
   exe_fspec.GetPath(exe_path, sizeof(exe_path));
@@ -42,7 +43,8 @@ void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
 
 // 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) {
+void RequestHandler::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";
diff --git a/lldb/tools/lldb-dap/Request/Request.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
similarity index 65%
rename from lldb/tools/lldb-dap/Request/Request.h
rename to lldb/tools/lldb-dap/Handler/RequestHandler.h
index 339c3b1fb323f..4a60a7f3d1142 100644
--- a/lldb/tools/lldb-dap/Request/Request.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
-#define LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
+#ifndef LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
+#define LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
@@ -15,28 +15,29 @@
 namespace lldb_dap {
 struct DAP;
 
-class Request {
+class RequestHandler {
 public:
-  Request(DAP &dap) : dap(dap) {}
-  virtual ~Request() = default;
+  RequestHandler(DAP &dap) : dap(dap) {}
+  virtual ~RequestHandler() = default;
 
   virtual void operator()(const llvm::json::Object &request) = 0;
 
-  static llvm::StringLiteral getName() { return "invalid"; };
-
+  /// Helpers used by multiple request handlers.
+  /// FIXME: Move these into the DAP class.
+  /// @{
   enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
-
   void SendProcessEvent(LaunchMethod launch_method);
   void SetSourceMapFromArguments(const llvm::json::Object &arguments);
+  /// @}
 
 protected:
   DAP &dap;
 };
 
-class AttachRequest : public Request {
+class AttachRequestHandler : public RequestHandler {
 public:
-  using Request::Request;
-  static llvm::StringLiteral getName() { return "attach"; }
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "attach"; }
   void operator()(const llvm::json::Object &request) override;
 };
 
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 997b654011b64..c84eb5d7c7419 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -8,9 +8,9 @@
 
 #include "DAP.h"
 #include "FifoFiles.h"
+#include "Handler/RequestHandler.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
-#include "Request/Request.h"
 #include "RunInTerminal.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
@@ -4813,7 +4813,7 @@ void request_setInstructionBreakpoints(DAP &dap,
 }
 
 void RegisterRequestCallbacks(DAP &dap) {
-  dap.RegisterRequest<AttachRequest>();
+  dap.RegisterRequest<AttachRequestHandler>();
 
   dap.RegisterRequestCallback("breakpointLocations",
                               request_breakpointLocations);

>From e2728b6dfba0f9cef3cb9def7d01567dc3e9b6a8 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 13:54:22 -0600
Subject: [PATCH 3/3] Address review feedback

---
 lldb/tools/lldb-dap/Handler/RequestHandler.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 4a60a7f3d1142..a0b66997ffda9 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -18,6 +18,13 @@ struct DAP;
 class RequestHandler {
 public:
   RequestHandler(DAP &dap) : dap(dap) {}
+
+  /// RequestHandler are not copyable.
+  /// @{
+  RequestHandler(const RequestHandler &) = delete;
+  RequestHandler& operator=(const RequestHandler &) = delete;
+  /// @}
+
   virtual ~RequestHandler() = default;
 
   virtual void operator()(const llvm::json::Object &request) = 0;



More information about the lldb-commits mailing list