https://github.com/ashgti updated 
https://github.com/llvm/llvm-project/pull/142831

>From 0824e3c626ad5e8c2ce37f2fdfcf0c2fa47b7b1d Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Wed, 4 Jun 2025 11:03:37 -0700
Subject: [PATCH 1/4] [lldb-dap] Creating a 'capabilities' event helper.

This adds a new 'CapabilitiesEventBody' type for having a well structured type 
for the event and updates the 'stepInTargets' and 'restart' request to 
dynamically set their capabilities.

This also fixes the 'stepInTargets' test on non-x86 platforms.
---
 .../test/tools/lldb-dap/dap_server.py         | 25 +++++++---
 .../tools/lldb-dap/launch/TestDAP_launch.py   |  2 +-
 .../stepInTargets/TestDAP_stepInTargets.py    |  2 +
 lldb/tools/lldb-dap/CMakeLists.txt            |  1 +
 lldb/tools/lldb-dap/EventHelper.cpp           | 18 ++++++++
 lldb/tools/lldb-dap/EventHelper.h             |  3 ++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  3 --
 .../Handler/RestartRequestHandler.cpp         | 17 -------
 lldb/tools/lldb-dap/Protocol/ProtocolBase.h   |  4 +-
 .../lldb-dap/Protocol/ProtocolEvents.cpp      | 21 +++++++++
 lldb/tools/lldb-dap/Protocol/ProtocolEvents.h | 46 +++++++++++++++++++
 11 files changed, 112 insertions(+), 30 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
 create mode 100644 lldb/tools/lldb-dap/Protocol/ProtocolEvents.h

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 6b41aef2bb5b8..c3c8dbb12e9d0 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -135,6 +135,10 @@ def as_dict(self):
         return source_dict
 
 
+class NotSupportedError(Exception):
+    """Raised if a feature is not supported due to its capabilities."""
+
+
 class DebugCommunication(object):
     def __init__(
         self,
@@ -153,7 +157,7 @@ def __init__(
         self.recv_thread = threading.Thread(target=self._read_packet_thread)
         self.process_event_body = None
         self.exit_status: Optional[int] = None
-        self.initialize_body = None
+        self.capabilities: dict[str, Any] = {}
         self.progress_events: list[Event] = []
         self.reverse_requests = []
         self.sequence = 1
@@ -300,6 +304,9 @@ def _handle_recv_packet(self, packet: 
Optional[ProtocolMessage]) -> bool:
             elif event == "breakpoint":
                 # Breakpoint events are sent when a breakpoint is resolved
                 self._update_verified_breakpoints([body["breakpoint"]])
+            elif event == "capabilities":
+                # Update the capabilities with new ones from the event.
+                self.capabilities.update(body["capabilities"])
 
         elif packet_type == "response":
             if packet["command"] == "disconnect":
@@ -488,13 +495,11 @@ def wait_for_terminated(self, timeout: Optional[float] = 
None):
             raise ValueError("didn't get terminated event")
         return event_dict
 
-    def get_initialize_value(self, key):
+    def get_capability(self, key, default=None):
         """Get a value for the given key if it there is a key/value pair in
-        the "initialize" request response body.
+        the capabilities reported by the adapter.
         """
-        if self.initialize_body and key in self.initialize_body:
-            return self.initialize_body[key]
-        return None
+        return self.capabilities.get(key, default)
 
     def get_threads(self):
         if self.threads is None:
@@ -760,6 +765,10 @@ def request_continue(self, threadId=None, 
singleThread=False):
         return response
 
     def request_restart(self, restartArguments=None):
+        if self.exit_status is not None:
+            raise ValueError("request_restart called after process exited")
+        if not self.get_capability("supportsRestartRequest", False):
+            raise NotSupportedError("supportsRestartRequest is not set")
         command_dict = {
             "command": "restart",
             "type": "request",
@@ -867,7 +876,7 @@ def request_initialize(self, sourceInitFile=False):
         response = self.send_recv(command_dict)
         if response:
             if "body" in response:
-                self.initialize_body = response["body"]
+                self.capabilities = response["body"]
         return response
 
     def request_launch(
@@ -972,6 +981,8 @@ def request_stepIn(self, threadId, targetId, 
granularity="statement"):
     def request_stepInTargets(self, frameId):
         if self.exit_status is not None:
             raise ValueError("request_stepInTargets called after process 
exited")
+        if not self.get_capability("supportsStepInTargetsRequest", False):
+            raise NotSupportedError("supportsStepInTargetsRequest is not set")
         args_dict = {"frameId": frameId}
         command_dict = {
             "command": "stepInTargets",
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py 
b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index c24df9a9bfcf4..ae8142ae4f484 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -566,7 +566,7 @@ def test_version(self):
         )
         version_eval_output = version_eval_response["body"]["result"]
 
-        version_string = 
self.dap_server.get_initialize_value("$__lldb_version")
+        version_string = self.dap_server.get_capability("$__lldb_version")
         self.assertEqual(
             version_eval_output.splitlines(),
             version_string.splitlines(),
diff --git 
a/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py 
b/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py
index 07acfe07c9ffc..a71aa729f1390 100644
--- a/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py
+++ b/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py
@@ -30,6 +30,8 @@ def test_basic(self):
         self.assertEqual(
             len(breakpoint_ids), len(lines), "expect correct number of 
breakpoints"
         )
+        # Target based capability 'supportsStepInTargetsRequest' is sent in
+        # 'configurationDone' which is called prior to continue.
         self.continue_to_breakpoints(breakpoint_ids)
 
         threads = self.dap_server.get_threads()
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt 
b/lldb/tools/lldb-dap/CMakeLists.txt
index f65c987d4c9ce..8bbb402fdf782 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -67,6 +67,7 @@ add_lldb_library(lldbDAP
   Handler/VariablesRequestHandler.cpp
 
   Protocol/ProtocolBase.cpp
+  Protocol/ProtocolEvents.cpp
   Protocol/ProtocolTypes.cpp
   Protocol/ProtocolRequests.cpp
 
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp 
b/lldb/tools/lldb-dap/EventHelper.cpp
index c698084836e2f..3d46a6381ff9c 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -11,6 +11,8 @@
 #include "DAPLog.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
+#include "Protocol/ProtocolEvents.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/API/SBFileSpec.h"
 
 #if defined(_WIN32)
@@ -33,6 +35,22 @@ static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) 
{
   dap.SendJSON(llvm::json::Value(std::move(event)));
 }
 
+void SendTargetBasedCapabilities(DAP &dap) {
+  if (!dap.target.IsValid())
+    return;
+
+  protocol::CapabilitiesEventBody body;
+
+  // We only support restarting launch requests not attach requests.
+  if (dap.last_launch_request)
+    body.capabilities.supportedFeatures.insert(
+        protocol::eAdapterFeatureRestartRequest);
+
+  // Only notify the client if supportedFeatures changed.
+  if (!body.capabilities.supportedFeatures.empty())
+    dap.Send(protocol::Event{"capabilities", body});
+}
+
 // "ProcessEvent": {
 //   "allOf": [
 //     { "$ref": "#/definitions/Event" },
diff --git a/lldb/tools/lldb-dap/EventHelper.h 
b/lldb/tools/lldb-dap/EventHelper.h
index 90b009c73089e..bf177d0017075 100644
--- a/lldb/tools/lldb-dap/EventHelper.h
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -16,6 +16,9 @@ struct DAP;
 
 enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
 
+/// Update capabilities based on the configured target.
+void SendTargetBasedCapabilities(DAP &dap);
+
 void SendProcessEvent(DAP &dap, LaunchMethod launch_method);
 
 void SendThreadStoppedEvent(DAP &dap);
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h 
b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 3a965bcc87a5e..0430bb12af703 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -334,9 +334,6 @@ class RestartRequestHandler : public LegacyRequestHandler {
 public:
   using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral GetCommand() { return "restart"; }
-  FeatureSet GetSupportedFeatures() const override {
-    return {protocol::eAdapterFeatureRestartRequest};
-  }
   void operator()(const llvm::json::Object &request) const override;
 };
 
diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index 58091b622f7e5..c7ef63472ad21 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -69,23 +69,6 @@ void RestartRequestHandler::operator()(
     dap.SendJSON(llvm::json::Value(std::move(response)));
     return;
   }
-  // Check if we were in a "launch" session or an "attach" session.
-  //
-  // Restarting is not well defined when we started the session by attaching to
-  // an existing process, because we don't know how the process was started, so
-  // we don't support it.
-  //
-  // Note that when using runInTerminal we're technically attached, but it's an
-  // implementation detail. The adapter *did* launch the process in response to
-  // a "launch" command, so we can still stop it and re-run it. This is why we
-  // don't just check `dap.is_attach`.
-  if (!dap.last_launch_request) {
-    response["success"] = llvm::json::Value(false);
-    EmplaceSafeString(response, "message",
-                      "Restarting an \"attach\" session is not supported.");
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
 
   const llvm::json::Object *arguments = request.getObject("arguments");
   if (arguments) {
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h 
b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
index 724da59b50cd2..81496380d412f 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
@@ -17,8 +17,8 @@
 //
 
//===----------------------------------------------------------------------===//
 
-#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
-#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
+#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H
+#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H
 
 #include "llvm/Support/JSON.h"
 #include <cstdint>
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp 
b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
new file mode 100644
index 0000000000000..ec4fef76f9d27
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
@@ -0,0 +1,21 @@
+//===-- ProtocolEvents.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 "Protocol/ProtocolEvents.h"
+#include "llvm/Support/JSON.h"
+
+using namespace llvm;
+
+namespace lldb_dap::protocol {
+
+json::Value toJSON(const CapabilitiesEventBody &CEB) {
+  return json::Object{{"capabilities", CEB.capabilities}};
+}
+
+} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h 
b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h
new file mode 100644
index 0000000000000..512106222362c
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h
@@ -0,0 +1,46 @@
+//===-- ProtocolEvents.h 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains POD structs based on the DAP specification at
+// https://microsoft.github.io/debug-adapter-protocol/specification
+//
+// This is not meant to be a complete implementation, new interfaces are added
+// when they're needed.
+//
+// Each struct has a toJSON and fromJSON function, that converts between
+// the struct and a JSON representation. (See JSON.h)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_EVENTS_H
+#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_EVENTS_H
+
+#include "Protocol/ProtocolTypes.h"
+#include "llvm/Support/JSON.h"
+
+namespace lldb_dap::protocol {
+
+/// The event indicates that one or more capabilities have changed.
+///
+/// Since the capabilities are dependent on the client and its UI, it might not
+/// be possible to change that at random times (or too late).
+///
+/// Consequently this event has a hint characteristic: a client can only be
+/// expected to make a 'best effort' in honoring individual capabilities but
+/// there are no guarantees.
+///
+/// Only changed capabilities need to be included, all other capabilities keep
+/// their values.
+struct CapabilitiesEventBody {
+  Capabilities capabilities;
+};
+llvm::json::Value toJSON(const CapabilitiesEventBody &);
+
+} // end namespace lldb_dap::protocol
+
+#endif

>From a40ba03029619b5ae481c9a8d93a04c6c387dee9 Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Wed, 4 Jun 2025 12:28:30 -0700
Subject: [PATCH 2/4] Creating a dedicated assert for capabilities and
 adjusting 'get_capability' to raise.

---
 .../lldbsuite/test/tools/lldb-dap/dap_server.py    | 14 +++++++-------
 .../test/tools/lldb-dap/lldbdap_testcase.py        | 10 ++++++++++
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index c3c8dbb12e9d0..33800fdcfd89e 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -135,7 +135,7 @@ def as_dict(self):
         return source_dict
 
 
-class NotSupportedError(Exception):
+class NotSupportedError(KeyError):
     """Raised if a feature is not supported due to its capabilities."""
 
 
@@ -495,11 +495,13 @@ def wait_for_terminated(self, timeout: Optional[float] = 
None):
             raise ValueError("didn't get terminated event")
         return event_dict
 
-    def get_capability(self, key, default=None):
+    def get_capability(self, key):
         """Get a value for the given key if it there is a key/value pair in
         the capabilities reported by the adapter.
         """
-        return self.capabilities.get(key, default)
+        if key in self.capabilities:
+            return self.capabilities[key]
+        raise NotSupportedError(key)
 
     def get_threads(self):
         if self.threads is None:
@@ -767,8 +769,7 @@ def request_continue(self, threadId=None, 
singleThread=False):
     def request_restart(self, restartArguments=None):
         if self.exit_status is not None:
             raise ValueError("request_restart called after process exited")
-        if not self.get_capability("supportsRestartRequest", False):
-            raise NotSupportedError("supportsRestartRequest is not set")
+        self.get_capability("supportsRestartRequest")
         command_dict = {
             "command": "restart",
             "type": "request",
@@ -981,8 +982,7 @@ def request_stepIn(self, threadId, targetId, 
granularity="statement"):
     def request_stepInTargets(self, frameId):
         if self.exit_status is not None:
             raise ValueError("request_stepInTargets called after process 
exited")
-        if not self.get_capability("supportsStepInTargetsRequest", False):
-            raise NotSupportedError("supportsStepInTargetsRequest is not set")
+        self.get_capability("supportsStepInTargetsRequest")
         args_dict = {"frameId": frameId}
         command_dict = {
             "command": "stepInTargets",
diff --git 
a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index e26dbf6ae514f..2fe7580d68482 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -128,6 +128,16 @@ def waitUntil(self, condition_callback):
             time.sleep(0.5)
         return False
 
+    def assertCapabilityIsSet(self, key: str, msg: Optional[str] = None) -> 
None:
+        """Assert that given capability is set in the client."""
+        self.assertIn(key, self.dap_server.capabilities, msg)
+        self.assertTrue(self.dap_server.capabilities[key], msg)
+
+    def assertCapabilityIsNotSet(self, key: str, msg: Optional[str] = None) -> 
None:
+        """Assert that given capability is not set in the client."""
+        if key in self.dap_server.capabilities:
+            self.assertFalse(self.dap_server.capabilities[key], msg)
+
     def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
         """Wait for the process we are debugging to stop, and verify we hit
         any breakpoint location in the "breakpoint_ids" array.

>From 4682407b3110e8449beaf00ec5743cf1447fdaeb Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Wed, 4 Jun 2025 12:41:48 -0700
Subject: [PATCH 3/4] Using `assertEqual` instead of
 `assertTrue`/`assertFalse`.

---
 .../Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git 
a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 2fe7580d68482..3b54d598c3509 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -131,12 +131,12 @@ def waitUntil(self, condition_callback):
     def assertCapabilityIsSet(self, key: str, msg: Optional[str] = None) -> 
None:
         """Assert that given capability is set in the client."""
         self.assertIn(key, self.dap_server.capabilities, msg)
-        self.assertTrue(self.dap_server.capabilities[key], msg)
+        self.assertEqual(self.dap_server.capabilities[key], True, msg)
 
     def assertCapabilityIsNotSet(self, key: str, msg: Optional[str] = None) -> 
None:
         """Assert that given capability is not set in the client."""
         if key in self.dap_server.capabilities:
-            self.assertFalse(self.dap_server.capabilities[key], msg)
+            self.assertEqual(self.dap_server.capabilities[key], False, msg)
 
     def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
         """Wait for the process we are debugging to stop, and verify we hit

>From c099f044409dafb0973acaaea523a28a9a17b5ca Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Thu, 5 Jun 2025 14:04:02 -0700
Subject: [PATCH 4/4] Rebasing work on main.

---
 .../lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp     | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
index 1281857ef4b60..28fe4228bb7f6 100644
--- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
@@ -40,6 +40,11 @@ ConfigurationDoneRequestHandler::Run(const 
ConfigurationDoneArguments &) const {
         "any debugger command scripts are not resuming the process during the "
         "launch sequence.");
 
+  // Waiting until 'configurationDone' to send target based capabilities in 
case
+  // the launch or attach scripts adjust the target. The initial dummy target
+  // may have different capabilities than the final target.
+  SendTargetBasedCapabilities(dap);
+
   // Clients can request a baseline of currently existing threads after
   // we acknowledge the configurationDone request.
   // Client requests the baseline of currently existing threads after

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

Reply via email to