[Lldb-commits] [lldb] [lldb-dap] Finish refactoring the request handlers (NFC) (PR #128553)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Mon Feb 24 11:11:52 PST 2025


https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/128553

Completes the work started in https://github.com/llvm/llvm-project/pull/128262. This PR removes the old way of register request handlers with callbacks. Builds on top of https://github.com/llvm/llvm-project/pull/128551. 

>From 0bf14ccd21f06ee3c53d64b5139760072d6405b1 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sun, 23 Feb 2025 21:07:55 -0600
Subject: [PATCH 1/5] [lldb-dap] Refactor stepping related request handlers
 (NFC)

Continuation of the work started in #128262.
---
 lldb/tools/lldb-dap/CMakeLists.txt            |   4 +
 .../lldb-dap/Handler/NextRequestHandler.cpp   |  79 ++++
 .../tools/lldb-dap/Handler/RequestHandler.cpp |   7 +
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  33 +-
 .../lldb-dap/Handler/StepInRequestHandler.cpp |  96 +++++
 .../Handler/StepInTargetsRequestHandler.cpp   | 149 ++++++++
 .../Handler/StepOutRequestHandler.cpp         |  68 ++++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 344 +-----------------
 8 files changed, 440 insertions(+), 340 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 73762af5c2fd7..61271e1a9f2a6 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -47,8 +47,12 @@ add_lldb_tool(lldb-dap
   Handler/ExceptionInfoRequestHandler.cpp
   Handler/InitializeRequestHandler.cpp
   Handler/LaunchRequestHandler.cpp
+  Handler/NextRequestHandler.cpp
   Handler/RequestHandler.cpp
   Handler/RestartRequestHandler.cpp
+  Handler/StepInRequestHandler.cpp
+  Handler/StepInTargetsRequestHandler.cpp
+  Handler/StepOutRequestHandler.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
new file mode 100644
index 0000000000000..695703fe301b3
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
@@ -0,0 +1,79 @@
+//===-- NextRequestHandler.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "NextRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Next request; value of command field is 'next'. The
+//                     request starts the debuggee to run again for one step.
+//                     The debug adapter first sends the NextResponse and then
+//                     a StoppedEvent (event type 'step') after the step has
+//                     completed.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "next" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/NextArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "NextArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'next' request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Execute 'next' for this thread."
+//     },
+//     "granularity": {
+//       "$ref": "#/definitions/SteppingGranularity",
+//       "description": "Stepping granularity. If no granularity is specified, a
+//                       granularity of `statement` is assumed."
+//     }
+//   },
+//   "required": [ "threadId" ]
+// },
+// "NextResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'next' request. This is just an
+//                     acknowledgement, so no body field is required."
+//   }]
+// }
+void NextRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
+  if (thread.IsValid()) {
+    // Remember the thread ID that caused the resume so we can set the
+    // "threadCausedFocus" boolean value in the "stopped" events.
+    dap.focus_tid = thread.GetThreadID();
+    if (HasInstructionGranularity(*arguments)) {
+      thread.StepInstruction(/*step_over=*/true);
+    } else {
+      thread.StepOver();
+    }
+  } else {
+    response["success"] = llvm::json::Value(false);
+  }
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index c09ddf55dd5e9..3b1c2b0dc7e31 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -225,4 +225,11 @@ void RequestHandler::PrintWelcomeMessage() {
 #endif
 }
 
+bool RequestHandler::HasInstructionGranularity(
+    const llvm::json::Object &request) {
+  if (std::optional<llvm::StringRef> value = request.getString("granularity"))
+    return value == "instruction";
+  return false;
+}
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 9bc8e60dbb858..2610a3d21ebc4 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -30,6 +30,7 @@ class RequestHandler {
 
   virtual void operator()(const llvm::json::Object &request) = 0;
 
+protected:
   /// Helpers used by multiple request handlers.
   /// FIXME: Move these into the DAP class?
   /// @{
@@ -48,9 +49,11 @@ class RequestHandler {
   // This way we can reuse the process launching logic for RestartRequest too.
   lldb::SBError LaunchProcess(const llvm::json::Object &request);
 
+  // Check if the step-granularity is `instruction`.
+  bool HasInstructionGranularity(const llvm::json::Object &request);
+
   /// @}
 
-protected:
   DAP &dap;
 };
 
@@ -131,6 +134,34 @@ class RestartRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class NextRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "next"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class StepInRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "stepIn"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class StepInTargetsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "stepInTargets"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class StepOutRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "stepOut"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp
new file mode 100644
index 0000000000000..f435436734538
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp
@@ -0,0 +1,96 @@
+//===-- StepInRequestHandler.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "StepInRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "StepIn request; value of command field is 'stepIn'. The
+//     request starts the debuggee to step into a function/method if possible.
+//     If it cannot step into a target, 'stepIn' behaves like 'next'. The debug
+//     adapter first sends the StepInResponse and then a StoppedEvent (event
+//     type 'step') after the step has completed. If there are multiple
+//     function/method calls (or other targets) on the source line, the optional
+//     argument 'targetId' can be used to control into which target the 'stepIn'
+//     should occur. The list of possible targets for a given source line can be
+//     retrieved via the 'stepInTargets' request.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "stepIn" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/StepInArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "StepInArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'stepIn' request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Execute 'stepIn' for this thread."
+//     },
+//     "targetId": {
+//       "type": "integer",
+//       "description": "Optional id of the target to step into."
+//     },
+//     "granularity": {
+//       "$ref": "#/definitions/SteppingGranularity",
+//       "description": "Stepping granularity. If no granularity is specified, a
+//                       granularity of `statement` is assumed."
+//     }
+//   },
+//   "required": [ "threadId" ]
+// },
+// "StepInResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'stepIn' request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// }
+void StepInRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+
+  std::string step_in_target;
+  uint64_t target_id = GetUnsigned(arguments, "targetId", 0);
+  auto it = dap.step_in_targets.find(target_id);
+  if (it != dap.step_in_targets.end())
+    step_in_target = it->second;
+
+  const bool single_thread = GetBoolean(arguments, "singleThread", false);
+  lldb::RunMode run_mode =
+      single_thread ? lldb::eOnlyThisThread : lldb::eOnlyDuringStepping;
+  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
+  if (thread.IsValid()) {
+    // Remember the thread ID that caused the resume so we can set the
+    // "threadCausedFocus" boolean value in the "stopped" events.
+    dap.focus_tid = thread.GetThreadID();
+    if (HasInstructionGranularity(*arguments)) {
+      thread.StepInstruction(/*step_over=*/false);
+    } else {
+      thread.StepInto(step_in_target.c_str(), run_mode);
+    }
+  } else {
+    response["success"] = llvm::json::Value(false);
+  }
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp
new file mode 100644
index 0000000000000..e771780711ae9
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp
@@ -0,0 +1,149 @@
+//===-- StepInTargetsRequestHandler.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBInstruction.h"
+
+namespace lldb_dap {
+
+// "StepInTargetsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "This request retrieves the possible step-in targets for
+//     the specified stack frame.\nThese targets can be used in the `stepIn`
+//     request.\nClients should only call this request if the corresponding
+//     capability `supportsStepInTargetsRequest` is true.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "stepInTargets" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/StepInTargetsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "StepInTargetsArguments": {
+//   "type": "object",
+//   "description": "Arguments for `stepInTargets` request.",
+//   "properties": {
+//     "frameId": {
+//       "type": "integer",
+//       "description": "The stack frame for which to retrieve the possible
+//       step-in targets."
+//     }
+//   },
+//   "required": [ "frameId" ]
+// },
+// "StepInTargetsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `stepInTargets` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "targets": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/StepInTarget"
+//             },
+//             "description": "The possible step-in targets of the specified
+//             source location."
+//           }
+//         },
+//         "required": [ "targets" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void StepInTargetsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+
+  dap.step_in_targets.clear();
+  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+  if (frame.IsValid()) {
+    lldb::SBAddress pc_addr = frame.GetPCAddress();
+    lldb::SBAddress line_end_addr =
+        pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true);
+    lldb::SBInstructionList insts = dap.target.ReadInstructions(
+        pc_addr, line_end_addr, /*flavor_string=*/nullptr);
+
+    if (!insts.IsValid()) {
+      response["success"] = false;
+      response["message"] = "Failed to get instructions for frame.";
+      dap.SendJSON(llvm::json::Value(std::move(response)));
+      return;
+    }
+
+    llvm::json::Array step_in_targets;
+    const auto num_insts = insts.GetSize();
+    for (size_t i = 0; i < num_insts; ++i) {
+      lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
+      if (!inst.IsValid())
+        break;
+
+      lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target);
+
+      // Note: currently only x86/x64 supports flow kind.
+      lldb::InstructionControlFlowKind flow_kind =
+          inst.GetControlFlowKind(dap.target);
+      if (flow_kind == lldb::eInstructionControlFlowKindCall) {
+        // Use call site instruction address as id which is easy to debug.
+        llvm::json::Object step_in_target;
+        step_in_target["id"] = inst_addr;
+
+        llvm::StringRef call_operand_name = inst.GetOperands(dap.target);
+        lldb::addr_t call_target_addr;
+        if (call_operand_name.getAsInteger(0, call_target_addr))
+          continue;
+
+        lldb::SBAddress call_target_load_addr =
+            dap.target.ResolveLoadAddress(call_target_addr);
+        if (!call_target_load_addr.IsValid())
+          continue;
+
+        // The existing ThreadPlanStepInRange only accept step in target
+        // function with debug info.
+        lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress(
+            call_target_load_addr, lldb::eSymbolContextFunction);
+
+        // The existing ThreadPlanStepInRange only accept step in target
+        // function with debug info.
+        std::string step_in_target_name;
+        if (sc.IsValid() && sc.GetFunction().IsValid())
+          step_in_target_name = sc.GetFunction().GetDisplayName();
+
+        // Skip call sites if we fail to resolve its symbol name.
+        if (step_in_target_name.empty())
+          continue;
+
+        dap.step_in_targets.try_emplace(inst_addr, step_in_target_name);
+        step_in_target.try_emplace("label", step_in_target_name);
+        step_in_targets.emplace_back(std::move(step_in_target));
+      }
+    }
+    llvm::json::Object body;
+    body.try_emplace("targets", std::move(step_in_targets));
+    response.try_emplace("body", std::move(body));
+  } else {
+    response["success"] = llvm::json::Value(false);
+    response["message"] = "Failed to get frame for input frameId.";
+  }
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp
new file mode 100644
index 0000000000000..d71547d579f1f
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp
@@ -0,0 +1,68 @@
+//===-- StepOutRequestHandler.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "StepOutRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "StepOut request; value of command field is 'stepOut'. The
+//     request starts the debuggee to run again for one step. The debug adapter
+//     first sends the StepOutResponse and then a StoppedEvent (event type
+//     'step') after the step has completed.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "stepOut" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/StepOutArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "StepOutArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'stepOut' request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Execute 'stepOut' for this thread."
+//     }
+//   },
+//   "required": [ "threadId" ]
+// },
+// "StepOutResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'stepOut' request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// }
+void StepOutRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
+  if (thread.IsValid()) {
+    // Remember the thread ID that caused the resume so we can set the
+    // "threadCausedFocus" boolean value in the "stopped" events.
+    dap.focus_tid = thread.GetThreadID();
+    thread.StepOut();
+  } else {
+    response["success"] = llvm::json::Value(false);
+  }
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 7935e88dba71a..22fff86066659 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -367,77 +367,6 @@ void request_modules(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// Check if the step-granularity is `instruction`
-static bool hasInstructionGranularity(const llvm::json::Object &requestArgs) {
-  if (std::optional<llvm::StringRef> value =
-          requestArgs.getString("granularity"))
-    return value == "instruction";
-  return false;
-}
-
-// "NextRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Next request; value of command field is 'next'. The
-//                     request starts the debuggee to run again for one step.
-//                     The debug adapter first sends the NextResponse and then
-//                     a StoppedEvent (event type 'step') after the step has
-//                     completed.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "next" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/NextArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "NextArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'next' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Execute 'next' for this thread."
-//     },
-//     "granularity": {
-//       "$ref": "#/definitions/SteppingGranularity",
-//       "description": "Stepping granularity. If no granularity is specified, a
-//                       granularity of `statement` is assumed."
-//     }
-//   },
-//   "required": [ "threadId" ]
-// },
-// "NextResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'next' request. This is just an
-//                     acknowledgement, so no body field is required."
-//   }]
-// }
-void request_next(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
-  if (thread.IsValid()) {
-    // Remember the thread ID that caused the resume so we can set the
-    // "threadCausedFocus" boolean value in the "stopped" events.
-    dap.focus_tid = thread.GetThreadID();
-    if (hasInstructionGranularity(*arguments)) {
-      thread.StepInstruction(/*step_over=*/true);
-    } else {
-      thread.StepOver();
-    }
-  } else {
-    response["success"] = llvm::json::Value(false);
-  }
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "PauseRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -1372,269 +1301,6 @@ void request_stackTrace(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// "StepInRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "StepIn request; value of command field is 'stepIn'. The
-//     request starts the debuggee to step into a function/method if possible.
-//     If it cannot step into a target, 'stepIn' behaves like 'next'. The debug
-//     adapter first sends the StepInResponse and then a StoppedEvent (event
-//     type 'step') after the step has completed. If there are multiple
-//     function/method calls (or other targets) on the source line, the optional
-//     argument 'targetId' can be used to control into which target the 'stepIn'
-//     should occur. The list of possible targets for a given source line can be
-//     retrieved via the 'stepInTargets' request.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "stepIn" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/StepInArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "StepInArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'stepIn' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Execute 'stepIn' for this thread."
-//     },
-//     "targetId": {
-//       "type": "integer",
-//       "description": "Optional id of the target to step into."
-//     },
-//     "granularity": {
-//       "$ref": "#/definitions/SteppingGranularity",
-//       "description": "Stepping granularity. If no granularity is specified, a
-//                       granularity of `statement` is assumed."
-//     }
-//   },
-//   "required": [ "threadId" ]
-// },
-// "StepInResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'stepIn' request. This is just an
-//     acknowledgement, so no body field is required."
-//   }]
-// }
-void request_stepIn(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-
-  std::string step_in_target;
-  uint64_t target_id = GetUnsigned(arguments, "targetId", 0);
-  auto it = dap.step_in_targets.find(target_id);
-  if (it != dap.step_in_targets.end())
-    step_in_target = it->second;
-
-  const bool single_thread = GetBoolean(arguments, "singleThread", false);
-  lldb::RunMode run_mode =
-      single_thread ? lldb::eOnlyThisThread : lldb::eOnlyDuringStepping;
-  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
-  if (thread.IsValid()) {
-    // Remember the thread ID that caused the resume so we can set the
-    // "threadCausedFocus" boolean value in the "stopped" events.
-    dap.focus_tid = thread.GetThreadID();
-    if (hasInstructionGranularity(*arguments)) {
-      thread.StepInstruction(/*step_over=*/false);
-    } else {
-      thread.StepInto(step_in_target.c_str(), run_mode);
-    }
-  } else {
-    response["success"] = llvm::json::Value(false);
-  }
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "StepInTargetsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "This request retrieves the possible step-in targets for
-//     the specified stack frame.\nThese targets can be used in the `stepIn`
-//     request.\nClients should only call this request if the corresponding
-//     capability `supportsStepInTargetsRequest` is true.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "stepInTargets" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/StepInTargetsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "StepInTargetsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `stepInTargets` request.",
-//   "properties": {
-//     "frameId": {
-//       "type": "integer",
-//       "description": "The stack frame for which to retrieve the possible
-//       step-in targets."
-//     }
-//   },
-//   "required": [ "frameId" ]
-// },
-// "StepInTargetsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `stepInTargets` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "targets": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/StepInTarget"
-//             },
-//             "description": "The possible step-in targets of the specified
-//             source location."
-//           }
-//         },
-//         "required": [ "targets" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_stepInTargets(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-
-  dap.step_in_targets.clear();
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  if (frame.IsValid()) {
-    lldb::SBAddress pc_addr = frame.GetPCAddress();
-    lldb::SBAddress line_end_addr =
-        pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true);
-    lldb::SBInstructionList insts = dap.target.ReadInstructions(
-        pc_addr, line_end_addr, /*flavor_string=*/nullptr);
-
-    if (!insts.IsValid()) {
-      response["success"] = false;
-      response["message"] = "Failed to get instructions for frame.";
-      dap.SendJSON(llvm::json::Value(std::move(response)));
-      return;
-    }
-
-    llvm::json::Array step_in_targets;
-    const auto num_insts = insts.GetSize();
-    for (size_t i = 0; i < num_insts; ++i) {
-      lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
-      if (!inst.IsValid())
-        break;
-
-      lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target);
-
-      // Note: currently only x86/x64 supports flow kind.
-      lldb::InstructionControlFlowKind flow_kind =
-          inst.GetControlFlowKind(dap.target);
-      if (flow_kind == lldb::eInstructionControlFlowKindCall) {
-        // Use call site instruction address as id which is easy to debug.
-        llvm::json::Object step_in_target;
-        step_in_target["id"] = inst_addr;
-
-        llvm::StringRef call_operand_name = inst.GetOperands(dap.target);
-        lldb::addr_t call_target_addr;
-        if (call_operand_name.getAsInteger(0, call_target_addr))
-          continue;
-
-        lldb::SBAddress call_target_load_addr =
-            dap.target.ResolveLoadAddress(call_target_addr);
-        if (!call_target_load_addr.IsValid())
-          continue;
-
-        // The existing ThreadPlanStepInRange only accept step in target
-        // function with debug info.
-        lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress(
-            call_target_load_addr, lldb::eSymbolContextFunction);
-
-        // The existing ThreadPlanStepInRange only accept step in target
-        // function with debug info.
-        std::string step_in_target_name;
-        if (sc.IsValid() && sc.GetFunction().IsValid())
-          step_in_target_name = sc.GetFunction().GetDisplayName();
-
-        // Skip call sites if we fail to resolve its symbol name.
-        if (step_in_target_name.empty())
-          continue;
-
-        dap.step_in_targets.try_emplace(inst_addr, step_in_target_name);
-        step_in_target.try_emplace("label", step_in_target_name);
-        step_in_targets.emplace_back(std::move(step_in_target));
-      }
-    }
-    llvm::json::Object body;
-    body.try_emplace("targets", std::move(step_in_targets));
-    response.try_emplace("body", std::move(body));
-  } else {
-    response["success"] = llvm::json::Value(false);
-    response["message"] = "Failed to get frame for input frameId.";
-  }
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "StepOutRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "StepOut request; value of command field is 'stepOut'. The
-//     request starts the debuggee to run again for one step. The debug adapter
-//     first sends the StepOutResponse and then a StoppedEvent (event type
-//     'step') after the step has completed.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "stepOut" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/StepOutArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "StepOutArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'stepOut' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Execute 'stepOut' for this thread."
-//     }
-//   },
-//   "required": [ "threadId" ]
-// },
-// "StepOutResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'stepOut' request. This is just an
-//     acknowledgement, so no body field is required."
-//   }]
-// }
-void request_stepOut(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
-  if (thread.IsValid()) {
-    // Remember the thread ID that caused the resume so we can set the
-    // "threadCausedFocus" boolean value in the "stopped" events.
-    dap.focus_tid = thread.GetThreadID();
-    thread.StepOut();
-  } else {
-    response["success"] = llvm::json::Value(false);
-  }
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "ThreadsRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -2777,16 +2443,19 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
   dap.RegisterRequest<CompletionsRequestHandler>();
-  dap.RegisterRequest<ContinueRequestHandler>();
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
+  dap.RegisterRequest<ContinueRequestHandler>();
   dap.RegisterRequest<DisconnectRequestHandler>();
   dap.RegisterRequest<EvaluateRequestHandler>();
   dap.RegisterRequest<ExceptionInfoRequestHandler>();
   dap.RegisterRequest<InitializeRequestHandler>();
   dap.RegisterRequest<LaunchRequestHandler>();
+  dap.RegisterRequest<NextRequestHandler>();
   dap.RegisterRequest<RestartRequestHandler>();
+  dap.RegisterRequest<StepInRequestHandler>();
+  dap.RegisterRequest<StepInTargetsRequestHandler>();
+  dap.RegisterRequest<StepOutRequestHandler>();
 
-  dap.RegisterRequestCallback("next", request_next);
   dap.RegisterRequestCallback("pause", request_pause);
   dap.RegisterRequestCallback("scopes", request_scopes);
   dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
@@ -2799,9 +2468,6 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequestCallback("setVariable", request_setVariable);
   dap.RegisterRequestCallback("source", request_source);
   dap.RegisterRequestCallback("stackTrace", request_stackTrace);
-  dap.RegisterRequestCallback("stepIn", request_stepIn);
-  dap.RegisterRequestCallback("stepInTargets", request_stepInTargets);
-  dap.RegisterRequestCallback("stepOut", request_stepOut);
   dap.RegisterRequestCallback("threads", request_threads);
   dap.RegisterRequestCallback("variables", request_variables);
   dap.RegisterRequestCallback("locations", request_locations);

>From d4c15e9c4c1f1822985c25a979f3e872cbaae57c Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 24 Feb 2025 11:28:17 -0600
Subject: [PATCH 2/5] [lldb-dap] Refactor custom & testing related request
 handlers (NFC)

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   3 +
 .../Handler/CompileUnitsRequestHandler.cpp    |  80 ++++++++++
 .../Handler/ModulesRequestHandler.cpp         |  58 ++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  27 ++++
 ...TestGetTargetBreakpointsRequestHandler.cpp |  31 ++++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 138 +-----------------
 6 files changed, 206 insertions(+), 131 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 61271e1a9f2a6..688a2e448f71d 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -39,6 +39,7 @@ add_lldb_tool(lldb-dap
 
   Handler/AttachRequestHandler.cpp
   Handler/BreakpointLocationsHandler.cpp
+  Handler/CompileUnitsRequestHandler.cpp
   Handler/CompletionsHandler.cpp
   Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
@@ -47,11 +48,13 @@ add_lldb_tool(lldb-dap
   Handler/ExceptionInfoRequestHandler.cpp
   Handler/InitializeRequestHandler.cpp
   Handler/LaunchRequestHandler.cpp
+  Handler/ModulesRequestHandler.cpp
   Handler/NextRequestHandler.cpp
   Handler/RequestHandler.cpp
   Handler/RestartRequestHandler.cpp
   Handler/StepInRequestHandler.cpp
   Handler/StepInTargetsRequestHandler.cpp
+  Handler/TestGetTargetBreakpointsRequestHandler.cpp
   Handler/StepOutRequestHandler.cpp
 
   LINK_LIBS
diff --git a/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp
new file mode 100644
index 0000000000000..c541d1cd039c8
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp
@@ -0,0 +1,80 @@
+//===-- CompileUnitsRequestHandler.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "compileUnitsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Compile Unit request; value of command field is
+//                     'compileUnits'.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "compileUnits" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/compileUnitRequestArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "compileUnitsRequestArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'compileUnits' request.",
+//   "properties": {
+//     "moduleId": {
+//       "type": "string",
+//       "description": "The ID of the module."
+//     }
+//   },
+//   "required": [ "moduleId" ]
+// },
+// "compileUnitsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'compileUnits' request.",
+//     "properties": {
+//       "body": {
+//         "description": "Response to 'compileUnits' request. Array of
+//                         paths of compile units."
+//       }
+//     }
+//   }]
+// }
+void CompileUnitsRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+  llvm::json::Array units;
+  const auto *arguments = request.getObject("arguments");
+  std::string module_id = std::string(GetString(arguments, "moduleId"));
+  int num_modules = dap.target.GetNumModules();
+  for (int i = 0; i < num_modules; i++) {
+    auto curr_module = dap.target.GetModuleAtIndex(i);
+    if (module_id == curr_module.GetUUIDString()) {
+      int num_units = curr_module.GetNumCompileUnits();
+      for (int j = 0; j < num_units; j++) {
+        auto curr_unit = curr_module.GetCompileUnitAtIndex(j);
+        units.emplace_back(CreateCompileUnit(curr_unit));
+      }
+      body.try_emplace("compileUnits", std::move(units));
+      break;
+    }
+  }
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
new file mode 100644
index 0000000000000..f72faa7be8963
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
@@ -0,0 +1,58 @@
+//===-- ModulesRequestHandler.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "modulesRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Modules request; value of command field is
+//                     'modules'.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "modules" ]
+//       },
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+// "modulesResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'modules' request.",
+//     "properties": {
+//       "body": {
+//         "description": "Response to 'modules' request. Array of
+//                         module objects."
+//       }
+//     }
+//   }]
+// }
+void ModulesRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+
+  llvm::json::Array modules;
+  for (size_t i = 0; i < dap.target.GetNumModules(); i++) {
+    lldb::SBModule module = dap.target.GetModuleAtIndex(i);
+    modules.emplace_back(CreateModule(dap.target, module));
+  }
+
+  llvm::json::Object body;
+  body.try_emplace("modules", std::move(modules));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 2610a3d21ebc4..874b600181f43 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -162,6 +162,33 @@ class StepOutRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class CompileUnitsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "compileUnits"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class ModulesRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "modules"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+/// A request used in testing to get the details on all breakpoints that are
+/// currently set in the target. This helps us to test "setBreakpoints" and
+/// "setFunctionBreakpoints" requests to verify we have the correct set of
+/// breakpoints currently set in LLDB.
+class TestGetTargetBreakpointsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() {
+    return "_testGetTargetBreakpoints";
+  }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..ad012d75f9059
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp
@@ -0,0 +1,31 @@
+//===-- TestGetTargetBreakpointsRequestHandler.cpp ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+void TestGetTargetBreakpointsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Array response_breakpoints;
+  for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
+    auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
+    AppendBreakpoint(&bp, response_breakpoints);
+  }
+  llvm::json::Object body;
+  body.try_emplace("breakpoints", std::move(response_breakpoints));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 22fff86066659..632629d56232c 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -261,112 +261,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "compileUnitsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Compile Unit request; value of command field is
-//                     'compileUnits'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "compileUnits" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/compileUnitRequestArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments" ]
-//   }]
-// },
-// "compileUnitsRequestArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'compileUnits' request.",
-//   "properties": {
-//     "moduleId": {
-//       "type": "string",
-//       "description": "The ID of the module."
-//     }
-//   },
-//   "required": [ "moduleId" ]
-// },
-// "compileUnitsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'compileUnits' request.",
-//     "properties": {
-//       "body": {
-//         "description": "Response to 'compileUnits' request. Array of
-//                         paths of compile units."
-//       }
-//     }
-//   }]
-// }
-void request_compileUnits(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  llvm::json::Array units;
-  const auto *arguments = request.getObject("arguments");
-  std::string module_id = std::string(GetString(arguments, "moduleId"));
-  int num_modules = dap.target.GetNumModules();
-  for (int i = 0; i < num_modules; i++) {
-    auto curr_module = dap.target.GetModuleAtIndex(i);
-    if (module_id == curr_module.GetUUIDString()) {
-      int num_units = curr_module.GetNumCompileUnits();
-      for (int j = 0; j < num_units; j++) {
-        auto curr_unit = curr_module.GetCompileUnitAtIndex(j);
-        units.emplace_back(CreateCompileUnit(curr_unit));
-      }
-      body.try_emplace("compileUnits", std::move(units));
-      break;
-    }
-  }
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "modulesRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Modules request; value of command field is
-//                     'modules'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "modules" ]
-//       },
-//     },
-//     "required": [ "command" ]
-//   }]
-// },
-// "modulesResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'modules' request.",
-//     "properties": {
-//       "body": {
-//         "description": "Response to 'modules' request. Array of
-//                         module objects."
-//       }
-//     }
-//   }]
-// }
-void request_modules(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-
-  llvm::json::Array modules;
-  for (size_t i = 0; i < dap.target.GetNumModules(); i++) {
-    lldb::SBModule module = dap.target.GetModuleAtIndex(i);
-    modules.emplace_back(CreateModule(dap.target, module));
-  }
-
-  llvm::json::Object body;
-  body.try_emplace("modules", std::move(modules));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "PauseRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -2187,25 +2081,6 @@ void request_readMemory(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// A request used in testing to get the details on all breakpoints that are
-// currently set in the target. This helps us to test "setBreakpoints" and
-// "setFunctionBreakpoints" requests to verify we have the correct set of
-// breakpoints currently set in LLDB.
-void request__testGetTargetBreakpoints(DAP &dap,
-                                       const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Array response_breakpoints;
-  for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
-    auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
-    AppendBreakpoint(&bp, response_breakpoints);
-  }
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "SetInstructionBreakpointsRequest": {
 //   "allOf": [
 //     {"$ref": "#/definitions/Request"},
@@ -2456,6 +2331,13 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<StepInTargetsRequestHandler>();
   dap.RegisterRequest<StepOutRequestHandler>();
 
+  // Custom requests
+  dap.RegisterRequest<CompileUnitsRequestHandler>();
+  dap.RegisterRequest<ModulesRequestHandler>();
+
+  // Testing requests
+  dap.RegisterRequest<TestGetTargetBreakpointsRequestHandler>();
+
   dap.RegisterRequestCallback("pause", request_pause);
   dap.RegisterRequestCallback("scopes", request_scopes);
   dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
@@ -2475,12 +2357,6 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequestCallback("readMemory", request_readMemory);
   dap.RegisterRequestCallback("setInstructionBreakpoints",
                               request_setInstructionBreakpoints);
-  // Custom requests
-  dap.RegisterRequestCallback("compileUnits", request_compileUnits);
-  dap.RegisterRequestCallback("modules", request_modules);
-  // Testing requests
-  dap.RegisterRequestCallback("_testGetTargetBreakpoints",
-                              request__testGetTargetBreakpoints);
 }
 
 } // anonymous namespace

>From e1f4611f708bc704c6a0a956ce0d8d1e6b9787dc Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 24 Feb 2025 11:51:28 -0600
Subject: [PATCH 3/5] [lldb-dap] Refactor breakpoint related request handlers
 (NFC)

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   7 +-
 .../DataBreakpointInfoRequestHandler.cpp      | 190 ++++
 .../tools/lldb-dap/Handler/RequestHandler.cpp |  55 ++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  47 +
 .../Handler/SetBreakpointsRequestHandler.cpp  | 182 ++++
 .../SetDataBreakpointsRequestHandler.cpp      | 114 +++
 .../SetExceptionBreakpointsRequestHandler.cpp |  93 ++
 .../SetFunctionBreakpointsRequestHandler.cpp  | 139 +++
 ...etInstructionBreakpointsRequestHandler.cpp | 249 +++++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 880 +-----------------
 10 files changed, 1081 insertions(+), 875 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 688a2e448f71d..c04b10861a4c5 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -43,6 +43,7 @@ add_lldb_tool(lldb-dap
   Handler/CompletionsHandler.cpp
   Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
+  Handler/DataBreakpointInfoRequestHandler.cpp
   Handler/DisconnectRequestHandler.cpp
   Handler/EvaluateRequestHandler.cpp
   Handler/ExceptionInfoRequestHandler.cpp
@@ -52,10 +53,14 @@ add_lldb_tool(lldb-dap
   Handler/NextRequestHandler.cpp
   Handler/RequestHandler.cpp
   Handler/RestartRequestHandler.cpp
+  Handler/SetBreakpointsRequestHandler.cpp
+  Handler/SetDataBreakpointsRequestHandler.cpp
+  Handler/SetExceptionBreakpointsRequestHandler.cpp
+  Handler/SetFunctionBreakpointsRequestHandler.cpp
+  Handler/SetInstructionBreakpointsRequestHandler.cpp Handler/StepOutRequestHandler.cpp
   Handler/StepInRequestHandler.cpp
   Handler/StepInTargetsRequestHandler.cpp
   Handler/TestGetTargetBreakpointsRequestHandler.cpp
-  Handler/StepOutRequestHandler.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
new file mode 100644
index 0000000000000..519a9c728e4b3
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -0,0 +1,190 @@
+//===-- DataBreakpointInfoRequestHandler.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBMemoryRegionInfo.h"
+#include "llvm/ADT/StringExtras.h"
+
+namespace lldb_dap {
+
+// "DataBreakpointInfoRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Obtains information on a possible data breakpoint that
+//     could be set on an expression or variable.\nClients should only call this
+//     request if the corresponding capability `supportsDataBreakpoints` is
+//     true.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "dataBreakpointInfo" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/DataBreakpointInfoArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "DataBreakpointInfoArguments": {
+//   "type": "object",
+//   "description": "Arguments for `dataBreakpointInfo` request.",
+//   "properties": {
+//     "variablesReference": {
+//       "type": "integer",
+//       "description": "Reference to the variable container if the data
+//       breakpoint is requested for a child of the container. The
+//       `variablesReference` must have been obtained in the current suspended
+//       state. See 'Lifetime of Object References' in the Overview section for
+//       details."
+//     },
+//     "name": {
+//       "type": "string",
+//       "description": "The name of the variable's child to obtain data
+//       breakpoint information for.\nIf `variablesReference` isn't specified,
+//       this can be an expression."
+//     },
+//     "frameId": {
+//       "type": "integer",
+//       "description": "When `name` is an expression, evaluate it in the scope
+//       of this stack frame. If not specified, the expression is evaluated in
+//       the global scope. When `variablesReference` is specified, this property
+//       has no effect."
+//     }
+//   },
+//   "required": [ "name" ]
+// },
+// "DataBreakpointInfoResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `dataBreakpointInfo` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "dataId": {
+//             "type": [ "string", "null" ],
+//             "description": "An identifier for the data on which a data
+//             breakpoint can be registered with the `setDataBreakpoints`
+//             request or null if no data breakpoint is available. If a
+//             `variablesReference` or `frameId` is passed, the `dataId` is
+//             valid in the current suspended state, otherwise it's valid
+//             indefinitely. See 'Lifetime of Object References' in the Overview
+//             section for details. Breakpoints set using the `dataId` in the
+//             `setDataBreakpoints` request may outlive the lifetime of the
+//             associated `dataId`."
+//           },
+//           "description": {
+//             "type": "string",
+//             "description": "UI string that describes on what data the
+//             breakpoint is set on or why a data breakpoint is not available."
+//           },
+//           "accessTypes": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/DataBreakpointAccessType"
+//             },
+//             "description": "Attribute lists the available access types for a
+//             potential data breakpoint. A UI client could surface this
+//             information."
+//           },
+//           "canPersist": {
+//             "type": "boolean",
+//             "description": "Attribute indicates that a potential data
+//             breakpoint could be persisted across sessions."
+//           }
+//         },
+//         "required": [ "dataId", "description" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void DataBreakpointInfoRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+  lldb::SBError error;
+  llvm::json::Array accessTypes{"read", "write", "readWrite"};
+  const auto *arguments = request.getObject("arguments");
+  const auto variablesReference =
+      GetUnsigned(arguments, "variablesReference", 0);
+  llvm::StringRef name = GetString(arguments, "name");
+  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+  lldb::SBValue variable = FindVariable(variablesReference, name);
+  std::string addr, size;
+
+  if (variable.IsValid()) {
+    lldb::addr_t load_addr = variable.GetLoadAddress();
+    size_t byte_size = variable.GetByteSize();
+    if (load_addr == LLDB_INVALID_ADDRESS) {
+      body.try_emplace("dataId", nullptr);
+      body.try_emplace("description",
+                       "does not exist in memory, its location is " +
+                           std::string(variable.GetLocation()));
+    } else if (byte_size == 0) {
+      body.try_emplace("dataId", nullptr);
+      body.try_emplace("description", "variable size is 0");
+    } else {
+      addr = llvm::utohexstr(load_addr);
+      size = llvm::utostr(byte_size);
+    }
+  } else if (variablesReference == 0 && frame.IsValid()) {
+    lldb::SBValue value = frame.EvaluateExpression(name.data());
+    if (value.GetError().Fail()) {
+      lldb::SBError error = value.GetError();
+      const char *error_cstr = error.GetCString();
+      body.try_emplace("dataId", nullptr);
+      body.try_emplace("description", error_cstr && error_cstr[0]
+                                          ? std::string(error_cstr)
+                                          : "evaluation failed");
+    } else {
+      uint64_t load_addr = value.GetValueAsUnsigned();
+      lldb::SBData data = value.GetPointeeData();
+      if (data.IsValid()) {
+        size = llvm::utostr(data.GetByteSize());
+        addr = llvm::utohexstr(load_addr);
+        lldb::SBMemoryRegionInfo region;
+        lldb::SBError err =
+            dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
+        // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
+        // request if SBProcess::GetMemoryRegionInfo returns error.
+        if (err.Success()) {
+          if (!(region.IsReadable() || region.IsWritable())) {
+            body.try_emplace("dataId", nullptr);
+            body.try_emplace("description",
+                             "memory region for address " + addr +
+                                 " has no read or write permissions");
+          }
+        }
+      } else {
+        body.try_emplace("dataId", nullptr);
+        body.try_emplace("description",
+                         "unable to get byte size for expression: " +
+                             name.str());
+      }
+    }
+  } else {
+    body.try_emplace("dataId", nullptr);
+    body.try_emplace("description", "variable not found: " + name.str());
+  }
+
+  if (!body.getObject("dataId")) {
+    body.try_emplace("dataId", addr + "/" + size);
+    body.try_emplace("accessTypes", std::move(accessTypes));
+    body.try_emplace("description",
+                     size + " bytes at " + addr + " " + name.str());
+  }
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 3b1c2b0dc7e31..5d7d00bec9bd1 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -232,4 +232,59 @@ bool RequestHandler::HasInstructionGranularity(
   return false;
 }
 
+lldb::SBValueList *
+RequestHandler::GetTopLevelScope(int64_t variablesReference) {
+  switch (variablesReference) {
+  case VARREF_LOCALS:
+    return &dap.variables.locals;
+  case VARREF_GLOBALS:
+    return &dap.variables.globals;
+  case VARREF_REGS:
+    return &dap.variables.registers;
+  default:
+    return nullptr;
+  }
+}
+
+lldb::SBValue RequestHandler::FindVariable(uint64_t variablesReference,
+                                           llvm::StringRef name) {
+  lldb::SBValue variable;
+  if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+    bool is_duplicated_variable_name = name.contains(" @");
+    // variablesReference is one of our scopes, not an actual variable it is
+    // asking for a variable in locals or globals or registers
+    int64_t end_idx = top_scope->GetSize();
+    // Searching backward so that we choose the variable in closest scope
+    // among variables of the same name.
+    for (int64_t i = end_idx - 1; i >= 0; --i) {
+      lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
+      std::string variable_name = CreateUniqueVariableNameForDisplay(
+          curr_variable, is_duplicated_variable_name);
+      if (variable_name == name) {
+        variable = curr_variable;
+        break;
+      }
+    }
+  } else {
+    // This is not under the globals or locals scope, so there are no duplicated
+    // names.
+
+    // We have a named item within an actual variable so we need to find it
+    // withing the container variable by name.
+    lldb::SBValue container = dap.variables.GetVariable(variablesReference);
+    variable = container.GetChildMemberWithName(name.data());
+    if (!variable.IsValid()) {
+      if (name.starts_with("[")) {
+        llvm::StringRef index_str(name.drop_front(1));
+        uint64_t index = 0;
+        if (!index_str.consumeInteger(0, index)) {
+          if (index_str == "]")
+            variable = container.GetChildAtIndex(index);
+        }
+      }
+    }
+  }
+  return variable;
+}
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 874b600181f43..a30e0dcc2bd04 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -52,6 +52,9 @@ class RequestHandler {
   // Check if the step-granularity is `instruction`.
   bool HasInstructionGranularity(const llvm::json::Object &request);
 
+  lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
+  lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
+
   /// @}
 
   DAP &dap;
@@ -162,6 +165,50 @@ class StepOutRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class SetBreakpointsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "setBreakpoints"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class SetExceptionBreakpointsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "setExceptionBreakpoints"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class SetFunctionBreakpointsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "setFunctionBreakpoints"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class DataBreakpointInfoRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "dataBreakpointInfo"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class SetDataBreakpointsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "setDataBreakpoints"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class SetInstructionBreakpointsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() {
+    return "setInstructionBreakpoints";
+  }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 class CompileUnitsRequestHandler : public RequestHandler {
 public:
   using RequestHandler::RequestHandler;
diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..6dbd24c130db6
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
@@ -0,0 +1,182 @@
+//===-- SetBreakpointsRequestHandler.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "SetBreakpointsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "SetBreakpoints request; value of command field is
+//     'setBreakpoints'. Sets multiple breakpoints for a single source and
+//     clears all previous breakpoints in that source. To clear all breakpoint
+//     for a source, specify an empty array. When a breakpoint is hit, a
+//     StoppedEvent (event type 'breakpoint') is generated.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "setBreakpoints" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/SetBreakpointsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "SetBreakpointsArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'setBreakpoints' request.",
+//   "properties": {
+//     "source": {
+//       "$ref": "#/definitions/Source",
+//       "description": "The source location of the breakpoints; either
+//       source.path or source.reference must be specified."
+//     },
+//     "breakpoints": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/SourceBreakpoint"
+//       },
+//       "description": "The code locations of the breakpoints."
+//     },
+//     "lines": {
+//       "type": "array",
+//       "items": {
+//         "type": "integer"
+//       },
+//       "description": "Deprecated: The code locations of the breakpoints."
+//     },
+//     "sourceModified": {
+//       "type": "boolean",
+//       "description": "A value of true indicates that the underlying source
+//       has been modified which results in new breakpoint locations."
+//     }
+//   },
+//   "required": [ "source" ]
+// },
+// "SetBreakpointsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'setBreakpoints' request. Returned is
+//     information about each breakpoint created by this request. This includes
+//     the actual code location and whether the breakpoint could be verified.
+//     The breakpoints returned are in the same order as the elements of the
+//     'breakpoints' (or the deprecated 'lines') in the
+//     SetBreakpointsArguments.", "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "breakpoints": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/Breakpoint"
+//             },
+//             "description": "Information about the breakpoints. The array
+//             elements are in the same order as the elements of the
+//             'breakpoints' (or the deprecated 'lines') in the
+//             SetBreakpointsArguments."
+//           }
+//         },
+//         "required": [ "breakpoints" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// },
+// "SourceBreakpoint": {
+//   "type": "object",
+//   "description": "Properties of a breakpoint or logpoint passed to the
+//   setBreakpoints request.", "properties": {
+//     "line": {
+//       "type": "integer",
+//       "description": "The source line of the breakpoint or logpoint."
+//     },
+//     "column": {
+//       "type": "integer",
+//       "description": "An optional source column of the breakpoint."
+//     },
+//     "condition": {
+//       "type": "string",
+//       "description": "An optional expression for conditional breakpoints."
+//     },
+//     "hitCondition": {
+//       "type": "string",
+//       "description": "An optional expression that controls how many hits of
+//       the breakpoint are ignored. The backend is expected to interpret the
+//       expression as needed."
+//     },
+//     "logMessage": {
+//       "type": "string",
+//       "description": "If this attribute exists and is non-empty, the backend
+//       must not 'break' (stop) but log the message instead. Expressions within
+//       {} are interpolated."
+//     }
+//   },
+//   "required": [ "line" ]
+// }
+void SetBreakpointsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  lldb::SBError error;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  const auto *source = arguments->getObject("source");
+  const auto path = GetString(source, "path");
+  const auto *breakpoints = arguments->getArray("breakpoints");
+  llvm::json::Array response_breakpoints;
+
+  // Decode the source breakpoint infos for this "setBreakpoints" request
+  SourceBreakpointMap request_bps;
+  // "breakpoints" may be unset, in which case we treat it the same as being set
+  // to an empty array.
+  if (breakpoints) {
+    for (const auto &bp : *breakpoints) {
+      const auto *bp_obj = bp.getAsObject();
+      if (bp_obj) {
+        SourceBreakpoint src_bp(dap, *bp_obj);
+        std::pair<uint32_t, uint32_t> bp_pos(src_bp.line, src_bp.column);
+        request_bps.try_emplace(bp_pos, src_bp);
+        const auto [iv, inserted] =
+            dap.source_breakpoints[path].try_emplace(bp_pos, src_bp);
+        // We check if this breakpoint already exists to update it
+        if (inserted)
+          iv->getSecond().SetBreakpoint(path.data());
+        else
+          iv->getSecond().UpdateBreakpoint(src_bp);
+        AppendBreakpoint(&iv->getSecond(), response_breakpoints, path,
+                         src_bp.line);
+      }
+    }
+  }
+
+  // Delete any breakpoints in this source file that aren't in the
+  // request_bps set. There is no call to remove breakpoints other than
+  // calling this function with a smaller or empty "breakpoints" list.
+  auto old_src_bp_pos = dap.source_breakpoints.find(path);
+  if (old_src_bp_pos != dap.source_breakpoints.end()) {
+    for (auto &old_bp : old_src_bp_pos->second) {
+      auto request_pos = request_bps.find(old_bp.first);
+      if (request_pos == request_bps.end()) {
+        // This breakpoint no longer exists in this source file, delete it
+        dap.target.BreakpointDelete(old_bp.second.bp.GetID());
+        old_src_bp_pos->second.erase(old_bp.first);
+      }
+    }
+  }
+
+  llvm::json::Object body;
+  body.try_emplace("breakpoints", std::move(response_breakpoints));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..9c2308f7a6bcd
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
@@ -0,0 +1,114 @@
+//===-- SetDataBreakpointsRequestHandler.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "Watchpoint.h"
+#include <set>
+
+namespace lldb_dap {
+
+// "SetDataBreakpointsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Replaces all existing data breakpoints with new data
+//     breakpoints.\nTo clear all data breakpoints, specify an empty
+//     array.\nWhen a data breakpoint is hit, a `stopped` event (with reason
+//     `data breakpoint`) is generated.\nClients should only call this request
+//     if the corresponding capability `supportsDataBreakpoints` is true.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "setDataBreakpoints" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/SetDataBreakpointsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "SetDataBreakpointsArguments": {
+//   "type": "object",
+//   "description": "Arguments for `setDataBreakpoints` request.",
+//   "properties": {
+//     "breakpoints": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/DataBreakpoint"
+//       },
+//       "description": "The contents of this array replaces all existing data
+//       breakpoints. An empty array clears all data breakpoints."
+//     }
+//   },
+//   "required": [ "breakpoints" ]
+// },
+// "SetDataBreakpointsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `setDataBreakpoints` request.\nReturned is
+//     information about each breakpoint created by this request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "breakpoints": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/Breakpoint"
+//             },
+//             "description": "Information about the data breakpoints. The array
+//             elements correspond to the elements of the input argument
+//             `breakpoints` array."
+//           }
+//         },
+//         "required": [ "breakpoints" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void SetDataBreakpointsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  lldb::SBError error;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  const auto *breakpoints = arguments->getArray("breakpoints");
+  llvm::json::Array response_breakpoints;
+  dap.target.DeleteAllWatchpoints();
+  std::vector<Watchpoint> watchpoints;
+  if (breakpoints) {
+    for (const auto &bp : *breakpoints) {
+      const auto *bp_obj = bp.getAsObject();
+      if (bp_obj)
+        watchpoints.emplace_back(dap, *bp_obj);
+    }
+  }
+  // If two watchpoints start at the same address, the latter overwrite the
+  // former. So, we only enable those at first-seen addresses when iterating
+  // backward.
+  std::set<lldb::addr_t> addresses;
+  for (auto iter = watchpoints.rbegin(); iter != watchpoints.rend(); ++iter) {
+    if (addresses.count(iter->addr) == 0) {
+      iter->SetWatchpoint();
+      addresses.insert(iter->addr);
+    }
+  }
+  for (auto wp : watchpoints)
+    AppendBreakpoint(&wp, response_breakpoints);
+
+  llvm::json::Object body;
+  body.try_emplace("breakpoints", std::move(response_breakpoints));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..d208525385094
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
@@ -0,0 +1,93 @@
+//===-- SetExceptionBreakpointsRequestHandler.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include <set>
+
+namespace lldb_dap {
+
+// "SetExceptionBreakpointsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "SetExceptionBreakpoints request; value of command field
+//     is 'setExceptionBreakpoints'. The request configures the debuggers
+//     response to thrown exceptions. If an exception is configured to break, a
+//     StoppedEvent is fired (event type 'exception').", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "setExceptionBreakpoints" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/SetExceptionBreakpointsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "SetExceptionBreakpointsArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'setExceptionBreakpoints' request.",
+//   "properties": {
+//     "filters": {
+//       "type": "array",
+//       "items": {
+//         "type": "string"
+//       },
+//       "description": "IDs of checked exception options. The set of IDs is
+//       returned via the 'exceptionBreakpointFilters' capability."
+//     },
+//     "exceptionOptions": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/ExceptionOptions"
+//       },
+//       "description": "Configuration options for selected exceptions."
+//     }
+//   },
+//   "required": [ "filters" ]
+// },
+// "SetExceptionBreakpointsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'setExceptionBreakpoints' request. This is
+//     just an acknowledgement, so no body field is required."
+//   }]
+// }
+void SetExceptionBreakpointsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  lldb::SBError error;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  const auto *filters = arguments->getArray("filters");
+  // Keep a list of any exception breakpoint filter names that weren't set
+  // so we can clear any exception breakpoints if needed.
+  std::set<std::string> unset_filters;
+  for (const auto &bp : *dap.exception_breakpoints)
+    unset_filters.insert(bp.filter);
+
+  for (const auto &value : *filters) {
+    const auto filter = GetAsString(value);
+    auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter));
+    if (exc_bp) {
+      exc_bp->SetBreakpoint();
+      unset_filters.erase(std::string(filter));
+    }
+  }
+  for (const auto &filter : unset_filters) {
+    auto *exc_bp = dap.GetExceptionBreakpoint(filter);
+    if (exc_bp)
+      exc_bp->ClearBreakpoint();
+  }
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..e55cfaef8c897
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
@@ -0,0 +1,139 @@
+//===-- SetFunctionBreakpointsRequestHandler.cpp --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "SetFunctionBreakpointsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "SetFunctionBreakpoints request; value of command field is
+//     'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
+//     all previous function breakpoints. To clear all function breakpoint,
+//     specify an empty array. When a function breakpoint is hit, a StoppedEvent
+//     (event type 'function breakpoint') is generated.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "setFunctionBreakpoints" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/SetFunctionBreakpointsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "SetFunctionBreakpointsArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'setFunctionBreakpoints' request.",
+//   "properties": {
+//     "breakpoints": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/FunctionBreakpoint"
+//       },
+//       "description": "The function names of the breakpoints."
+//     }
+//   },
+//   "required": [ "breakpoints" ]
+// },
+// "FunctionBreakpoint": {
+//   "type": "object",
+//   "description": "Properties of a breakpoint passed to the
+//   setFunctionBreakpoints request.", "properties": {
+//     "name": {
+//       "type": "string",
+//       "description": "The name of the function."
+//     },
+//     "condition": {
+//       "type": "string",
+//       "description": "An optional expression for conditional breakpoints."
+//     },
+//     "hitCondition": {
+//       "type": "string",
+//       "description": "An optional expression that controls how many hits of
+//       the breakpoint are ignored. The backend is expected to interpret the
+//       expression as needed."
+//     }
+//   },
+//   "required": [ "name" ]
+// },
+// "SetFunctionBreakpointsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'setFunctionBreakpoints' request. Returned is
+//     information about each breakpoint created by this request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "breakpoints": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/Breakpoint"
+//             },
+//             "description": "Information about the breakpoints. The array
+//             elements correspond to the elements of the 'breakpoints' array."
+//           }
+//         },
+//         "required": [ "breakpoints" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void SetFunctionBreakpointsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  lldb::SBError error;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  const auto *breakpoints = arguments->getArray("breakpoints");
+  llvm::json::Array response_breakpoints;
+
+  // Disable any function breakpoints that aren't in this request.
+  // There is no call to remove function breakpoints other than calling this
+  // function with a smaller or empty "breakpoints" list.
+  const auto name_iter = dap.function_breakpoints.keys();
+  llvm::DenseSet<llvm::StringRef> seen(name_iter.begin(), name_iter.end());
+  for (const auto &value : *breakpoints) {
+    const auto *bp_obj = value.getAsObject();
+    if (!bp_obj)
+      continue;
+    FunctionBreakpoint fn_bp(dap, *bp_obj);
+    const auto [it, inserted] =
+        dap.function_breakpoints.try_emplace(fn_bp.functionName, dap, *bp_obj);
+    if (inserted)
+      it->second.SetBreakpoint();
+    else
+      it->second.UpdateBreakpoint(fn_bp);
+
+    AppendBreakpoint(&it->second, response_breakpoints);
+    seen.erase(fn_bp.functionName);
+  }
+
+  // Remove any breakpoints that are no longer in our list
+  for (const auto &name : seen) {
+    auto fn_bp = dap.function_breakpoints.find(name);
+    if (fn_bp == dap.function_breakpoints.end())
+      continue;
+    dap.target.BreakpointDelete(fn_bp->second.bp.GetID());
+    dap.function_breakpoints.erase(name);
+  }
+
+  llvm::json::Object body;
+  body.try_emplace("breakpoints", std::move(response_breakpoints));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..636d9b814ab76
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
@@ -0,0 +1,249 @@
+//===-- SetInstructionBreakpointsRequestHandler.cpp -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "SetInstructionBreakpointsRequest": {
+//   "allOf": [
+//     {"$ref": "#/definitions/Request"},
+//     {
+//       "type": "object",
+//       "description" :
+//           "Replaces all existing instruction breakpoints. Typically, "
+//           "instruction breakpoints would be set from a disassembly window. "
+//           "\nTo clear all instruction breakpoints, specify an empty "
+//           "array.\nWhen an instruction breakpoint is hit, a `stopped` event "
+//           "(with reason `instruction breakpoint`) is generated.\nClients "
+//           "should only call this request if the corresponding capability "
+//           "`supportsInstructionBreakpoints` is true.",
+//       "properties": {
+//         "command": { "type": "string", "enum": ["setInstructionBreakpoints"]
+//         }, "arguments": {"$ref":
+//         "#/definitions/SetInstructionBreakpointsArguments"}
+//       },
+//       "required": [ "command", "arguments" ]
+//     }
+//   ]
+// },
+// "SetInstructionBreakpointsArguments": {
+//   "type": "object",
+//   "description": "Arguments for `setInstructionBreakpoints` request",
+//   "properties": {
+//     "breakpoints": {
+//       "type": "array",
+//       "items": {"$ref": "#/definitions/InstructionBreakpoint"},
+//       "description": "The instruction references of the breakpoints"
+//     }
+//   },
+//   "required": ["breakpoints"]
+// },
+// "SetInstructionBreakpointsResponse": {
+//   "allOf": [
+//     {"$ref": "#/definitions/Response"},
+//     {
+//       "type": "object",
+//       "description": "Response to `setInstructionBreakpoints` request",
+//       "properties": {
+//         "body": {
+//           "type": "object",
+//           "properties": {
+//             "breakpoints": {
+//               "type": "array",
+//               "items": {"$ref": "#/definitions/Breakpoint"},
+//               "description":
+//                   "Information about the breakpoints. The array elements
+//                   " "correspond to the elements of the `breakpoints`
+//                   array."
+//             }
+//           },
+//           "required": ["breakpoints"]
+//         }
+//       },
+//       "required": ["body"]
+//     }
+//   ]
+// },
+// "InstructionBreakpoint": {
+//   "type": "object",
+//   "description": "Properties of a breakpoint passed to the "
+//                   "`setInstructionBreakpoints` request",
+//   "properties": {
+//     "instructionReference": {
+//       "type": "string",
+//       "description" :
+//           "The instruction reference of the breakpoint.\nThis should be a "
+//           "memory or instruction pointer reference from an
+//           `EvaluateResponse`, "
+//           "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`."
+//     },
+//     "offset": {
+//       "type": "integer",
+//       "description": "The offset from the instruction reference in "
+//                       "bytes.\nThis can be negative."
+//     },
+//     "condition": {
+//       "type": "string",
+//       "description": "An expression for conditional breakpoints.\nIt is only
+//       "
+//                       "honored by a debug adapter if the corresponding "
+//                       "capability `supportsConditionalBreakpoints` is true."
+//     },
+//     "hitCondition": {
+//       "type": "string",
+//       "description": "An expression that controls how many hits of the "
+//                       "breakpoint are ignored.\nThe debug adapter is expected
+//                       " "to interpret the expression as needed.\nThe
+//                       attribute " "is only honored by a debug adapter if the
+//                       corresponding " "capability
+//                       `supportsHitConditionalBreakpoints` is true."
+//     },
+//     "mode": {
+//       "type": "string",
+//       "description": "The mode of this breakpoint. If defined, this must be
+//       "
+//                       "one of the `breakpointModes` the debug adapter "
+//                       "advertised in its `Capabilities`."
+//     }
+//   },
+//   "required": ["instructionReference"]
+// },
+// "Breakpoint": {
+//   "type": "object",
+//   "description" :
+//       "Information about a breakpoint created in `setBreakpoints`, "
+//       "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or "
+//       "`setDataBreakpoints` requests.",
+//   "properties": {
+//     "id": {
+//       "type": "integer",
+//       "description" :
+//           "The identifier for the breakpoint. It is needed if breakpoint
+//           " "events are used to update or remove breakpoints."
+//     },
+//     "verified": {
+//       "type": "boolean",
+//       "description": "If true, the breakpoint could be set (but not "
+//                       "necessarily at the desired location)."
+//     },
+//     "message": {
+//       "type": "string",
+//       "description": "A message about the state of the breakpoint.\nThis
+//       "
+//                       "is shown to the user and can be used to explain
+//                       why " "a breakpoint could not be verified."
+//     },
+//     "source": {
+//       "$ref": "#/definitions/Source",
+//       "description": "The source where the breakpoint is located."
+//     },
+//     "line": {
+//       "type": "integer",
+//       "description" :
+//           "The start line of the actual range covered by the breakpoint."
+//     },
+//     "column": {
+//       "type": "integer",
+//       "description" :
+//           "Start position of the source range covered by the breakpoint.
+//           " "It is measured in UTF-16 code units and the client
+//           capability "
+//           "`columnsStartAt1` determines whether it is 0- or 1-based."
+//     },
+//     "endLine": {
+//       "type": "integer",
+//       "description" :
+//           "The end line of the actual range covered by the breakpoint."
+//     },
+//     "endColumn": {
+//       "type": "integer",
+//       "description" :
+//           "End position of the source range covered by the breakpoint. It
+//           " "is measured in UTF-16 code units and the client capability "
+//           "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf
+//           " "no end line is given, then the end column is assumed to be
+//           in " "the start line."
+//     },
+//     "instructionReference": {
+//       "type": "string",
+//       "description": "A memory reference to where the breakpoint is
+//       set."
+//     },
+//     "offset": {
+//       "type": "integer",
+//       "description": "The offset from the instruction reference.\nThis "
+//                       "can be negative."
+//     },
+//     "reason": {
+//       "type": "string",
+//       "description" :
+//           "A machine-readable explanation of why a breakpoint may not be
+//           " "verified. If a breakpoint is verified or a specific reason
+//           is " "not known, the adapter should omit this property.
+//           Possible " "values include:\n\n- `pending`: Indicates a
+//           breakpoint might be " "verified in the future, but the adapter
+//           cannot verify it in the " "current state.\n - `failed`:
+//           Indicates a breakpoint was not " "able to be verified, and the
+//           adapter does not believe it can be " "verified without
+//           intervention.",
+//       "enum": [ "pending", "failed" ]
+//     }
+//   },
+//   "required": ["verified"]
+// },
+void SetInstructionBreakpointsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  llvm::json::Array response_breakpoints;
+  llvm::json::Object body;
+  FillResponse(request, response);
+
+  const auto *arguments = request.getObject("arguments");
+  const auto *breakpoints = arguments->getArray("breakpoints");
+
+  // Disable any instruction breakpoints that aren't in this request.
+  // There is no call to remove instruction breakpoints other than calling this
+  // function with a smaller or empty "breakpoints" list.
+  llvm::DenseSet<lldb::addr_t> seen;
+  for (const auto &addr : dap.instruction_breakpoints)
+    seen.insert(addr.first);
+
+  for (const auto &bp : *breakpoints) {
+    const auto *bp_obj = bp.getAsObject();
+    if (!bp_obj)
+      continue;
+    // Read instruction breakpoint request.
+    InstructionBreakpoint inst_bp(dap, *bp_obj);
+    const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace(
+        inst_bp.instructionAddressReference, dap, *bp_obj);
+    if (inserted)
+      iv->second.SetBreakpoint();
+    else
+      iv->second.UpdateBreakpoint(inst_bp);
+    AppendBreakpoint(&iv->second, response_breakpoints);
+    seen.erase(inst_bp.instructionAddressReference);
+  }
+
+  for (const auto &addr : seen) {
+    auto inst_bp = dap.instruction_breakpoints.find(addr);
+    if (inst_bp == dap.instruction_breakpoints.end())
+      continue;
+    dap.target.BreakpointDelete(inst_bp->second.bp.GetID());
+    dap.instruction_breakpoints.erase(addr);
+  }
+
+  body.try_emplace("breakpoints", std::move(response_breakpoints));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 632629d56232c..fd4615897841c 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -18,8 +18,6 @@
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBFile.h"
 #include "lldb/API/SBInstruction.h"
-#include "lldb/API/SBListener.h"
-#include "lldb/API/SBMemoryRegionInfo.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/Host/Config.h"
 #include "lldb/Host/MainLoop.h"
@@ -395,636 +393,6 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// "SetBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "SetBreakpoints request; value of command field is
-//     'setBreakpoints'. Sets multiple breakpoints for a single source and
-//     clears all previous breakpoints in that source. To clear all breakpoint
-//     for a source, specify an empty array. When a breakpoint is hit, a
-//     StoppedEvent (event type 'breakpoint') is generated.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setBreakpoints' request.",
-//   "properties": {
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The source location of the breakpoints; either
-//       source.path or source.reference must be specified."
-//     },
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/SourceBreakpoint"
-//       },
-//       "description": "The code locations of the breakpoints."
-//     },
-//     "lines": {
-//       "type": "array",
-//       "items": {
-//         "type": "integer"
-//       },
-//       "description": "Deprecated: The code locations of the breakpoints."
-//     },
-//     "sourceModified": {
-//       "type": "boolean",
-//       "description": "A value of true indicates that the underlying source
-//       has been modified which results in new breakpoint locations."
-//     }
-//   },
-//   "required": [ "source" ]
-// },
-// "SetBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setBreakpoints' request. Returned is
-//     information about each breakpoint created by this request. This includes
-//     the actual code location and whether the breakpoint could be verified.
-//     The breakpoints returned are in the same order as the elements of the
-//     'breakpoints' (or the deprecated 'lines') in the
-//     SetBreakpointsArguments.", "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Breakpoint"
-//             },
-//             "description": "Information about the breakpoints. The array
-//             elements are in the same order as the elements of the
-//             'breakpoints' (or the deprecated 'lines') in the
-//             SetBreakpointsArguments."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// },
-// "SourceBreakpoint": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint or logpoint passed to the
-//   setBreakpoints request.", "properties": {
-//     "line": {
-//       "type": "integer",
-//       "description": "The source line of the breakpoint or logpoint."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "An optional source column of the breakpoint."
-//     },
-//     "condition": {
-//       "type": "string",
-//       "description": "An optional expression for conditional breakpoints."
-//     },
-//     "hitCondition": {
-//       "type": "string",
-//       "description": "An optional expression that controls how many hits of
-//       the breakpoint are ignored. The backend is expected to interpret the
-//       expression as needed."
-//     },
-//     "logMessage": {
-//       "type": "string",
-//       "description": "If this attribute exists and is non-empty, the backend
-//       must not 'break' (stop) but log the message instead. Expressions within
-//       {} are interpolated."
-//     }
-//   },
-//   "required": [ "line" ]
-// }
-void request_setBreakpoints(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *source = arguments->getObject("source");
-  const auto path = GetString(source, "path");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-  llvm::json::Array response_breakpoints;
-
-  // Decode the source breakpoint infos for this "setBreakpoints" request
-  SourceBreakpointMap request_bps;
-  // "breakpoints" may be unset, in which case we treat it the same as being set
-  // to an empty array.
-  if (breakpoints) {
-    for (const auto &bp : *breakpoints) {
-      const auto *bp_obj = bp.getAsObject();
-      if (bp_obj) {
-        SourceBreakpoint src_bp(dap, *bp_obj);
-        std::pair<uint32_t, uint32_t> bp_pos(src_bp.line, src_bp.column);
-        request_bps.try_emplace(bp_pos, src_bp);
-        const auto [iv, inserted] =
-            dap.source_breakpoints[path].try_emplace(bp_pos, src_bp);
-        // We check if this breakpoint already exists to update it
-        if (inserted)
-          iv->getSecond().SetBreakpoint(path.data());
-        else
-          iv->getSecond().UpdateBreakpoint(src_bp);
-        AppendBreakpoint(&iv->getSecond(), response_breakpoints, path,
-                         src_bp.line);
-      }
-    }
-  }
-
-  // Delete any breakpoints in this source file that aren't in the
-  // request_bps set. There is no call to remove breakpoints other than
-  // calling this function with a smaller or empty "breakpoints" list.
-  auto old_src_bp_pos = dap.source_breakpoints.find(path);
-  if (old_src_bp_pos != dap.source_breakpoints.end()) {
-    for (auto &old_bp : old_src_bp_pos->second) {
-      auto request_pos = request_bps.find(old_bp.first);
-      if (request_pos == request_bps.end()) {
-        // This breakpoint no longer exists in this source file, delete it
-        dap.target.BreakpointDelete(old_bp.second.bp.GetID());
-        old_src_bp_pos->second.erase(old_bp.first);
-      }
-    }
-  }
-
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "SetExceptionBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "SetExceptionBreakpoints request; value of command field
-//     is 'setExceptionBreakpoints'. The request configures the debuggers
-//     response to thrown exceptions. If an exception is configured to break, a
-//     StoppedEvent is fired (event type 'exception').", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setExceptionBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetExceptionBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetExceptionBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setExceptionBreakpoints' request.",
-//   "properties": {
-//     "filters": {
-//       "type": "array",
-//       "items": {
-//         "type": "string"
-//       },
-//       "description": "IDs of checked exception options. The set of IDs is
-//       returned via the 'exceptionBreakpointFilters' capability."
-//     },
-//     "exceptionOptions": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/ExceptionOptions"
-//       },
-//       "description": "Configuration options for selected exceptions."
-//     }
-//   },
-//   "required": [ "filters" ]
-// },
-// "SetExceptionBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setExceptionBreakpoints' request. This is
-//     just an acknowledgement, so no body field is required."
-//   }]
-// }
-void request_setExceptionBreakpoints(DAP &dap,
-                                     const llvm::json::Object &request) {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *filters = arguments->getArray("filters");
-  // Keep a list of any exception breakpoint filter names that weren't set
-  // so we can clear any exception breakpoints if needed.
-  std::set<std::string> unset_filters;
-  for (const auto &bp : *dap.exception_breakpoints)
-    unset_filters.insert(bp.filter);
-
-  for (const auto &value : *filters) {
-    const auto filter = GetAsString(value);
-    auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter));
-    if (exc_bp) {
-      exc_bp->SetBreakpoint();
-      unset_filters.erase(std::string(filter));
-    }
-  }
-  for (const auto &filter : unset_filters) {
-    auto *exc_bp = dap.GetExceptionBreakpoint(filter);
-    if (exc_bp)
-      exc_bp->ClearBreakpoint();
-  }
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "SetFunctionBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "SetFunctionBreakpoints request; value of command field is
-//     'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
-//     all previous function breakpoints. To clear all function breakpoint,
-//     specify an empty array. When a function breakpoint is hit, a StoppedEvent
-//     (event type 'function breakpoint') is generated.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setFunctionBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetFunctionBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetFunctionBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setFunctionBreakpoints' request.",
-//   "properties": {
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/FunctionBreakpoint"
-//       },
-//       "description": "The function names of the breakpoints."
-//     }
-//   },
-//   "required": [ "breakpoints" ]
-// },
-// "FunctionBreakpoint": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint passed to the
-//   setFunctionBreakpoints request.", "properties": {
-//     "name": {
-//       "type": "string",
-//       "description": "The name of the function."
-//     },
-//     "condition": {
-//       "type": "string",
-//       "description": "An optional expression for conditional breakpoints."
-//     },
-//     "hitCondition": {
-//       "type": "string",
-//       "description": "An optional expression that controls how many hits of
-//       the breakpoint are ignored. The backend is expected to interpret the
-//       expression as needed."
-//     }
-//   },
-//   "required": [ "name" ]
-// },
-// "SetFunctionBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setFunctionBreakpoints' request. Returned is
-//     information about each breakpoint created by this request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Breakpoint"
-//             },
-//             "description": "Information about the breakpoints. The array
-//             elements correspond to the elements of the 'breakpoints' array."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_setFunctionBreakpoints(DAP &dap,
-                                    const llvm::json::Object &request) {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-  llvm::json::Array response_breakpoints;
-
-  // Disable any function breakpoints that aren't in this request.
-  // There is no call to remove function breakpoints other than calling this
-  // function with a smaller or empty "breakpoints" list.
-  const auto name_iter = dap.function_breakpoints.keys();
-  llvm::DenseSet<llvm::StringRef> seen(name_iter.begin(), name_iter.end());
-  for (const auto &value : *breakpoints) {
-    const auto *bp_obj = value.getAsObject();
-    if (!bp_obj)
-      continue;
-    FunctionBreakpoint fn_bp(dap, *bp_obj);
-    const auto [it, inserted] =
-        dap.function_breakpoints.try_emplace(fn_bp.functionName, dap, *bp_obj);
-    if (inserted)
-      it->second.SetBreakpoint();
-    else
-      it->second.UpdateBreakpoint(fn_bp);
-
-    AppendBreakpoint(&it->second, response_breakpoints);
-    seen.erase(fn_bp.functionName);
-  }
-
-  // Remove any breakpoints that are no longer in our list
-  for (const auto &name : seen) {
-    auto fn_bp = dap.function_breakpoints.find(name);
-    if (fn_bp == dap.function_breakpoints.end())
-      continue;
-    dap.target.BreakpointDelete(fn_bp->second.bp.GetID());
-    dap.function_breakpoints.erase(name);
-  }
-
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "DataBreakpointInfoRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Obtains information on a possible data breakpoint that
-//     could be set on an expression or variable.\nClients should only call this
-//     request if the corresponding capability `supportsDataBreakpoints` is
-//     true.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "dataBreakpointInfo" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/DataBreakpointInfoArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "DataBreakpointInfoArguments": {
-//   "type": "object",
-//   "description": "Arguments for `dataBreakpointInfo` request.",
-//   "properties": {
-//     "variablesReference": {
-//       "type": "integer",
-//       "description": "Reference to the variable container if the data
-//       breakpoint is requested for a child of the container. The
-//       `variablesReference` must have been obtained in the current suspended
-//       state. See 'Lifetime of Object References' in the Overview section for
-//       details."
-//     },
-//     "name": {
-//       "type": "string",
-//       "description": "The name of the variable's child to obtain data
-//       breakpoint information for.\nIf `variablesReference` isn't specified,
-//       this can be an expression."
-//     },
-//     "frameId": {
-//       "type": "integer",
-//       "description": "When `name` is an expression, evaluate it in the scope
-//       of this stack frame. If not specified, the expression is evaluated in
-//       the global scope. When `variablesReference` is specified, this property
-//       has no effect."
-//     }
-//   },
-//   "required": [ "name" ]
-// },
-// "DataBreakpointInfoResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `dataBreakpointInfo` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "dataId": {
-//             "type": [ "string", "null" ],
-//             "description": "An identifier for the data on which a data
-//             breakpoint can be registered with the `setDataBreakpoints`
-//             request or null if no data breakpoint is available. If a
-//             `variablesReference` or `frameId` is passed, the `dataId` is
-//             valid in the current suspended state, otherwise it's valid
-//             indefinitely. See 'Lifetime of Object References' in the Overview
-//             section for details. Breakpoints set using the `dataId` in the
-//             `setDataBreakpoints` request may outlive the lifetime of the
-//             associated `dataId`."
-//           },
-//           "description": {
-//             "type": "string",
-//             "description": "UI string that describes on what data the
-//             breakpoint is set on or why a data breakpoint is not available."
-//           },
-//           "accessTypes": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/DataBreakpointAccessType"
-//             },
-//             "description": "Attribute lists the available access types for a
-//             potential data breakpoint. A UI client could surface this
-//             information."
-//           },
-//           "canPersist": {
-//             "type": "boolean",
-//             "description": "Attribute indicates that a potential data
-//             breakpoint could be persisted across sessions."
-//           }
-//         },
-//         "required": [ "dataId", "description" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_dataBreakpointInfo(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  lldb::SBError error;
-  llvm::json::Array accessTypes{"read", "write", "readWrite"};
-  const auto *arguments = request.getObject("arguments");
-  const auto variablesReference =
-      GetUnsigned(arguments, "variablesReference", 0);
-  llvm::StringRef name = GetString(arguments, "name");
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  lldb::SBValue variable = FindVariable(dap, variablesReference, name);
-  std::string addr, size;
-
-  if (variable.IsValid()) {
-    lldb::addr_t load_addr = variable.GetLoadAddress();
-    size_t byte_size = variable.GetByteSize();
-    if (load_addr == LLDB_INVALID_ADDRESS) {
-      body.try_emplace("dataId", nullptr);
-      body.try_emplace("description",
-                       "does not exist in memory, its location is " +
-                           std::string(variable.GetLocation()));
-    } else if (byte_size == 0) {
-      body.try_emplace("dataId", nullptr);
-      body.try_emplace("description", "variable size is 0");
-    } else {
-      addr = llvm::utohexstr(load_addr);
-      size = llvm::utostr(byte_size);
-    }
-  } else if (variablesReference == 0 && frame.IsValid()) {
-    lldb::SBValue value = frame.EvaluateExpression(name.data());
-    if (value.GetError().Fail()) {
-      lldb::SBError error = value.GetError();
-      const char *error_cstr = error.GetCString();
-      body.try_emplace("dataId", nullptr);
-      body.try_emplace("description", error_cstr && error_cstr[0]
-                                          ? std::string(error_cstr)
-                                          : "evaluation failed");
-    } else {
-      uint64_t load_addr = value.GetValueAsUnsigned();
-      lldb::SBData data = value.GetPointeeData();
-      if (data.IsValid()) {
-        size = llvm::utostr(data.GetByteSize());
-        addr = llvm::utohexstr(load_addr);
-        lldb::SBMemoryRegionInfo region;
-        lldb::SBError err =
-            dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
-        // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
-        // request if SBProcess::GetMemoryRegionInfo returns error.
-        if (err.Success()) {
-          if (!(region.IsReadable() || region.IsWritable())) {
-            body.try_emplace("dataId", nullptr);
-            body.try_emplace("description",
-                             "memory region for address " + addr +
-                                 " has no read or write permissions");
-          }
-        }
-      } else {
-        body.try_emplace("dataId", nullptr);
-        body.try_emplace("description",
-                         "unable to get byte size for expression: " +
-                             name.str());
-      }
-    }
-  } else {
-    body.try_emplace("dataId", nullptr);
-    body.try_emplace("description", "variable not found: " + name.str());
-  }
-
-  if (!body.getObject("dataId")) {
-    body.try_emplace("dataId", addr + "/" + size);
-    body.try_emplace("accessTypes", std::move(accessTypes));
-    body.try_emplace("description",
-                     size + " bytes at " + addr + " " + name.str());
-  }
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "SetDataBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Replaces all existing data breakpoints with new data
-//     breakpoints.\nTo clear all data breakpoints, specify an empty
-//     array.\nWhen a data breakpoint is hit, a `stopped` event (with reason
-//     `data breakpoint`) is generated.\nClients should only call this request
-//     if the corresponding capability `supportsDataBreakpoints` is true.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setDataBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetDataBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetDataBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `setDataBreakpoints` request.",
-//   "properties": {
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/DataBreakpoint"
-//       },
-//       "description": "The contents of this array replaces all existing data
-//       breakpoints. An empty array clears all data breakpoints."
-//     }
-//   },
-//   "required": [ "breakpoints" ]
-// },
-// "SetDataBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `setDataBreakpoints` request.\nReturned is
-//     information about each breakpoint created by this request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Breakpoint"
-//             },
-//             "description": "Information about the data breakpoints. The array
-//             elements correspond to the elements of the input argument
-//             `breakpoints` array."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-  llvm::json::Array response_breakpoints;
-  dap.target.DeleteAllWatchpoints();
-  std::vector<Watchpoint> watchpoints;
-  if (breakpoints) {
-    for (const auto &bp : *breakpoints) {
-      const auto *bp_obj = bp.getAsObject();
-      if (bp_obj)
-        watchpoints.emplace_back(dap, *bp_obj);
-    }
-  }
-  // If two watchpoints start at the same address, the latter overwrite the
-  // former. So, we only enable those at first-seen addresses when iterating
-  // backward.
-  std::set<lldb::addr_t> addresses;
-  for (auto iter = watchpoints.rbegin(); iter != watchpoints.rend(); ++iter) {
-    if (addresses.count(iter->addr) == 0) {
-      iter->SetWatchpoint();
-      addresses.insert(iter->addr);
-    }
-  }
-  for (auto wp : watchpoints)
-    AppendBreakpoint(&wp, response_breakpoints);
-
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "SourceRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -2081,245 +1449,13 @@ void request_readMemory(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// "SetInstructionBreakpointsRequest": {
-//   "allOf": [
-//     {"$ref": "#/definitions/Request"},
-//     {
-//       "type": "object",
-//       "description" :
-//           "Replaces all existing instruction breakpoints. Typically, "
-//           "instruction breakpoints would be set from a disassembly window. "
-//           "\nTo clear all instruction breakpoints, specify an empty "
-//           "array.\nWhen an instruction breakpoint is hit, a `stopped` event "
-//           "(with reason `instruction breakpoint`) is generated.\nClients "
-//           "should only call this request if the corresponding capability "
-//           "`supportsInstructionBreakpoints` is true.",
-//       "properties": {
-//         "command": { "type": "string", "enum": ["setInstructionBreakpoints"]
-//         }, "arguments": {"$ref":
-//         "#/definitions/SetInstructionBreakpointsArguments"}
-//       },
-//       "required": [ "command", "arguments" ]
-//     }
-//   ]
-// },
-// "SetInstructionBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `setInstructionBreakpoints` request",
-//   "properties": {
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {"$ref": "#/definitions/InstructionBreakpoint"},
-//       "description": "The instruction references of the breakpoints"
-//     }
-//   },
-//   "required": ["breakpoints"]
-// },
-// "SetInstructionBreakpointsResponse": {
-//   "allOf": [
-//     {"$ref": "#/definitions/Response"},
-//     {
-//       "type": "object",
-//       "description": "Response to `setInstructionBreakpoints` request",
-//       "properties": {
-//         "body": {
-//           "type": "object",
-//           "properties": {
-//             "breakpoints": {
-//               "type": "array",
-//               "items": {"$ref": "#/definitions/Breakpoint"},
-//               "description":
-//                   "Information about the breakpoints. The array elements
-//                   " "correspond to the elements of the `breakpoints`
-//                   array."
-//             }
-//           },
-//           "required": ["breakpoints"]
-//         }
-//       },
-//       "required": ["body"]
-//     }
-//   ]
-// },
-// "InstructionBreakpoint": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint passed to the "
-//                   "`setInstructionBreakpoints` request",
-//   "properties": {
-//     "instructionReference": {
-//       "type": "string",
-//       "description" :
-//           "The instruction reference of the breakpoint.\nThis should be a "
-//           "memory or instruction pointer reference from an
-//           `EvaluateResponse`, "
-//           "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`."
-//     },
-//     "offset": {
-//       "type": "integer",
-//       "description": "The offset from the instruction reference in "
-//                       "bytes.\nThis can be negative."
-//     },
-//     "condition": {
-//       "type": "string",
-//       "description": "An expression for conditional breakpoints.\nIt is only
-//       "
-//                       "honored by a debug adapter if the corresponding "
-//                       "capability `supportsConditionalBreakpoints` is true."
-//     },
-//     "hitCondition": {
-//       "type": "string",
-//       "description": "An expression that controls how many hits of the "
-//                       "breakpoint are ignored.\nThe debug adapter is expected
-//                       " "to interpret the expression as needed.\nThe
-//                       attribute " "is only honored by a debug adapter if the
-//                       corresponding " "capability
-//                       `supportsHitConditionalBreakpoints` is true."
-//     },
-//     "mode": {
-//       "type": "string",
-//       "description": "The mode of this breakpoint. If defined, this must be
-//       "
-//                       "one of the `breakpointModes` the debug adapter "
-//                       "advertised in its `Capabilities`."
-//     }
-//   },
-//   "required": ["instructionReference"]
-// },
-// "Breakpoint": {
-//   "type": "object",
-//   "description" :
-//       "Information about a breakpoint created in `setBreakpoints`, "
-//       "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or "
-//       "`setDataBreakpoints` requests.",
-//   "properties": {
-//     "id": {
-//       "type": "integer",
-//       "description" :
-//           "The identifier for the breakpoint. It is needed if breakpoint
-//           " "events are used to update or remove breakpoints."
-//     },
-//     "verified": {
-//       "type": "boolean",
-//       "description": "If true, the breakpoint could be set (but not "
-//                       "necessarily at the desired location)."
-//     },
-//     "message": {
-//       "type": "string",
-//       "description": "A message about the state of the breakpoint.\nThis
-//       "
-//                       "is shown to the user and can be used to explain
-//                       why " "a breakpoint could not be verified."
-//     },
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The source where the breakpoint is located."
-//     },
-//     "line": {
-//       "type": "integer",
-//       "description" :
-//           "The start line of the actual range covered by the breakpoint."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description" :
-//           "Start position of the source range covered by the breakpoint.
-//           " "It is measured in UTF-16 code units and the client
-//           capability "
-//           "`columnsStartAt1` determines whether it is 0- or 1-based."
-//     },
-//     "endLine": {
-//       "type": "integer",
-//       "description" :
-//           "The end line of the actual range covered by the breakpoint."
-//     },
-//     "endColumn": {
-//       "type": "integer",
-//       "description" :
-//           "End position of the source range covered by the breakpoint. It
-//           " "is measured in UTF-16 code units and the client capability "
-//           "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf
-//           " "no end line is given, then the end column is assumed to be
-//           in " "the start line."
-//     },
-//     "instructionReference": {
-//       "type": "string",
-//       "description": "A memory reference to where the breakpoint is
-//       set."
-//     },
-//     "offset": {
-//       "type": "integer",
-//       "description": "The offset from the instruction reference.\nThis "
-//                       "can be negative."
-//     },
-//     "reason": {
-//       "type": "string",
-//       "description" :
-//           "A machine-readable explanation of why a breakpoint may not be
-//           " "verified. If a breakpoint is verified or a specific reason
-//           is " "not known, the adapter should omit this property.
-//           Possible " "values include:\n\n- `pending`: Indicates a
-//           breakpoint might be " "verified in the future, but the adapter
-//           cannot verify it in the " "current state.\n - `failed`:
-//           Indicates a breakpoint was not " "able to be verified, and the
-//           adapter does not believe it can be " "verified without
-//           intervention.",
-//       "enum": [ "pending", "failed" ]
-//     }
-//   },
-//   "required": ["verified"]
-// },
-void request_setInstructionBreakpoints(DAP &dap,
-                                       const llvm::json::Object &request) {
-  llvm::json::Object response;
-  llvm::json::Array response_breakpoints;
-  llvm::json::Object body;
-  FillResponse(request, response);
-
-  const auto *arguments = request.getObject("arguments");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-
-  // Disable any instruction breakpoints that aren't in this request.
-  // There is no call to remove instruction breakpoints other than calling this
-  // function with a smaller or empty "breakpoints" list.
-  llvm::DenseSet<lldb::addr_t> seen;
-  for (const auto &addr : dap.instruction_breakpoints)
-    seen.insert(addr.first);
-
-  for (const auto &bp : *breakpoints) {
-    const auto *bp_obj = bp.getAsObject();
-    if (!bp_obj)
-      continue;
-    // Read instruction breakpoint request.
-    InstructionBreakpoint inst_bp(dap, *bp_obj);
-    const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace(
-        inst_bp.instructionAddressReference, dap, *bp_obj);
-    if (inserted)
-      iv->second.SetBreakpoint();
-    else
-      iv->second.UpdateBreakpoint(inst_bp);
-    AppendBreakpoint(&iv->second, response_breakpoints);
-    seen.erase(inst_bp.instructionAddressReference);
-  }
-
-  for (const auto &addr : seen) {
-    auto inst_bp = dap.instruction_breakpoints.find(addr);
-    if (inst_bp == dap.instruction_breakpoints.end())
-      continue;
-    dap.target.BreakpointDelete(inst_bp->second.bp.GetID());
-    dap.instruction_breakpoints.erase(addr);
-  }
-
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
   dap.RegisterRequest<CompletionsRequestHandler>();
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
   dap.RegisterRequest<ContinueRequestHandler>();
+  dap.RegisterRequest<DataBreakpointInfoRequestHandler>();
   dap.RegisterRequest<DisconnectRequestHandler>();
   dap.RegisterRequest<EvaluateRequestHandler>();
   dap.RegisterRequest<ExceptionInfoRequestHandler>();
@@ -2327,6 +1463,11 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<LaunchRequestHandler>();
   dap.RegisterRequest<NextRequestHandler>();
   dap.RegisterRequest<RestartRequestHandler>();
+  dap.RegisterRequest<SetBreakpointsRequestHandler>();
+  dap.RegisterRequest<SetDataBreakpointsRequestHandler>();
+  dap.RegisterRequest<SetExceptionBreakpointsRequestHandler>();
+  dap.RegisterRequest<SetFunctionBreakpointsRequestHandler>();
+  dap.RegisterRequest<SetInstructionBreakpointsRequestHandler>();
   dap.RegisterRequest<StepInRequestHandler>();
   dap.RegisterRequest<StepInTargetsRequestHandler>();
   dap.RegisterRequest<StepOutRequestHandler>();
@@ -2340,13 +1481,6 @@ void RegisterRequestCallbacks(DAP &dap) {
 
   dap.RegisterRequestCallback("pause", request_pause);
   dap.RegisterRequestCallback("scopes", request_scopes);
-  dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
-  dap.RegisterRequestCallback("setExceptionBreakpoints",
-                              request_setExceptionBreakpoints);
-  dap.RegisterRequestCallback("setFunctionBreakpoints",
-                              request_setFunctionBreakpoints);
-  dap.RegisterRequestCallback("dataBreakpointInfo", request_dataBreakpointInfo);
-  dap.RegisterRequestCallback("setDataBreakpoints", request_setDataBreakpoints);
   dap.RegisterRequestCallback("setVariable", request_setVariable);
   dap.RegisterRequestCallback("source", request_source);
   dap.RegisterRequestCallback("stackTrace", request_stackTrace);
@@ -2355,8 +1489,6 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequestCallback("locations", request_locations);
   dap.RegisterRequestCallback("disassemble", request_disassemble);
   dap.RegisterRequestCallback("readMemory", request_readMemory);
-  dap.RegisterRequestCallback("setInstructionBreakpoints",
-                              request_setInstructionBreakpoints);
 }
 
 } // anonymous namespace

>From 0f33f54a4001b783eb4baa8a55e2c8687611cb67 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 24 Feb 2025 12:43:34 -0600
Subject: [PATCH 4/5] [lldb-dap] Refactor remaining request handlers (NFC)

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   13 +-
 .../Handler/DisassembleRequestHandler.cpp     |  222 +++
 .../Handler/LocationsRequestHandler.cpp       |  160 ++
 .../lldb-dap/Handler/PauseRequestHandler.cpp  |   60 +
 .../Handler/ReadMemoryRequestHandler.cpp      |  139 ++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   70 +
 .../lldb-dap/Handler/ScopesRequestHandler.cpp |  106 ++
 .../Handler/SetVariableRequestHandler.cpp     |  177 +++
 .../lldb-dap/Handler/SourceRequestHandler.cpp |   82 +
 .../Handler/StackTraceRequestHandler.cpp      |  197 +++
 .../Handler/ThreadsRequestHandler.cpp         |   71 +
 .../Handler/VariablesRequestHandler.cpp       |  217 +++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 1345 +----------------
 13 files changed, 1525 insertions(+), 1334 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/LocationsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/PauseRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/ReadMemoryRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/ThreadsRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index c04b10861a4c5..804dd8e4cc2a0 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -44,23 +44,34 @@ add_lldb_tool(lldb-dap
   Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
   Handler/DataBreakpointInfoRequestHandler.cpp
+  Handler/DisassembleRequestHandler.cpp
   Handler/DisconnectRequestHandler.cpp
   Handler/EvaluateRequestHandler.cpp
   Handler/ExceptionInfoRequestHandler.cpp
   Handler/InitializeRequestHandler.cpp
   Handler/LaunchRequestHandler.cpp
+  Handler/LocationsRequestHandler.cpp
   Handler/ModulesRequestHandler.cpp
   Handler/NextRequestHandler.cpp
+  Handler/PauseRequestHandler.cpp
+  Handler/ReadMemoryRequestHandler.cpp
   Handler/RequestHandler.cpp
   Handler/RestartRequestHandler.cpp
+  Handler/ScopesRequestHandler.cpp
   Handler/SetBreakpointsRequestHandler.cpp
   Handler/SetDataBreakpointsRequestHandler.cpp
   Handler/SetExceptionBreakpointsRequestHandler.cpp
   Handler/SetFunctionBreakpointsRequestHandler.cpp
-  Handler/SetInstructionBreakpointsRequestHandler.cpp Handler/StepOutRequestHandler.cpp
+  Handler/SetInstructionBreakpointsRequestHandler.cpp
+  Handler/SetVariableRequestHandler.cpp
+  Handler/SourceRequestHandler.cpp
+  Handler/StackTraceRequestHandler.cpp
   Handler/StepInRequestHandler.cpp
   Handler/StepInTargetsRequestHandler.cpp
+  Handler/StepOutRequestHandler.cpp
   Handler/TestGetTargetBreakpointsRequestHandler.cpp
+  Handler/ThreadsRequestHandler.cpp
+  Handler/VariablesRequestHandler.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp
new file mode 100644
index 0000000000000..6d25ef0fc5d74
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp
@@ -0,0 +1,222 @@
+//===-- DisassembleRequestHandler.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBInstruction.h"
+#include "llvm/ADT/StringExtras.h"
+
+namespace lldb_dap {
+
+// "DisassembleRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Disassembles code stored at the provided
+//     location.\nClients should only call this request if the corresponding
+//     capability `supportsDisassembleRequest` is true.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "disassemble" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/DisassembleArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "DisassembleArguments": {
+//   "type": "object",
+//   "description": "Arguments for `disassemble` request.",
+//   "properties": {
+//     "memoryReference": {
+//       "type": "string",
+//       "description": "Memory reference to the base location containing the
+//       instructions to disassemble."
+//     },
+//     "offset": {
+//       "type": "integer",
+//       "description": "Offset (in bytes) to be applied to the reference
+//       location before disassembling. Can be negative."
+//     },
+//     "instructionOffset": {
+//       "type": "integer",
+//       "description": "Offset (in instructions) to be applied after the byte
+//       offset (if any) before disassembling. Can be negative."
+//     },
+//     "instructionCount": {
+//       "type": "integer",
+//       "description": "Number of instructions to disassemble starting at the
+//       specified location and offset.\nAn adapter must return exactly this
+//       number of instructions - any unavailable instructions should be
+//       replaced with an implementation-defined 'invalid instruction' value."
+//     },
+//     "resolveSymbols": {
+//       "type": "boolean",
+//       "description": "If true, the adapter should attempt to resolve memory
+//       addresses and other values to symbolic names."
+//     }
+//   },
+//   "required": [ "memoryReference", "instructionCount" ]
+// },
+// "DisassembleResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `disassemble` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "instructions": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/DisassembledInstruction"
+//             },
+//             "description": "The list of disassembled instructions."
+//           }
+//         },
+//         "required": [ "instructions" ]
+//       }
+//     }
+//   }]
+// }
+void DisassembleRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  auto *arguments = request.getObject("arguments");
+
+  llvm::StringRef memoryReference = GetString(arguments, "memoryReference");
+  auto addr_opt = DecodeMemoryReference(memoryReference);
+  if (!addr_opt.has_value()) {
+    response["success"] = false;
+    response["message"] =
+        "Malformed memory reference: " + memoryReference.str();
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+  lldb::addr_t addr_ptr = *addr_opt;
+
+  addr_ptr += GetSigned(arguments, "instructionOffset", 0);
+  lldb::SBAddress addr(addr_ptr, dap.target);
+  if (!addr.IsValid()) {
+    response["success"] = false;
+    response["message"] = "Memory reference not found in the current binary.";
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  const auto inst_count = GetUnsigned(arguments, "instructionCount", 0);
+  lldb::SBInstructionList insts = dap.target.ReadInstructions(addr, inst_count);
+
+  if (!insts.IsValid()) {
+    response["success"] = false;
+    response["message"] = "Failed to find instructions for memory address.";
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  const bool resolveSymbols = GetBoolean(arguments, "resolveSymbols", false);
+  llvm::json::Array instructions;
+  const auto num_insts = insts.GetSize();
+  for (size_t i = 0; i < num_insts; ++i) {
+    lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
+    auto addr = inst.GetAddress();
+    const auto inst_addr = addr.GetLoadAddress(dap.target);
+    const char *m = inst.GetMnemonic(dap.target);
+    const char *o = inst.GetOperands(dap.target);
+    const char *c = inst.GetComment(dap.target);
+    auto d = inst.GetData(dap.target);
+
+    std::string bytes;
+    llvm::raw_string_ostream sb(bytes);
+    for (unsigned i = 0; i < inst.GetByteSize(); i++) {
+      lldb::SBError error;
+      uint8_t b = d.GetUnsignedInt8(error, i);
+      if (error.Success()) {
+        sb << llvm::format("%2.2x ", b);
+      }
+    }
+
+    llvm::json::Object disassembled_inst{
+        {"address", "0x" + llvm::utohexstr(inst_addr)},
+        {"instructionBytes",
+         bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""},
+    };
+
+    std::string instruction;
+    llvm::raw_string_ostream si(instruction);
+
+    lldb::SBSymbol symbol = addr.GetSymbol();
+    // Only add the symbol on the first line of the function.
+    if (symbol.IsValid() && symbol.GetStartAddress() == addr) {
+      // If we have a valid symbol, append it as a label prefix for the first
+      // instruction. This is so you can see the start of a function/callsite
+      // in the assembly, at the moment VS Code (1.80) does not visualize the
+      // symbol associated with the assembly instruction.
+      si << (symbol.GetMangledName() != nullptr ? symbol.GetMangledName()
+                                                : symbol.GetName())
+         << ": ";
+
+      if (resolveSymbols) {
+        disassembled_inst.try_emplace("symbol", symbol.GetDisplayName());
+      }
+    }
+
+    si << llvm::formatv("{0,7} {1,12}", m, o);
+    if (c && c[0]) {
+      si << " ; " << c;
+    }
+
+    disassembled_inst.try_emplace("instruction", instruction);
+
+    auto line_entry = addr.GetLineEntry();
+    // If the line number is 0 then the entry represents a compiler generated
+    // location.
+    if (line_entry.GetStartAddress() == addr && line_entry.IsValid() &&
+        line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) {
+      auto source = CreateSource(line_entry);
+      disassembled_inst.try_emplace("location", source);
+
+      const auto line = line_entry.GetLine();
+      if (line && line != LLDB_INVALID_LINE_NUMBER) {
+        disassembled_inst.try_emplace("line", line);
+      }
+      const auto column = line_entry.GetColumn();
+      if (column && column != LLDB_INVALID_COLUMN_NUMBER) {
+        disassembled_inst.try_emplace("column", column);
+      }
+
+      auto end_line_entry = line_entry.GetEndAddress().GetLineEntry();
+      if (end_line_entry.IsValid() &&
+          end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) {
+        const auto end_line = end_line_entry.GetLine();
+        if (end_line && end_line != LLDB_INVALID_LINE_NUMBER &&
+            end_line != line) {
+          disassembled_inst.try_emplace("endLine", end_line);
+
+          const auto end_column = end_line_entry.GetColumn();
+          if (end_column && end_column != LLDB_INVALID_COLUMN_NUMBER &&
+              end_column != column) {
+            disassembled_inst.try_emplace("endColumn", end_column - 1);
+          }
+        }
+      }
+    }
+
+    instructions.emplace_back(std::move(disassembled_inst));
+  }
+
+  llvm::json::Object body;
+  body.try_emplace("instructions", std::move(instructions));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/LocationsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LocationsRequestHandler.cpp
new file mode 100644
index 0000000000000..81ec42689b13f
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/LocationsRequestHandler.cpp
@@ -0,0 +1,160 @@
+//===-- LocationsRequestHandler.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBDeclaration.h"
+
+namespace lldb_dap {
+
+// "LocationsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Looks up information about a location reference
+//                     previously returned by the debug adapter.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "locations" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/LocationsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "LocationsArguments": {
+//   "type": "object",
+//   "description": "Arguments for `locations` request.",
+//   "properties": {
+//     "locationReference": {
+//       "type": "integer",
+//       "description": "Location reference to resolve."
+//     }
+//   },
+//   "required": [ "locationReference" ]
+// },
+// "LocationsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `locations` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "source": {
+//             "$ref": "#/definitions/Source",
+//             "description": "The source containing the location; either
+//                             `source.path` or `source.sourceReference` must be
+//                             specified."
+//           },
+//           "line": {
+//             "type": "integer",
+//             "description": "The line number of the location. The client
+//                             capability `linesStartAt1` determines whether it
+//                             is 0- or 1-based."
+//           },
+//           "column": {
+//             "type": "integer",
+//             "description": "Position of the location within the `line`. It is
+//                             measured in UTF-16 code units and the client
+//                             capability `columnsStartAt1` determines whether
+//                             it is 0- or 1-based. If no column is given, the
+//                             first position in the start line is assumed."
+//           },
+//           "endLine": {
+//             "type": "integer",
+//             "description": "End line of the location, present if the location
+//                             refers to a range.  The client capability
+//                             `linesStartAt1` determines whether it is 0- or
+//                             1-based."
+//           },
+//           "endColumn": {
+//             "type": "integer",
+//             "description": "End position of the location within `endLine`,
+//                             present if the location refers to a range. It is
+//                             measured in UTF-16 code units and the client
+//                             capability `columnsStartAt1` determines whether
+//                             it is 0- or 1-based."
+//           }
+//         },
+//         "required": [ "source", "line" ]
+//       }
+//     }
+//   }]
+// },
+void LocationsRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  auto *arguments = request.getObject("arguments");
+
+  uint64_t location_id = GetUnsigned(arguments, "locationReference", 0);
+  // We use the lowest bit to distinguish between value location and declaration
+  // location
+  auto [var_ref, is_value_location] = UnpackLocation(location_id);
+  lldb::SBValue variable = dap.variables.GetVariable(var_ref);
+  if (!variable.IsValid()) {
+    response["success"] = false;
+    response["message"] = "Invalid variable reference";
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  llvm::json::Object body;
+  if (is_value_location) {
+    // Get the value location
+    if (!variable.GetType().IsPointerType() &&
+        !variable.GetType().IsReferenceType()) {
+      response["success"] = false;
+      response["message"] =
+          "Value locations are only available for pointers and references";
+      dap.SendJSON(llvm::json::Value(std::move(response)));
+      return;
+    }
+
+    lldb::addr_t addr = variable.GetValueAsAddress();
+    lldb::SBLineEntry line_entry =
+        dap.target.ResolveLoadAddress(addr).GetLineEntry();
+
+    if (!line_entry.IsValid()) {
+      response["success"] = false;
+      response["message"] = "Failed to resolve line entry for location";
+      dap.SendJSON(llvm::json::Value(std::move(response)));
+      return;
+    }
+
+    body.try_emplace("source", CreateSource(line_entry.GetFileSpec()));
+    if (int line = line_entry.GetLine())
+      body.try_emplace("line", line);
+    if (int column = line_entry.GetColumn())
+      body.try_emplace("column", column);
+  } else {
+    // Get the declaration location
+    lldb::SBDeclaration decl = variable.GetDeclaration();
+    if (!decl.IsValid()) {
+      response["success"] = false;
+      response["message"] = "No declaration location available";
+      dap.SendJSON(llvm::json::Value(std::move(response)));
+      return;
+    }
+
+    body.try_emplace("source", CreateSource(decl.GetFileSpec()));
+    if (int line = decl.GetLine())
+      body.try_emplace("line", line);
+    if (int column = decl.GetColumn())
+      body.try_emplace("column", column);
+  }
+
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/PauseRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/PauseRequestHandler.cpp
new file mode 100644
index 0000000000000..69b5d60655a1d
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/PauseRequestHandler.cpp
@@ -0,0 +1,60 @@
+//===-- PauseRequestHandler.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "PauseRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Pause request; value of command field is 'pause'. The
+//     request suspenses the debuggee. The debug adapter first sends the
+//     PauseResponse and then a StoppedEvent (event type 'pause') after the
+//     thread has been paused successfully.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "pause" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/PauseArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "PauseArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'pause' request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Pause execution for this thread."
+//     }
+//   },
+//   "required": [ "threadId" ]
+// },
+// "PauseResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'pause' request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// }
+void PauseRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  lldb::SBProcess process = dap.target.GetProcess();
+  lldb::SBError error = process.Stop();
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/ReadMemoryRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ReadMemoryRequestHandler.cpp
new file mode 100644
index 0000000000000..f258dae3e4a22
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ReadMemoryRequestHandler.cpp
@@ -0,0 +1,139 @@
+//===-- ReadMemoryRequestHandler.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Base64.h"
+
+namespace lldb_dap {
+
+// "ReadMemoryRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Reads bytes from memory at the provided location. Clients
+//                     should only call this request if the corresponding
+//                     capability `supportsReadMemoryRequest` is true.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "readMemory" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/ReadMemoryArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "ReadMemoryArguments": {
+//   "type": "object",
+//   "description": "Arguments for `readMemory` request.",
+//   "properties": {
+//     "memoryReference": {
+//       "type": "string",
+//       "description": "Memory reference to the base location from which data
+//                       should be read."
+//     },
+//     "offset": {
+//       "type": "integer",
+//       "description": "Offset (in bytes) to be applied to the reference
+//                       location before reading data. Can be negative."
+//     },
+//     "count": {
+//       "type": "integer",
+//       "description": "Number of bytes to read at the specified location and
+//                       offset."
+//     }
+//   },
+//   "required": [ "memoryReference", "count" ]
+// },
+// "ReadMemoryResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `readMemory` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "address": {
+//             "type": "string",
+//             "description": "The address of the first byte of data returned.
+//                             Treated as a hex value if prefixed with `0x`, or
+//                             as a decimal value otherwise."
+//           },
+//           "unreadableBytes": {
+//             "type": "integer",
+//             "description": "The number of unreadable bytes encountered after
+//                             the last successfully read byte.\nThis can be
+//                             used to determine the number of bytes that should
+//                             be skipped before a subsequent
+//             `readMemory` request succeeds."
+//           },
+//           "data": {
+//             "type": "string",
+//             "description": "The bytes read from memory, encoded using base64.
+//                             If the decoded length of `data` is less than the
+//                             requested `count` in the original `readMemory`
+//                             request, and `unreadableBytes` is zero or
+//                             omitted, then the client should assume it's
+//                             reached the end of readable memory."
+//           }
+//         },
+//         "required": [ "address" ]
+//       }
+//     }
+//   }]
+// },
+void ReadMemoryRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  auto *arguments = request.getObject("arguments");
+
+  llvm::StringRef memoryReference = GetString(arguments, "memoryReference");
+  auto addr_opt = DecodeMemoryReference(memoryReference);
+  if (!addr_opt.has_value()) {
+    response["success"] = false;
+    response["message"] =
+        "Malformed memory reference: " + memoryReference.str();
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+  lldb::addr_t addr_int = *addr_opt;
+  addr_int += GetSigned(arguments, "offset", 0);
+  const uint64_t count_requested = GetUnsigned(arguments, "count", 0);
+
+  // We also need support reading 0 bytes
+  // VS Code sends those requests to check if a `memoryReference`
+  // can be dereferenced.
+  const uint64_t count_read = std::max<uint64_t>(count_requested, 1);
+  std::vector<uint8_t> buf;
+  buf.resize(count_read);
+  lldb::SBError error;
+  lldb::SBAddress addr{addr_int, dap.target};
+  size_t count_result =
+      dap.target.ReadMemory(addr, buf.data(), count_read, error);
+  if (count_result == 0) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", error.GetCString());
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+  buf.resize(std::min<size_t>(count_result, count_requested));
+
+  llvm::json::Object body;
+  std::string formatted_addr = "0x" + llvm::utohexstr(addr_int);
+  body.try_emplace("address", formatted_addr);
+  body.try_emplace("data", llvm::encodeBase64(buf));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index a30e0dcc2bd04..d73b6c8520659 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -223,6 +223,76 @@ class ModulesRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class PauseRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "pause"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class ScopesRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "scopes"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class SetVariableRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "setVariable"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class SourceRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "source"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class StackTraceRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "stackTrace"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class ThreadsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "threads"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class VariablesRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "variables"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class LocationsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "locations"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class DisassembleRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "disassemble"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+class ReadMemoryRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "readMemory"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 /// A request used in testing to get the details on all breakpoints that are
 /// currently set in the target. This helps us to test "setBreakpoints" and
 /// "setFunctionBreakpoints" requests to verify we have the correct set of
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
new file mode 100644
index 0000000000000..d9a831436ec0e
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -0,0 +1,106 @@
+//===-- ScopesRequestHandler.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "ScopesRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Scopes request; value of command field is 'scopes'. The
+//     request returns the variable scopes for a given stackframe ID.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "scopes" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/ScopesArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "ScopesArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'scopes' request.",
+//   "properties": {
+//     "frameId": {
+//       "type": "integer",
+//       "description": "Retrieve the scopes for this stackframe."
+//     }
+//   },
+//   "required": [ "frameId" ]
+// },
+// "ScopesResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'scopes' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "scopes": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/Scope"
+//             },
+//             "description": "The scopes of the stackframe. If the array has
+//             length zero, there are no scopes available."
+//           }
+//         },
+//         "required": [ "scopes" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void ScopesRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+  const auto *arguments = request.getObject("arguments");
+  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+  // As the user selects different stack frames in the GUI, a "scopes" request
+  // will be sent to the DAP. This is the only way we know that the user has
+  // selected a frame in a thread. There are no other notifications that are
+  // sent and VS code doesn't allow multiple frames to show variables
+  // concurrently. If we select the thread and frame as the "scopes" requests
+  // are sent, this allows users to type commands in the debugger console
+  // with a backtick character to run lldb commands and these lldb commands
+  // will now have the right context selected as they are run. If the user
+  // types "`bt" into the debugger console and we had another thread selected
+  // in the LLDB library, we would show the wrong thing to the user. If the
+  // users switches threads with a lldb command like "`thread select 14", the
+  // GUI will not update as there are no "event" notification packets that
+  // allow us to change the currently selected thread or frame in the GUI that
+  // I am aware of.
+  if (frame.IsValid()) {
+    frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
+    frame.GetThread().SetSelectedFrame(frame.GetFrameID());
+  }
+
+  dap.variables.locals = frame.GetVariables(/*arguments=*/true,
+                                            /*locals=*/true,
+                                            /*statics=*/false,
+                                            /*in_scope_only=*/true);
+  dap.variables.globals = frame.GetVariables(/*arguments=*/false,
+                                             /*locals=*/false,
+                                             /*statics=*/true,
+                                             /*in_scope_only=*/true);
+  dap.variables.registers = frame.GetRegisters();
+  body.try_emplace("scopes", dap.CreateTopLevelScopes());
+  response.try_emplace("body", std::move(body));
+  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
new file mode 100644
index 0000000000000..943e3a8a5060e
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp
@@ -0,0 +1,177 @@
+//===-- SetVariableRequestHandler.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "SetVariableRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "setVariable request; value of command field is
+//     'setVariable'. Set the variable with the given name in the variable
+//     container to a new value.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "setVariable" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/SetVariableArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "SetVariableArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'setVariable' request.",
+//   "properties": {
+//     "variablesReference": {
+//       "type": "integer",
+//       "description": "The reference of the variable container."
+//     },
+//     "name": {
+//       "type": "string",
+//       "description": "The name of the variable."
+//     },
+//     "value": {
+//       "type": "string",
+//       "description": "The value of the variable."
+//     },
+//     "format": {
+//       "$ref": "#/definitions/ValueFormat",
+//       "description": "Specifies details on how to format the response value."
+//     }
+//   },
+//   "required": [ "variablesReference", "name", "value" ]
+// },
+// "SetVariableResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'setVariable' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "value": {
+//             "type": "string",
+//             "description": "The new value of the variable."
+//           },
+//           "type": {
+//             "type": "string",
+//             "description": "The type of the new value. Typically shown in the
+//             UI when hovering over the value."
+//           },
+//           "variablesReference": {
+//             "type": "number",
+//             "description": "If variablesReference is > 0, the new value is
+//             structured and its children can be retrieved by passing
+//             variablesReference to the VariablesRequest."
+//           },
+//           "namedVariables": {
+//             "type": "number",
+//             "description": "The number of named child variables. The client
+//             can use this optional information to present the variables in a
+//             paged UI and fetch them in chunks."
+//           },
+//           "indexedVariables": {
+//             "type": "number",
+//             "description": "The number of indexed child variables. The client
+//             can use this optional information to present the variables in a
+//             paged UI and fetch them in chunks."
+//           },
+//           "valueLocationReference": {
+//             "type": "integer",
+//             "description": "A reference that allows the client to request the
+//             location where the new value is declared. For example, if the new
+//             value is function pointer, the adapter may be able to look up the
+//             function's location. This should be present only if the adapter
+//             is likely to be able to resolve the location.\n\nThis reference
+//             shares the same lifetime as the `variablesReference`. See
+//             'Lifetime of Object References' in the Overview section for
+//             details."
+//           }
+//         },
+//         "required": [ "value" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void SetVariableRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Array variables;
+  llvm::json::Object body;
+  const auto *arguments = request.getObject("arguments");
+  // This is a reference to the containing variable/scope
+  const auto variablesReference =
+      GetUnsigned(arguments, "variablesReference", 0);
+  llvm::StringRef name = GetString(arguments, "name");
+
+  const auto value = GetString(arguments, "value");
+  // Set success to false just in case we don't find the variable by name
+  response.try_emplace("success", false);
+
+  lldb::SBValue variable;
+
+  // The "id" is the unique integer ID that is unique within the enclosing
+  // variablesReference. It is optionally added to any "interface Variable"
+  // objects to uniquely identify a variable within an enclosing
+  // variablesReference. It helps to disambiguate between two variables that
+  // have the same name within the same scope since the "setVariables" request
+  // only specifies the variable reference of the enclosing scope/variable, and
+  // the name of the variable. We could have two shadowed variables with the
+  // same name in "Locals" or "Globals". In our case the "id" absolute index
+  // of the variable within the dap.variables list.
+  const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
+  if (id_value != UINT64_MAX) {
+    variable = dap.variables.GetVariable(id_value);
+  } else {
+    variable = FindVariable(variablesReference, name);
+  }
+
+  if (variable.IsValid()) {
+    lldb::SBError error;
+    bool success = variable.SetValueFromCString(value.data(), error);
+    if (success) {
+      VariableDescription desc(variable, dap.enable_auto_variable_summaries);
+      EmplaceSafeString(body, "result", desc.display_value);
+      EmplaceSafeString(body, "type", desc.display_type_name);
+
+      // We don't know the index of the variable in our dap.variables
+      // so always insert a new one to get its variablesReference.
+      // is_permanent is false because debug console does not support
+      // setVariable request.
+      int64_t new_var_ref =
+          dap.variables.InsertVariable(variable, /*is_permanent=*/false);
+      if (variable.MightHaveChildren())
+        body.try_emplace("variablesReference", new_var_ref);
+      else
+        body.try_emplace("variablesReference", 0);
+      if (lldb::addr_t addr = variable.GetLoadAddress();
+          addr != LLDB_INVALID_ADDRESS)
+        body.try_emplace("memoryReference", EncodeMemoryReference(addr));
+      if (ValuePointsToCode(variable))
+        body.try_emplace("valueLocationReference", new_var_ref);
+    } else {
+      EmplaceSafeString(body, "message", std::string(error.GetCString()));
+    }
+    response["success"] = llvm::json::Value(success);
+  } else {
+    response["success"] = llvm::json::Value(false);
+  }
+
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
new file mode 100644
index 0000000000000..03561c9e64922
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
@@ -0,0 +1,82 @@
+//===-- SourceRequestHandler.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "SourceRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Source request; value of command field is 'source'. The
+//     request retrieves the source code for a given source reference.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "source" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/SourceArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "SourceArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'source' request.",
+//   "properties": {
+//     "source": {
+//       "$ref": "#/definitions/Source",
+//       "description": "Specifies the source content to load. Either
+//       source.path or source.sourceReference must be specified."
+//     },
+//     "sourceReference": {
+//       "type": "integer",
+//       "description": "The reference to the source. This is the same as
+//       source.sourceReference. This is provided for backward compatibility
+//       since old backends do not understand the 'source' attribute."
+//     }
+//   },
+//   "required": [ "sourceReference" ]
+// },
+// "SourceResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'source' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "content": {
+//             "type": "string",
+//             "description": "Content of the source reference."
+//           },
+//           "mimeType": {
+//             "type": "string",
+//             "description": "Optional content type (mime type) of the source."
+//           }
+//         },
+//         "required": [ "content" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void SourceRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body{{"content", ""}};
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
new file mode 100644
index 0000000000000..c7c8bbf6902e9
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
@@ -0,0 +1,197 @@
+//===-- StackTraceRequestHandler.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+/// Page size used for reporting addtional frames in the 'stackTrace' request.
+static constexpr int StackPageSize = 20;
+
+// Fill in the stack frames of the thread.
+//
+// Threads stacks may contain runtime specific extended backtraces, when
+// constructing a stack trace first report the full thread stack trace then
+// perform a breadth first traversal of any extended backtrace frames.
+//
+// For example:
+//
+// Thread (id=th0) stack=[s0, s1, s2, s3]
+//   \ Extended backtrace "libdispatch" Thread (id=th1) stack=[s0, s1]
+//     \ Extended backtrace "libdispatch" Thread (id=th2) stack=[s0, s1]
+//   \ Extended backtrace "Application Specific Backtrace" Thread (id=th3)
+//   stack=[s0, s1, s2]
+//
+// Which will flatten into:
+//
+//  0. th0->s0
+//  1. th0->s1
+//  2. th0->s2
+//  3. th0->s3
+//  4. label - Enqueued from th1, sf=-1, i=-4
+//  5. th1->s0
+//  6. th1->s1
+//  7. label - Enqueued from th2
+//  8. th2->s0
+//  9. th2->s1
+// 10. label - Application Specific Backtrace
+// 11. th3->s0
+// 12. th3->s1
+// 13. th3->s2
+//
+// s=3,l=3 = [th0->s3, label1, th1->s0]
+static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
+                            llvm::json::Array &stack_frames, int64_t &offset,
+                            const int64_t start_frame, const int64_t levels) {
+  bool reached_end_of_stack = false;
+  for (int64_t i = start_frame;
+       static_cast<int64_t>(stack_frames.size()) < levels; i++) {
+    if (i == -1) {
+      stack_frames.emplace_back(
+          CreateExtendedStackFrameLabel(thread, dap.frame_format));
+      continue;
+    }
+
+    lldb::SBFrame frame = thread.GetFrameAtIndex(i);
+    if (!frame.IsValid()) {
+      offset += thread.GetNumFrames() + 1 /* label between threads */;
+      reached_end_of_stack = true;
+      break;
+    }
+
+    stack_frames.emplace_back(CreateStackFrame(frame, dap.frame_format));
+  }
+
+  if (dap.display_extended_backtrace && reached_end_of_stack) {
+    // Check for any extended backtraces.
+    for (uint32_t bt = 0;
+         bt < thread.GetProcess().GetNumExtendedBacktraceTypes(); bt++) {
+      lldb::SBThread backtrace = thread.GetExtendedBacktraceThread(
+          thread.GetProcess().GetExtendedBacktraceTypeAtIndex(bt));
+      if (!backtrace.IsValid())
+        continue;
+
+      reached_end_of_stack = FillStackFrames(
+          dap, backtrace, stack_frames, offset,
+          (start_frame - offset) > 0 ? start_frame - offset : -1, levels);
+      if (static_cast<int64_t>(stack_frames.size()) >= levels)
+        break;
+    }
+  }
+
+  return reached_end_of_stack;
+}
+
+// "StackTraceRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "StackTrace request; value of command field is
+//     'stackTrace'. The request returns a stacktrace from the current execution
+//     state.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "stackTrace" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/StackTraceArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "StackTraceArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'stackTrace' request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Retrieve the stacktrace for this thread."
+//     },
+//     "startFrame": {
+//       "type": "integer",
+//       "description": "The index of the first frame to return; if omitted
+//       frames start at 0."
+//     },
+//     "levels": {
+//       "type": "integer",
+//       "description": "The maximum number of frames to return. If levels is
+//       not specified or 0, all frames are returned."
+//     },
+//     "format": {
+//       "$ref": "#/definitions/StackFrameFormat",
+//       "description": "Specifies details on how to format the stack frames.
+//       The attribute is only honored by a debug adapter if the corresponding
+//       capability `supportsValueFormattingOptions` is true."
+//     }
+//  },
+//   "required": [ "threadId" ]
+// },
+// "StackTraceResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `stackTrace` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "stackFrames": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/StackFrame"
+//             },
+//             "description": "The frames of the stackframe. If the array has
+//             length zero, there are no stackframes available. This means that
+//             there is no location information available."
+//           },
+//           "totalFrames": {
+//             "type": "integer",
+//             "description": "The total number of frames available in the
+//             stack. If omitted or if `totalFrames` is larger than the
+//             available frames, a client is expected to request frames until
+//             a request returns less frames than requested (which indicates
+//             the end of the stack). Returning monotonically increasing
+//             `totalFrames` values for subsequent requests can be used to
+//             enforce paging in the client."
+//           }
+//         },
+//         "required": [ "stackFrames" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void StackTraceRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  lldb::SBError error;
+  const auto *arguments = request.getObject("arguments");
+  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
+  llvm::json::Array stack_frames;
+  llvm::json::Object body;
+
+  if (thread.IsValid()) {
+    const auto start_frame = GetUnsigned(arguments, "startFrame", 0);
+    const auto levels = GetUnsigned(arguments, "levels", 0);
+    int64_t offset = 0;
+    bool reached_end_of_stack =
+        FillStackFrames(dap, thread, stack_frames, offset, start_frame,
+                        levels == 0 ? INT64_MAX : levels);
+    body.try_emplace("totalFrames",
+                     start_frame + stack_frames.size() +
+                         (reached_end_of_stack ? 0 : StackPageSize));
+  }
+
+  body.try_emplace("stackFrames", std::move(stack_frames));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/ThreadsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ThreadsRequestHandler.cpp
new file mode 100644
index 0000000000000..a70722b11fb29
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ThreadsRequestHandler.cpp
@@ -0,0 +1,71 @@
+//===-- ThreadsRequestHandler.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "ThreadsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Thread request; value of command field is 'threads'. The
+//     request retrieves a list of all threads.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "threads" ]
+//       }
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+// "ThreadsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'threads' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "threads": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/Thread"
+//             },
+//             "description": "All threads."
+//           }
+//         },
+//         "required": [ "threads" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void ThreadsRequestHandler::operator()(const llvm::json::Object &request) {
+  lldb::SBProcess process = dap.target.GetProcess();
+  llvm::json::Object response;
+  FillResponse(request, response);
+
+  const uint32_t num_threads = process.GetNumThreads();
+  llvm::json::Array threads;
+  for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+    lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
+    threads.emplace_back(CreateThread(thread, dap.thread_format));
+  }
+  if (threads.size() == 0) {
+    response["success"] = llvm::json::Value(false);
+  }
+  llvm::json::Object body;
+  body.try_emplace("threads", std::move(threads));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
new file mode 100644
index 0000000000000..641e27189fa18
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -0,0 +1,217 @@
+//===-- VariablesRequestHandler.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "VariablesRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Variables request; value of command field is 'variables'.
+//     Retrieves all child variables for the given variable reference. An
+//     optional filter can be used to limit the fetched children to either named
+//     or indexed children.", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "variables" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/VariablesArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "VariablesArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'variables' request.",
+//   "properties": {
+//     "variablesReference": {
+//       "type": "integer",
+//       "description": "The Variable reference."
+//     },
+//     "filter": {
+//       "type": "string",
+//       "enum": [ "indexed", "named" ],
+//       "description": "Optional filter to limit the child variables to either
+//       named or indexed. If ommited, both types are fetched."
+//     },
+//     "start": {
+//       "type": "integer",
+//       "description": "The index of the first variable to return; if omitted
+//       children start at 0."
+//     },
+//     "count": {
+//       "type": "integer",
+//       "description": "The number of variables to return. If count is missing
+//       or 0, all variables are returned."
+//     },
+//     "format": {
+//       "$ref": "#/definitions/ValueFormat",
+//       "description": "Specifies details on how to format the Variable
+//       values."
+//     }
+//   },
+//   "required": [ "variablesReference" ]
+// },
+// "VariablesResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'variables' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "variables": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/Variable"
+//             },
+//             "description": "All (or a range) of variables for the given
+//             variable reference."
+//           }
+//         },
+//         "required": [ "variables" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void VariablesRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Array variables;
+  const auto *arguments = request.getObject("arguments");
+  const auto variablesReference =
+      GetUnsigned(arguments, "variablesReference", 0);
+  const int64_t start = GetSigned(arguments, "start", 0);
+  const int64_t count = GetSigned(arguments, "count", 0);
+  bool hex = false;
+  const auto *format = arguments->getObject("format");
+  if (format)
+    hex = GetBoolean(format, "hex", false);
+
+  if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+    // variablesReference is one of our scopes, not an actual variable it is
+    // asking for the list of args, locals or globals.
+    int64_t start_idx = 0;
+    int64_t num_children = 0;
+
+    if (variablesReference == VARREF_REGS) {
+      // Change the default format of any pointer sized registers in the first
+      // register set to be the lldb::eFormatAddressInfo so we show the pointer
+      // and resolve what the pointer resolves to. Only change the format if the
+      // format was set to the default format or if it was hex as some registers
+      // have formats set for them.
+      const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize();
+      lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(0);
+      const uint32_t num_regs = reg_set.GetNumChildren();
+      for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+        lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx);
+        const lldb::Format format = reg.GetFormat();
+        if (format == lldb::eFormatDefault || format == lldb::eFormatHex) {
+          if (reg.GetByteSize() == addr_size)
+            reg.SetFormat(lldb::eFormatAddressInfo);
+        }
+      }
+    }
+
+    num_children = top_scope->GetSize();
+    if (num_children == 0 && variablesReference == VARREF_LOCALS) {
+      // Check for an error in the SBValueList that might explain why we don't
+      // have locals. If we have an error display it as the sole value in the
+      // the locals.
+
+      // "error" owns the error string so we must keep it alive as long as we
+      // want to use the returns "const char *"
+      lldb::SBError error = top_scope->GetError();
+      const char *var_err = error.GetCString();
+      if (var_err) {
+        // Create a fake variable named "error" to explain why variables were
+        // not available. This new error will help let users know when there was
+        // a problem that kept variables from being available for display and
+        // allow users to fix this issue instead of seeing no variables. The
+        // errors are only set when there is a problem that the user could
+        // fix, so no error will show up when you have no debug info, only when
+        // we do have debug info and something that is fixable can be done.
+        llvm::json::Object object;
+        EmplaceSafeString(object, "name", "<error>");
+        EmplaceSafeString(object, "type", "const char *");
+        EmplaceSafeString(object, "value", var_err);
+        object.try_emplace("variablesReference", (int64_t)0);
+        variables.emplace_back(std::move(object));
+      }
+    }
+    const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
+
+    // We first find out which variable names are duplicated
+    std::map<std::string, int> variable_name_counts;
+    for (auto i = start_idx; i < end_idx; ++i) {
+      lldb::SBValue variable = top_scope->GetValueAtIndex(i);
+      if (!variable.IsValid())
+        break;
+      variable_name_counts[GetNonNullVariableName(variable)]++;
+    }
+
+    // Now we construct the result with unique display variable names
+    for (auto i = start_idx; i < end_idx; ++i) {
+      lldb::SBValue variable = top_scope->GetValueAtIndex(i);
+
+      if (!variable.IsValid())
+        break;
+
+      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_name_counts[GetNonNullVariableName(variable)] > 1));
+    }
+  } else {
+    // We are expanding a variable that has children, so we will return its
+    // children.
+    lldb::SBValue variable = dap.variables.GetVariable(variablesReference);
+    if (variable.IsValid()) {
+      auto addChild = [&](lldb::SBValue child,
+                          std::optional<std::string> custom_name = {}) {
+        if (!child.IsValid())
+          return;
+        bool is_permanent =
+            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,
+            /*is_name_duplicated=*/false, custom_name));
+      };
+      const int64_t num_children = variable.GetNumChildren();
+      int64_t end_idx = start + ((count == 0) ? num_children : count);
+      int64_t i = start;
+      for (; i < end_idx && i < num_children; ++i)
+        addChild(variable.GetChildAtIndex(i));
+
+      // If we haven't filled the count quota from the request, we insert a new
+      // "[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)
+        addChild(variable.GetNonSyntheticValue(), "[raw]");
+    }
+  }
+  llvm::json::Object body;
+  body.try_emplace("variables", std::move(variables));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index fd4615897841c..fcf2d122e5313 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -14,7 +14,6 @@
 #include "LLDBUtils.h"
 #include "RunInTerminal.h"
 #include "Watchpoint.h"
-#include "lldb/API/SBDeclaration.h"
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBFile.h"
 #include "lldb/API/SBInstruction.h"
@@ -36,7 +35,6 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
-#include "llvm/Support/Base64.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/InitLLVM.h"
@@ -127,1328 +125,6 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable {
 
 typedef void (*RequestCallback)(const llvm::json::Object &command);
 
-/// Page size used for reporting addtional frames in the 'stackTrace' request.
-constexpr int StackPageSize = 20;
-
-lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
-  switch (variablesReference) {
-  case VARREF_LOCALS:
-    return &dap.variables.locals;
-  case VARREF_GLOBALS:
-    return &dap.variables.globals;
-  case VARREF_REGS:
-    return &dap.variables.registers;
-  default:
-    return nullptr;
-  }
-}
-
-lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference,
-                           llvm::StringRef name) {
-  lldb::SBValue variable;
-  if (lldb::SBValueList *top_scope =
-          GetTopLevelScope(dap, variablesReference)) {
-    bool is_duplicated_variable_name = name.contains(" @");
-    // variablesReference is one of our scopes, not an actual variable it is
-    // asking for a variable in locals or globals or registers
-    int64_t end_idx = top_scope->GetSize();
-    // Searching backward so that we choose the variable in closest scope
-    // among variables of the same name.
-    for (int64_t i = end_idx - 1; i >= 0; --i) {
-      lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
-      std::string variable_name = CreateUniqueVariableNameForDisplay(
-          curr_variable, is_duplicated_variable_name);
-      if (variable_name == name) {
-        variable = curr_variable;
-        break;
-      }
-    }
-  } else {
-    // This is not under the globals or locals scope, so there are no duplicated
-    // names.
-
-    // We have a named item within an actual variable so we need to find it
-    // withing the container variable by name.
-    lldb::SBValue container = dap.variables.GetVariable(variablesReference);
-    variable = container.GetChildMemberWithName(name.data());
-    if (!variable.IsValid()) {
-      if (name.starts_with("[")) {
-        llvm::StringRef index_str(name.drop_front(1));
-        uint64_t index = 0;
-        if (!index_str.consumeInteger(0, index)) {
-          if (index_str == "]")
-            variable = container.GetChildAtIndex(index);
-        }
-      }
-    }
-  }
-  return variable;
-}
-
-// Fill in the stack frames of the thread.
-//
-// Threads stacks may contain runtime specific extended backtraces, when
-// constructing a stack trace first report the full thread stack trace then
-// perform a breadth first traversal of any extended backtrace frames.
-//
-// For example:
-//
-// Thread (id=th0) stack=[s0, s1, s2, s3]
-//   \ Extended backtrace "libdispatch" Thread (id=th1) stack=[s0, s1]
-//     \ Extended backtrace "libdispatch" Thread (id=th2) stack=[s0, s1]
-//   \ Extended backtrace "Application Specific Backtrace" Thread (id=th3)
-//   stack=[s0, s1, s2]
-//
-// Which will flatten into:
-//
-//  0. th0->s0
-//  1. th0->s1
-//  2. th0->s2
-//  3. th0->s3
-//  4. label - Enqueued from th1, sf=-1, i=-4
-//  5. th1->s0
-//  6. th1->s1
-//  7. label - Enqueued from th2
-//  8. th2->s0
-//  9. th2->s1
-// 10. label - Application Specific Backtrace
-// 11. th3->s0
-// 12. th3->s1
-// 13. th3->s2
-//
-// s=3,l=3 = [th0->s3, label1, th1->s0]
-bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
-                     llvm::json::Array &stack_frames, int64_t &offset,
-                     const int64_t start_frame, const int64_t levels) {
-  bool reached_end_of_stack = false;
-  for (int64_t i = start_frame;
-       static_cast<int64_t>(stack_frames.size()) < levels; i++) {
-    if (i == -1) {
-      stack_frames.emplace_back(
-          CreateExtendedStackFrameLabel(thread, dap.frame_format));
-      continue;
-    }
-
-    lldb::SBFrame frame = thread.GetFrameAtIndex(i);
-    if (!frame.IsValid()) {
-      offset += thread.GetNumFrames() + 1 /* label between threads */;
-      reached_end_of_stack = true;
-      break;
-    }
-
-    stack_frames.emplace_back(CreateStackFrame(frame, dap.frame_format));
-  }
-
-  if (dap.display_extended_backtrace && reached_end_of_stack) {
-    // Check for any extended backtraces.
-    for (uint32_t bt = 0;
-         bt < thread.GetProcess().GetNumExtendedBacktraceTypes(); bt++) {
-      lldb::SBThread backtrace = thread.GetExtendedBacktraceThread(
-          thread.GetProcess().GetExtendedBacktraceTypeAtIndex(bt));
-      if (!backtrace.IsValid())
-        continue;
-
-      reached_end_of_stack = FillStackFrames(
-          dap, backtrace, stack_frames, offset,
-          (start_frame - offset) > 0 ? start_frame - offset : -1, levels);
-      if (static_cast<int64_t>(stack_frames.size()) >= levels)
-        break;
-    }
-  }
-
-  return reached_end_of_stack;
-}
-
-// "PauseRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Pause request; value of command field is 'pause'. The
-//     request suspenses the debuggee. The debug adapter first sends the
-//     PauseResponse and then a StoppedEvent (event type 'pause') after the
-//     thread has been paused successfully.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "pause" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/PauseArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "PauseArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'pause' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Pause execution for this thread."
-//     }
-//   },
-//   "required": [ "threadId" ]
-// },
-// "PauseResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'pause' request. This is just an
-//     acknowledgement, so no body field is required."
-//   }]
-// }
-void request_pause(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  lldb::SBProcess process = dap.target.GetProcess();
-  lldb::SBError error = process.Stop();
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "ScopesRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Scopes request; value of command field is 'scopes'. The
-//     request returns the variable scopes for a given stackframe ID.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "scopes" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/ScopesArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "ScopesArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'scopes' request.",
-//   "properties": {
-//     "frameId": {
-//       "type": "integer",
-//       "description": "Retrieve the scopes for this stackframe."
-//     }
-//   },
-//   "required": [ "frameId" ]
-// },
-// "ScopesResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'scopes' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "scopes": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Scope"
-//             },
-//             "description": "The scopes of the stackframe. If the array has
-//             length zero, there are no scopes available."
-//           }
-//         },
-//         "required": [ "scopes" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_scopes(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  // As the user selects different stack frames in the GUI, a "scopes" request
-  // will be sent to the DAP. This is the only way we know that the user has
-  // selected a frame in a thread. There are no other notifications that are
-  // sent and VS code doesn't allow multiple frames to show variables
-  // concurrently. If we select the thread and frame as the "scopes" requests
-  // are sent, this allows users to type commands in the debugger console
-  // with a backtick character to run lldb commands and these lldb commands
-  // will now have the right context selected as they are run. If the user
-  // types "`bt" into the debugger console and we had another thread selected
-  // in the LLDB library, we would show the wrong thing to the user. If the
-  // users switches threads with a lldb command like "`thread select 14", the
-  // GUI will not update as there are no "event" notification packets that
-  // allow us to change the currently selected thread or frame in the GUI that
-  // I am aware of.
-  if (frame.IsValid()) {
-    frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
-    frame.GetThread().SetSelectedFrame(frame.GetFrameID());
-  }
-
-  dap.variables.locals = frame.GetVariables(/*arguments=*/true,
-                                            /*locals=*/true,
-                                            /*statics=*/false,
-                                            /*in_scope_only=*/true);
-  dap.variables.globals = frame.GetVariables(/*arguments=*/false,
-                                             /*locals=*/false,
-                                             /*statics=*/true,
-                                             /*in_scope_only=*/true);
-  dap.variables.registers = frame.GetRegisters();
-  body.try_emplace("scopes", dap.CreateTopLevelScopes());
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "SourceRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Source request; value of command field is 'source'. The
-//     request retrieves the source code for a given source reference.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "source" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SourceArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SourceArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'source' request.",
-//   "properties": {
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "Specifies the source content to load. Either
-//       source.path or source.sourceReference must be specified."
-//     },
-//     "sourceReference": {
-//       "type": "integer",
-//       "description": "The reference to the source. This is the same as
-//       source.sourceReference. This is provided for backward compatibility
-//       since old backends do not understand the 'source' attribute."
-//     }
-//   },
-//   "required": [ "sourceReference" ]
-// },
-// "SourceResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'source' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "content": {
-//             "type": "string",
-//             "description": "Content of the source reference."
-//           },
-//           "mimeType": {
-//             "type": "string",
-//             "description": "Optional content type (mime type) of the source."
-//           }
-//         },
-//         "required": [ "content" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_source(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body{{"content", ""}};
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "StackTraceRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "StackTrace request; value of command field is
-//     'stackTrace'. The request returns a stacktrace from the current execution
-//     state.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "stackTrace" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/StackTraceArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "StackTraceArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'stackTrace' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Retrieve the stacktrace for this thread."
-//     },
-//     "startFrame": {
-//       "type": "integer",
-//       "description": "The index of the first frame to return; if omitted
-//       frames start at 0."
-//     },
-//     "levels": {
-//       "type": "integer",
-//       "description": "The maximum number of frames to return. If levels is
-//       not specified or 0, all frames are returned."
-//     },
-//     "format": {
-//       "$ref": "#/definitions/StackFrameFormat",
-//       "description": "Specifies details on how to format the stack frames.
-//       The attribute is only honored by a debug adapter if the corresponding
-//       capability `supportsValueFormattingOptions` is true."
-//     }
-//  },
-//   "required": [ "threadId" ]
-// },
-// "StackTraceResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `stackTrace` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "stackFrames": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/StackFrame"
-//             },
-//             "description": "The frames of the stackframe. If the array has
-//             length zero, there are no stackframes available. This means that
-//             there is no location information available."
-//           },
-//           "totalFrames": {
-//             "type": "integer",
-//             "description": "The total number of frames available in the
-//             stack. If omitted or if `totalFrames` is larger than the
-//             available frames, a client is expected to request frames until
-//             a request returns less frames than requested (which indicates
-//             the end of the stack). Returning monotonically increasing
-//             `totalFrames` values for subsequent requests can be used to
-//             enforce paging in the client."
-//           }
-//         },
-//         "required": [ "stackFrames" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_stackTrace(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  lldb::SBError error;
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
-  llvm::json::Array stack_frames;
-  llvm::json::Object body;
-
-  if (thread.IsValid()) {
-    const auto start_frame = GetUnsigned(arguments, "startFrame", 0);
-    const auto levels = GetUnsigned(arguments, "levels", 0);
-    int64_t offset = 0;
-    bool reached_end_of_stack =
-        FillStackFrames(dap, thread, stack_frames, offset, start_frame,
-                        levels == 0 ? INT64_MAX : levels);
-    body.try_emplace("totalFrames",
-                     start_frame + stack_frames.size() +
-                         (reached_end_of_stack ? 0 : StackPageSize));
-  }
-
-  body.try_emplace("stackFrames", std::move(stack_frames));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "ThreadsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Thread request; value of command field is 'threads'. The
-//     request retrieves a list of all threads.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "threads" ]
-//       }
-//     },
-//     "required": [ "command" ]
-//   }]
-// },
-// "ThreadsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'threads' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "threads": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Thread"
-//             },
-//             "description": "All threads."
-//           }
-//         },
-//         "required": [ "threads" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_threads(DAP &dap, const llvm::json::Object &request) {
-  lldb::SBProcess process = dap.target.GetProcess();
-  llvm::json::Object response;
-  FillResponse(request, response);
-
-  const uint32_t num_threads = process.GetNumThreads();
-  llvm::json::Array threads;
-  for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
-    lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
-    threads.emplace_back(CreateThread(thread, dap.thread_format));
-  }
-  if (threads.size() == 0) {
-    response["success"] = llvm::json::Value(false);
-  }
-  llvm::json::Object body;
-  body.try_emplace("threads", std::move(threads));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "SetVariableRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "setVariable request; value of command field is
-//     'setVariable'. Set the variable with the given name in the variable
-//     container to a new value.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setVariable" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetVariableArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetVariableArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setVariable' request.",
-//   "properties": {
-//     "variablesReference": {
-//       "type": "integer",
-//       "description": "The reference of the variable container."
-//     },
-//     "name": {
-//       "type": "string",
-//       "description": "The name of the variable."
-//     },
-//     "value": {
-//       "type": "string",
-//       "description": "The value of the variable."
-//     },
-//     "format": {
-//       "$ref": "#/definitions/ValueFormat",
-//       "description": "Specifies details on how to format the response value."
-//     }
-//   },
-//   "required": [ "variablesReference", "name", "value" ]
-// },
-// "SetVariableResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setVariable' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "value": {
-//             "type": "string",
-//             "description": "The new value of the variable."
-//           },
-//           "type": {
-//             "type": "string",
-//             "description": "The type of the new value. Typically shown in the
-//             UI when hovering over the value."
-//           },
-//           "variablesReference": {
-//             "type": "number",
-//             "description": "If variablesReference is > 0, the new value is
-//             structured and its children can be retrieved by passing
-//             variablesReference to the VariablesRequest."
-//           },
-//           "namedVariables": {
-//             "type": "number",
-//             "description": "The number of named child variables. The client
-//             can use this optional information to present the variables in a
-//             paged UI and fetch them in chunks."
-//           },
-//           "indexedVariables": {
-//             "type": "number",
-//             "description": "The number of indexed child variables. The client
-//             can use this optional information to present the variables in a
-//             paged UI and fetch them in chunks."
-//           },
-//           "valueLocationReference": {
-//             "type": "integer",
-//             "description": "A reference that allows the client to request the
-//             location where the new value is declared. For example, if the new
-//             value is function pointer, the adapter may be able to look up the
-//             function's location. This should be present only if the adapter
-//             is likely to be able to resolve the location.\n\nThis reference
-//             shares the same lifetime as the `variablesReference`. See
-//             'Lifetime of Object References' in the Overview section for
-//             details."
-//           }
-//         },
-//         "required": [ "value" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_setVariable(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Array variables;
-  llvm::json::Object body;
-  const auto *arguments = request.getObject("arguments");
-  // This is a reference to the containing variable/scope
-  const auto variablesReference =
-      GetUnsigned(arguments, "variablesReference", 0);
-  llvm::StringRef name = GetString(arguments, "name");
-
-  const auto value = GetString(arguments, "value");
-  // Set success to false just in case we don't find the variable by name
-  response.try_emplace("success", false);
-
-  lldb::SBValue variable;
-
-  // The "id" is the unique integer ID that is unique within the enclosing
-  // variablesReference. It is optionally added to any "interface Variable"
-  // objects to uniquely identify a variable within an enclosing
-  // variablesReference. It helps to disambiguate between two variables that
-  // have the same name within the same scope since the "setVariables" request
-  // only specifies the variable reference of the enclosing scope/variable, and
-  // the name of the variable. We could have two shadowed variables with the
-  // same name in "Locals" or "Globals". In our case the "id" absolute index
-  // of the variable within the dap.variables list.
-  const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
-  if (id_value != UINT64_MAX) {
-    variable = dap.variables.GetVariable(id_value);
-  } else {
-    variable = FindVariable(dap, variablesReference, name);
-  }
-
-  if (variable.IsValid()) {
-    lldb::SBError error;
-    bool success = variable.SetValueFromCString(value.data(), error);
-    if (success) {
-      VariableDescription desc(variable, dap.enable_auto_variable_summaries);
-      EmplaceSafeString(body, "result", desc.display_value);
-      EmplaceSafeString(body, "type", desc.display_type_name);
-
-      // We don't know the index of the variable in our dap.variables
-      // so always insert a new one to get its variablesReference.
-      // is_permanent is false because debug console does not support
-      // setVariable request.
-      int64_t new_var_ref =
-          dap.variables.InsertVariable(variable, /*is_permanent=*/false);
-      if (variable.MightHaveChildren())
-        body.try_emplace("variablesReference", new_var_ref);
-      else
-        body.try_emplace("variablesReference", 0);
-      if (lldb::addr_t addr = variable.GetLoadAddress();
-          addr != LLDB_INVALID_ADDRESS)
-        body.try_emplace("memoryReference", EncodeMemoryReference(addr));
-      if (ValuePointsToCode(variable))
-        body.try_emplace("valueLocationReference", new_var_ref);
-    } else {
-      EmplaceSafeString(body, "message", std::string(error.GetCString()));
-    }
-    response["success"] = llvm::json::Value(success);
-  } else {
-    response["success"] = llvm::json::Value(false);
-  }
-
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "VariablesRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Variables request; value of command field is 'variables'.
-//     Retrieves all child variables for the given variable reference. An
-//     optional filter can be used to limit the fetched children to either named
-//     or indexed children.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "variables" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/VariablesArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "VariablesArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'variables' request.",
-//   "properties": {
-//     "variablesReference": {
-//       "type": "integer",
-//       "description": "The Variable reference."
-//     },
-//     "filter": {
-//       "type": "string",
-//       "enum": [ "indexed", "named" ],
-//       "description": "Optional filter to limit the child variables to either
-//       named or indexed. If ommited, both types are fetched."
-//     },
-//     "start": {
-//       "type": "integer",
-//       "description": "The index of the first variable to return; if omitted
-//       children start at 0."
-//     },
-//     "count": {
-//       "type": "integer",
-//       "description": "The number of variables to return. If count is missing
-//       or 0, all variables are returned."
-//     },
-//     "format": {
-//       "$ref": "#/definitions/ValueFormat",
-//       "description": "Specifies details on how to format the Variable
-//       values."
-//     }
-//   },
-//   "required": [ "variablesReference" ]
-// },
-// "VariablesResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'variables' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "variables": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Variable"
-//             },
-//             "description": "All (or a range) of variables for the given
-//             variable reference."
-//           }
-//         },
-//         "required": [ "variables" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_variables(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Array variables;
-  const auto *arguments = request.getObject("arguments");
-  const auto variablesReference =
-      GetUnsigned(arguments, "variablesReference", 0);
-  const int64_t start = GetSigned(arguments, "start", 0);
-  const int64_t count = GetSigned(arguments, "count", 0);
-  bool hex = false;
-  const auto *format = arguments->getObject("format");
-  if (format)
-    hex = GetBoolean(format, "hex", false);
-
-  if (lldb::SBValueList *top_scope =
-          GetTopLevelScope(dap, variablesReference)) {
-    // variablesReference is one of our scopes, not an actual variable it is
-    // asking for the list of args, locals or globals.
-    int64_t start_idx = 0;
-    int64_t num_children = 0;
-
-    if (variablesReference == VARREF_REGS) {
-      // Change the default format of any pointer sized registers in the first
-      // register set to be the lldb::eFormatAddressInfo so we show the pointer
-      // and resolve what the pointer resolves to. Only change the format if the
-      // format was set to the default format or if it was hex as some registers
-      // have formats set for them.
-      const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize();
-      lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(0);
-      const uint32_t num_regs = reg_set.GetNumChildren();
-      for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
-        lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx);
-        const lldb::Format format = reg.GetFormat();
-        if (format == lldb::eFormatDefault || format == lldb::eFormatHex) {
-          if (reg.GetByteSize() == addr_size)
-            reg.SetFormat(lldb::eFormatAddressInfo);
-        }
-      }
-    }
-
-    num_children = top_scope->GetSize();
-    if (num_children == 0 && variablesReference == VARREF_LOCALS) {
-      // Check for an error in the SBValueList that might explain why we don't
-      // have locals. If we have an error display it as the sole value in the
-      // the locals.
-
-      // "error" owns the error string so we must keep it alive as long as we
-      // want to use the returns "const char *"
-      lldb::SBError error = top_scope->GetError();
-      const char *var_err = error.GetCString();
-      if (var_err) {
-        // Create a fake variable named "error" to explain why variables were
-        // not available. This new error will help let users know when there was
-        // a problem that kept variables from being available for display and
-        // allow users to fix this issue instead of seeing no variables. The
-        // errors are only set when there is a problem that the user could
-        // fix, so no error will show up when you have no debug info, only when
-        // we do have debug info and something that is fixable can be done.
-        llvm::json::Object object;
-        EmplaceSafeString(object, "name", "<error>");
-        EmplaceSafeString(object, "type", "const char *");
-        EmplaceSafeString(object, "value", var_err);
-        object.try_emplace("variablesReference", (int64_t)0);
-        variables.emplace_back(std::move(object));
-      }
-    }
-    const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
-
-    // We first find out which variable names are duplicated
-    std::map<std::string, int> variable_name_counts;
-    for (auto i = start_idx; i < end_idx; ++i) {
-      lldb::SBValue variable = top_scope->GetValueAtIndex(i);
-      if (!variable.IsValid())
-        break;
-      variable_name_counts[GetNonNullVariableName(variable)]++;
-    }
-
-    // Now we construct the result with unique display variable names
-    for (auto i = start_idx; i < end_idx; ++i) {
-      lldb::SBValue variable = top_scope->GetValueAtIndex(i);
-
-      if (!variable.IsValid())
-        break;
-
-      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_name_counts[GetNonNullVariableName(variable)] > 1));
-    }
-  } else {
-    // We are expanding a variable that has children, so we will return its
-    // children.
-    lldb::SBValue variable = dap.variables.GetVariable(variablesReference);
-    if (variable.IsValid()) {
-      auto addChild = [&](lldb::SBValue child,
-                          std::optional<std::string> custom_name = {}) {
-        if (!child.IsValid())
-          return;
-        bool is_permanent =
-            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,
-            /*is_name_duplicated=*/false, custom_name));
-      };
-      const int64_t num_children = variable.GetNumChildren();
-      int64_t end_idx = start + ((count == 0) ? num_children : count);
-      int64_t i = start;
-      for (; i < end_idx && i < num_children; ++i)
-        addChild(variable.GetChildAtIndex(i));
-
-      // If we haven't filled the count quota from the request, we insert a new
-      // "[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)
-        addChild(variable.GetNonSyntheticValue(), "[raw]");
-    }
-  }
-  llvm::json::Object body;
-  body.try_emplace("variables", std::move(variables));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "LocationsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Looks up information about a location reference
-//                     previously returned by the debug adapter.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "locations" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/LocationsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments" ]
-//   }]
-// },
-// "LocationsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `locations` request.",
-//   "properties": {
-//     "locationReference": {
-//       "type": "integer",
-//       "description": "Location reference to resolve."
-//     }
-//   },
-//   "required": [ "locationReference" ]
-// },
-// "LocationsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `locations` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "source": {
-//             "$ref": "#/definitions/Source",
-//             "description": "The source containing the location; either
-//                             `source.path` or `source.sourceReference` must be
-//                             specified."
-//           },
-//           "line": {
-//             "type": "integer",
-//             "description": "The line number of the location. The client
-//                             capability `linesStartAt1` determines whether it
-//                             is 0- or 1-based."
-//           },
-//           "column": {
-//             "type": "integer",
-//             "description": "Position of the location within the `line`. It is
-//                             measured in UTF-16 code units and the client
-//                             capability `columnsStartAt1` determines whether
-//                             it is 0- or 1-based. If no column is given, the
-//                             first position in the start line is assumed."
-//           },
-//           "endLine": {
-//             "type": "integer",
-//             "description": "End line of the location, present if the location
-//                             refers to a range.  The client capability
-//                             `linesStartAt1` determines whether it is 0- or
-//                             1-based."
-//           },
-//           "endColumn": {
-//             "type": "integer",
-//             "description": "End position of the location within `endLine`,
-//                             present if the location refers to a range. It is
-//                             measured in UTF-16 code units and the client
-//                             capability `columnsStartAt1` determines whether
-//                             it is 0- or 1-based."
-//           }
-//         },
-//         "required": [ "source", "line" ]
-//       }
-//     }
-//   }]
-// },
-void request_locations(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  auto *arguments = request.getObject("arguments");
-
-  uint64_t location_id = GetUnsigned(arguments, "locationReference", 0);
-  // We use the lowest bit to distinguish between value location and declaration
-  // location
-  auto [var_ref, is_value_location] = UnpackLocation(location_id);
-  lldb::SBValue variable = dap.variables.GetVariable(var_ref);
-  if (!variable.IsValid()) {
-    response["success"] = false;
-    response["message"] = "Invalid variable reference";
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  llvm::json::Object body;
-  if (is_value_location) {
-    // Get the value location
-    if (!variable.GetType().IsPointerType() &&
-        !variable.GetType().IsReferenceType()) {
-      response["success"] = false;
-      response["message"] =
-          "Value locations are only available for pointers and references";
-      dap.SendJSON(llvm::json::Value(std::move(response)));
-      return;
-    }
-
-    lldb::addr_t addr = variable.GetValueAsAddress();
-    lldb::SBLineEntry line_entry =
-        dap.target.ResolveLoadAddress(addr).GetLineEntry();
-
-    if (!line_entry.IsValid()) {
-      response["success"] = false;
-      response["message"] = "Failed to resolve line entry for location";
-      dap.SendJSON(llvm::json::Value(std::move(response)));
-      return;
-    }
-
-    body.try_emplace("source", CreateSource(line_entry.GetFileSpec()));
-    if (int line = line_entry.GetLine())
-      body.try_emplace("line", line);
-    if (int column = line_entry.GetColumn())
-      body.try_emplace("column", column);
-  } else {
-    // Get the declaration location
-    lldb::SBDeclaration decl = variable.GetDeclaration();
-    if (!decl.IsValid()) {
-      response["success"] = false;
-      response["message"] = "No declaration location available";
-      dap.SendJSON(llvm::json::Value(std::move(response)));
-      return;
-    }
-
-    body.try_emplace("source", CreateSource(decl.GetFileSpec()));
-    if (int line = decl.GetLine())
-      body.try_emplace("line", line);
-    if (int column = decl.GetColumn())
-      body.try_emplace("column", column);
-  }
-
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "DisassembleRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Disassembles code stored at the provided
-//     location.\nClients should only call this request if the corresponding
-//     capability `supportsDisassembleRequest` is true.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "disassemble" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/DisassembleArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments" ]
-//   }]
-// },
-// "DisassembleArguments": {
-//   "type": "object",
-//   "description": "Arguments for `disassemble` request.",
-//   "properties": {
-//     "memoryReference": {
-//       "type": "string",
-//       "description": "Memory reference to the base location containing the
-//       instructions to disassemble."
-//     },
-//     "offset": {
-//       "type": "integer",
-//       "description": "Offset (in bytes) to be applied to the reference
-//       location before disassembling. Can be negative."
-//     },
-//     "instructionOffset": {
-//       "type": "integer",
-//       "description": "Offset (in instructions) to be applied after the byte
-//       offset (if any) before disassembling. Can be negative."
-//     },
-//     "instructionCount": {
-//       "type": "integer",
-//       "description": "Number of instructions to disassemble starting at the
-//       specified location and offset.\nAn adapter must return exactly this
-//       number of instructions - any unavailable instructions should be
-//       replaced with an implementation-defined 'invalid instruction' value."
-//     },
-//     "resolveSymbols": {
-//       "type": "boolean",
-//       "description": "If true, the adapter should attempt to resolve memory
-//       addresses and other values to symbolic names."
-//     }
-//   },
-//   "required": [ "memoryReference", "instructionCount" ]
-// },
-// "DisassembleResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `disassemble` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "instructions": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/DisassembledInstruction"
-//             },
-//             "description": "The list of disassembled instructions."
-//           }
-//         },
-//         "required": [ "instructions" ]
-//       }
-//     }
-//   }]
-// }
-void request_disassemble(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  auto *arguments = request.getObject("arguments");
-
-  llvm::StringRef memoryReference = GetString(arguments, "memoryReference");
-  auto addr_opt = DecodeMemoryReference(memoryReference);
-  if (!addr_opt.has_value()) {
-    response["success"] = false;
-    response["message"] =
-        "Malformed memory reference: " + memoryReference.str();
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-  lldb::addr_t addr_ptr = *addr_opt;
-
-  addr_ptr += GetSigned(arguments, "instructionOffset", 0);
-  lldb::SBAddress addr(addr_ptr, dap.target);
-  if (!addr.IsValid()) {
-    response["success"] = false;
-    response["message"] = "Memory reference not found in the current binary.";
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  const auto inst_count = GetUnsigned(arguments, "instructionCount", 0);
-  lldb::SBInstructionList insts = dap.target.ReadInstructions(addr, inst_count);
-
-  if (!insts.IsValid()) {
-    response["success"] = false;
-    response["message"] = "Failed to find instructions for memory address.";
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  const bool resolveSymbols = GetBoolean(arguments, "resolveSymbols", false);
-  llvm::json::Array instructions;
-  const auto num_insts = insts.GetSize();
-  for (size_t i = 0; i < num_insts; ++i) {
-    lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
-    auto addr = inst.GetAddress();
-    const auto inst_addr = addr.GetLoadAddress(dap.target);
-    const char *m = inst.GetMnemonic(dap.target);
-    const char *o = inst.GetOperands(dap.target);
-    const char *c = inst.GetComment(dap.target);
-    auto d = inst.GetData(dap.target);
-
-    std::string bytes;
-    llvm::raw_string_ostream sb(bytes);
-    for (unsigned i = 0; i < inst.GetByteSize(); i++) {
-      lldb::SBError error;
-      uint8_t b = d.GetUnsignedInt8(error, i);
-      if (error.Success()) {
-        sb << llvm::format("%2.2x ", b);
-      }
-    }
-
-    llvm::json::Object disassembled_inst{
-        {"address", "0x" + llvm::utohexstr(inst_addr)},
-        {"instructionBytes",
-         bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""},
-    };
-
-    std::string instruction;
-    llvm::raw_string_ostream si(instruction);
-
-    lldb::SBSymbol symbol = addr.GetSymbol();
-    // Only add the symbol on the first line of the function.
-    if (symbol.IsValid() && symbol.GetStartAddress() == addr) {
-      // If we have a valid symbol, append it as a label prefix for the first
-      // instruction. This is so you can see the start of a function/callsite
-      // in the assembly, at the moment VS Code (1.80) does not visualize the
-      // symbol associated with the assembly instruction.
-      si << (symbol.GetMangledName() != nullptr ? symbol.GetMangledName()
-                                                : symbol.GetName())
-         << ": ";
-
-      if (resolveSymbols) {
-        disassembled_inst.try_emplace("symbol", symbol.GetDisplayName());
-      }
-    }
-
-    si << llvm::formatv("{0,7} {1,12}", m, o);
-    if (c && c[0]) {
-      si << " ; " << c;
-    }
-
-    disassembled_inst.try_emplace("instruction", instruction);
-
-    auto line_entry = addr.GetLineEntry();
-    // If the line number is 0 then the entry represents a compiler generated
-    // location.
-    if (line_entry.GetStartAddress() == addr && line_entry.IsValid() &&
-        line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) {
-      auto source = CreateSource(line_entry);
-      disassembled_inst.try_emplace("location", source);
-
-      const auto line = line_entry.GetLine();
-      if (line && line != LLDB_INVALID_LINE_NUMBER) {
-        disassembled_inst.try_emplace("line", line);
-      }
-      const auto column = line_entry.GetColumn();
-      if (column && column != LLDB_INVALID_COLUMN_NUMBER) {
-        disassembled_inst.try_emplace("column", column);
-      }
-
-      auto end_line_entry = line_entry.GetEndAddress().GetLineEntry();
-      if (end_line_entry.IsValid() &&
-          end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) {
-        const auto end_line = end_line_entry.GetLine();
-        if (end_line && end_line != LLDB_INVALID_LINE_NUMBER &&
-            end_line != line) {
-          disassembled_inst.try_emplace("endLine", end_line);
-
-          const auto end_column = end_line_entry.GetColumn();
-          if (end_column && end_column != LLDB_INVALID_COLUMN_NUMBER &&
-              end_column != column) {
-            disassembled_inst.try_emplace("endColumn", end_column - 1);
-          }
-        }
-      }
-    }
-
-    instructions.emplace_back(std::move(disassembled_inst));
-  }
-
-  llvm::json::Object body;
-  body.try_emplace("instructions", std::move(instructions));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "ReadMemoryRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Reads bytes from memory at the provided location. Clients
-//                     should only call this request if the corresponding
-//                     capability `supportsReadMemoryRequest` is true.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "readMemory" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/ReadMemoryArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments" ]
-//   }]
-// },
-// "ReadMemoryArguments": {
-//   "type": "object",
-//   "description": "Arguments for `readMemory` request.",
-//   "properties": {
-//     "memoryReference": {
-//       "type": "string",
-//       "description": "Memory reference to the base location from which data
-//                       should be read."
-//     },
-//     "offset": {
-//       "type": "integer",
-//       "description": "Offset (in bytes) to be applied to the reference
-//                       location before reading data. Can be negative."
-//     },
-//     "count": {
-//       "type": "integer",
-//       "description": "Number of bytes to read at the specified location and
-//                       offset."
-//     }
-//   },
-//   "required": [ "memoryReference", "count" ]
-// },
-// "ReadMemoryResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `readMemory` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "address": {
-//             "type": "string",
-//             "description": "The address of the first byte of data returned.
-//                             Treated as a hex value if prefixed with `0x`, or
-//                             as a decimal value otherwise."
-//           },
-//           "unreadableBytes": {
-//             "type": "integer",
-//             "description": "The number of unreadable bytes encountered after
-//                             the last successfully read byte.\nThis can be
-//                             used to determine the number of bytes that should
-//                             be skipped before a subsequent
-//             `readMemory` request succeeds."
-//           },
-//           "data": {
-//             "type": "string",
-//             "description": "The bytes read from memory, encoded using base64.
-//                             If the decoded length of `data` is less than the
-//                             requested `count` in the original `readMemory`
-//                             request, and `unreadableBytes` is zero or
-//                             omitted, then the client should assume it's
-//                             reached the end of readable memory."
-//           }
-//         },
-//         "required": [ "address" ]
-//       }
-//     }
-//   }]
-// },
-void request_readMemory(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  auto *arguments = request.getObject("arguments");
-
-  llvm::StringRef memoryReference = GetString(arguments, "memoryReference");
-  auto addr_opt = DecodeMemoryReference(memoryReference);
-  if (!addr_opt.has_value()) {
-    response["success"] = false;
-    response["message"] =
-        "Malformed memory reference: " + memoryReference.str();
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-  lldb::addr_t addr_int = *addr_opt;
-  addr_int += GetSigned(arguments, "offset", 0);
-  const uint64_t count_requested = GetUnsigned(arguments, "count", 0);
-
-  // We also need support reading 0 bytes
-  // VS Code sends those requests to check if a `memoryReference`
-  // can be dereferenced.
-  const uint64_t count_read = std::max<uint64_t>(count_requested, 1);
-  std::vector<uint8_t> buf;
-  buf.resize(count_read);
-  lldb::SBError error;
-  lldb::SBAddress addr{addr_int, dap.target};
-  size_t count_result =
-      dap.target.ReadMemory(addr, buf.data(), count_read, error);
-  if (count_result == 0) {
-    response["success"] = false;
-    EmplaceSafeString(response, "message", error.GetCString());
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-  buf.resize(std::min<size_t>(count_result, count_requested));
-
-  llvm::json::Object body;
-  std::string formatted_addr = "0x" + llvm::utohexstr(addr_int);
-  body.try_emplace("address", formatted_addr);
-  body.try_emplace("data", llvm::encodeBase64(buf));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
@@ -1456,21 +132,31 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
   dap.RegisterRequest<ContinueRequestHandler>();
   dap.RegisterRequest<DataBreakpointInfoRequestHandler>();
+  dap.RegisterRequest<DisassembleRequestHandler>();
   dap.RegisterRequest<DisconnectRequestHandler>();
   dap.RegisterRequest<EvaluateRequestHandler>();
   dap.RegisterRequest<ExceptionInfoRequestHandler>();
   dap.RegisterRequest<InitializeRequestHandler>();
   dap.RegisterRequest<LaunchRequestHandler>();
+  dap.RegisterRequest<LocationsRequestHandler>();
   dap.RegisterRequest<NextRequestHandler>();
+  dap.RegisterRequest<PauseRequestHandler>();
+  dap.RegisterRequest<ReadMemoryRequestHandler>();
   dap.RegisterRequest<RestartRequestHandler>();
+  dap.RegisterRequest<ScopesRequestHandler>();
   dap.RegisterRequest<SetBreakpointsRequestHandler>();
   dap.RegisterRequest<SetDataBreakpointsRequestHandler>();
   dap.RegisterRequest<SetExceptionBreakpointsRequestHandler>();
   dap.RegisterRequest<SetFunctionBreakpointsRequestHandler>();
   dap.RegisterRequest<SetInstructionBreakpointsRequestHandler>();
+  dap.RegisterRequest<SetVariableRequestHandler>();
+  dap.RegisterRequest<SourceRequestHandler>();
+  dap.RegisterRequest<StackTraceRequestHandler>();
   dap.RegisterRequest<StepInRequestHandler>();
   dap.RegisterRequest<StepInTargetsRequestHandler>();
   dap.RegisterRequest<StepOutRequestHandler>();
+  dap.RegisterRequest<ThreadsRequestHandler>();
+  dap.RegisterRequest<VariablesRequestHandler>();
 
   // Custom requests
   dap.RegisterRequest<CompileUnitsRequestHandler>();
@@ -1479,16 +165,9 @@ void RegisterRequestCallbacks(DAP &dap) {
   // Testing requests
   dap.RegisterRequest<TestGetTargetBreakpointsRequestHandler>();
 
-  dap.RegisterRequestCallback("pause", request_pause);
-  dap.RegisterRequestCallback("scopes", request_scopes);
-  dap.RegisterRequestCallback("setVariable", request_setVariable);
-  dap.RegisterRequestCallback("source", request_source);
-  dap.RegisterRequestCallback("stackTrace", request_stackTrace);
-  dap.RegisterRequestCallback("threads", request_threads);
-  dap.RegisterRequestCallback("variables", request_variables);
-  dap.RegisterRequestCallback("locations", request_locations);
-  dap.RegisterRequestCallback("disassemble", request_disassemble);
+#if 0
   dap.RegisterRequestCallback("readMemory", request_readMemory);
+#endif
 }
 
 } // anonymous namespace

>From 76c1fd886868c1f330e79997a499330ee308dee2 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 24 Feb 2025 12:50:21 -0600
Subject: [PATCH 5/5] [lldb-dap] Finish refactoring the request handlers (NFC)

This removes the old way of register request handlers with callbacks.
---
 lldb/tools/lldb-dap/DAP.cpp      | 17 ++---------------
 lldb/tools/lldb-dap/DAP.h        | 19 ++-----------------
 lldb/tools/lldb-dap/lldb-dap.cpp |  6 ------
 3 files changed, 4 insertions(+), 38 deletions(-)

diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 9b22b60a68d94..d5dd0304f7221 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -758,20 +758,12 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
   if (packet_type == "request") {
     const auto command = GetString(object, "command");
 
-    // Try the new request handler first.
-    auto new_handler_pos = new_request_handlers.find(command);
-    if (new_handler_pos != new_request_handlers.end()) {
+    auto new_handler_pos = request_handlers.find(command);
+    if (new_handler_pos != request_handlers.end()) {
       (*new_handler_pos->second)(object);
       return true; // Success
     }
 
-    // FIXME: Remove request_handlers once everything has been migrated.
-    auto handler_pos = request_handlers.find(command);
-    if (handler_pos != request_handlers.end()) {
-      handler_pos->second(*this, object);
-      return true; // Success
-    }
-
     if (log)
       *log << "error: unhandled command \"" << command.data() << "\""
            << std::endl;
@@ -900,11 +892,6 @@ void DAP::SendReverseRequest(llvm::StringRef command,
   });
 }
 
-void DAP::RegisterRequestCallback(std::string request,
-                                  RequestCallback callback) {
-  request_handlers[request] = callback;
-}
-
 lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
   lldb::SBError error;
   lldb::SBProcess process = target.GetProcess();
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 18de39838f218..ddda8603c189f 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -68,7 +68,6 @@ enum DAPBroadcasterBits {
   eBroadcastBitStopProgressThread = 1u << 1
 };
 
-typedef void (*RequestCallback)(DAP &dap, const llvm::json::Object &command);
 typedef void (*ResponseCallback)(llvm::Expected<llvm::json::Value> value);
 
 enum class PacketStatus {
@@ -186,8 +185,7 @@ struct DAP {
   // the old process here so we can detect this case and keep running.
   lldb::pid_t restarting_process_id;
   bool configuration_done_sent;
-  std::map<std::string, RequestCallback, std::less<>> request_handlers;
-  llvm::StringMap<std::unique_ptr<RequestHandler>> new_request_handlers;
+  llvm::StringMap<std::unique_ptr<RequestHandler>> request_handlers;
   bool waiting_for_run_in_terminal;
   ProgressEventReporter progress_event_reporter;
   // Keep track of the last stop thread index IDs as threads won't go away
@@ -305,8 +303,6 @@ struct DAP {
   /// listeing for its breakpoint events.
   void SetTarget(const lldb::SBTarget target);
 
-  const std::map<std::string, RequestCallback> &GetRequestHandlers();
-
   PacketStatus GetNextObject(llvm::json::Object &object);
   bool HandleObject(const llvm::json::Object &object);
 
@@ -334,20 +330,9 @@ struct DAP {
   void SendReverseRequest(llvm::StringRef command, llvm::json::Value arguments,
                           ResponseCallback callback);
 
-  /// Registers a callback handler for a Debug Adapter Protocol request
-  ///
-  /// \param[in] request
-  ///     The name of the request following the Debug Adapter Protocol
-  ///     specification.
-  ///
-  /// \param[in] callback
-  ///     The callback to execute when the given request is triggered by the
-  ///     IDE.
-  void RegisterRequestCallback(std::string request, RequestCallback callback);
-
   /// Registers a request handler.
   template <typename Handler> void RegisterRequest() {
-    new_request_handlers[Handler::getCommand()] =
+    request_handlers[Handler::getCommand()] =
         std::make_unique<Handler>(*this);
   }
 
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index fcf2d122e5313..5345d57a0b910 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -123,8 +123,6 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable {
                                    InfoTable, true) {}
 };
 
-typedef void (*RequestCallback)(const llvm::json::Object &command);
-
 void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
@@ -164,10 +162,6 @@ void RegisterRequestCallbacks(DAP &dap) {
 
   // Testing requests
   dap.RegisterRequest<TestGetTargetBreakpointsRequestHandler>();
-
-#if 0
-  dap.RegisterRequestCallback("readMemory", request_readMemory);
-#endif
 }
 
 } // anonymous namespace



More information about the lldb-commits mailing list