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
