Skip to content

Commit 9813b8a

Browse files
[lldb] Override UpdateBreakpointSites in ProcessGDBRemote to use MultiBreakpoint
This concludes the implementation of MultiBreakpoint by actually using the new packet to batch breakpoint requests. #192910
1 parent 474bad5 commit 9813b8a

2 files changed

Lines changed: 182 additions & 0 deletions

File tree

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6215,3 +6215,177 @@ void ProcessGDBRemote::DidExec() {
62156215
}
62166216
Process::DidExec();
62176217
}
6218+
6219+
llvm::Error ProcessGDBRemote::UpdateBreakpointSitesNotBatched(
6220+
const std::map<lldb::BreakpointSiteSP, Process::BreakpointAction>
6221+
&site_to_action) {
6222+
llvm::Error joined = llvm::Error::success();
6223+
for (auto &[site, action] : site_to_action) {
6224+
llvm::Error error = action == Process::BreakpointAction::Enable
6225+
? DoEnableBreakpointSite(*site)
6226+
: DoDisableBreakpointSite(*site);
6227+
joined = llvm::joinErrors(std::move(joined), std::move(error));
6228+
}
6229+
return joined;
6230+
}
6231+
6232+
static llvm::Expected<StringExtractorGDBRemote>
6233+
SendMultiBreakpointPacket(GDBRemoteCommunicationClient &gdb_comm,
6234+
llvm::StringRef packet_str,
6235+
std::chrono::seconds interrupt_timeout) {
6236+
StringExtractorGDBRemote response;
6237+
GDBRemoteCommunication::PacketResult packet_result =
6238+
gdb_comm.SendPacketAndWaitForResponse(packet_str, response,
6239+
interrupt_timeout);
6240+
if (packet_result != GDBRemoteCommunication::PacketResult::Success)
6241+
return llvm::createStringErrorV(
6242+
"MultiBreakpoint failed to send packet: '{0}'", packet_str);
6243+
6244+
if (response.IsUnsupportedResponse())
6245+
return llvm::createStringErrorV(
6246+
"MultiBreakpoint unsupported response: '{0}'", response.GetStringRef());
6247+
6248+
return response;
6249+
}
6250+
6251+
/// Parse a MultiBreakpoint response into per-request results.
6252+
/// Returns a vector of results: std::nullopt means OK, a uint8_t value is the
6253+
/// error code from an Exx response.
6254+
static llvm::SmallVector<std::optional<uint8_t>>
6255+
ParseMultiBreakpointResponse(llvm::StringRef response_str) {
6256+
llvm::SmallVector<std::optional<uint8_t>> results;
6257+
llvm::SmallVector<llvm::StringRef> tokens;
6258+
6259+
// KeepEmpty = false to ignore trailings `;` in the response.
6260+
response_str.split(tokens, ';', -1, /*KeepEmpty=*/ false);
6261+
for (llvm::StringRef token : tokens) {
6262+
if (token == "OK") {
6263+
results.push_back(std::nullopt);
6264+
continue;
6265+
}
6266+
if (token.size() != 3 || !token.starts_with("E")) {
6267+
results.push_back(uint8_t(0xff));
6268+
continue;
6269+
}
6270+
uint8_t error_code = 0;
6271+
if (token.drop_front(1).getAsInteger(16, error_code))
6272+
results.push_back(0xff);
6273+
else
6274+
results.push_back(error_code);
6275+
}
6276+
return results;
6277+
}
6278+
6279+
/// Determine the GDB stoppoint type for a breakpoint site by checking which
6280+
/// packet types the remote supports (for insertions), or by checking the site
6281+
/// type (for deletions).
6282+
static std::optional<GDBStoppointType>
6283+
GetStoppointType(BreakpointSite &site, bool insert,
6284+
GDBRemoteCommunicationClient &gdb_comm) {
6285+
if (insert) {
6286+
if (!site.HardwareRequired() &&
6287+
gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
6288+
return eBreakpointSoftware;
6289+
if (gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware))
6290+
return eBreakpointHardware;
6291+
return std::nullopt;
6292+
}
6293+
6294+
switch (site.GetType()) {
6295+
case BreakpointSite::eExternal:
6296+
return eBreakpointSoftware;
6297+
case BreakpointSite::eHardware:
6298+
return eBreakpointHardware;
6299+
case BreakpointSite::eSoftware:
6300+
return std::nullopt;
6301+
}
6302+
llvm_unreachable("unhandled BreakpointSite type");
6303+
}
6304+
6305+
namespace {
6306+
struct BreakpointPacketInfo {
6307+
BreakpointSite &site;
6308+
size_t trap_opcode;
6309+
GDBStoppointType type;
6310+
bool is_enable;
6311+
};
6312+
6313+
llvm::raw_ostream &operator<<(llvm::raw_ostream &stream,
6314+
const BreakpointPacketInfo &info) {
6315+
char packet = info.is_enable ? 'Z' : 'z';
6316+
return stream << llvm::formatv("{0}{1},{2:x-},{3:x-}", packet,
6317+
static_cast<int>(info.type),
6318+
info.site.GetLoadAddress(), info.trap_opcode);
6319+
}
6320+
} // namespace
6321+
6322+
llvm::Error ProcessGDBRemote::UpdateBreakpointSites(
6323+
const std::map<lldb::BreakpointSiteSP, BreakpointAction> &site_to_action) {
6324+
if (site_to_action.empty())
6325+
return llvm::Error::success();
6326+
if (!m_gdb_comm.GetMultiBreakpointSupported())
6327+
return UpdateBreakpointSitesNotBatched(site_to_action);
6328+
6329+
Log *log = GetLog(GDBRLog::Breakpoints);
6330+
6331+
std::vector<BreakpointPacketInfo> breakpoint_infos;
6332+
6333+
for (auto [site, action] : site_to_action) {
6334+
addr_t addr = site->GetLoadAddress();
6335+
size_t trap_opcode = GetSoftwareBreakpointTrapOpcode(site.get());
6336+
std::optional<GDBStoppointType> type =
6337+
GetStoppointType(*site, action == BreakpointAction::Enable, m_gdb_comm);
6338+
if (!type) {
6339+
LLDB_LOG(log, "MultiBreakpoint: site {0} at {1:x} can't be batched",
6340+
site->GetID(), addr);
6341+
return UpdateBreakpointSitesNotBatched(site_to_action);
6342+
}
6343+
breakpoint_infos.push_back(
6344+
{*site, trap_opcode, *type, action == BreakpointAction::Enable});
6345+
}
6346+
6347+
std::string packet_str;
6348+
llvm::raw_string_ostream stream(packet_str);
6349+
stream << "MultiBreakpoint:";
6350+
llvm::interleave(breakpoint_infos, stream, ";");
6351+
6352+
llvm::Expected<StringExtractorGDBRemote> response =
6353+
SendMultiBreakpointPacket(m_gdb_comm, packet_str, GetInterruptTimeout());
6354+
6355+
if (!response) {
6356+
LLDB_LOG_ERROR(log, response.takeError(), "MultiBreakpoint failed: {0}");
6357+
return UpdateBreakpointSitesNotBatched(site_to_action);
6358+
}
6359+
6360+
llvm::SmallVector<std::optional<uint8_t>> results =
6361+
ParseMultiBreakpointResponse(response->GetStringRef());
6362+
6363+
// This is a protocol violation, do nothing.
6364+
if (results.size() != site_to_action.size())
6365+
return llvm::createStringErrorV(
6366+
"MultiBreakpoint response count mismatch (expected {0}, got {1})",
6367+
site_to_action.size(), results.size());
6368+
6369+
// Process results: mark successful sites as enabled/disabled, retry failed
6370+
// sites individually.
6371+
llvm::Error joined = llvm::Error::success();
6372+
for (auto [error_code, bp_info] : llvm::zip(results, breakpoint_infos)) {
6373+
BreakpointSite &site = bp_info.site;
6374+
if (error_code == std::nullopt) {
6375+
SetBreakpointSiteEnabled(site, bp_info.is_enable);
6376+
if (bp_info.is_enable)
6377+
site.SetType(bp_info.type == eBreakpointHardware
6378+
? BreakpointSite::eHardware
6379+
: BreakpointSite::eExternal);
6380+
continue;
6381+
}
6382+
LLDB_LOG(log,
6383+
"MultiBreakpoint: site {0} at {1:x} failed (E{2:X-2}), retrying",
6384+
site.GetID(), site.GetLoadAddress(), *error_code);
6385+
llvm::Error error = bp_info.is_enable ? DoEnableBreakpointSite(site)
6386+
: DoDisableBreakpointSite(site);
6387+
joined = llvm::joinErrors(std::move(joined), std::move(error));
6388+
}
6389+
6390+
return joined;
6391+
}

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ class ProcessGDBRemote : public Process,
170170
// Process Breakpoints
171171
Status EnableBreakpointSite(BreakpointSite *bp_site) override;
172172

173+
llvm::Error UpdateBreakpointSites(
174+
const std::map<lldb::BreakpointSiteSP, BreakpointAction> &site_to_action)
175+
override;
176+
173177
Status DisableBreakpointSite(BreakpointSite *bp_site) override;
174178

175179
// Process Watchpoints
@@ -458,6 +462,10 @@ class ProcessGDBRemote : public Process,
458462
/// z packet or restoring the original instruction.
459463
llvm::Error DoDisableBreakpointSite(BreakpointSite &bp_site);
460464

465+
llvm::Error UpdateBreakpointSitesNotBatched(
466+
const std::map<lldb::BreakpointSiteSP, Process::BreakpointAction>
467+
&site_to_action);
468+
461469
static bool NewThreadNotifyBreakpointHit(void *baton,
462470
StoppointCallbackContext *context,
463471
lldb::user_id_t break_id,

0 commit comments

Comments
 (0)