[llvm-branch-commits] [lldb] [lldb] Override UpdateBreakpointSites in ProcessGDBRemote to use MultiBreakpoint (PR #192988)
Felipe de Azevedo Piovezan via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Apr 20 07:53:52 PDT 2026
https://github.com/felipepiovezan updated https://github.com/llvm/llvm-project/pull/192988
>From c71ed50644de31570858bb66d260da5dc6e6580b Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Thu, 9 Apr 2026 15:07:40 +0100
Subject: [PATCH] [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
---
.../Process/gdb-remote/ProcessGDBRemote.cpp | 174 ++++++++++++++++++
.../Process/gdb-remote/ProcessGDBRemote.h | 8 +
2 files changed, 182 insertions(+)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index e4e0c00a8f648..fc694a0fad004 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -6215,3 +6215,177 @@ 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;
+ llvm::SmallVector<llvm::StringRef> tokens;
+
+ // KeepEmpty = false to ignore trailings `;` in the response.
+ response_str.split(tokens, ';', -1, /*KeepEmpty=*/false);
+ for (llvm::StringRef token : tokens) {
+ if (token == "OK") {
+ results.push_back(std::nullopt);
+ continue;
+ }
+ if (token.size() != 3 || !token.starts_with("E")) {
+ results.push_back(uint8_t(0xff));
+ continue;
+ }
+ 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 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;
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &stream,
+ const BreakpointPacketInfo &info) {
+ char packet = info.is_enable ? 'Z' : 'z';
+ return stream << llvm::formatv(
+ "{0}{1},{2:x-},{3:x-}", packet, static_cast<int>(info.type),
+ info.site.GetLoadAddress(), info.trap_opcode_size);
+}
+} // 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});
+ }
+
+ std::string packet_str;
+ llvm::raw_string_ostream stream(packet_str);
+ stream << "MultiBreakpoint:";
+ llvm::interleave(breakpoint_infos, stream, ";");
+
+ llvm::Expected<StringExtractorGDBRemote> response =
+ SendMultiBreakpointPacket(m_gdb_comm, packet_str, GetInterruptTimeout());
+
+ if (!response) {
+ LLDB_LOG_ERROR(log, response.takeError(), "MultiBreakpoint 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 15d9506479231..a4e92fd425998 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
@@ -458,6 +462,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,
More information about the llvm-branch-commits
mailing list