https://github.com/felipepiovezan updated 
https://github.com/llvm/llvm-project/pull/192988

>From ea640e773d65618aedc2066db9f6551ba99fe5d3 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Thu, 9 Apr 2026 15:07:40 +0100
Subject: [PATCH 1/9] [lldb] Override UpdateBreakpointSites in ProcessGDBRemote
 to use MultiBreakpoint

This concludes the implementation of MultiBreakpoint by actually using
the new packet to batch breakpoint requests.

https://github.com/llvm/llvm-project/pull/192910
---
 lldb/include/lldb/Utility/GDBRemote.h         |   3 +
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 192 ++++++++++++++++++
 .../Process/gdb-remote/ProcessGDBRemote.h     |   8 +
 lldb/source/Utility/GDBRemote.cpp             |   4 +
 4 files changed, 207 insertions(+)

diff --git a/lldb/include/lldb/Utility/GDBRemote.h 
b/lldb/include/lldb/Utility/GDBRemote.h
index 3b839c5d79485..ab33f02ebb012 100644
--- a/lldb/include/lldb/Utility/GDBRemote.h
+++ b/lldb/include/lldb/Utility/GDBRemote.h
@@ -42,6 +42,9 @@ class StreamGDBRemote : public StreamString {
   ///     Number of bytes written.
   // TODO: Convert this function to take ArrayRef<uint8_t>
   int PutEscapedBytes(const void *s, size_t src_len);
+
+  /// Equivalent to PutEscapedBytes(str.data(), str.size());
+  int PutEscapedBytes(llvm::StringRef str);
 };
 
 /// GDB remote packet as used by the GDB remote communication history. Packets
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index adf108919b36e..3641deae8c463 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6392,3 +6392,195 @@ void ProcessGDBRemote::DidExec() {
   }
   Process::DidExec();
 }
+
+llvm::Error ProcessGDBRemote::UpdateBreakpointSitesNotBatched(
+    const std::map<lldb::BreakpointSiteSP, Process::BreakpointAction>
+        &site_to_action) {
+  llvm::Error joined = llvm::Error::success();
+  for (auto &[site, action] : site_to_action) {
+    llvm::Error error = action == Process::BreakpointAction::Enable
+                            ? DoEnableBreakpointSite(*site)
+                            : DoDisableBreakpointSite(*site);
+    joined = llvm::joinErrors(std::move(joined), std::move(error));
+  }
+  return joined;
+}
+
+static llvm::Expected<StringExtractorGDBRemote>
+SendMultiBreakpointPacket(GDBRemoteCommunicationClient &gdb_comm,
+                          llvm::StringRef packet_str,
+                          std::chrono::seconds interrupt_timeout) {
+  StringExtractorGDBRemote response;
+  GDBRemoteCommunication::PacketResult packet_result =
+      gdb_comm.SendPacketAndWaitForResponse(packet_str, response,
+                                            interrupt_timeout);
+  if (packet_result != GDBRemoteCommunication::PacketResult::Success)
+    return llvm::createStringErrorV(
+        "MultiBreakpoint failed to send packet: '{0}'", packet_str);
+
+  if (response.IsUnsupportedResponse())
+    return llvm::createStringErrorV(
+        "MultiBreakpoint unsupported response: '{0}'", 
response.GetStringRef());
+
+  return response;
+}
+
+/// Parse a MultiBreakpoint response into per-request results.
+/// Returns a vector of results: std::nullopt means OK, a uint8_t value is the
+/// error code from an Exx response.
+static llvm::SmallVector<std::optional<uint8_t>>
+ParseMultiBreakpointResponse(llvm::StringRef response_str) {
+  llvm::SmallVector<std::optional<uint8_t>> results;
+
+  StructuredData::ObjectSP parsed = StructuredData::ParseJSON(response_str);
+  StructuredData::Dictionary *dict =
+      parsed ? parsed->GetAsDictionary() : nullptr;
+  StructuredData::Array *array = nullptr;
+  if (dict)
+    dict->GetValueForKeyAsArray("results", array);
+  if (!array)
+    return results;
+
+  array->ForEach([&results](StructuredData::Object *object) -> bool {
+    llvm::StringRef token;
+    if (auto *string = object->GetAsString())
+      token = string->GetValue();
+    if (token == "OK") {
+      results.push_back(std::nullopt);
+      return true;
+    }
+    if (token.size() != 3 || !token.starts_with("E")) {
+      results.push_back(uint8_t(0xff));
+      return true;
+    }
+    uint8_t error_code = 0;
+    if (token.drop_front(1).getAsInteger(16, error_code))
+      results.push_back(0xff);
+    else
+      results.push_back(error_code);
+    return true;
+  });
+  return results;
+}
+
+/// Determine the GDB stoppoint type for a breakpoint site by checking which
+/// packet types the remote supports (for insertions), or by checking the site
+/// type (for deletions).
+static std::optional<GDBStoppointType>
+GetStoppointType(BreakpointSite &site, bool insert,
+                 GDBRemoteCommunicationClient &gdb_comm) {
+  if (insert) {
+    if (!site.HardwareRequired() &&
+        gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
+      return eBreakpointSoftware;
+    if (gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware))
+      return eBreakpointHardware;
+    return std::nullopt;
+  }
+
+  switch (site.GetType()) {
+  case BreakpointSite::eExternal:
+    return eBreakpointSoftware;
+  case BreakpointSite::eHardware:
+    return eBreakpointHardware;
+  case BreakpointSite::eSoftware:
+    return std::nullopt;
+  }
+  llvm_unreachable("unhandled BreakpointSite type");
+}
+
+namespace {
+struct BreakpointPacketInfo {
+  BreakpointSite &site;
+  size_t trap_opcode_size;
+  GDBStoppointType type;
+  bool is_enable;
+};
+
+std::string to_string(const BreakpointPacketInfo &info) {
+  char packet = info.is_enable ? 'Z' : 'z';
+  return llvm::formatv("{0}{1},{2:x-},{3:x-}", packet,
+                       static_cast<int>(info.type), info.site.GetLoadAddress(),
+                       info.trap_opcode_size)
+      .str();
+}
+} // namespace
+
+llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
+    const std::map<lldb::BreakpointSiteSP, BreakpointAction> &site_to_action) {
+  if (site_to_action.empty())
+    return llvm::Error::success();
+  if (!m_gdb_comm.GetMultiBreakpointSupported())
+    return UpdateBreakpointSitesNotBatched(site_to_action);
+
+  Log *log = GetLog(GDBRLog::Breakpoints);
+
+  std::vector<BreakpointPacketInfo> breakpoint_infos;
+
+  for (auto [site, action] : site_to_action) {
+    addr_t addr = site->GetLoadAddress();
+    size_t trap_opcode_size = GetSoftwareBreakpointTrapOpcode(site.get());
+    std::optional<GDBStoppointType> type =
+        GetStoppointType(*site, action == BreakpointAction::Enable, 
m_gdb_comm);
+    if (!type) {
+      LLDB_LOG(log, "MultiBreakpoint: site {0} at {1:x} can't be batched",
+               site->GetID(), addr);
+      return UpdateBreakpointSitesNotBatched(site_to_action);
+    }
+    breakpoint_infos.push_back(
+        {*site, trap_opcode_size, *type, action == BreakpointAction::Enable});
+  }
+
+  StreamString stream;
+  stream << "jMultiBreakpoint:";
+
+  auto args_array = std::make_shared<StructuredData::Array>();
+  for (auto &bp_info : breakpoint_infos)
+    args_array->AddStringItem(to_string(bp_info));
+
+  StructuredData::Dictionary packet_dict;
+  packet_dict.AddItem("breakpoint_requests", args_array);
+  packet_dict.Dump(stream, false);
+
+  StreamGDBRemote escaped_stream;
+  escaped_stream.PutEscapedBytes(stream.GetString());
+  llvm::Expected<StringExtractorGDBRemote> response = 
SendMultiBreakpointPacket(
+      m_gdb_comm, escaped_stream.GetString(), GetInterruptTimeout());
+
+  if (!response) {
+    LLDB_LOG_ERROR(log, response.takeError(), "jMultiBreakpoint failed: {0}");
+    return UpdateBreakpointSitesNotBatched(site_to_action);
+  }
+
+  llvm::SmallVector<std::optional<uint8_t>> results =
+      ParseMultiBreakpointResponse(response->GetStringRef());
+
+  // This is a protocol violation, do nothing.
+  if (results.size() != site_to_action.size())
+    return llvm::createStringErrorV(
+        "MultiBreakpoint response count mismatch (expected {0}, got {1})",
+        site_to_action.size(), results.size());
+
+  // Process results: mark successful sites as enabled/disabled, retry failed
+  // sites individually.
+  llvm::Error joined = llvm::Error::success();
+  for (auto [error_code, bp_info] : llvm::zip(results, breakpoint_infos)) {
+    BreakpointSite &site = bp_info.site;
+    if (error_code == std::nullopt) {
+      SetBreakpointSiteEnabled(site, bp_info.is_enable);
+      if (bp_info.is_enable)
+        site.SetType(bp_info.type == eBreakpointHardware
+                         ? BreakpointSite::eHardware
+                         : BreakpointSite::eExternal);
+      continue;
+    }
+    LLDB_LOG(log,
+             "MultiBreakpoint: site {0} at {1:x} failed (E{2:X-2}), retrying",
+             site.GetID(), site.GetLoadAddress(), *error_code);
+    llvm::Error error = bp_info.is_enable ? DoEnableBreakpointSite(site)
+                                          : DoDisableBreakpointSite(site);
+    joined = llvm::joinErrors(std::move(joined), std::move(error));
+  }
+
+  return joined;
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 7c2877fa71d49..fa59655fe8a89 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -170,6 +170,10 @@ class ProcessGDBRemote : public Process,
   // Process Breakpoints
   Status EnableBreakpointSite(BreakpointSite *bp_site) override;
 
+  llvm::Error UpdateBreakpointSites(
+      const std::map<lldb::BreakpointSiteSP, BreakpointAction> &site_to_action)
+      override;
+
   Status DisableBreakpointSite(BreakpointSite *bp_site) override;
 
   // Process Watchpoints
@@ -462,6 +466,10 @@ class ProcessGDBRemote : public Process,
   /// z packet or restoring the original instruction.
   llvm::Error DoDisableBreakpointSite(BreakpointSite &bp_site);
 
+  llvm::Error UpdateBreakpointSitesNotBatched(
+      const std::map<lldb::BreakpointSiteSP, Process::BreakpointAction>
+          &site_to_action);
+
   static bool NewThreadNotifyBreakpointHit(void *baton,
                                            StoppointCallbackContext *context,
                                            lldb::user_id_t break_id,
diff --git a/lldb/source/Utility/GDBRemote.cpp 
b/lldb/source/Utility/GDBRemote.cpp
index f987ebcd4f63e..bd322a5e9e540 100644
--- a/lldb/source/Utility/GDBRemote.cpp
+++ b/lldb/source/Utility/GDBRemote.cpp
@@ -24,6 +24,10 @@ StreamGDBRemote::StreamGDBRemote(uint32_t flags, ByteOrder 
byte_order)
 
 StreamGDBRemote::~StreamGDBRemote() = default;
 
+int StreamGDBRemote::PutEscapedBytes(llvm::StringRef str) {
+  return PutEscapedBytes(str.data(), str.size());
+}
+
 int StreamGDBRemote::PutEscapedBytes(const void *s, size_t src_len) {
   int bytes_written = 0;
   const uint8_t *src = static_cast<const uint8_t *>(s);

>From e9d10eb7d38151882e3f5319bb0f3aa8fa87b84a Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Tue, 28 Apr 2026 08:41:09 +0100
Subject: [PATCH 2/9] fixup! use map typedef

---
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 5 ++---
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h   | 6 ++----
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 3641deae8c463..b02d37e602733 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6394,8 +6394,7 @@ void ProcessGDBRemote::DidExec() {
 }
 
 llvm::Error ProcessGDBRemote::UpdateBreakpointSitesNotBatched(
-    const std::map<lldb::BreakpointSiteSP, Process::BreakpointAction>
-        &site_to_action) {
+    const BreakpointSiteToActionMap &site_to_action) {
   llvm::Error joined = llvm::Error::success();
   for (auto &[site, action] : site_to_action) {
     llvm::Error error = action == Process::BreakpointAction::Enable
@@ -6507,7 +6506,7 @@ std::string to_string(const BreakpointPacketInfo &info) {
 } // namespace
 
 llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
-    const std::map<lldb::BreakpointSiteSP, BreakpointAction> &site_to_action) {
+    const BreakpointSiteToActionMap &site_to_action) {
   if (site_to_action.empty())
     return llvm::Error::success();
   if (!m_gdb_comm.GetMultiBreakpointSupported())
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index fa59655fe8a89..63215f3e612c8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -171,8 +171,7 @@ class ProcessGDBRemote : public Process,
   Status EnableBreakpointSite(BreakpointSite *bp_site) override;
 
   llvm::Error UpdateBreakpointSites(
-      const std::map<lldb::BreakpointSiteSP, BreakpointAction> &site_to_action)
-      override;
+      const BreakpointSiteToActionMap &site_to_action) override;
 
   Status DisableBreakpointSite(BreakpointSite *bp_site) override;
 
@@ -467,8 +466,7 @@ class ProcessGDBRemote : public Process,
   llvm::Error DoDisableBreakpointSite(BreakpointSite &bp_site);
 
   llvm::Error UpdateBreakpointSitesNotBatched(
-      const std::map<lldb::BreakpointSiteSP, Process::BreakpointAction>
-          &site_to_action);
+      const BreakpointSiteToActionMap &site_to_action);
 
   static bool NewThreadNotifyBreakpointHit(void *baton,
                                            StoppointCallbackContext *context,

>From 681c516666c64a08dad73fab1578dfbbe34aeb27 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Wed, 29 Apr 2026 14:00:01 +0100
Subject: [PATCH 3/9] fixup! make helper method to send packet

---
 .../gdb-remote/GDBRemoteClientBase.cpp        | 17 +++++++++++++
 .../Process/gdb-remote/GDBRemoteClientBase.h  |  5 ++++
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 24 +++----------------
 3 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
index 406fa06ea011a..46c5d62f551e2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -9,6 +9,7 @@
 #include "GDBRemoteClientBase.h"
 
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ErrorExtras.h"
 
 #include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/LLDBAssert.h"
@@ -194,6 +195,22 @@ GDBRemoteClientBase::SendPacketAndWaitForResponse(
   return SendPacketAndWaitForResponseNoLock(payload, response, 
sync_on_timeout);
 }
 
+llvm::Expected<StringExtractorGDBRemote>
+GDBRemoteClientBase::SendPacketAndExpectResponse(
+    llvm::StringRef payload, std::chrono::seconds interrupt_timeout) {
+  StringExtractorGDBRemote response;
+  GDBRemoteCommunication::PacketResult packet_result =
+      SendPacketAndWaitForResponse(payload, response, interrupt_timeout);
+  if (packet_result != GDBRemoteCommunication::PacketResult::Success)
+    return llvm::createStringErrorV("Failed to send packet: '{0}'", payload);
+
+  if (response.IsUnsupportedResponse())
+    return llvm::createStringErrorV("Unsupported response: '{0}'",
+                                    response.GetStringRef());
+
+  return std::move(response);
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteClientBase::ReadPacketWithOutputSupport(
     StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
index 9c17a8c1de057..83d252bbc0649 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -74,6 +74,11 @@ class GDBRemoteClientBase : public GDBRemoteCommunication, 
public Broadcaster {
       std::chrono::seconds interrupt_timeout,
       llvm::function_ref<void(llvm::StringRef)> output_callback);
 
+  /// Wrapper around SendPacketAndWaitForResponse that returns and `Expected`.
+  llvm::Expected<StringExtractorGDBRemote> SendPacketAndExpectResponse(
+      llvm::StringRef payload,
+      std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
+
   class Lock {
   public:
     // If interrupt_timeout == 0 seconds, only take the lock if the target is
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index b02d37e602733..c110e0ad21b20 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6405,25 +6405,6 @@ llvm::Error 
ProcessGDBRemote::UpdateBreakpointSitesNotBatched(
   return joined;
 }
 
-static llvm::Expected<StringExtractorGDBRemote>
-SendMultiBreakpointPacket(GDBRemoteCommunicationClient &gdb_comm,
-                          llvm::StringRef packet_str,
-                          std::chrono::seconds interrupt_timeout) {
-  StringExtractorGDBRemote response;
-  GDBRemoteCommunication::PacketResult packet_result =
-      gdb_comm.SendPacketAndWaitForResponse(packet_str, response,
-                                            interrupt_timeout);
-  if (packet_result != GDBRemoteCommunication::PacketResult::Success)
-    return llvm::createStringErrorV(
-        "MultiBreakpoint failed to send packet: '{0}'", packet_str);
-
-  if (response.IsUnsupportedResponse())
-    return llvm::createStringErrorV(
-        "MultiBreakpoint unsupported response: '{0}'", 
response.GetStringRef());
-
-  return response;
-}
-
 /// Parse a MultiBreakpoint response into per-request results.
 /// Returns a vector of results: std::nullopt means OK, a uint8_t value is the
 /// error code from an Exx response.
@@ -6543,8 +6524,9 @@ llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
 
   StreamGDBRemote escaped_stream;
   escaped_stream.PutEscapedBytes(stream.GetString());
-  llvm::Expected<StringExtractorGDBRemote> response = 
SendMultiBreakpointPacket(
-      m_gdb_comm, escaped_stream.GetString(), GetInterruptTimeout());
+  llvm::Expected<StringExtractorGDBRemote> response =
+      m_gdb_comm.SendPacketAndExpectResponse(escaped_stream.GetString(),
+                                             GetInterruptTimeout());
 
   if (!response) {
     LLDB_LOG_ERROR(log, response.takeError(), "jMultiBreakpoint failed: {0}");

>From 679f72aecee21c6e09243061ac53db25e5b6a18d Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Wed, 29 Apr 2026 15:00:25 +0100
Subject: [PATCH 4/9] fixup! don't fallback on all packets

---
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 32 ++++++++++++-------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index c110e0ad21b20..0670e56c8445b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6493,22 +6493,19 @@ llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
   if (!m_gdb_comm.GetMultiBreakpointSupported())
     return UpdateBreakpointSitesNotBatched(site_to_action);
 
-  Log *log = GetLog(GDBRLog::Breakpoints);
-
   std::vector<BreakpointPacketInfo> breakpoint_infos;
+  std::vector<std::pair<BreakpointSiteSP, BreakpointAction>>
+      non_batchable_sites;
 
   for (auto [site, action] : site_to_action) {
-    addr_t addr = site->GetLoadAddress();
     size_t trap_opcode_size = GetSoftwareBreakpointTrapOpcode(site.get());
     std::optional<GDBStoppointType> type =
         GetStoppointType(*site, action == BreakpointAction::Enable, 
m_gdb_comm);
-    if (!type) {
-      LLDB_LOG(log, "MultiBreakpoint: site {0} at {1:x} can't be batched",
-               site->GetID(), addr);
-      return UpdateBreakpointSitesNotBatched(site_to_action);
-    }
-    breakpoint_infos.push_back(
-        {*site, trap_opcode_size, *type, action == BreakpointAction::Enable});
+    if (type)
+      breakpoint_infos.push_back(
+          {*site, trap_opcode_size, *type, action == 
BreakpointAction::Enable});
+    else
+      non_batchable_sites.emplace_back(site, action);
   }
 
   StreamString stream;
@@ -6528,6 +6525,8 @@ llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
       m_gdb_comm.SendPacketAndExpectResponse(escaped_stream.GetString(),
                                              GetInterruptTimeout());
 
+  Log *log = GetLog(GDBRLog::Breakpoints);
+
   if (!response) {
     LLDB_LOG_ERROR(log, response.takeError(), "jMultiBreakpoint failed: {0}");
     return UpdateBreakpointSitesNotBatched(site_to_action);
@@ -6542,9 +6541,20 @@ llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
         "MultiBreakpoint response count mismatch (expected {0}, got {1})",
         site_to_action.size(), results.size());
 
+  llvm::Error joined = llvm::Error::success();
+  for (auto [site, action] : non_batchable_sites) {
+    LLDB_LOG(log,
+             "MultiBreakpoint: site {0} at {1:x} can't be batched, trying on "
+             "its own",
+             site->GetID(), site->GetLoadAddress());
+    llvm::Error error = action == BreakpointAction::Enable
+                            ? DoEnableBreakpointSite(*site)
+                            : DoDisableBreakpointSite(*site);
+    joined = llvm::joinErrors(std::move(joined), std::move(error));
+  }
+
   // Process results: mark successful sites as enabled/disabled, retry failed
   // sites individually.
-  llvm::Error joined = llvm::Error::success();
   for (auto [error_code, bp_info] : llvm::zip(results, breakpoint_infos)) {
     BreakpointSite &site = bp_info.site;
     if (error_code == std::nullopt) {

>From a252e4927682745a9c35af3fb58fb328de0a5719 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Fri, 1 May 2026 08:05:45 +0100
Subject: [PATCH 5/9] fixup! cosmetic changes

---
 .../source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp | 4 ++--
 lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h  | 2 +-
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp   | 3 ++-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
index 46c5d62f551e2..fa927b74d56fa 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -202,10 +202,10 @@ GDBRemoteClientBase::SendPacketAndExpectResponse(
   GDBRemoteCommunication::PacketResult packet_result =
       SendPacketAndWaitForResponse(payload, response, interrupt_timeout);
   if (packet_result != GDBRemoteCommunication::PacketResult::Success)
-    return llvm::createStringErrorV("Failed to send packet: '{0}'", payload);
+    return llvm::createStringErrorV("failed to send packet: '{0}'", payload);
 
   if (response.IsUnsupportedResponse())
-    return llvm::createStringErrorV("Unsupported response: '{0}'",
+    return llvm::createStringErrorV("unsupported response: '{0}'",
                                     response.GetStringRef());
 
   return std::move(response);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
index 83d252bbc0649..65738491c21dd 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -74,7 +74,7 @@ class GDBRemoteClientBase : public GDBRemoteCommunication, 
public Broadcaster {
       std::chrono::seconds interrupt_timeout,
       llvm::function_ref<void(llvm::StringRef)> output_callback);
 
-  /// Wrapper around SendPacketAndWaitForResponse that returns and `Expected`.
+  /// Wrapper around SendPacketAndWaitForResponse that returns an `Expected`.
   llvm::Expected<StringExtractorGDBRemote> SendPacketAndExpectResponse(
       llvm::StringRef payload,
       std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 0670e56c8445b..bbc233d3f8d76 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6555,7 +6555,8 @@ llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
 
   // Process results: mark successful sites as enabled/disabled, retry failed
   // sites individually.
-  for (auto [error_code, bp_info] : llvm::zip(results, breakpoint_infos)) {
+  for (auto [error_code, bp_info] :
+       llvm::zip_equal(results, breakpoint_infos)) {
     BreakpointSite &site = bp_info.site;
     if (error_code == std::nullopt) {
       SetBreakpointSiteEnabled(site, bp_info.is_enable);

>From 063ab455a8a23e78a9ddeb27d15a9fa26677f5db Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Fri, 1 May 2026 08:06:08 +0100
Subject: [PATCH 6/9] fixup! use breakpoint_infos size

---
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index bbc233d3f8d76..f526b763e04d3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6536,7 +6536,7 @@ llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
       ParseMultiBreakpointResponse(response->GetStringRef());
 
   // This is a protocol violation, do nothing.
-  if (results.size() != site_to_action.size())
+  if (results.size() != breakpoint_infos.size())
     return llvm::createStringErrorV(
         "MultiBreakpoint response count mismatch (expected {0}, got {1})",
         site_to_action.size(), results.size());

>From 82a7d240691eb1436e624b2bc986e128ed396af2 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Mon, 4 May 2026 11:38:40 +0000
Subject: [PATCH 7/9] fixup! also flush breakpoints when process is being
 forked

---
 lldb/source/Target/Target.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index e2bae8fae1a26..28f15a45d8b8e 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -271,6 +271,7 @@ void Target::CleanupProcess() {
   m_breakpoint_list.ClearAllBreakpointSites();
   m_internal_breakpoint_list.ClearAllBreakpointSites();
   ResetBreakpointHitCounts();
+  llvm::consumeError(m_process_sp->FlushDelayedBreakpoints());
   // Disable watchpoints just on the debugger side.
   std::unique_lock<std::recursive_mutex> lock;
   this->GetWatchpointList().GetListMutex(lock);

>From 7e921fabfe6f2514e1f7a1d80668095bb3de5464 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Mon, 4 May 2026 11:54:19 +0000
Subject: [PATCH 8/9] fixup! Fix TestDelayedBreakpoint

---
 .../breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git 
a/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
 
b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
index 70a28bb935027..d18c10515a7e3 100644
--- 
a/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
+++ 
b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
@@ -38,4 +38,8 @@ def test(self):
         self.assertNotIn("send packet: $z", log_before_continue)
 
         log_after = log_text.split("AFTER_BPS", 1)[-1].split("AFTER_CONTINUE", 
1)[0]
-        self.assertIn("send packet: $Z", log_after)
+
+        if "jMultiBreakpoint+" in lldbutil.get_qsupported_capabilities(self):
+            self.assertIn("send packet: $jMultiBreakpoint", log_after)
+        else:
+            self.assertIn("send packet: $Z", log_after)

>From 6e98a47bebc39773b4a598b87e747c1bed034e77 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <[email protected]>
Date: Mon, 4 May 2026 13:07:38 +0000
Subject: [PATCH 9/9] fixup! Fix Reverse breakpoint tests

---
 .../Python/lldbsuite/test/lldbreverse.py      | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/lldb/packages/Python/lldbsuite/test/lldbreverse.py 
b/lldb/packages/Python/lldbsuite/test/lldbreverse.py
index d9a8daba3772d..50b97b5597f3a 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbreverse.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbreverse.py
@@ -5,6 +5,7 @@
 from lldbsuite.test.gdbclientutils import *
 from lldbsuite.test.lldbgdbproxy import *
 import lldbgdbserverutils
+import json
 import re
 
 
@@ -132,6 +133,10 @@ def respond(self, packet):
             if reply == "OK":
                 self.update_breakpoints(packet)
             return reply
+        if packet.startswith("jMultiBreakpoint:"):
+            reply = self.pass_through(packet)
+            self.update_multi_breakpoints(packet, reply)
+            return reply
         return GDBProxyTestBase.respond(self, packet)
 
     def start_recording(self):
@@ -161,6 +166,23 @@ def update_breakpoints(self, packet):
         else:
             self.breakpoints[t].discard((addr, kind))
 
+    def update_multi_breakpoints(self, packet, reply):
+        # Remove the final ], which is binary-escaping the }.
+        packet = packet[:-1]
+        reply = reply[:-1]
+        body = packet[len("jMultiBreakpoint:") :]
+        requests = json.loads(body)["breakpoint_requests"]
+        try:
+            results = json.loads(reply)["results"]
+        except (ValueError, KeyError):
+            # Empty/unsupported/error reply: nothing was installed.
+            return
+        if len(requests) != len(results):
+            raise ValueError("jMultiBreakpoint response count mismatch")
+        for inner_packet, result in zip(requests, results):
+            if result == "OK":
+                self.update_breakpoints(inner_packet)
+
     def breakpoint_triggered_at(self, pc):
         if any(addr == pc for addr, kind in 
self.breakpoints[SOFTWARE_BREAKPOINTS]):
             return True

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to