https://github.com/da-viper updated 
https://github.com/llvm/llvm-project/pull/141122

>From a839dde6f9be0d3cbec73b9c001144b6cdbdc732 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimy...@gmail.com>
Date: Wed, 21 May 2025 23:26:14 +0100
Subject: [PATCH 1/4] [lldb][lldb-dap] support DataBreakpointBytes capability

---
 .../DataBreakpointInfoRequestHandler.cpp      | 48 ++++++++++++++++++-
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  3 ++
 .../lldb-dap/Protocol/ProtocolRequests.h      |  2 +-
 3 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 8cb25d0603449..9b969560d7973 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -7,7 +7,6 @@
 
//===----------------------------------------------------------------------===//
 
 #include "DAP.h"
-#include "EventHelper.h"
 #include "Protocol/ProtocolTypes.h"
 #include "RequestHandler.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
@@ -16,12 +15,59 @@
 
 namespace lldb_dap {
 
+static llvm::Expected<protocol::DataBreakpointInfoResponseBody>
+HandleDataBreakpointBytes(DAP &dap,
+                          const protocol::DataBreakpointInfoArguments &args) {
+  llvm::StringRef address = args.name;
+
+  unsigned long long load_addr = LLDB_INVALID_ADDRESS;
+  if (llvm::getAsUnsignedInteger(address, 0, load_addr)) {
+    return llvm::make_error<DAPError>(llvm::formatv("invalid address"),
+                                      llvm::inconvertibleErrorCode(), false);
+  }
+
+  lldb::SBAddress sb_addr(load_addr, dap.target);
+  if (!sb_addr.IsValid()) {
+    return llvm::make_error<DAPError>(
+        llvm::formatv("address {:x} does not exist in the debuggee", 
load_addr),
+        llvm::inconvertibleErrorCode(), false);
+  }
+
+  const uint32_t byte_size =
+      args.bytes.value_or(dap.target.GetAddressByteSize());
+
+  protocol::DataBreakpointInfoResponseBody response;
+  response.dataId = llvm::formatv("{:x}/{}", load_addr, byte_size);
+
+  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() && !(region.IsReadable() || region.IsWritable())) {
+    response.description = llvm::formatv(
+        "memory region for address {} has no read or write permissions",
+        load_addr);
+  } else {
+    response.description = llvm::formatv("{} bytes at {:x}", load_addr);
+    response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
+                            protocol::eDataBreakpointAccessTypeWrite,
+                            protocol::eDataBreakpointAccessTypeReadWrite};
+  }
+
+  return response;
+}
+
 /// Obtains information on a possible data breakpoint that could be set on an
 /// expression or variable. Clients should only call this request if the
 /// corresponding capability supportsDataBreakpoints is true.
 llvm::Expected<protocol::DataBreakpointInfoResponseBody>
 DataBreakpointInfoRequestHandler::Run(
     const protocol::DataBreakpointInfoArguments &args) const {
+
+  if (args.asAddress.value_or(false))
+    return HandleDataBreakpointBytes(dap, args);
+
   protocol::DataBreakpointInfoResponseBody response;
   lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX));
   lldb::SBValue variable = dap.variables.FindVariable(
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h 
b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 3a965bcc87a5e..dec68683fee65 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -420,6 +420,9 @@ class DataBreakpointInfoRequestHandler
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; }
+  FeatureSet GetSupportedFeatures() const override {
+    return {protocol::eAdapterFeatureDataBreakpointBytes};
+  }
   llvm::Expected<protocol::DataBreakpointInfoResponseBody>
   Run(const protocol::DataBreakpointInfoArguments &args) const override;
 };
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h 
b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 7c774e50d6e56..cde441351fc88 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -668,7 +668,7 @@ struct DataBreakpointInfoArguments {
   /// pause on data access anywhere within that range.
   /// Clients may set this property only if the `supportsDataBreakpointBytes`
   /// capability is true.
-  std::optional<int64_t> bytes;
+  std::optional<uint64_t> bytes;
 
   /// If `true`, the `name` is a memory address and the debugger should
   /// interpret it as a decimal value, or hex value if it is prefixed with 
`0x`.

>From 7abbc1f90272587e225ed6ba4a978661c9449cab Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimy...@gmail.com>
Date: Thu, 22 May 2025 15:18:57 +0100
Subject: [PATCH 2/4] [lldb][lldb-dap] support DataBreakpointBytes capability

---
 .../Handler/DataBreakpointInfoRequestHandler.cpp     | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 9b969560d7973..2e429c045cdc0 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -37,7 +37,7 @@ HandleDataBreakpointBytes(DAP &dap,
       args.bytes.value_or(dap.target.GetAddressByteSize());
 
   protocol::DataBreakpointInfoResponseBody response;
-  response.dataId = llvm::formatv("{:x}/{}", load_addr, byte_size);
+  response.dataId = llvm::formatv("{:x-}/{}", load_addr, byte_size);
 
   lldb::SBMemoryRegionInfo region;
   lldb::SBError err =
@@ -48,8 +48,10 @@ HandleDataBreakpointBytes(DAP &dap,
     response.description = llvm::formatv(
         "memory region for address {} has no read or write permissions",
         load_addr);
+
   } else {
-    response.description = llvm::formatv("{} bytes at {:x}", load_addr);
+    response.description =
+        llvm::formatv("{} bytes at {:x}", byte_size, load_addr);
     response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
                             protocol::eDataBreakpointAccessTypeWrite,
                             protocol::eDataBreakpointAccessTypeReadWrite};
@@ -86,7 +88,7 @@ DataBreakpointInfoRequestHandler::Run(
       is_data_ok = false;
       response.description = "variable size is 0";
     } else {
-      addr = llvm::utohexstr(load_addr);
+      addr = llvm::utohexstr(load_addr, /*lowerCase=*/true);
       size = llvm::utostr(byte_size);
     }
   } else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
@@ -103,7 +105,7 @@ DataBreakpointInfoRequestHandler::Run(
       lldb::SBData data = value.GetPointeeData();
       if (data.IsValid()) {
         size = llvm::utostr(data.GetByteSize());
-        addr = llvm::utohexstr(load_addr);
+        addr = llvm::utohexstr(load_addr, /*lowerCase=*/true);
         lldb::SBMemoryRegionInfo region;
         lldb::SBError err =
             dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
@@ -132,7 +134,7 @@ DataBreakpointInfoRequestHandler::Run(
     response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
                             protocol::eDataBreakpointAccessTypeWrite,
                             protocol::eDataBreakpointAccessTypeReadWrite};
-    response.description = size + " bytes at " + addr + " " + args.name;
+    response.description = size + " bytes at 0x" + addr + " " + args.name;
   }
 
   return response;

>From e11eeee4cba135590a5f39f4ecdd170cf3a18132 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimy...@gmail.com>
Date: Thu, 22 May 2025 19:19:07 +0100
Subject: [PATCH 3/4] [lldb][lldb-dap] add DataBreakpointBytes test case

---
 .../test/tools/lldb-dap/dap_server.py         | 33 ++++++++----
 .../test/tools/lldb-dap/lldbdap_testcase.py   | 19 ++++---
 .../TestDAP_setDataBreakpoints.py             | 53 ++++++++++++++++---
 .../tools/lldb-dap/databreakpoint/main.cpp    |  3 ++
 4 files changed, 87 insertions(+), 21 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 a028381a0a4f9..2468ca7e37f14 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
@@ -1042,16 +1042,31 @@ def request_setFunctionBreakpoints(self, names, 
condition=None, hitCondition=Non
         return self.send_recv(command_dict)
 
     def request_dataBreakpointInfo(
-        self, variablesReference, name, frameIndex=0, threadId=None
+            self,
+            name: str,
+            variablesReference: int = None,
+            frameIndex: int = 0,
+            bytes_: int = None,
+            asAddress: bool = None,
     ):
-        stackFrame = self.get_stackFrame(frameIndex=frameIndex, 
threadId=threadId)
-        if stackFrame is None:
-            return []
-        args_dict = {
-            "variablesReference": variablesReference,
-            "name": name,
-            "frameId": stackFrame["id"],
-        }
+
+        args_dict = {}
+        if asAddress is not None:
+            args_dict = {
+                "name": name,
+                "asAddress": asAddress,
+                "bytes": bytes_,
+            }
+        else:
+            stackFrame = self.get_stackFrame(frameIndex=frameIndex, 
threadId=None)
+            if stackFrame is None:
+                return []
+            args_dict = {
+                "variablesReference": variablesReference,
+                "name": name,
+                "frameId": stackFrame["id"],
+            }
+
         command_dict = {
             "command": "dataBreakpointInfo",
             "type": "request",
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 91ae55977046b..996c85ea69622 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
@@ -104,7 +104,9 @@ def waitUntil(self, condition_callback):
             time.sleep(0.5)
         return False
 
-    def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+    def verify_breakpoint_hit(
+            self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+    ):
         """Wait for the process we are debugging to stop, and verify we hit
         any breakpoint location in the "breakpoint_ids" array.
         "breakpoint_ids" should be a list of breakpoint ID strings
@@ -131,9 +133,10 @@ def verify_breakpoint_hit(self, breakpoint_ids, 
timeout=DEFAULT_TIMEOUT):
                 # So when looking at the description we just want to make sure
                 # the right breakpoint matches and not worry about the actual
                 # location.
+                type_name = "watchpoint" if is_watchpoint else "breakpoint"
                 description = body["description"]
                 for breakpoint_id in breakpoint_ids:
-                    match_desc = f"breakpoint {breakpoint_id}."
+                    match_desc = f"{type_name} {breakpoint_id}"
                     if match_desc in description:
                         return
         self.assertTrue(False, f"breakpoint not hit, 
stopped_events={stopped_events}")
@@ -329,12 +332,16 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
         self.do_continue()
         return self.dap_server.wait_for_stopped(timeout)
 
-    def continue_to_breakpoint(self, breakpoint_id: str, 
timeout=DEFAULT_TIMEOUT):
-        self.continue_to_breakpoints((breakpoint_id), timeout)
+    def continue_to_breakpoint(
+            self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, 
is_watchpoint=False
+    ):
+        self.continue_to_breakpoints([breakpoint_id], timeout, is_watchpoint)
 
-    def continue_to_breakpoints(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+    def continue_to_breakpoints(
+            self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+    ):
         self.do_continue()
-        self.verify_breakpoint_hit(breakpoint_ids, timeout)
+        self.verify_breakpoint_hit(breakpoint_ids, timeout, is_watchpoint)
 
     def continue_to_exception_breakpoint(self, filter_label, 
timeout=DEFAULT_TIMEOUT):
         self.do_continue()
diff --git 
a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py 
b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
index a542a318050dd..62392f2c49afd 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
@@ -23,8 +23,8 @@ def test_duplicate_start_addresses(self):
         self.continue_to_next_stop()
         self.dap_server.get_stackFrame()
         # Test setting write watchpoint using expressions: &x, arr+2
-        response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
-        response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
+        response_x = self.dap_server.request_dataBreakpointInfo("&x", 0)
+        response_arr_2 = self.dap_server.request_dataBreakpointInfo("arr+2", 0)
         # Test response from dataBreakpointInfo request.
         self.assertEqual(response_x["body"]["dataId"].split("/")[1], "4")
         self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes)
@@ -56,6 +56,47 @@ def test_duplicate_start_addresses(self):
         self.assertEqual(arr_2["value"], "42")
         self.assertEqual(i_val, "2")
 
+    @skipIfWindows
+    def test_breakpoint_info_bytes(self):
+        """Test supportBreakpointInfoBytes
+        Set the watchpoint on `var` variable address + 6 characters.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+        source = "main.cpp"
+        first_loop_break_line = line_number(source, "// first loop breakpoint")
+        self.set_source_breakpoints(source, [first_loop_break_line])
+        self.continue_to_next_stop()
+
+        # get the address of `var` variable
+        eval_response = self.dap_server.request_evaluate("&var", 
context="watch")
+        self.assertTrue(eval_response["success"])
+        var_address = eval_response["body"]["result"]
+
+        var_byte_watch_size = 5
+        bp_resp = self.dap_server.request_dataBreakpointInfo(
+            var_address, asAddress=True, bytes_=var_byte_watch_size
+        )
+        resp_data_id = bp_resp["body"]["dataId"]
+        self.assertTrue(
+            bp_resp["success"], f"dataBreakpointInfo request failed: {bp_resp}"
+        )
+        self.assertEqual(resp_data_id.split("/")[1], str(var_byte_watch_size))
+
+        data_breakpoints = [{"dataId": resp_data_id, "accessType": "write"}]
+        self.dap_server.request_setDataBreakpoint(data_breakpoints)
+
+        self.continue_to_breakpoint(breakpoint_id=1, is_watchpoint=True)
+        eval_response = self.dap_server.request_evaluate("var", 
context="watch")
+        self.assertTrue(eval_response["success"])
+        var_value = eval_response["body"]["result"]
+        self.assertEqual(var_value, '"HALLO"')
+
+        # Remove the watchpoint because once it leaves this function scope, 
the address can be
+        # be used by another variable or register.
+        self.dap_server.request_setDataBreakpoint([])
+        self.continue_to_exit()
+
     @skipIfWindows
     def test_expression(self):
         """Tests setting data breakpoints on expression."""
@@ -67,8 +108,8 @@ def test_expression(self):
         self.continue_to_next_stop()
         self.dap_server.get_stackFrame()
         # Test setting write watchpoint using expressions: &x, arr+2
-        response_x = self.dap_server.request_dataBreakpointInfo(0, "&x")
-        response_arr_2 = self.dap_server.request_dataBreakpointInfo(0, "arr+2")
+        response_x = self.dap_server.request_dataBreakpointInfo("&x", 0)
+        response_arr_2 = self.dap_server.request_dataBreakpointInfo("arr+2", 0)
         # Test response from dataBreakpointInfo request.
         self.assertEqual(response_x["body"]["dataId"].split("/")[1], "4")
         self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes)
@@ -107,10 +148,10 @@ def test_functionality(self):
         self.continue_to_next_stop()
         self.dap_server.get_local_variables()
         # Test write watchpoints on x, arr[2]
-        response_x = self.dap_server.request_dataBreakpointInfo(1, "x")
+        response_x = self.dap_server.request_dataBreakpointInfo("x", 1)
         arr = self.dap_server.get_local_variable("arr")
         response_arr_2 = self.dap_server.request_dataBreakpointInfo(
-            arr["variablesReference"], "[2]"
+            "[2]", arr["variablesReference"]
         )
 
         # Test response from dataBreakpointInfo request.
diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp 
b/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp
index bef09c203845e..e4007980d27a8 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/main.cpp
@@ -1,5 +1,6 @@
 int main(int argc, char const *argv[]) {
   // Test for data breakpoint
+  char var[6] = "HELLO";
   int x = 0;
   int arr[4] = {1, 2, 3, 4};
   for (int i = 0; i < 5; ++i) { // first loop breakpoint
@@ -10,6 +11,8 @@ int main(int argc, char const *argv[]) {
     }
   }
 
+  var[1] = 'A';
+
   x = 1;
   for (int i = 0; i < 10; ++i) { // second loop breakpoint
     ++x;

>From bcfea6b18adfe82c82f8825ca42518d15471614a Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimy...@gmail.com>
Date: Thu, 22 May 2025 20:57:27 +0100
Subject: [PATCH 4/4] [lldb] format files.

---
 .../lldbsuite/test/tools/lldb-dap/dap_server.py      | 12 ++++++------
 .../test/tools/lldb-dap/lldbdap_testcase.py          |  6 +++---
 2 files changed, 9 insertions(+), 9 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 2468ca7e37f14..a287979492528 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
@@ -1042,12 +1042,12 @@ def request_setFunctionBreakpoints(self, names, 
condition=None, hitCondition=Non
         return self.send_recv(command_dict)
 
     def request_dataBreakpointInfo(
-            self,
-            name: str,
-            variablesReference: int = None,
-            frameIndex: int = 0,
-            bytes_: int = None,
-            asAddress: bool = None,
+        self,
+        name: str,
+        variablesReference: int = None,
+        frameIndex: int = 0,
+        bytes_: int = None,
+        asAddress: bool = None,
     ):
 
         args_dict = {}
diff --git 
a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 996c85ea69622..dadaf0fc53145 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
@@ -105,7 +105,7 @@ def waitUntil(self, condition_callback):
         return False
 
     def verify_breakpoint_hit(
-            self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+        self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
     ):
         """Wait for the process we are debugging to stop, and verify we hit
         any breakpoint location in the "breakpoint_ids" array.
@@ -333,12 +333,12 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
         return self.dap_server.wait_for_stopped(timeout)
 
     def continue_to_breakpoint(
-            self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, 
is_watchpoint=False
+        self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
     ):
         self.continue_to_breakpoints([breakpoint_id], timeout, is_watchpoint)
 
     def continue_to_breakpoints(
-            self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
+        self, breakpoint_ids, timeout=DEFAULT_TIMEOUT, is_watchpoint=False
     ):
         self.do_continue()
         self.verify_breakpoint_hit(breakpoint_ids, timeout, is_watchpoint)

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

Reply via email to