Author: John Harrison
Date: 2025-05-08T14:12:49-07:00
New Revision: 03896403d3bf330c8163aa9ae3fe2aa284e273be

URL: 
https://github.com/llvm/llvm-project/commit/03896403d3bf330c8163aa9ae3fe2aa284e273be
DIFF: 
https://github.com/llvm/llvm-project/commit/03896403d3bf330c8163aa9ae3fe2aa284e273be.diff

LOG: [lldb-dap] Migrate attach to typed RequestHandler. (#137911)

This updates the `attach` request to the typed
`RequestHandler<protocol::AttachRequestArguments,
protocol::AttachResponse>`.

Added a few more overlapping configurations to
`lldb_dap::protocol::Configuration` that are shared between launching
and attaching.

There may be some additional code we could clean-up that is no longer
referenced now that this has migrated to use well defined types.

Added: 
    

Modified: 
    lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
    lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
    lldb/tools/lldb-dap/DAP.cpp
    lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
    lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
    lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
    lldb/tools/lldb-dap/Handler/RequestHandler.cpp
    lldb/tools/lldb-dap/Handler/RequestHandler.h
    lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
    lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
    lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
    lldb/tools/lldb-dap/package.json

Removed: 
    


################################################################################
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 b5569642f9d34..a9218d3c3dde3 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -78,7 +78,7 @@ def test_by_name(self):
     def test_by_name_waitFor(self):
         """
         Tests attaching to a process by process name and waiting for the
-        next instance of a process to be launched, ingoring all current
+        next instance of a process to be launched, ignoring all current
         ones.
         """
         program = self.build_and_create_debug_adapter_for_attach()
@@ -101,7 +101,7 @@ def test_commands(self):
         that can be passed during attach.
 
         "initCommands" are a list of LLDB commands that get executed
-        before the targt is created.
+        before the target is created.
         "preRunCommands" are a list of LLDB commands that get executed
         after the target has been created and before the launch.
         "stopCommands" are a list of LLDB commands that get executed each
@@ -179,6 +179,24 @@ def test_commands(self):
         self.verify_commands("exitCommands", output, exitCommands)
         self.verify_commands("terminateCommands", output, terminateCommands)
 
+    def test_attach_command_process_failures(self):
+        """
+        Tests that a 'attachCommands' is expected to leave the debugger's
+        selected target with a valid process.
+        """
+        program = self.build_and_create_debug_adapter_for_attach()
+        attachCommands = ['script print("oops, forgot to attach to a 
process...")']
+        resp = self.attach(
+            program=program,
+            attachCommands=attachCommands,
+            expectFailure=True,
+        )
+        self.assertFalse(resp["success"])
+        self.assertIn(
+            "attachCommands failed to attach to a process",
+            resp["body"]["error"]["format"],
+        )
+
     @skipIfNetBSD  # Hangs on NetBSD as well
     @skipIf(
         archs=["arm", "aarch64"]

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 acbe0366d1ecc..7c85f05c1ba45 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -56,7 +56,7 @@ def test_failing_launch_commands_and_run_in_terminal(self):
         self.assertFalse(response["success"])
         self.assertTrue(self.get_dict_value(response, ["body", "error", 
"showUser"]))
         self.assertEqual(
-            "launchCommands and runInTerminal are mutually exclusive",
+            "'launchCommands' and 'runInTerminal' are mutually exclusive",
             self.get_dict_value(response, ["body", "error", "format"]),
         )
 

diff  --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 62c60cc3a9b3b..1b118bb8c6a2b 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -676,10 +676,10 @@ lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) {
   // omitted at all), so it is good to leave the user an opportunity to specify
   // those. Any of those three can be left empty.
   auto target = this->debugger.CreateTarget(
-      configuration.program.value_or("").data(),
-      configuration.targetTriple.value_or("").data(),
-      configuration.platformName.value_or("").data(),
-      true, // Add dependent modules.
+      /*filename=*/configuration.program.data(),
+      /*target_triple=*/configuration.targetTriple.data(),
+      /*platform_name=*/configuration.platformName.data(),
+      /*add_dependent_modules=*/true, // Add dependent modules.
       error);
 
   return target;
@@ -1203,7 +1203,7 @@ bool SendEventRequestHandler::DoExecute(lldb::SBDebugger 
debugger,
 }
 
 void DAP::ConfigureSourceMaps() {
-  if (configuration.sourceMap.empty() && !configuration.sourcePath)
+  if (configuration.sourceMap.empty() && configuration.sourcePath.empty())
     return;
 
   std::string sourceMapCommand;
@@ -1214,8 +1214,8 @@ void DAP::ConfigureSourceMaps() {
     for (const auto &kv : configuration.sourceMap) {
       strm << "\"" << kv.first << "\" \"" << kv.second << "\" ";
     }
-  } else if (configuration.sourcePath) {
-    strm << "\".\" \"" << *configuration.sourcePath << "\"";
+  } else if (!configuration.sourcePath.empty()) {
+    strm << "\".\" \"" << configuration.sourcePath << "\"";
   }
 
   RunLLDBCommands("Setting source map:", {sourceMapCommand});
@@ -1224,6 +1224,7 @@ void DAP::ConfigureSourceMaps() {
 void DAP::SetConfiguration(const protocol::Configuration &config,
                            bool is_attach) {
   configuration = config;
+  stop_at_entry = config.stopOnEntry;
   this->is_attach = is_attach;
 
   if (configuration.customFrameFormat)
@@ -1243,8 +1244,6 @@ void DAP::SetConfigurationDone() {
 }
 
 void DAP::SetFrameFormat(llvm::StringRef format) {
-  if (format.empty())
-    return;
   lldb::SBError error;
   frame_format = lldb::SBFormat(format.str().c_str(), error);
   if (error.Fail()) {
@@ -1257,8 +1256,6 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
 }
 
 void DAP::SetThreadFormat(llvm::StringRef format) {
-  if (format.empty())
-    return;
   lldb::SBError error;
   thread_format = lldb::SBFormat(format.str().c_str(), error);
   if (error.Fail()) {

diff  --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index 5dc9c3f9772e3..0293ffbd0c922 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -10,183 +10,138 @@
 #include "EventHelper.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RequestHandler.h"
+#include "lldb/API/SBAttachInfo.h"
 #include "lldb/API/SBListener.h"
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 
+using namespace llvm;
+using namespace lldb_dap::protocol;
+
 namespace lldb_dap {
 
-// "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 AttachRequestHandler::operator()(const llvm::json::Object &request) const 
{
-  dap.is_attach = true;
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const int invalid_port = 0;
-  const auto *arguments = request.getObject("arguments");
-  const lldb::pid_t pid =
-      GetInteger<uint64_t>(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID);
-  const auto gdb_remote_port =
-      GetInteger<uint64_t>(arguments, 
"gdb-remote-port").value_or(invalid_port);
-  const auto gdb_remote_hostname =
-      GetString(arguments, "gdb-remote-hostname").value_or("localhost");
-  const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false);
-  dap.configuration.initCommands = GetStrings(arguments, "initCommands");
-  dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands");
-  dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands");
-  dap.configuration.stopCommands = GetStrings(arguments, "stopCommands");
-  dap.configuration.exitCommands = GetStrings(arguments, "exitCommands");
-  dap.configuration.terminateCommands =
-      GetStrings(arguments, "terminateCommands");
-  auto attachCommands = GetStrings(arguments, "attachCommands");
-  llvm::StringRef core_file = GetString(arguments, "coreFile").value_or("");
-  const uint64_t timeout_seconds =
-      GetInteger<uint64_t>(arguments, "timeout").value_or(30);
-  dap.stop_at_entry = core_file.empty()
-                          ? GetBoolean(arguments, 
"stopOnEntry").value_or(false)
-                          : true;
-  const llvm::StringRef debuggerRoot =
-      GetString(arguments, "debuggerRoot").value_or("");
-  dap.configuration.enableAutoVariableSummaries =
-      GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false);
-  dap.configuration.enableSyntheticChildDebugging =
-      GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false);
-  dap.configuration.displayExtendedBacktrace =
-      GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
-  dap.configuration.commandEscapePrefix =
-      GetString(arguments, "commandEscapePrefix").value_or("`");
-  dap.configuration.program = GetString(arguments, "program");
-  dap.configuration.targetTriple = GetString(arguments, "targetTriple");
-  dap.configuration.platformName = GetString(arguments, "platformName");
-  dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
-  dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
+/// The `attach` request is sent from the client to the debug adapter to attach
+/// to a debuggee that is already running.
+///
+/// Since attaching is debugger/runtime specific, the arguments for this 
request
+/// are not part of this specification.
+Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
+  // 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)
+    return make_error<DAPError>(
+        "expected one of 'pid', 'program', 'attachCommands', "
+        "'coreFile' or 'gdb-remote-port' 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;
 
   PrintWelcomeMessage();
 
   // 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);
+  // 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 (!dap.configuration.debuggerRoot.empty())
+    sys::fs::set_current_path(dap.configuration.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;
-  }
+  if (llvm::Error err = dap.RunInitCommands())
+    return err;
 
-  SetSourceMapFromArguments(*arguments);
+  dap.ConfigureSourceMaps();
 
-  lldb::SBError status;
-  dap.SetTarget(dap.CreateTarget(status));
-  if (status.Fail()) {
-    response["success"] = llvm::json::Value(false);
-    EmplaceSafeString(response, "message", status.GetCString());
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
+  lldb::SBError error;
+  lldb::SBTarget target = dap.CreateTarget(error);
+  if (error.Fail())
+    return ToError(error);
+
+  dap.SetTarget(target);
 
   // 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 (Error err = dap.RunPreRunCommands())
+    return err;
 
-  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());
+  if ((args.pid == LLDB_INVALID_PROCESS_ID ||
+       args.gdbRemotePort == LLDB_DAP_INVALID_PORT) &&
+      args.waitFor) {
     dap.SendOutput(OutputType::Console,
-                   llvm::StringRef(attach_msg, attach_msg_len));
+                   llvm::formatv("Waiting to attach to \"{0}\"...",
+                                 dap.target.GetExecutable().GetFilename())
+                       .str());
   }
 
   {
     // Perform the launch in synchronous mode so that we don't have to worry
     // about process state changes during the launch.
     ScopeSyncMode scope_sync_mode(dap.debugger);
-    if (attachCommands.empty()) {
-      // No "attachCommands", just attach normally.
-      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 pid or process name.
-          lldb::SBAttachInfo attach_info;
-          if (pid != LLDB_INVALID_PROCESS_ID)
-            attach_info.SetProcessID(pid);
-          else if (dap.configuration.program.has_value())
-            attach_info.SetExecutable(dap.configuration.program->data());
-          attach_info.SetWaitForLaunch(wait_for, false /*async*/);
-          dap.target.Attach(attach_info, error);
-        }
-      } else {
-        dap.target.LoadCore(core_file.data(), error);
-      }
-    } 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.
+
+    if (!args.attachCommands.empty()) {
+      // Run the attach commands, after which we expect the debugger's selected
+      // target to contain a valid and stopped process. Otherwise inform the
+      // user that their command failed or the debugger is in an unexpected
+      // state.
+      if (llvm::Error err = dap.RunAttachCommands(args.attachCommands))
+        return err;
+
       dap.target = dap.debugger.GetSelectedTarget();
+
+      // Validate the attachCommand results.
+      if (!dap.target.GetProcess().IsValid())
+        return make_error<DAPError>(
+            "attachCommands failed to attach to a process");
+    } else if (!args.coreFile.empty()) {
+      dap.target.LoadCore(args.coreFile.data(), error);
+    } else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) {
+      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}:", args.gdbRemoteHostname);
+      connect_url += std::to_string(args.gdbRemotePort);
+      dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
+                               error);
+    } else {
+      // Attach by pid or process name.
+      lldb::SBAttachInfo attach_info;
+      if (args.pid != LLDB_INVALID_PROCESS_ID)
+        attach_info.SetProcessID(args.pid);
+      else if (!dap.configuration.program.empty())
+        attach_info.SetExecutable(dap.configuration.program.data());
+      attach_info.SetWaitForLaunch(args.waitFor, /*async=*/false);
+      dap.target.Attach(attach_info, error);
     }
   }
 
   // Make sure the process is attached and stopped.
-  error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
+  error = dap.WaitForProcessToStop(args.configuration.timeout);
+  if (error.Fail())
+    return ToError(error);
+
+  if (args.coreFile.empty() && !dap.target.GetProcess().IsValid())
+    return make_error<DAPError>("failed to attach to process");
+
+  dap.RunPostRunCommands();
+
+  return Error::success();
+}
+
+void AttachRequestHandler::PostRun() const {
+  if (!dap.target.GetProcess().IsValid())
+    return;
 
   // Clients can request a baseline of currently existing threads after
   // we acknowledge the configurationDone request.
@@ -197,36 +152,12 @@ void AttachRequestHandler::operator()(const 
llvm::json::Object &request) const {
   dap.initial_thread_list =
       GetThreads(dap.target.GetProcess(), dap.thread_format);
 
-  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)));
+  SendProcessEvent(dap, Attach);
 
-  // FIXME: Move this into PostRun.
-  if (error.Success()) {
-    if (dap.target.GetProcess().IsValid()) {
-      SendProcessEvent(dap, Attach);
-
-      if (dap.stop_at_entry)
-        SendThreadStoppedEvent(dap);
-      else
-        dap.target.GetProcess().Continue();
-    }
-  }
+  if (dap.stop_at_entry)
+    SendThreadStoppedEvent(dap);
+  else
+    dap.target.GetProcess().Continue();
 }
 
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index aa947d3cb5ab9..6d1e2c7402f4c 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -262,7 +262,7 @@ static void EventThreadFunction(DAP &dap) {
 }
 
 /// Initialize request; value of command field is 'initialize'.
-llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
+llvm::Expected<InitializeResponse> InitializeRequestHandler::Run(
     const InitializeRequestArguments &arguments) const {
   dap.clientFeatures = arguments.supportedFeatures;
 

diff  --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
index 7e0e76935dd02..22d1a090187d8 100644
--- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
@@ -22,13 +22,13 @@ namespace lldb_dap {
 
 /// Launch request; value of command field is 'launch'.
 Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const 
{
-  dap.SetConfiguration(arguments.configuration, /*is_attach=*/false);
-  dap.last_launch_request = arguments;
-  dap.stop_at_entry = arguments.stopOnEntry;
-
+  // Validate that we have a well formed launch request.
   if (!arguments.launchCommands.empty() && arguments.runInTerminal)
     return make_error<DAPError>(
-        "launchCommands and runInTerminal are mutually exclusive");
+        "'launchCommands' and 'runInTerminal' are mutually exclusive");
+
+  dap.SetConfiguration(arguments.configuration, /*is_attach=*/false);
+  dap.last_launch_request = arguments;
 
   PrintWelcomeMessage();
 
@@ -36,9 +36,8 @@ Error LaunchRequestHandler::Run(const LaunchRequestArguments 
&arguments) const {
   // 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.
-  const std::string debugger_root = 
dap.configuration.debuggerRoot.value_or("");
-  if (!debugger_root.empty())
-    sys::fs::set_current_path(debugger_root);
+  if (!dap.configuration.debuggerRoot.empty())
+    sys::fs::set_current_path(dap.configuration.debuggerRoot);
 
   // Run any initialize LLDB commands the user specified in the launch.json.
   // This is run before target is created, so commands can't do anything with
@@ -68,15 +67,25 @@ Error LaunchRequestHandler::Run(const 
LaunchRequestArguments &arguments) const {
 }
 
 void LaunchRequestHandler::PostRun() const {
-  if (dap.target.GetProcess().IsValid()) {
-    // Attach happens when launching with runInTerminal.
-    SendProcessEvent(dap, dap.is_attach ? Attach : Launch);
-
-    if (dap.stop_at_entry)
-      SendThreadStoppedEvent(dap);
-    else
-      dap.target.GetProcess().Continue();
-  }
+  if (!dap.target.GetProcess().IsValid())
+    return;
+
+  // Clients can request a baseline of currently existing threads after
+  // we acknowledge the configurationDone request.
+  // Client requests the baseline of currently existing threads after
+  // a successful or attach by sending a 'threads' request
+  // right after receiving the configurationDone response.
+  // Obtain the list of threads before we resume the process
+  dap.initial_thread_list =
+      GetThreads(dap.target.GetProcess(), dap.thread_format);
+
+  // Attach happens when launching with runInTerminal.
+  SendProcessEvent(dap, dap.is_attach ? Attach : Launch);
+
+  if (dap.stop_at_entry)
+    SendThreadStoppedEvent(dap);
+  else
+    dap.target.GetProcess().Continue();
 }
 
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 282c5f4ab15a5..93bc80a38e29d 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -51,56 +51,6 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag,
   return flags;
 }
 
-// Both attach and launch take either a sourcePath or a sourceMap
-// argument (or neither), from which we need to set the target.source-map.
-void BaseRequestHandler::SetSourceMapFromArguments(
-    const llvm::json::Object &arguments) const {
-  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").value_or("");
-
-  // 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});
-  }
-}
-
 static llvm::Error
 RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
   if (!dap.clientFeatures.contains(
@@ -108,8 +58,7 @@ RunInTerminal(DAP &dap, const 
protocol::LaunchRequestArguments &arguments) {
     return llvm::make_error<DAPError>("Cannot use runInTerminal, feature is "
                                       "not supported by the connected client");
 
-  if (!arguments.configuration.program ||
-      arguments.configuration.program->empty())
+  if (arguments.configuration.program.empty())
     return llvm::make_error<DAPError>(
         "program must be set to when using runInTerminal");
 
@@ -130,8 +79,8 @@ RunInTerminal(DAP &dap, const 
protocol::LaunchRequestArguments &arguments) {
 #endif
 
   llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
-      *arguments.configuration.program, arguments.args, arguments.env,
-      arguments.cwd.value_or(""), comm_file.m_path, debugger_pid);
+      arguments.configuration.program, arguments.args, arguments.env,
+      arguments.cwd, comm_file.m_path, debugger_pid);
   dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
                                                     
std::move(reverse_request));
 
@@ -211,9 +160,8 @@ llvm::Error BaseRequestHandler::LaunchProcess(
 
   // Grab the current working directory if there is one and set it in the
   // launch info.
-  const auto cwd = arguments.cwd.value_or("");
-  if (!cwd.empty())
-    launch_info.SetWorkingDirectory(cwd.data());
+  if (!arguments.cwd.empty())
+    launch_info.SetWorkingDirectory(arguments.cwd.data());
 
   // Extract any extra arguments and append them to our program arguments for
   // when we launch
@@ -251,7 +199,7 @@ llvm::Error BaseRequestHandler::LaunchProcess(
       lldb::SBError error;
       dap.target.Launch(launch_info, error);
       if (error.Fail())
-        return llvm::make_error<DAPError>(error.GetCString());
+        return ToError(error);
     } else {
       // Set the launch info so that run commands can access the configured
       // launch details.
@@ -267,18 +215,10 @@ llvm::Error BaseRequestHandler::LaunchProcess(
 
   // Make sure the process is launched and stopped at the entry point before
   // proceeding.
-  lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
+  lldb::SBError error =
+      dap.WaitForProcessToStop(arguments.configuration.timeout);
   if (error.Fail())
-    return llvm::make_error<DAPError>(error.GetCString());
-
-  // Clients can request a baseline of currently existing threads after
-  // we acknowledge the configurationDone request.
-  // Client requests the baseline of currently existing threads after
-  // a successful or attach by sending a 'threads' request
-  // right after receiving the configurationDone response.
-  // Obtain the list of threads before we resume the process
-  dap.initial_thread_list =
-      GetThreads(dap.target.GetProcess(), dap.thread_format);
+    return ToError(error);
 
   return llvm::Error::success();
 }

diff  --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h 
b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 25534b5675e45..948a29a005aa7 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -59,10 +59,6 @@ class BaseRequestHandler {
   /// FIXME: Move these into the DAP class?
   /// @{
 
-  /// 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 SetSourceMapFromArguments(const llvm::json::Object &arguments) const;
-
   /// Prints a welcome message on the editor if the preprocessor variable
   /// LLDB_DAP_WELCOME_MESSAGE is defined.
   void PrintWelcomeMessage() const;
@@ -172,6 +168,9 @@ class RequestHandler : public BaseRequestHandler {
 
   /// A hook for a request handler to run additional operations after the
   /// request response is sent but before the next request handler.
+  ///
+  /// *NOTE*: PostRun will be invoked even if the `Run` operation returned an
+  /// error.
   virtual void PostRun() const {};
 
   protocol::ErrorResponseBody ToResponse(llvm::Error err) const {
@@ -197,11 +196,14 @@ class RequestHandler : public BaseRequestHandler {
   }
 };
 
-class AttachRequestHandler : public LegacyRequestHandler {
+class AttachRequestHandler
+    : public RequestHandler<protocol::AttachRequestArguments,
+                            protocol::AttachResponse> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "attach"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Error Run(const protocol::AttachRequestArguments &args) const override;
+  void PostRun() const override;
 };
 
 class BreakpointLocationsRequestHandler : public LegacyRequestHandler {
@@ -279,18 +281,18 @@ class ExceptionInfoRequestHandler : public 
LegacyRequestHandler {
 
 class InitializeRequestHandler
     : public RequestHandler<protocol::InitializeRequestArguments,
-                            llvm::Expected<protocol::InitializeResponseBody>> {
+                            llvm::Expected<protocol::InitializeResponse>> {
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "initialize"; }
-  llvm::Expected<protocol::InitializeResponseBody>
+  llvm::Expected<protocol::InitializeResponse>
   Run(const protocol::InitializeRequestArguments &args) const override;
   void PostRun() const override;
 };
 
 class LaunchRequestHandler
     : public RequestHandler<protocol::LaunchRequestArguments,
-                            protocol::LaunchResponseBody> {
+                            protocol::LaunchResponse> {
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "launch"; }
@@ -508,9 +510,8 @@ class ReadMemoryRequestHandler : public 
LegacyRequestHandler {
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class CancelRequestHandler
-    : public RequestHandler<protocol::CancelArguments,
-                            protocol::CancelResponseBody> {
+class CancelRequestHandler : public RequestHandler<protocol::CancelArguments,
+                                                   protocol::CancelResponse> {
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "cancel"; }

diff  --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index 47fedf9dd779c..58091b622f7e5 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -109,7 +109,6 @@ void RestartRequestHandler::operator()(
       // Update DAP configuration based on the latest copy of the launch
       // arguments.
       dap.SetConfiguration(updated_arguments.configuration, false);
-      dap.stop_at_entry = updated_arguments.stopOnEntry;
       dap.ConfigureSourceMaps();
     }
   }

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp 
b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 950e8d17e3489..cd507692f0c21 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -223,26 +223,29 @@ bool fromJSON(const json::Value &Params, 
InitializeRequestArguments &IRA,
 
 bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) {
   json::ObjectMapper O(Params, P);
-  return O.map("debuggerRoot", C.debuggerRoot) &&
+  return O.mapOptional("debuggerRoot", C.debuggerRoot) &&
          O.mapOptional("enableAutoVariableSummaries",
                        C.enableAutoVariableSummaries) &&
          O.mapOptional("enableSyntheticChildDebugging",
                        C.enableSyntheticChildDebugging) &&
          O.mapOptional("displayExtendedBacktrace",
                        C.displayExtendedBacktrace) &&
+         O.mapOptional("stopOnEntry", C.stopOnEntry) &&
          O.mapOptional("commandEscapePrefix", C.commandEscapePrefix) &&
-         O.map("customFrameFormat", C.customFrameFormat) &&
-         O.map("customThreadFormat", C.customThreadFormat) &&
-         O.map("sourcePath", C.sourcePath) &&
+         O.mapOptional("customFrameFormat", C.customFrameFormat) &&
+         O.mapOptional("customThreadFormat", C.customThreadFormat) &&
+         O.mapOptional("sourcePath", C.sourcePath) &&
          O.mapOptional("initCommands", C.initCommands) &&
          O.mapOptional("preRunCommands", C.preRunCommands) &&
          O.mapOptional("postRunCommands", C.postRunCommands) &&
          O.mapOptional("stopCommands", C.stopCommands) &&
          O.mapOptional("exitCommands", C.exitCommands) &&
          O.mapOptional("terminateCommands", C.terminateCommands) &&
-         O.map("program", C.program) && O.map("targetTriple", C.targetTriple) 
&&
-         O.map("platformName", C.platformName) &&
-         parseSourceMap(Params, C.sourceMap, P);
+         O.mapOptional("program", C.program) &&
+         O.mapOptional("targetTriple", C.targetTriple) &&
+         O.mapOptional("platformName", C.platformName) &&
+         parseSourceMap(Params, C.sourceMap, P) &&
+         parseTimeout(Params, C.timeout, P);
 }
 
 bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
@@ -251,14 +254,26 @@ bool fromJSON(const json::Value &Params, 
LaunchRequestArguments &LRA,
   return O && fromJSON(Params, LRA.configuration, P) &&
          O.mapOptional("noDebug", LRA.noDebug) &&
          O.mapOptional("launchCommands", LRA.launchCommands) &&
-         O.map("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) &&
+         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("stopOnEntry", LRA.stopOnEntry) &&
+
          O.mapOptional("runInTerminal", LRA.runInTerminal) &&
-         parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P);
+         parseEnv(Params, LRA.env, P);
+}
+
+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);
 }
 
 bool fromJSON(const llvm::json::Value &Params, ContinueArguments &CA,

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h 
b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 18222d61f9a14..acc3358736e70 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -23,6 +23,7 @@
 #include "Protocol/ProtocolBase.h"
 #include "Protocol/ProtocolTypes.h"
 #include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/JSON.h"
@@ -51,7 +52,7 @@ bool fromJSON(const llvm::json::Value &, CancelArguments &, 
llvm::json::Path);
 
 /// Response to `cancel` request. This is just an acknowledgement, so no body
 /// field is required.
-using CancelResponseBody = VoidResponse;
+using CancelResponse = VoidResponse;
 
 /// Arguments for `disconnect` request.
 struct DisconnectArguments {
@@ -139,15 +140,18 @@ bool fromJSON(const llvm::json::Value &, 
InitializeRequestArguments &,
               llvm::json::Path);
 
 /// Response to `initialize` request. The capabilities of this debug adapter.
-using InitializeResponseBody = std::optional<Capabilities>;
+using InitializeResponse = std::optional<Capabilities>;
 
 /// DAP Launch and Attach common configurations.
+///
+/// See package.json debuggers > configurationAttributes > launch or attach >
+/// properties for common configurations.
 struct Configuration {
   /// Specify a working directory to use when launching `lldb-dap`. If the 
debug
   /// information in your executable contains relative paths, this option can 
be
   /// used so that `lldb-dap` can find source files and object files that have
   /// relative paths.
-  std::optional<std::string> debuggerRoot;
+  std::string debuggerRoot;
 
   /// Enable auto generated summaries for variables when no summaries exist for
   /// a given type. This feature can cause performance delays in large projects
@@ -156,13 +160,20 @@ struct Configuration {
 
   /// If a variable is displayed using a synthetic children, also display the
   /// actual contents of the variable at the end under a [raw] entry. This is
-  /// useful when creating sythetic child plug-ins as it lets you see the 
actual
-  /// contents of the variable.
+  /// useful when creating synthetic child plug-ins as it lets you see the
+  /// actual contents of the variable.
   bool enableSyntheticChildDebugging = false;
 
   /// Enable language specific extended backtraces.
   bool displayExtendedBacktrace = false;
 
+  /// Stop at the entry point of the program when launching or attaching.
+  bool stopOnEntry = false;
+
+  /// Optional timeout when waiting for the program to `runInTerminal` or
+  /// attach.
+  std::chrono::seconds timeout = std::chrono::seconds(30);
+
   /// The escape prefix to use for executing regular LLDB commands in the Debug
   /// Console, instead of printing variables. Defaults to a backtick. If it's 
an
   /// empty string, then all expression in the Debug Console are treated as
@@ -183,7 +194,7 @@ struct Configuration {
 
   /// Specify a source path to remap "./" to allow full paths to be used when
   /// setting breakpoints in binaries that have relative source paths.
-  std::optional<std::string> sourcePath;
+  std::string sourcePath;
 
   /// Specify an array of path re-mappings. Each element in the array must be a
   /// two element array containing a source and destination pathname. Overrides
@@ -219,15 +230,15 @@ struct Configuration {
   ///
   /// *NOTE:* When launching, either `launchCommands` or `program` must be
   /// configured. If both are configured then `launchCommands` takes priority.
-  std::optional<std::string> program;
+  std::string program;
 
   /// Target triple for the program (arch-vendor-os). If not set, inferred from
   /// the binary.
-  std::optional<std::string> targetTriple;
+  std::string targetTriple;
 
   /// Specify name of the platform to use for this target, creating the 
platform
   /// if necessary.
-  std::optional<std::string> platformName;
+  std::string platformName;
 };
 
 /// lldb-dap specific launch arguments.
@@ -240,6 +251,9 @@ struct LaunchRequestArguments {
   bool noDebug = false;
 
   /// Launch specific operations.
+  ///
+  /// See package.json debuggers > configurationAttributes > launch >
+  /// properties.
   /// @{
 
   /// LLDB commands executed to launch the program.
@@ -250,7 +264,7 @@ struct LaunchRequestArguments {
   std::vector<std::string> launchCommands;
 
   /// The program working directory.
-  std::optional<std::string> cwd;
+  std::string cwd;
 
   /// An array of command line argument strings to be passed to the program
   /// being launched.
@@ -261,8 +275,8 @@ struct LaunchRequestArguments {
   /// with values or just "VAR" for environment variables with no values.
   llvm::StringMap<std::string> env;
 
-  /// If set, then the client stub should detach rather than killing the 
debugee
-  /// if it loses connection with lldb.
+  /// If set, then the client stub should detach rather than killing the
+  /// debuggee if it loses connection with lldb.
   bool detachOnError = false;
 
   /// Disable ASLR (Address Space Layout Randomization) when launching the
@@ -275,16 +289,10 @@ struct LaunchRequestArguments {
   /// Set whether to shell expand arguments to the process when launching.
   bool shellExpandArguments = false;
 
-  /// Stop at the entry point of the program when launching a process.
-  bool stopOnEntry = false;
-
   /// Launch the program inside an integrated terminal in the IDE. Useful for
   /// debugging interactive command line programs.
   bool runInTerminal = false;
 
-  /// Optional timeout for `runInTerminal` requests.
-  std::chrono::seconds timeout = std::chrono::seconds(30);
-
   /// @}
 };
 bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
@@ -292,7 +300,52 @@ bool fromJSON(const llvm::json::Value &, 
LaunchRequestArguments &,
 
 /// Response to `launch` request. This is just an acknowledgement, so no body
 /// field is required.
-using LaunchResponseBody = VoidResponse;
+using LaunchResponse = VoidResponse;
+
+#define LLDB_DAP_INVALID_PORT -1
+
+/// lldb-dap specific attach arguments.
+struct AttachRequestArguments {
+  /// Common lldb-dap configuration values for launching/attaching operations.
+  Configuration configuration;
+
+  /// Attach specific operations.
+  ///
+  /// See package.json debuggers > configurationAttributes > attach >
+  /// properties.
+  /// @{
+
+  /// 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.
+  std::vector<std::string> attachCommands;
+
+  /// System process ID to attach to.
+  lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+  /// Wait for the process to launch.
+  bool waitFor = false;
+
+  /// TCP/IP port to attach to a remote system. Specifying both pid and port is
+  /// an error.
+  int32_t gdbRemotePort = LLDB_DAP_INVALID_PORT;
+
+  /// The hostname to connect to a remote system. The default hostname being
+  /// used `localhost`.
+  std::string gdbRemoteHostname = "localhost";
+
+  /// Path to the core file to debug.
+  std::string coreFile;
+
+  /// @}
+};
+bool fromJSON(const llvm::json::Value &, AttachRequestArguments &,
+              llvm::json::Path);
+
+/// Response to `attach` request. This is just an acknowledgement, so no body
+/// field is required.
+using AttachResponse = VoidResponse;
 
 /// Arguments for `continue` request.
 struct ContinueArguments {

diff  --git a/lldb/tools/lldb-dap/package.json 
b/lldb/tools/lldb-dap/package.json
index f66badc2a930f..a7631464d236a 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -552,10 +552,7 @@
                 "description": "Path to the program to attach to."
               },
               "pid": {
-                "type": [
-                  "number",
-                  "string"
-                ],
+                "type": "number",
                 "description": "System process ID to attach to."
               },
               "waitFor": {


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to