[Lldb-commits] [lldb] [lldb-dap] Refactoring lldb-dap 'launch' request to use typed RequestHandler<>. (PR #133624)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Sat Mar 29 23:44:50 PDT 2025


https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/133624

This converts a number of json::Value's into well defined types that are used throughout lldb-dap and updates the 'launch' command to use the new well defined types.

>From 3240fe49515e5f59c5b9ff9c02423b77504d8a43 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Fri, 28 Mar 2025 14:02:53 -0700
Subject: [PATCH] [lldb-dap] Refactoring lldb-dap 'launch' request to use typed
 RequestHandler<>.

This converts a number of json::Value's into well defined types that are used throughout lldb-dap and updates the 'launch' command to use the new well defined types.
---
 .../test/tools/lldb-dap/dap_server.py         |   3 +-
 .../test/tools/lldb-dap/lldbdap_testcase.py   |   2 +-
 .../tools/lldb-dap/launch/TestDAP_launch.py   |   6 +-
 .../restart/TestDAP_restart_runInTerminal.py  |   4 +-
 .../runInTerminal/TestDAP_runInTerminal.py    |  16 +-
 lldb/tools/lldb-dap/DAP.cpp                   |  82 +++++---
 lldb/tools/lldb-dap/DAP.h                     |  44 +++--
 .../lldb-dap/Handler/AttachRequestHandler.cpp |  33 ++--
 .../lldb-dap/Handler/CompletionsHandler.cpp   |   7 +-
 .../Handler/EvaluateRequestHandler.cpp        |   3 +-
 .../lldb-dap/Handler/LaunchRequestHandler.cpp | 118 +++---------
 .../tools/lldb-dap/Handler/RequestHandler.cpp |  96 +++++-----
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  19 +-
 .../Handler/RestartRequestHandler.cpp         |  54 ++++--
 .../Handler/SetVariableRequestHandler.cpp     |   3 +-
 .../Handler/StackTraceRequestHandler.cpp      |   2 +-
 .../Handler/VariablesRequestHandler.cpp       |  20 +-
 lldb/tools/lldb-dap/JSONUtils.cpp             |  48 ++---
 lldb/tools/lldb-dap/JSONUtils.h               |  11 +-
 .../lldb-dap/Protocol/ProtocolRequests.cpp    | 175 +++++++++++++++++-
 .../lldb-dap/Protocol/ProtocolRequests.h      | 150 +++++++++++++++
 lldb/tools/lldb-dap/SourceBreakpoint.cpp      |   6 +-
 22 files changed, 616 insertions(+), 286 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 01ef4b68f2653..6e13fcddcc933 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -862,7 +862,8 @@ def request_launch(
         args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
         args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
         args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace
-        args_dict["commandEscapePrefix"] = commandEscapePrefix
+        if commandEscapePrefix:
+            args_dict["commandEscapePrefix"] = commandEscapePrefix
         command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
         response = self.send_recv(command_dict)
 
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 70b04b051e0ec..9ab8a905a79dd 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -443,7 +443,7 @@ def cleanup():
 
         if not (response and response["success"]):
             self.assertTrue(
-                response["success"], "launch failed (%s)" % (response["message"])
+                response["success"], "launch failed (%s)" % (response["body"]["error"]["format"])
             )
         return response
 
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 64c99019a1c9b..c6a3e9cc879a4 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -41,7 +41,9 @@ def test_termination(self):
         self.dap_server.request_disconnect()
 
         # Wait until the underlying lldb-dap process dies.
-        self.dap_server.process.wait(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        self.dap_server.process.wait(
+            timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval
+        )
 
         # Check the return code
         self.assertEqual(self.dap_server.process.poll(), 0)
@@ -459,7 +461,7 @@ def test_failing_launch_commands(self):
 
         self.assertFalse(response["success"])
         self.assertRegex(
-            response["message"],
+            response["body"]["error"]["format"],
             r"Failed to run launch commands\. See the Debug Console for more details",
         )
 
diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py
index 5a9938c25c2c8..a94c9860c1508 100644
--- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py
+++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py
@@ -21,7 +21,7 @@ def isTestSupported(self):
             return False
 
     @skipIfWindows
-    @skipIf(archs=["arm"])  # Always times out on buildbot
+    @skipIf(oslist=["linux"], archs=["arm"])  # Always times out on buildbot
     def test_basic_functionality(self):
         """
         Test basic restarting functionality when the process is running in
@@ -61,7 +61,7 @@ def test_basic_functionality(self):
         )
 
     @skipIfWindows
-    @skipIf(archs=["arm"])  # Always times out on buildbot
+    @skipIf(oslist=["linux"], archs=["arm"])  # Always times out on buildbot
     def test_stopOnEntry(self):
         """
         Check that stopOnEntry works correctly when using runInTerminal.
diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
index 9141565ac1b9b..9aab7ca3293db 100644
--- a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
+++ b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
@@ -44,7 +44,7 @@ def isTestSupported(self):
             return False
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_runInTerminal(self):
         if not self.isTestSupported():
             return
@@ -90,7 +90,7 @@ def test_runInTerminal(self):
         env = self.dap_server.request_evaluate("foo")["body"]["result"]
         self.assertIn("bar", env)
 
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_runInTerminalWithObjectEnv(self):
         if not self.isTestSupported():
             return
@@ -114,7 +114,7 @@ def test_runInTerminalWithObjectEnv(self):
         self.assertEqual("BAR", request_envs["FOO"])
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_runInTerminalInvalidTarget(self):
         if not self.isTestSupported():
             return
@@ -133,7 +133,7 @@ def test_runInTerminalInvalidTarget(self):
         )
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_missingArgInRunInTerminalLauncher(self):
         if not self.isTestSupported():
             return
@@ -148,7 +148,7 @@ def test_missingArgInRunInTerminalLauncher(self):
         )
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_FakeAttachedRunInTerminalLauncherWithInvalidProgram(self):
         if not self.isTestSupported():
             return
@@ -175,7 +175,7 @@ def test_FakeAttachedRunInTerminalLauncherWithInvalidProgram(self):
         self.assertIn("No such file or directory", stderr)
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_FakeAttachedRunInTerminalLauncherWithValidProgram(self):
         if not self.isTestSupported():
             return
@@ -202,7 +202,7 @@ def test_FakeAttachedRunInTerminalLauncherWithValidProgram(self):
         self.assertIn("foo", stdout)
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_FakeAttachedRunInTerminalLauncherAndCheckEnvironment(self):
         if not self.isTestSupported():
             return
@@ -223,7 +223,7 @@ def test_FakeAttachedRunInTerminalLauncherAndCheckEnvironment(self):
         self.assertIn("FOO=BAR", stdout)
 
     @skipIfWindows
-    @skipIf(archs=no_match(["x86_64"]))
+    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_NonAttachedRunInTerminalLauncher(self):
         if not self.isTestSupported():
             return
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 23f0400c8bd4d..52dd377bc40fb 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -14,6 +14,7 @@
 #include "LLDBUtils.h"
 #include "OutputRedirector.h"
 #include "Protocol/ProtocolBase.h"
+#include "Protocol/ProtocolRequests.h"
 #include "Protocol/ProtocolTypes.h"
 #include "Transport.h"
 #include "lldb/API/SBBreakpoint.h"
@@ -73,11 +74,8 @@ DAP::DAP(llvm::StringRef path, Log *log, const ReplMode default_repl_mode,
          std::vector<std::string> pre_init_commands, Transport &transport)
     : debug_adapter_path(path), log(log), transport(transport),
       broadcaster("lldb-dap"), exception_breakpoints(),
+      focus_tid(LLDB_INVALID_THREAD_ID), is_attach(false),
       pre_init_commands(std::move(pre_init_commands)),
-      focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
-      enable_auto_variable_summaries(false),
-      enable_synthetic_child_debugging(false),
-      display_extended_backtrace(false),
       restarting_process_id(LLDB_INVALID_PROCESS_ID),
       configuration_done_sent(false), waiting_for_run_in_terminal(false),
       progress_event_reporter(
@@ -505,8 +503,9 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
                              bool partial_expression) {
   // Check for the escape hatch prefix.
   if (!expression.empty() &&
-      llvm::StringRef(expression).starts_with(command_escape_prefix)) {
-    expression = expression.substr(command_escape_prefix.size());
+      llvm::StringRef(expression)
+          .starts_with(configuration.commandEscapePrefix)) {
+    expression = expression.substr(configuration.commandEscapePrefix.size());
     return ReplMode::Command;
   }
 
@@ -546,7 +545,7 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
           << "Warning: Expression '" << term
           << "' is both an LLDB command and variable. It will be evaluated as "
              "a variable. To evaluate the expression as an LLDB command, use '"
-          << command_escape_prefix << "' as a prefix.\n";
+          << configuration.commandEscapePrefix << "' as a prefix.\n";
     }
 
     // Variables take preference to commands in auto, since commands can always
@@ -593,7 +592,7 @@ DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {
 }
 
 llvm::Error DAP::RunInitCommands() {
-  if (!RunLLDBCommands("Running initCommands:", init_commands))
+  if (!RunLLDBCommands("Running initCommands:", configuration.initCommands))
     return createRunLLDBCommandsErrorMessage("initCommands");
   return llvm::Error::success();
 }
@@ -605,29 +604,31 @@ llvm::Error DAP::RunPreInitCommands() {
 }
 
 llvm::Error DAP::RunPreRunCommands() {
-  if (!RunLLDBCommands("Running preRunCommands:", pre_run_commands))
+  if (!RunLLDBCommands("Running preRunCommands:", configuration.preRunCommands))
     return createRunLLDBCommandsErrorMessage("preRunCommands");
   return llvm::Error::success();
 }
 
 void DAP::RunPostRunCommands() {
-  RunLLDBCommands("Running postRunCommands:", post_run_commands);
+  RunLLDBCommands("Running postRunCommands:", configuration.postRunCommands);
 }
 void DAP::RunStopCommands() {
-  RunLLDBCommands("Running stopCommands:", stop_commands);
+  RunLLDBCommands("Running stopCommands:", configuration.stopCommands);
 }
 
 void DAP::RunExitCommands() {
-  RunLLDBCommands("Running exitCommands:", exit_commands);
+  RunLLDBCommands("Running exitCommands:", configuration.exitCommands);
 }
 
 void DAP::RunTerminateCommands() {
-  RunLLDBCommands("Running terminateCommands:", terminate_commands);
+  RunLLDBCommands("Running terminateCommands:",
+                  configuration.terminateCommands);
 }
 
-lldb::SBTarget
-DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
-                               lldb::SBError &error) {
+lldb::SBTarget DAP::CreateTargetFromArguments(llvm::StringRef program,
+                                              llvm::StringRef targetTriple,
+                                              llvm::StringRef platformName,
+                                              lldb::SBError &error) {
   // Grab the name of the program we need to debug and create a target using
   // the given program as an argument. Executable file can be a source of target
   // architecture and platform, if they differ from the host. Setting exe path
@@ -638,15 +639,10 @@ DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
   // enough information to determine correct arch and platform (or ELF can be
   // omitted at all), so it is good to leave the user an apportunity to specify
   // those. Any of those three can be left empty.
-  const llvm::StringRef target_triple =
-      GetString(arguments, "targetTriple").value_or("");
-  const llvm::StringRef platform_name =
-      GetString(arguments, "platformName").value_or("");
-  const llvm::StringRef program = GetString(arguments, "program").value_or("");
-  auto target = this->debugger.CreateTarget(
-      program.data(), target_triple.data(), platform_name.data(),
-      true, // Add dependent modules.
-      error);
+  auto target = this->debugger.CreateTarget(program.data(), targetTriple.data(),
+                                            platformName.data(),
+                                            true, // Add dependent modules.
+                                            error);
 
   if (error.Fail()) {
     // Update message if there was an error.
@@ -802,7 +798,7 @@ llvm::Error DAP::Loop() {
   return llvm::Error::success();
 }
 
-lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
+lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) {
   lldb::SBError error;
   lldb::SBProcess process = target.GetProcess();
   if (!process.IsValid()) {
@@ -837,8 +833,8 @@ lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
     }
     std::this_thread::sleep_for(std::chrono::microseconds(250));
   }
-  error.SetErrorStringWithFormat("process failed to stop within %u seconds",
-                                 seconds);
+  error.SetErrorStringWithFormat("process failed to stop within %lld seconds",
+                                 seconds.count());
   return error;
 }
 
@@ -1035,6 +1031,36 @@ bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
   return true;
 }
 
+void DAP::ConfigureSourceMaps() {
+  if (configuration.sourceMap.empty() && !configuration.sourcePath)
+    return;
+
+  std::string sourceMapCommand;
+  llvm::raw_string_ostream strm(sourceMapCommand);
+  strm << "settings set target.source-map ";
+
+  if (!configuration.sourceMap.empty()) {
+    for (const auto &kv : configuration.sourceMap) {
+      strm << "\"" << kv.first << "\" \"" << kv.second << "\" ";
+    }
+  } else if (configuration.sourcePath) {
+    strm << "\".\" \"" << *configuration.sourcePath << "\"";
+  }
+
+  RunLLDBCommands("Setting source map:", {sourceMapCommand});
+}
+
+void DAP::SetConfiguration(const protocol::DAPConfiguration &config,
+                           bool is_attach) {
+  configuration = config;
+  this->is_attach = is_attach;
+
+  if (configuration.customFrameFormat)
+    SetFrameFormat(*configuration.customFrameFormat);
+  if (configuration.customThreadFormat)
+    SetThreadFormat(*configuration.customThreadFormat);
+}
+
 void DAP::SetFrameFormat(llvm::StringRef format) {
   if (format.empty())
     return;
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 6689980806047..f3c4a7bf22798 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -35,6 +35,7 @@
 #include "lldb/lldb-types.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
@@ -128,21 +129,21 @@ struct Variables {
 
 struct StartDebuggingRequestHandler : public lldb::SBCommandPluginInterface {
   DAP &dap;
-  explicit StartDebuggingRequestHandler(DAP &d) : dap(d) {};
+  explicit StartDebuggingRequestHandler(DAP &d) : dap(d){};
   bool DoExecute(lldb::SBDebugger debugger, char **command,
                  lldb::SBCommandReturnObject &result) override;
 };
 
 struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
   DAP &dap;
-  explicit ReplModeRequestHandler(DAP &d) : dap(d) {};
+  explicit ReplModeRequestHandler(DAP &d) : dap(d){};
   bool DoExecute(lldb::SBDebugger debugger, char **command,
                  lldb::SBCommandReturnObject &result) override;
 };
 
 struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
   DAP &dap;
-  explicit SendEventRequestHandler(DAP &d) : dap(d) {};
+  explicit SendEventRequestHandler(DAP &d) : dap(d){};
   bool DoExecute(lldb::SBDebugger debugger, char **command,
                  lldb::SBCommandReturnObject &result) override;
 };
@@ -165,26 +166,21 @@ struct DAP {
   InstructionBreakpointMap instruction_breakpoints;
   std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
   llvm::once_flag init_exception_breakpoints_flag;
-  std::vector<std::string> pre_init_commands;
-  std::vector<std::string> init_commands;
-  std::vector<std::string> pre_run_commands;
-  std::vector<std::string> post_run_commands;
-  std::vector<std::string> exit_commands;
-  std::vector<std::string> stop_commands;
-  std::vector<std::string> terminate_commands;
+
   // Map step in target id to list of function targets that user can choose.
   llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;
-  // A copy of the last LaunchRequest or AttachRequest so we can reuse its
-  // arguments if we get a RestartRequest.
-  std::optional<llvm::json::Object> last_launch_or_attach_request;
+  // A copy of the last LaunchRequest so we can reuse its arguments if we get a
+  // RestartRequest. Restarting an AttachRequest is not supported.
+  std::optional<protocol::LaunchRequestArguments> last_launch_request;
   lldb::tid_t focus_tid;
   bool disconnecting = false;
   llvm::once_flag terminated_event_flag;
-  bool stop_at_entry;
   bool is_attach;
-  bool enable_auto_variable_summaries;
-  bool enable_synthetic_child_debugging;
-  bool display_extended_backtrace;
+  bool stop_at_entry;
+  /// pre_init_commands are configured by a CLI flag and are not part of the
+  /// common launching/attaching definition.
+  std::vector<std::string> pre_init_commands;
+  protocol::DAPConfiguration configuration;
   // The process event thread normally responds to process exited events by
   // shutting down the entire adapter. When we're restarting, we keep the id of
   // the old process here so we can detect this case and keep running.
@@ -201,7 +197,6 @@ struct DAP {
   llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>>
       inflight_reverse_requests;
   ReplMode repl_mode;
-  std::string command_escape_prefix = "`";
   lldb::SBFormat frame_format;
   lldb::SBFormat thread_format;
   // This is used to allow request_evaluate to handle empty expressions
@@ -249,6 +244,13 @@ struct DAP {
   /// Stop event handler threads.
   void StopEventHandlers();
 
+  /// Configures the debug adapter for launching/attaching.
+  void SetConfiguration(const protocol::DAPConfiguration &confing,
+                        bool is_attach);
+
+  /// Configure source maps based on the current `DAPConfiguration`.
+  void ConfigureSourceMaps();
+
   /// Serialize the JSON value into a string and send the JSON packet to the
   /// "out" stream.
   void SendJSON(const llvm::json::Value &json);
@@ -320,7 +322,9 @@ struct DAP {
   ///
   /// \return
   ///     An SBTarget object.
-  lldb::SBTarget CreateTargetFromArguments(const llvm::json::Object &arguments,
+  lldb::SBTarget CreateTargetFromArguments(llvm::StringRef program,
+                                           llvm::StringRef targetTriple,
+                                           llvm::StringRef platformName,
                                            lldb::SBError &error);
 
   /// Set given target object as a current target for lldb-dap and start
@@ -395,7 +399,7 @@ struct DAP {
   ///   The number of seconds to poll the process to wait until it is stopped.
   ///
   /// \return Error if waiting for the process fails, no error if succeeds.
-  lldb::SBError WaitForProcessToStop(uint32_t seconds);
+  lldb::SBError WaitForProcessToStop(std::chrono::seconds seconds);
 
   void SetFrameFormat(llvm::StringRef format);
 
diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index 20f7c80a1ed90..cd7fbf4f7be9f 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -43,10 +43,8 @@ namespace lldb_dap {
 //     acknowledgement, so no body field is required."
 //   }]
 // }
-
 void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
   dap.is_attach = true;
-  dap.last_launch_or_attach_request = request;
   llvm::json::Object response;
   lldb::SBError error;
   FillResponse(request, response);
@@ -63,11 +61,13 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
     attach_info.SetProcessID(pid);
   const auto wait_for = GetBoolean(arguments, "waitFor").value_or(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");
+  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 =
@@ -75,16 +75,15 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
   dap.stop_at_entry = core_file.empty()
                           ? GetBoolean(arguments, "stopOnEntry").value_or(false)
                           : true;
-  dap.post_run_commands = GetStrings(arguments, "postRunCommands");
   const llvm::StringRef debuggerRoot =
       GetString(arguments, "debuggerRoot").value_or("");
-  dap.enable_auto_variable_summaries =
+  dap.configuration.enableAutoVariableSummaries =
       GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false);
-  dap.enable_synthetic_child_debugging =
+  dap.configuration.enableSyntheticChildDebugging =
       GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false);
-  dap.display_extended_backtrace =
+  dap.configuration.displayExtendedBacktrace =
       GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
-  dap.command_escape_prefix =
+  dap.configuration.commandEscapePrefix =
       GetString(arguments, "commandEscapePrefix").value_or("`");
   dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
   dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
@@ -108,8 +107,14 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
 
   SetSourceMapFromArguments(*arguments);
 
+  llvm::StringRef program = GetString(arguments, "program").value_or("");
+  llvm::StringRef target_triple =
+      GetString(arguments, "targetTriple").value_or("");
+  llvm::StringRef platform_name =
+      GetString(arguments, "platformName").value_or("");
   lldb::SBError status;
-  dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
+  dap.SetTarget(dap.CreateTargetFromArguments(program, target_triple,
+                                              platform_name, status));
   if (status.Fail()) {
     response["success"] = llvm::json::Value(false);
     EmplaceSafeString(response, "message", status.GetCString());
@@ -179,7 +184,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
 
     // 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);
+    error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
   }
 
   if (error.Success() && core_file.empty()) {
diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
index 5414aaeb2c317..c72fc5686cd5b 100644
--- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
@@ -157,19 +157,20 @@ void CompletionsRequestHandler::operator()(
   llvm::json::Array targets;
 
   bool had_escape_prefix =
-      llvm::StringRef(text).starts_with(dap.command_escape_prefix);
+      llvm::StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
   ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
 
   // Handle the offset change introduced by stripping out the
   // `command_escape_prefix`.
   if (had_escape_prefix) {
-    if (offset < static_cast<int64_t>(dap.command_escape_prefix.size())) {
+    if (offset <
+        static_cast<int64_t>(dap.configuration.commandEscapePrefix.size())) {
       body.try_emplace("targets", std::move(targets));
       response.try_emplace("body", std::move(body));
       dap.SendJSON(llvm::json::Value(std::move(response)));
       return;
     }
-    offset -= dap.command_escape_prefix.size();
+    offset -= dap.configuration.commandEscapePrefix.size();
   }
 
   // While the user is typing then we likely have an incomplete input and cannot
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index e9f08a1017abc..8ed09fa2a931a 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -205,7 +205,8 @@ void EvaluateRequestHandler::operator()(
       else
         EmplaceSafeString(response, "message", "evaluate failed");
     } else {
-      VariableDescription desc(value, dap.enable_auto_variable_summaries);
+      VariableDescription desc(value,
+                               dap.configuration.enableAutoVariableSummaries);
       EmplaceSafeString(body, "result", desc.GetResult(context));
       EmplaceSafeString(body, "type", desc.display_type_name);
       int64_t var_ref = 0;
diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
index f64c186376a36..66e37b02db0a3 100644
--- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
@@ -9,70 +9,18 @@
 #include "DAP.h"
 #include "EventHelper.h"
 #include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RequestHandler.h"
 #include "llvm/Support/FileSystem.h"
 
 namespace lldb_dap {
 
-// "LaunchRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Launch request; value of command field is 'launch'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "launch" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/LaunchRequestArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "LaunchRequestArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'launch' request.",
-//   "properties": {
-//     "noDebug": {
-//       "type": "boolean",
-//       "description": "If noDebug is true the launch request should launch
-//                       the program without enabling debugging."
-//     }
-//   }
-// },
-// "LaunchResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'launch' request. This is just an
-//                     acknowledgement, so no body field is required."
-//   }]
-// }
-void LaunchRequestHandler::operator()(const llvm::json::Object &request) const {
-  dap.is_attach = false;
-  dap.last_launch_or_attach_request = request;
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  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");
-  dap.post_run_commands = GetStrings(arguments, "postRunCommands");
-  dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry").value_or(false);
-  const llvm::StringRef debuggerRoot =
-      GetString(arguments, "debuggerRoot").value_or("");
-  dap.enable_auto_variable_summaries =
-      GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false);
-  dap.enable_synthetic_child_debugging =
-      GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false);
-  dap.display_extended_backtrace =
-      GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
-  dap.command_escape_prefix =
-      GetString(arguments, "commandEscapePrefix").value_or("`");
-  dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
-  dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
+/// Launch request; value of command field is 'launch'.
+llvm::Expected<protocol::LaunchResponseBody> LaunchRequestHandler::Run(
+    const protocol::LaunchRequestArguments &arguments) const {
+  dap.SetConfiguration(arguments.configuration, /*is_attach=*/false);
+  dap.last_launch_request = arguments;
+  dap.stop_at_entry = arguments.stopOnEntry;
 
   PrintWelcomeMessage();
 
@@ -80,55 +28,47 @@ void LaunchRequestHandler::operator()(const llvm::json::Object &request) 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 debuggerRoot = dap.configuration.debuggerRoot.value_or("");
   if (!debuggerRoot.empty())
     llvm::sys::fs::set_current_path(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
   // the targets - preRunCommands are run with the target.
-  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.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;
-  }
+  dap.SetTarget(dap.CreateTargetFromArguments(
+      arguments.program.value_or(""), arguments.targetTriple.value_or(""),
+      arguments.platformName.value_or(""), status));
+  if (status.Fail())
+    return llvm::make_error<DAPError>(status.GetCString());
 
   // 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 (llvm::Error err = dap.RunPreRunCommands())
+    return err;
 
-  status = LaunchProcess(request);
+  if (llvm::Error err = LaunchProcess(arguments))
+    return err;
 
-  if (status.Fail()) {
-    response["success"] = llvm::json::Value(false);
-    EmplaceSafeString(response, "message", std::string(status.GetCString()));
-  } else {
-    dap.RunPostRunCommands();
-  }
+  dap.RunPostRunCommands();
 
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return protocol::LaunchResponseBody();
+}
 
-  if (!status.Fail()) {
+void LaunchRequestHandler::PostRun() const {
+  if (dap.target.GetProcess().IsValid()) {
     if (dap.is_attach)
-      SendProcessEvent(dap, Attach); // this happens when doing runInTerminal
+      // this happens when doing runInTerminal
+      SendProcessEvent(dap, Attach);
     else
       SendProcessEvent(dap, Launch);
   }
+
   dap.SendJSON(CreateEventObject("initialized"));
 }
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index f067dfc5544fe..23904848de5cb 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -10,8 +10,10 @@
 #include "DAP.h"
 #include "Handler/ResponseHandler.h"
 #include "JSONUtils.h"
-#include "LLDBUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RunInTerminal.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBEnvironment.h"
 #include "llvm/Support/Error.h"
 
 #if !defined(_WIN32)
@@ -33,15 +35,12 @@ MakeArgv(const llvm::ArrayRef<std::string> &strs) {
   return argv;
 }
 
-static uint32_t SetLaunchFlag(uint32_t flags, const llvm::json::Object *obj,
-                              llvm::StringRef key, lldb::LaunchFlags mask) {
-  if (const auto opt_value = GetBoolean(obj, key)) {
-    if (*opt_value)
-      flags |= mask;
-    else
-      flags &= ~mask;
-  }
-
+static uint32_t SetLaunchFlag(uint32_t flags, bool opt,
+                              lldb::LaunchFlags mask) {
+  if (opt)
+    flags |= mask;
+  else
+    flags &= ~mask;
   return flags;
 }
 
@@ -95,14 +94,17 @@ void BaseRequestHandler::SetSourceMapFromArguments(
   }
 }
 
-static llvm::Error RunInTerminal(DAP &dap,
-                                 const llvm::json::Object &launch_request,
-                                 const uint64_t timeout_seconds) {
+static llvm::Error
+RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
   if (!dap.clientFeatures.contains(
           protocol::eClientFeatureRunInTerminalRequest))
     return llvm::make_error<DAPError>("Cannot use runInTerminal, feature is "
                                       "not supported by the connected client");
 
+  if (!arguments.program || arguments.program->empty())
+    return llvm::make_error<DAPError>(
+        "program must be set to when using runInTerminal");
+
   dap.is_attach = true;
   lldb::SBAttachInfo attach_info;
 
@@ -118,8 +120,11 @@ static llvm::Error RunInTerminal(DAP &dap,
 #if !defined(_WIN32)
   debugger_pid = getpid();
 #endif
+
   llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
-      launch_request, dap.debug_adapter_path, comm_file.m_path, debugger_pid);
+      *arguments.program, arguments.args, arguments.env,
+      arguments.cwd.value_or(""), dap.debug_adapter_path, comm_file.m_path,
+      debugger_pid);
   dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
                                                     std::move(reverse_request));
 
@@ -165,74 +170,75 @@ static llvm::Error RunInTerminal(DAP &dap,
                                  error.GetCString());
 }
 
-lldb::SBError
-BaseRequestHandler::LaunchProcess(const llvm::json::Object &request) const {
-  lldb::SBError error;
-  const auto *arguments = request.getObject("arguments");
-  auto launchCommands = GetStrings(arguments, "launchCommands");
+llvm::Error BaseRequestHandler::LaunchProcess(
+    const protocol::LaunchRequestArguments &arguments) const {
+  auto launchCommands = arguments.launchCommands;
 
   // Instantiate a launch info instance for the target.
   auto launch_info = dap.target.GetLaunchInfo();
 
   // Grab the current working directory if there is one and set it in the
   // launch info.
-  const auto cwd = GetString(arguments, "cwd").value_or("");
+  const auto cwd = arguments.cwd.value_or("");
   if (!cwd.empty())
     launch_info.SetWorkingDirectory(cwd.data());
 
   // Extract any extra arguments and append them to our program arguments for
   // when we launch
-  auto args = GetStrings(arguments, "args");
-  if (!args.empty())
-    launch_info.SetArguments(MakeArgv(args).data(), true);
+  if (!arguments.args.empty())
+    launch_info.SetArguments(MakeArgv(arguments.args).data(), true);
 
   // Pass any environment variables along that the user specified.
-  const auto envs = GetEnvironmentFromArguments(*arguments);
-  launch_info.SetEnvironment(envs, true);
+  if (!arguments.env.empty()) {
+    lldb::SBEnvironment env;
+    for (const auto &kv : arguments.env)
+      env.Set(kv.first().data(), kv.second.c_str(), true);
+    launch_info.SetEnvironment(env, true);
+  }
 
   auto flags = launch_info.GetLaunchFlags();
 
-  flags = SetLaunchFlag(flags, arguments, "disableASLR",
-                        lldb::eLaunchFlagDisableASLR);
-  flags = SetLaunchFlag(flags, arguments, "disableSTDIO",
+  flags =
+      SetLaunchFlag(flags, arguments.disableASLR, lldb::eLaunchFlagDisableASLR);
+  flags = SetLaunchFlag(flags, arguments.disableSTDIO,
                         lldb::eLaunchFlagDisableSTDIO);
-  flags = SetLaunchFlag(flags, arguments, "shellExpandArguments",
+  flags = SetLaunchFlag(flags, arguments.shellExpandArguments,
                         lldb::eLaunchFlagShellExpandArguments);
-
-  const bool detachOnError =
-      GetBoolean(arguments, "detachOnError").value_or(false);
-  launch_info.SetDetachOnError(detachOnError);
+  launch_info.SetDetachOnError(arguments.detachOnError);
   launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
                              lldb::eLaunchFlagStopAtEntry);
-  const auto timeout_seconds =
-      GetInteger<uint64_t>(arguments, "timeout").value_or(30);
 
-  if (GetBoolean(arguments, "runInTerminal").value_or(false)) {
-    if (llvm::Error err = RunInTerminal(dap, request, timeout_seconds))
-      error.SetErrorString(llvm::toString(std::move(err)).c_str());
-  } else if (launchCommands.empty()) {
+  if (arguments.runInTerminal) {
+    if (llvm::Error err = RunInTerminal(dap, arguments))
+      return err;
+  } else if (!launchCommands) {
+    lldb::SBError error;
     // Disable async events so the launch will be successful when we return from
     // the launch call and the launch will happen synchronously
     dap.debugger.SetAsync(false);
     dap.target.Launch(launch_info, error);
     dap.debugger.SetAsync(true);
+    if (error.Fail())
+      return llvm::make_error<DAPError>(error.GetCString());
   } else {
     // Set the launch info so that run commands can access the configured
     // launch details.
     dap.target.SetLaunchInfo(launch_info);
-    if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) {
-      error.SetErrorString(llvm::toString(std::move(err)).c_str());
-      return error;
-    }
+    if (llvm::Error err = dap.RunLaunchCommands(*launchCommands))
+      return err;
+
     // 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 launched and stopped at the entry point before
     // proceeding as the launch commands are not run using the synchronous
     // mode.
-    error = dap.WaitForProcessToStop(timeout_seconds);
+    lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
+    if (error.Fail())
+      return llvm::make_error<DAPError>(error.GetCString());
   }
-  return error;
+
+  return llvm::Error::success();
 }
 
 void BaseRequestHandler::PrintWelcomeMessage() const {
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 396815b04c84a..aa3031fcce680 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -69,7 +69,8 @@ class BaseRequestHandler {
   // runInTerminal if applicable. It doesn't do any of the additional
   // initialization and bookkeeping stuff that is needed for `request_launch`.
   // This way we can reuse the process launching logic for RestartRequest too.
-  lldb::SBError LaunchProcess(const llvm::json::Object &request) const;
+  llvm::Error
+  LaunchProcess(const protocol::LaunchRequestArguments &request) const;
 
   // Check if the step-granularity is `instruction`.
   bool HasInstructionGranularity(const llvm::json::Object &request) const;
@@ -162,9 +163,15 @@ class RequestHandler : public BaseRequestHandler {
     }
 
     dap.Send(response);
+
+    PostRun();
   };
 
   virtual llvm::Expected<Body> Run(const Args &) const = 0;
+
+  /// A hook for a handler to asynchrounsly run an operation after the handler
+  /// is complete.
+  virtual void PostRun() const {};
 };
 
 class AttachRequestHandler : public LegacyRequestHandler {
@@ -254,11 +261,15 @@ class InitializeRequestHandler
   Run(const protocol::InitializeRequestArguments &args) const override;
 };
 
-class LaunchRequestHandler : public LegacyRequestHandler {
+class LaunchRequestHandler
+    : public RequestHandler<protocol::LaunchRequestArguments,
+                            protocol::LaunchResponseBody> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "launch"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::LaunchResponseBody>
+  Run(const protocol::LaunchRequestArguments &arguments) const override;
+  void PostRun() const override;
 };
 
 class RestartRequestHandler : public LegacyRequestHandler {
diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index c8f43b7a76e8b..a009ad12ddd31 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -9,9 +9,11 @@
 #include "DAP.h"
 #include "EventHelper.h"
 #include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RequestHandler.h"
-#include "lldb/API/SBListener.h"
-#include "llvm/Support/FileSystem.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace lldb_dap {
 
@@ -60,7 +62,7 @@ void RestartRequestHandler::operator()(
     const llvm::json::Object &request) const {
   llvm::json::Object response;
   FillResponse(request, response);
-  if (!dap.last_launch_or_attach_request) {
+  if (!dap.target.GetProcess().IsValid()) {
     response["success"] = llvm::json::Value(false);
     EmplaceSafeString(response, "message",
                       "Restart request received but no process was launched.");
@@ -77,7 +79,7 @@ void RestartRequestHandler::operator()(
   // implementation detail. The adapter *did* launch the process in response to
   // a "launch" command, so we can still stop it and re-run it. This is why we
   // don't just check `dap.is_attach`.
-  if (GetString(*dap.last_launch_or_attach_request, "command") == "attach") {
+  if (!dap.last_launch_request) {
     response["success"] = llvm::json::Value(false);
     EmplaceSafeString(response, "message",
                       "Restarting an \"attach\" session is not supported.");
@@ -85,15 +87,30 @@ void RestartRequestHandler::operator()(
     return;
   }
 
-  // The optional `arguments` field in RestartRequest can contain an updated
-  // version of the launch arguments. If there's one, use it.
-  const auto *restart_arguments = request.getObject("arguments");
-  if (restart_arguments) {
-    const auto *launch_request_arguments =
-        restart_arguments->getObject("arguments");
-    if (launch_request_arguments) {
-      (*dap.last_launch_or_attach_request)["arguments"] =
-          llvm::json::Value(llvm::json::Object(*launch_request_arguments));
+  const llvm::json::Object *arguments = request.getObject("arguments");
+  if (arguments) {
+    // The optional `arguments` field in RestartRequest can contain an updated
+    // version of the launch arguments. If there's one, use it.
+    const llvm::json::Value *restart_arguments = arguments->get("arguments");
+    if (restart_arguments) {
+      protocol::LaunchRequestArguments updated_arguments;
+      llvm::json::Path::Root root;
+      if (!fromJSON(*restart_arguments, updated_arguments, root)) {
+        response["success"] = llvm::json::Value(false);
+        EmplaceSafeString(
+            response, "message",
+            llvm::formatv("Failed to parse updated launch arguments: {0}",
+                          llvm::toString(root.getError()))
+                .str());
+        dap.SendJSON(llvm::json::Value(std::move(response)));
+        return;
+      }
+      dap.last_launch_request = updated_arguments;
+      // 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();
     }
   }
 
@@ -116,7 +133,15 @@ void RestartRequestHandler::operator()(
     dap.thread_ids.clear();
   }
   dap.debugger.SetAsync(true);
-  LaunchProcess(*dap.last_launch_or_attach_request);
+
+  // FIXME: Should we run 'preRunCommands'?
+  // FIXME: Should we add a 'preRestartCommands'?
+  if (llvm::Error err = LaunchProcess(*dap.last_launch_request)) {
+    response["success"] = llvm::json::Value(false);
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
 
   // This is normally done after receiving a "configuration done" request.
   // Because we're restarting, configuration has already happened so we can
@@ -129,4 +154,5 @@ void RestartRequestHandler::operator()(
 
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp
index a7896b7fefa29..c48bcd84c9ddc 100644
--- a/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp
@@ -145,7 +145,8 @@ void SetVariableRequestHandler::operator()(
     lldb::SBError error;
     bool success = variable.SetValueFromCString(value.data(), error);
     if (success) {
-      VariableDescription desc(variable, dap.enable_auto_variable_summaries);
+      VariableDescription desc(variable,
+                               dap.configuration.enableAutoVariableSummaries);
       EmplaceSafeString(body, "value", desc.display_value);
       EmplaceSafeString(body, "type", desc.display_type_name);
 
diff --git a/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
index 220be0f99be6b..a58e3325af100 100644
--- a/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
@@ -70,7 +70,7 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
     stack_frames.emplace_back(CreateStackFrame(frame, dap.frame_format));
   }
 
-  if (dap.display_extended_backtrace && reached_end_of_stack) {
+  if (dap.configuration.displayExtendedBacktrace && reached_end_of_stack) {
     // Check for any extended backtraces.
     for (uint32_t bt = 0;
          bt < thread.GetProcess().GetNumExtendedBacktraceTypes(); bt++) {
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
index 6bb0a0f160499..19bcca2b22b9b 100644
--- a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -180,10 +180,10 @@ void VariablesRequestHandler::operator()(
           return_var_ref = dap.variables.InsertVariable(stop_return_value,
                                                         /*is_permanent=*/false);
         }
-        variables.emplace_back(
-            CreateVariable(renamed_return_value, return_var_ref, hex,
-                           dap.enable_auto_variable_summaries,
-                           dap.enable_synthetic_child_debugging, false));
+        variables.emplace_back(CreateVariable(
+            renamed_return_value, return_var_ref, hex,
+            dap.configuration.enableAutoVariableSummaries,
+            dap.configuration.enableSyntheticChildDebugging, false));
       }
     }
 
@@ -197,8 +197,8 @@ void VariablesRequestHandler::operator()(
       int64_t var_ref =
           dap.variables.InsertVariable(variable, /*is_permanent=*/false);
       variables.emplace_back(CreateVariable(
-          variable, var_ref, hex, dap.enable_auto_variable_summaries,
-          dap.enable_synthetic_child_debugging,
+          variable, var_ref, hex, dap.configuration.enableAutoVariableSummaries,
+          dap.configuration.enableSyntheticChildDebugging,
           variable_name_counts[GetNonNullVariableName(variable)] > 1));
     }
   } else {
@@ -214,8 +214,8 @@ void VariablesRequestHandler::operator()(
             dap.variables.IsPermanentVariableReference(variablesReference);
         int64_t var_ref = dap.variables.InsertVariable(child, is_permanent);
         variables.emplace_back(CreateVariable(
-            child, var_ref, hex, dap.enable_auto_variable_summaries,
-            dap.enable_synthetic_child_debugging,
+            child, var_ref, hex, dap.configuration.enableAutoVariableSummaries,
+            dap.configuration.enableSyntheticChildDebugging,
             /*is_name_duplicated=*/false, custom_name));
       };
       const int64_t num_children = variable.GetNumChildren();
@@ -228,8 +228,8 @@ void VariablesRequestHandler::operator()(
       // "[raw]" child that can be used to inspect the raw version of a
       // synthetic member. That eliminates the need for the user to go to the
       // debug console and type `frame var <variable> to get these values.
-      if (dap.enable_synthetic_child_debugging && variable.IsSynthetic() &&
-          i == num_children)
+      if (dap.configuration.enableSyntheticChildDebugging &&
+          variable.IsSynthetic() && i == num_children)
         addChild(variable.GetNonSyntheticValue(), "[raw]");
     }
   }
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 9773b91a35a45..8e5b92e08c69f 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1398,48 +1398,40 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) {
 
 /// See
 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
-llvm::json::Object
-CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
-                                  llvm::StringRef debug_adapter_path,
-                                  llvm::StringRef comm_file,
-                                  lldb::pid_t debugger_pid) {
+llvm::json::Object CreateRunInTerminalReverseRequest(
+    llvm::StringRef program, const std::vector<std::string> &args,
+    const llvm::StringMap<std::string> env, llvm::StringRef cwd,
+    llvm::StringRef debug_adapter_path, llvm::StringRef comm_file,
+    lldb::pid_t debugger_pid) {
   llvm::json::Object run_in_terminal_args;
   // This indicates the IDE to open an embedded terminal, instead of opening
   // the terminal in a new window.
   run_in_terminal_args.try_emplace("kind", "integrated");
 
-  const auto *launch_request_arguments = launch_request.getObject("arguments");
   // The program path must be the first entry in the "args" field
-  std::vector<std::string> args = {debug_adapter_path.str(), "--comm-file",
-                                   comm_file.str()};
+  std::vector<std::string> req_args = {debug_adapter_path.str(), "--comm-file",
+                                       comm_file.str()};
   if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
-    args.push_back("--debugger-pid");
-    args.push_back(std::to_string(debugger_pid));
+    req_args.push_back("--debugger-pid");
+    req_args.push_back(std::to_string(debugger_pid));
   }
-  args.push_back("--launch-target");
-  args.push_back(
-      GetString(launch_request_arguments, "program").value_or("").str());
-  std::vector<std::string> target_args =
-      GetStrings(launch_request_arguments, "args");
-  args.insert(args.end(), target_args.begin(), target_args.end());
+  req_args.push_back("--launch-target");
+  req_args.push_back(program.str());
+  req_args.insert(req_args.end(), args.begin(), args.end());
   run_in_terminal_args.try_emplace("args", args);
 
-  const auto cwd = GetString(launch_request_arguments, "cwd").value_or("");
   if (!cwd.empty())
     run_in_terminal_args.try_emplace("cwd", cwd);
 
-  auto envs = GetEnvironmentFromArguments(*launch_request_arguments);
-  llvm::json::Object env_json;
-  for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count;
-       index++) {
-    llvm::StringRef key = envs.GetNameAtIndex(index);
-    llvm::StringRef value = envs.GetValueAtIndex(index);
-
-    if (!key.empty())
-      env_json.try_emplace(key, value);
+  if (!env.empty()) {
+    llvm::json::Object env_json;
+    for (const auto &kv : env) {
+      if (!kv.first().empty())
+        env_json.try_emplace(kv.first(), kv.second);
+    }
+    run_in_terminal_args.try_emplace("env",
+                                     llvm::json::Value(std::move(env_json)));
   }
-  run_in_terminal_args.try_emplace("env",
-                                   llvm::json::Value(std::move(env_json)));
 
   return run_in_terminal_args;
 }
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 5d403d39a76d4..6bf180ba7e2c7 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -18,6 +18,7 @@
 #include "lldb/API/SBType.h"
 #include "lldb/API/SBValue.h"
 #include "lldb/lldb-types.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
 #include <cstdint>
@@ -580,11 +581,11 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
 /// \return
 ///     A "runInTerminal" JSON object that follows the specification outlined by
 ///     Microsoft.
-llvm::json::Object
-CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
-                                  llvm::StringRef debug_adapter_path,
-                                  llvm::StringRef comm_file,
-                                  lldb::pid_t debugger_pid);
+llvm::json::Object CreateRunInTerminalReverseRequest(
+    llvm::StringRef program, const std::vector<std::string> &args,
+    const llvm::StringMap<std::string> env, llvm::StringRef cwd,
+    llvm::StringRef debug_adapter_path, llvm::StringRef comm_file,
+    lldb::pid_t debugger_pid);
 
 /// Create a "Terminated" JSON object that contains statistics
 ///
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 9a613128739c2..84a21e5074447 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Protocol/ProtocolRequests.h"
-#include "DAP.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
@@ -25,8 +25,7 @@ bool fromJSON(const json::Value &Params, DisconnectArguments &DA,
          O.mapOptional("suspendDebuggee", DA.suspendDebuggee);
 }
 
-bool fromJSON(const llvm::json::Value &Params, PathFormat &PF,
-              llvm::json::Path P) {
+bool fromJSON(const json::Value &Params, PathFormat &PF, json::Path P) {
   auto rawPathFormat = Params.getAsString();
   if (!rawPathFormat) {
     P.report("expected a string");
@@ -47,7 +46,7 @@ bool fromJSON(const llvm::json::Value &Params, PathFormat &PF,
   return true;
 }
 
-static const llvm::StringMap<ClientFeature> ClientFeatureByKey{
+static const StringMap<ClientFeature> ClientFeatureByKey{
     {"supportsVariableType", eClientFeatureVariableType},
     {"supportsVariablePaging", eClientFeatureVariablePaging},
     {"supportsRunInTerminalRequest", eClientFeatureRunInTerminalRequest},
@@ -60,8 +59,8 @@ static const llvm::StringMap<ClientFeature> ClientFeatureByKey{
     {"supportsStartDebuggingRequest", eClientFeatureStartDebuggingRequest},
     {"supportsANSIStyling", eClientFeatureANSIStyling}};
 
-bool fromJSON(const llvm::json::Value &Params, InitializeRequestArguments &IRA,
-              llvm::json::Path P) {
+bool fromJSON(const json::Value &Params, InitializeRequestArguments &IRA,
+              json::Path P) {
   json::ObjectMapper OM(Params, P);
   if (!OM)
     return false;
@@ -82,6 +81,170 @@ bool fromJSON(const llvm::json::Value &Params, InitializeRequestArguments &IRA,
          OM.mapOptional("$__lldb_sourceInitFile", IRA.lldbExtSourceInitFile);
 }
 
+static bool
+parseSourceMap(const json::Value &Params,
+               std::vector<std::pair<std::string, std::string>> &sourceMap,
+               json::Path P) {
+  const json::Object *O = Params.getAsObject();
+  if (!O) {
+    P.report("expected object");
+    return false;
+  }
+
+  const json::Value *value = O->get("sourceMap");
+  if (!value)
+    return true;
+
+  if (const json::Object *map_obj = value->getAsObject()) {
+    for (const auto &kv : *map_obj) {
+      const std::optional<StringRef> value = kv.second.getAsString();
+      if (!value) {
+        P.field("sourceMap").field(kv.first).report("expected string value");
+        return false;
+      }
+      sourceMap.emplace_back(std::make_pair(kv.first.str(), value->str()));
+    }
+    return true;
+  }
+
+  if (const json::Array *env_arr = value->getAsArray()) {
+    for (size_t i = 0; i < env_arr->size(); ++i) {
+      const json::Array *kv = (*env_arr)[i].getAsArray();
+      if (!kv) {
+        P.field("sourceMap").index(i).report("expected array");
+        return false;
+      }
+      if (kv->size() != 2) {
+        P.field("sourceMap").index(i).report("expected array of pairs");
+        return false;
+      }
+      const std::optional<StringRef> first = (*kv)[0].getAsString();
+      if (!first) {
+        P.field("sourceMap").index(0).report("expected string");
+        return false;
+      }
+      const std::optional<StringRef> second = (*kv)[1].getAsString();
+      if (!second) {
+        P.field("sourceMap").index(1).report("expected string");
+        return false;
+      }
+      sourceMap.emplace_back(std::make_pair(*first, second->str()));
+    }
+
+    return true;
+  }
+
+  P.report("invalid format, expected array or object");
+  return false;
+}
+
+bool fromJSON(const json::Value &Params, DAPConfiguration &C, json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O.mapOptional("debuggerRoot", C.debuggerRoot) &&
+         O.mapOptional("enableAutoVariableSummaries",
+                       C.enableAutoVariableSummaries) &&
+         O.mapOptional("enableSyntheticChildDebugging",
+                       C.enableSyntheticChildDebugging) &&
+         O.mapOptional("displayExtendedBacktrace",
+                       C.displayExtendedBacktrace) &&
+         O.mapOptional("commandEscapePrefix", C.commandEscapePrefix) &&
+         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) &&
+         parseSourceMap(Params, C.sourceMap, P);
+}
+
+// The 'env' field is either an object as a map of strings or as an array of
+// strings formatted like 'key=value'.
+static bool parseEnv(const json::Value &Params, StringMap<std::string> &env,
+                     json::Path P) {
+  const json::Object *O = Params.getAsObject();
+  if (!O) {
+    P.report("expected object");
+    return false;
+  }
+
+  const json::Value *value = O->get("env");
+  if (!value)
+    return true;
+
+  if (const json::Object *env_obj = value->getAsObject()) {
+    for (const auto &kv : *env_obj) {
+      const std::optional<StringRef> value = kv.second.getAsString();
+      if (!value) {
+        P.field("env").field(kv.first).report("expected string value");
+        return false;
+      }
+      env.insert({kv.first.str(), value->str()});
+    }
+    return true;
+  }
+
+  if (const json::Array *env_arr = value->getAsArray()) {
+    for (size_t i = 0; i < env_arr->size(); ++i) {
+      const std::optional<StringRef> value = (*env_arr)[i].getAsString();
+      if (!value) {
+        P.field("env").index(i).report("expected string");
+        return false;
+      }
+      std::pair<StringRef, StringRef> kv = value->split("=");
+      env.insert({kv.first, kv.second.str()});
+    }
+
+    return true;
+  }
+
+  P.field("env").report("invalid format, expected array or object");
+  return false;
+}
+
+bool parseTimeout(const json::Value &Params, std::chrono::seconds &S,
+                  json::Path P) {
+  const json::Object *O = Params.getAsObject();
+  if (!O) {
+    P.report("expected object");
+    return false;
+  }
+
+  const json::Value *value = O->get("timeout");
+  if (!value)
+    return true;
+  std::optional<double> timeout = value->getAsNumber();
+  if (!timeout) {
+    P.field("timeout").report("expected number");
+    return false;
+  }
+
+  S = std::chrono::duration_cast<std::chrono::seconds>(
+      std::chrono::duration<double>(*value->getAsNumber()));
+  return true;
+}
+
+bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
+              json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && fromJSON(Params, LRA.configuration, P) &&
+         O.mapOptional("noDebug", LRA.noDebug) &&
+         O.map("program", LRA.program) &&
+         O.map("launchCommands", LRA.launchCommands) && O.map("cwd", LRA.cwd) &&
+         O.mapOptional("args", LRA.args) && parseEnv(Params, LRA.env, P) &&
+         O.map("targetTriple", LRA.targetTriple) &&
+         O.map("platformName", LRA.platformName) &&
+         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) &&
+         parseTimeout(Params, LRA.timeout, P);
+}
+
 bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
   json::ObjectMapper O(Params, P);
   return O && O.mapOptional("source", SA.source) &&
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index c49a13711f8c7..388953e0ee041 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -22,7 +22,9 @@
 
 #include "Protocol/ProtocolBase.h"
 #include "Protocol/ProtocolTypes.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Support/JSON.h"
+#include <chrono>
 #include <cstdint>
 #include <optional>
 #include <string>
@@ -117,6 +119,154 @@ bool fromJSON(const llvm::json::Value &, InitializeRequestArguments &,
 /// Response to `initialize` request. The capabilities of this debug adapter.
 using InitializeResponseBody = std::optional<Capabilities>;
 
+/// DAP Launch and Attach common configurations.
+struct DAPConfiguration {
+  /// 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;
+
+  /// Enable auto generated summaries for variables when no summaries exist for
+  /// a given type. This feature can cause performance delays in large projects
+  /// when viewing variables.
+  bool enableAutoVariableSummaries = false;
+
+  /// 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.
+  bool enableSyntheticChildDebugging = false;
+
+  /// Enable language specific extended backtraces.
+  bool displayExtendedBacktrace = false;
+
+  /// 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
+  /// regular LLDB commands.
+  std::string commandEscapePrefix = "`";
+
+  /// If non-empty, stack frames will have descriptions generated based on the
+  /// provided format. See https://lldb.llvm.org/use/formatting.html for an
+  /// explanation on format strings for frames. If the format string contains
+  /// errors, an error message will be displayed on the Debug Console and the
+  /// default frame names will be used. This might come with a performance cost
+  /// because debug information might need to be processed to generate the
+  /// description.
+  std::optional<std::string> customFrameFormat;
+
+  /// Same as `customFrameFormat`, but for threads instead of stack frames.
+  std::optional<std::string> customThreadFormat;
+
+  /// 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;
+
+  /// 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
+  /// sourcePath.
+  std::vector<std::pair<std::string, std::string>> sourceMap;
+
+  /// LLDB commands executed upon debugger startup prior to creating the LLDB
+  /// target.
+  std::vector<std::string> initCommands;
+
+  /// LLDB commands executed just before launching/attaching, after the LLDB
+  /// target has been created.
+  std::vector<std::string> preRunCommands;
+
+  /// LLDB commands executed just after launching/attaching, after the LLDB
+  /// target has been created.
+  std::vector<std::string> postRunCommands;
+
+  /// LLDB commands executed just after each stop.
+  std::vector<std::string> stopCommands;
+
+  /// LLDB commands executed when the program exits.
+  std::vector<std::string> exitCommands;
+
+  /// LLDB commands executed when the debugging session ends.
+  std::vector<std::string> terminateCommands;
+};
+
+/// lldb-dap specific launch arguments.
+struct LaunchRequestArguments {
+  /// Common lldb-dap configuration values for launching/attaching operations.
+  DAPConfiguration configuration;
+
+  /// If true, the launch request should launch the program without enabling
+  /// debugging.
+  std::optional<bool> noDebug;
+
+  /// Launch specific operations.
+  /// @{
+
+  /// Path to the executable to launch.
+  ///
+  /// *NOTE:* Either launchCommands or program must be configured.
+  std::optional<std::string> program;
+
+  /// LLDB commands executed to launch the program.
+  ///
+  /// *NOTE:* Either launchCommands or program must be configured.
+  ///
+  /// If set, takes priority over the 'program' when launching the target.
+  std::optional<std::vector<std::string>> launchCommands;
+
+  /// The program working directory.
+  std::optional<std::string> cwd;
+
+  /// An array of command line argument strings to be passed to the program
+  /// being launched.
+  std::vector<std::string> args;
+
+  /// Environment variables to set when launching the program. The format of
+  /// each environment variable string is "VAR=VALUE" for environment variables
+  /// with values or just "VAR" for environment variables with no values.
+  llvm::StringMap<std::string> env;
+
+  /// Target triple for the program (arch-vendor-os). If not set, inferred from
+  /// the binary.
+  std::optional<std::string> targetTriple;
+
+  /// Specify name of the platform to use for this target, creating the platform
+  /// if necessary.
+  std::optional<std::string> platformName;
+
+  /// If set, then the client stub should detach rather than killing the debugee
+  /// if it loses connection with lldb.
+  bool detachOnError = false;
+
+  /// Disable ASLR (Address Space Layout Randomization) when launching the
+  /// process.
+  bool disableASLR = false;
+
+  /// Do not set up for terminal I/O to go to running process.
+  bool disableSTDIO = false;
+
+  /// 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 &,
+              llvm::json::Path);
+
+/// Response to `launch` request. This is just an acknowledgement, so no body
+/// field is required.
+using LaunchResponseBody = VoidResponse;
+
 /// Arguments for `source` request.
 struct SourceArguments {
   /// Specifies the source content to load. Either `source.path` or
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index 4c6b36119c84f..39d06c0be32d9 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -319,9 +319,9 @@ bool SourceBreakpoint::BreakpointHitCallback(
           frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget);
       if (value.GetError().Fail())
         value = frame.EvaluateExpression(expr);
-      output +=
-          VariableDescription(value, bp->dap.enable_auto_variable_summaries)
-              .display_value;
+      output += VariableDescription(
+                    value, bp->dap.configuration.enableAutoVariableSummaries)
+                    .display_value;
     } else {
       output += messagePart.text;
     }



More information about the lldb-commits mailing list