[Lldb-commits] [lldb] [lldb-server] Add breakpoint support to accelerator plugin protocol (PR #200584)
satyanarayana reddy janga via lldb-commits
lldb-commits at lists.llvm.org
Tue Jun 2 19:12:54 PDT 2026
https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/200584
>From 1f88f26faf5be0d8a87af0a5683cd63872cbf453 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Sat, 30 May 2026 07:59:39 -0700
Subject: [PATCH 1/6] [lldb-server] Add breakpoint support to accelerator
plugin protocol
Extend the accelerator plugin infrastructure with breakpoint request
and hit handling, allowing plugins to set breakpoints in the native
process and respond when those breakpoints are hit.
This patch adds:
- AcceleratorBreakpointByName, AcceleratorBreakpointByAddress,
AcceleratorBreakpointInfo, and SymbolValue structs for defining
breakpoints and requesting symbol values at hit time
- AcceleratorBreakpointHitArgs and AcceleratorBreakpointHitResponse
structs with JSON encode/decode for the hit round-trip
- breakpoints field in AcceleratorActions for plugins to request
breakpoints during initialization
- BreakpointWasHit() pure virtual method on LLDBServerAcceleratorPlugin
- jAcceleratorPluginBreakpointHit packet handler in
GDBRemoteCommunicationServerLLGS that routes hits to the correct
plugin by name and returns the plugin's response
- Mock plugin requests a breakpoint on "main" and responds to hits
with disable_bp=true, auto_resume_native=false
- Tests verifying breakpoints in initialize response and breakpoint
hit round-trip with JSON validation
- Packet documentation in lldbgdbremote.md
---
lldb/docs/resources/lldbgdbremote.md | 44 ++++++-
.../Utility/AcceleratorGDBRemotePackets.h | 84 +++++++++++++
.../lldb/Utility/StringExtractorGDBRemote.h | 1 +
.../GDBRemoteCommunicationServerLLGS.cpp | 31 +++++
.../GDBRemoteCommunicationServerLLGS.h | 3 +
.../gdb-remote/LLDBServerAcceleratorPlugin.h | 3 +
.../Utility/AcceleratorGDBRemotePackets.cpp | 111 ++++++++++++++++--
.../Utility/StringExtractorGDBRemote.cpp | 2 +
.../mock/TestMockAcceleratorPlugin.py | 84 +++++++++----
.../Mock/LLDBServerMockAcceleratorPlugin.cpp | 20 +++-
.../Mock/LLDBServerMockAcceleratorPlugin.h | 5 +
11 files changed, 353 insertions(+), 35 deletions(-)
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 59a68e34e73d0..3c73a87d71545 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -159,9 +159,49 @@ STUB REPLIES: [{"plugin_name":"amdgpu","session_name":"AMD GPU Session","identi
If no accelerator plugins are installed, the server does not advertise the
`accelerator-plugins+` feature and this packet should not be sent.
+Each `accelerator_action` may include a `breakpoints` array requesting
+breakpoints to be set in the native process. See
+`jAcceleratorPluginBreakpointHit` for the callback when those breakpoints
+are hit.
+
In future patches, each `accelerator_action` will include additional fields
-such as breakpoints to set in the native process, connection info for
-secondary debug sessions, and synchronization options.
+such as connection info for secondary debug sessions and synchronization
+options.
+
+**Priority To Implement:** Low. Only needed for hardware accelerator
+debugging support.
+
+## jAcceleratorPluginBreakpointHit
+
+Sent by the client when a breakpoint requested by an accelerator plugin
+(via `jAcceleratorPluginInitialize`) is hit in the native process. This
+packet requires the `accelerator-plugins+` feature from `qSupported`.
+
+```
+LLDB SENDS: jAcceleratorPluginBreakpointHit:<json>
+STUB REPLIES: <json_response>
+```
+
+The request JSON has the following fields:
+
+| Key | Type | Description |
+|-----------------|--------|-------------|
+| `plugin_name` | string | Name of the plugin that requested the breakpoint. |
+| `breakpoint` | object | The `AcceleratorBreakpointInfo` that was hit, including its `identifier`. |
+| `symbol_values` | array | Array of `{"name": "<name>", "value": <addr>}` for each symbol requested in the breakpoint's `symbol_names`. |
+
+The response JSON has the following fields:
+
+| Key | Type | Description |
+|----------------------|------|-------------|
+| `disable_bp` | bool | If true, the client should disable this breakpoint. |
+| `auto_resume_native` | bool | If true, the native process should automatically resume after handling the hit. |
+
+Example:
+```
+LLDB SENDS: jAcceleratorPluginBreakpointHit:{"plugin_name":"mock","breakpoint":{"identifier":1,"symbol_names":[]},"symbol_values":[]}
+STUB REPLIES: {"disable_bp":true,"auto_resume_native":false}
+```
**Priority To Implement:** Low. Only needed for hardware accelerator
debugging support.
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index 479503bf47a3c..1618b1d80ea2e 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -11,10 +11,92 @@
#include "llvm/Support/JSON.h"
#include <cstdint>
+#include <optional>
#include <string>
+#include <vector>
namespace lldb_private {
+struct SymbolValue {
+ /// Symbol name as requested in AcceleratorBreakpointInfo::symbol_names.
+ std::string name;
+ /// Load address of the symbol in the native process, or nullopt if not found.
+ std::optional<uint64_t> value;
+};
+
+bool fromJSON(const llvm::json::Value &value, SymbolValue &data,
+ llvm::json::Path path);
+llvm::json::Value toJSON(const SymbolValue &data);
+
+struct AcceleratorBreakpointByName {
+ /// Optional shared library name to limit the breakpoint scope.
+ std::optional<std::string> shlib;
+ /// Function name to set a breakpoint at.
+ std::string function_name;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorBreakpointByName &data,
+ llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorBreakpointByName &data);
+
+struct AcceleratorBreakpointByAddress {
+ /// Load address in the native debug target.
+ uint64_t load_address = 0;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorBreakpointByAddress &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorBreakpointByAddress &data);
+
+/// A breakpoint definition. Clients fill in either \a by_name or
+/// \a by_address. If the breakpoint callback needs symbol values from
+/// the native process, fill in \a symbol_names — those values will be
+/// delivered in the breakpoint hit callback.
+struct AcceleratorBreakpointInfo {
+ /// Unique breakpoint ID used to identify this breakpoint in the
+ /// BreakpointWasHit callback.
+ int64_t identifier = 0;
+ /// Breakpoint by function name.
+ std::optional<AcceleratorBreakpointByName> by_name;
+ /// Breakpoint by load address.
+ std::optional<AcceleratorBreakpointByAddress> by_address;
+ /// Symbol names whose values should be supplied when the breakpoint is hit.
+ std::vector<std::string> symbol_names;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorBreakpointInfo &data,
+ llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorBreakpointInfo &data);
+
+/// Sent by the client when a plugin-requested breakpoint is hit.
+struct AcceleratorBreakpointHitArgs {
+ AcceleratorBreakpointHitArgs() = default;
+ AcceleratorBreakpointHitArgs(llvm::StringRef plugin_name)
+ : plugin_name(plugin_name) {}
+
+ std::string plugin_name;
+ AcceleratorBreakpointInfo breakpoint;
+ std::vector<SymbolValue> symbol_values;
+
+ std::optional<uint64_t> GetSymbolValue(llvm::StringRef symbol_name) const;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorBreakpointHitArgs &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorBreakpointHitArgs &data);
+
+/// Response from the plugin when a breakpoint is hit.
+struct AcceleratorBreakpointHitResponse {
+ /// Set to true if this breakpoint should be disabled.
+ bool disable_bp = false;
+ /// Set to true if the native process should automatically resume.
+ bool auto_resume_native = true;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorBreakpointHitResponse &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorBreakpointHitResponse &data);
+
struct AcceleratorActions {
AcceleratorActions() = default;
AcceleratorActions(llvm::StringRef plugin_name, int64_t action_id)
@@ -26,6 +108,8 @@ struct AcceleratorActions {
std::string session_name;
/// Unique identifier for this action within the plugin.
int64_t identifier = 0;
+ /// Breakpoints to set in the native process.
+ std::vector<AcceleratorBreakpointInfo> breakpoints;
};
bool fromJSON(const llvm::json::Value &value, AcceleratorActions &data,
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 85ec27cac9ac0..ff3af73285427 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -174,6 +174,7 @@ class StringExtractorGDBRemote : public StringExtractor {
eServerPacketType_jLLDBTraceGetBinaryData,
eServerPacketType_jMultiBreakpoint,
eServerPacketType_jAcceleratorPluginInitialize,
+ eServerPacketType_jAcceleratorPluginBreakpointHit,
eServerPacketType_qMemTags, // read memory tags
eServerPacketType_QMemTags, // write memory tags
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index a84e965b04bee..1f3598fe1c2d1 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -226,6 +226,11 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
RegisterMemberFunctionHandler(
StringExtractorGDBRemote::eServerPacketType_jAcceleratorPluginInitialize,
&GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::
+ eServerPacketType_jAcceleratorPluginBreakpointHit,
+ &GDBRemoteCommunicationServerLLGS::
+ Handle_jAcceleratorPluginBreakpointHit);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
&GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -4548,3 +4553,29 @@ GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize(
response.PutAsJSONArray(accelerator_actions, /*hex_ascii=*/false);
return SendPacketNoLock(response.GetString());
}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginBreakpointHit(
+ StringExtractorGDBRemote &packet) {
+ packet.ConsumeFront("jAcceleratorPluginBreakpointHit:");
+ llvm::Expected<AcceleratorBreakpointHitArgs> args =
+ llvm::json::parse<AcceleratorBreakpointHitArgs>(
+ packet.Peek(), "AcceleratorBreakpointHitArgs");
+ if (!args)
+ return SendErrorResponse(args.takeError());
+
+ for (auto &plugin_up : m_accelerator_plugins) {
+ if (plugin_up->GetPluginName() == args->plugin_name) {
+ llvm::Expected<AcceleratorBreakpointHitResponse> bp_response =
+ plugin_up->BreakpointWasHit(*args);
+ if (!bp_response)
+ return SendErrorResponse(bp_response.takeError());
+
+ StreamGDBRemote response;
+ response.PutAsJSON(*bp_response, /*hex_ascii=*/false);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(
+ Status::FromErrorString("unknown accelerator plugin name"));
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 5a088f8de2380..d582d2d77b639 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -287,6 +287,9 @@ class GDBRemoteCommunicationServerLLGS
PacketResult
Handle_jAcceleratorPluginInitialize(StringExtractorGDBRemote &packet);
+ PacketResult
+ Handle_jAcceleratorPluginBreakpointHit(StringExtractorGDBRemote &packet);
+
void SetCurrentThreadID(lldb::tid_t tid);
lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
index cdb9b20b435ca..0230b38e5b0fa 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
@@ -33,6 +33,9 @@ class LLDBServerAcceleratorPlugin {
virtual std::optional<AcceleratorActions> GetInitializeActions() = 0;
+ virtual llvm::Expected<AcceleratorBreakpointHitResponse>
+ BreakpointWasHit(AcceleratorBreakpointHitArgs &args) = 0;
+
protected:
GDBServer &m_gdb_server;
MainLoop &m_main_loop;
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
index 8019db4328c78..c05646c26b3bc 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -8,20 +8,113 @@
#include "lldb/Utility/AcceleratorGDBRemotePackets.h"
-using namespace lldb_private;
+using namespace llvm;
+using namespace llvm::json;
-llvm::json::Value lldb_private::toJSON(const AcceleratorActions &data) {
- return llvm::json::Object{
- {"plugin_name", data.plugin_name},
- {"session_name", data.session_name},
+namespace lldb_private {
+
+bool fromJSON(const Value &value, SymbolValue &data, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("name", data.name) && o.map("value", data.value);
+}
+
+json::Value toJSON(const SymbolValue &data) {
+ return Object{{"name", data.name}, {"value", data.value}};
+}
+
+bool fromJSON(const Value &value, AcceleratorBreakpointByName &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.mapOptional("shlib", data.shlib) &&
+ o.map("function_name", data.function_name);
+}
+
+json::Value toJSON(const AcceleratorBreakpointByName &data) {
+ return Object{{"shlib", data.shlib}, {"function_name", data.function_name}};
+}
+
+bool fromJSON(const Value &value, AcceleratorBreakpointByAddress &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("load_address", data.load_address);
+}
+
+json::Value toJSON(const AcceleratorBreakpointByAddress &data) {
+ return Object{{"load_address", static_cast<int64_t>(data.load_address)}};
+}
+
+bool fromJSON(const Value &value, AcceleratorBreakpointInfo &data, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("identifier", data.identifier) &&
+ o.mapOptional("by_name", data.by_name) &&
+ o.mapOptional("by_address", data.by_address) &&
+ o.map("symbol_names", data.symbol_names);
+}
+
+json::Value toJSON(const AcceleratorBreakpointInfo &data) {
+ return Object{
{"identifier", data.identifier},
+ {"by_name", data.by_name},
+ {"by_address", data.by_address},
+ {"symbol_names", data.symbol_names},
+ };
+}
+
+bool fromJSON(const Value &value, AcceleratorBreakpointHitArgs &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("plugin_name", data.plugin_name) &&
+ o.map("breakpoint", data.breakpoint) &&
+ o.map("symbol_values", data.symbol_values);
+}
+
+json::Value toJSON(const AcceleratorBreakpointHitArgs &data) {
+ return Object{
+ {"plugin_name", data.plugin_name},
+ {"breakpoint", data.breakpoint},
+ {"symbol_values", data.symbol_values},
};
}
-bool lldb_private::fromJSON(const llvm::json::Value &value,
- AcceleratorActions &data, llvm::json::Path path) {
- llvm::json::ObjectMapper o(value, path);
+std::optional<uint64_t>
+AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef symbol_name) const {
+ auto it = llvm::find_if(symbol_values, [&](const SymbolValue &symbol) {
+ return symbol.name == symbol_name;
+ });
+ if (it != symbol_values.end())
+ return it->value;
+ return std::nullopt;
+}
+
+bool fromJSON(const Value &value, AcceleratorBreakpointHitResponse &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("disable_bp", data.disable_bp) &&
+ o.map("auto_resume_native", data.auto_resume_native);
+}
+
+json::Value toJSON(const AcceleratorBreakpointHitResponse &data) {
+ return Object{
+ {"disable_bp", data.disable_bp},
+ {"auto_resume_native", data.auto_resume_native},
+ };
+}
+
+bool fromJSON(const Value &value, AcceleratorActions &data, Path path) {
+ ObjectMapper o(value, path);
return o && o.map("plugin_name", data.plugin_name) &&
o.map("session_name", data.session_name) &&
- o.map("identifier", data.identifier);
+ o.map("identifier", data.identifier) &&
+ o.map("breakpoints", data.breakpoints);
}
+
+json::Value toJSON(const AcceleratorActions &data) {
+ return Object{
+ {"plugin_name", data.plugin_name},
+ {"session_name", data.session_name},
+ {"identifier", data.identifier},
+ {"breakpoints", data.breakpoints},
+ };
+}
+
+} // namespace lldb_private
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 7b46bef933ac2..683559bd0fcf8 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -334,6 +334,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
return eServerPacketType_jMultiBreakpoint;
if (PACKET_MATCHES("jAcceleratorPluginInitialize"))
return eServerPacketType_jAcceleratorPluginInitialize;
+ if (PACKET_STARTS_WITH("jAcceleratorPluginBreakpointHit:"))
+ return eServerPacketType_jAcceleratorPluginBreakpointHit;
break;
case 'v':
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
index 3e9ca174cd533..e0dabdf7874dd 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
@@ -1,14 +1,16 @@
"""
Tests for the lldb-server mock accelerator plugin.
-Verifies the accelerator-plugins+ feature in qSupported and
-the jAcceleratorPluginInitialize packet response.
+Verifies the accelerator-plugins+ feature in qSupported,
+the jAcceleratorPluginInitialize packet response, and
+the jAcceleratorPluginBreakpointHit round-trip.
"""
import json
import gdbremote_testcase
from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import escape_binary
from lldbsuite.test.lldbtest import *
from lldbsuite.test import configuration
@@ -26,6 +28,24 @@ def setUp(self):
if "mock-accelerator" not in configuration.enabled_plugins:
self.skipTest("mock-accelerator plugin is not enabled")
+ def send_and_decode_json(self, packet):
+ """Send a packet and return the decoded JSON response."""
+ self.test_sequence.add_log_lines(
+ [
+ "read packet: $%s#00" % packet,
+ {
+ "direction": "send",
+ "regex": r"^\$(.+)#[0-9a-fA-F]{2}",
+ "capture": {1: "response"},
+ },
+ ],
+ True,
+ )
+ context = self.expect_gdbremote_sequence()
+ raw = context.get("response")
+ self.assertIsNotNone(raw)
+ return json.loads(self.decode_gdbremote_binary(raw))
+
@add_test_categories(["llgs"])
def test_qSupported_reports_accelerator_plugins(self):
self.build()
@@ -40,7 +60,7 @@ def test_qSupported_reports_accelerator_plugins(self):
self.assertEqual(features["accelerator-plugins"], "+")
@add_test_categories(["llgs"])
- def test_jAcceleratorPluginInitialize_returns_mock_actions(self):
+ def test_jAcceleratorPluginInitialize_returns_breakpoints(self):
self.build()
self.set_inferior_startup_launch()
self.prep_debug_monitor_and_inferior()
@@ -48,27 +68,45 @@ def test_jAcceleratorPluginInitialize_returns_mock_actions(self):
self.add_qSupported_packets()
self.expect_gdbremote_sequence()
- self.test_sequence.add_log_lines(
- [
- "read packet: $jAcceleratorPluginInitialize#00",
- {
- "direction": "send",
- "regex": r"^\$(.+)#[0-9a-fA-F]{2}",
- "capture": {1: "accel_init_response"},
- },
- ],
- True,
- )
- context = self.expect_gdbremote_sequence()
-
- raw_response = context.get("accel_init_response")
- self.assertIsNotNone(raw_response)
-
- decoded = self.decode_gdbremote_binary(raw_response)
- actions = json.loads(decoded)
+ actions = self.send_and_decode_json("jAcceleratorPluginInitialize")
self.assertIsInstance(actions, list)
mock_action = get_accelerator_action(actions, "mock")
self.assertIsNotNone(mock_action)
- self.assertIn("session_name", mock_action)
- self.assertIn("identifier", mock_action)
+ self.assertIn("breakpoints", mock_action)
+
+ breakpoints = mock_action["breakpoints"]
+ self.assertGreater(len(breakpoints), 0)
+
+ bp = breakpoints[0]
+ self.assertIn("identifier", bp)
+ self.assertIn("by_name", bp)
+ self.assertEqual(bp["by_name"]["function_name"], "main")
+
+ @add_test_categories(["llgs"])
+ def test_jAcceleratorPluginBreakpointHit_response(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ self.prep_debug_monitor_and_inferior()
+
+ self.add_qSupported_packets()
+ self.expect_gdbremote_sequence()
+
+ hit_args = {
+ "plugin_name": "mock",
+ "breakpoint": {
+ "identifier": 1,
+ "symbol_names": [],
+ },
+ "symbol_values": [],
+ }
+ hit_json = json.dumps(hit_args, separators=(",", ":"))
+ escaped_json = escape_binary(hit_json)
+ response = self.send_and_decode_json(
+ "jAcceleratorPluginBreakpointHit:" + escaped_json
+ )
+
+ self.assertIn("disable_bp", response)
+ self.assertTrue(response["disable_bp"])
+ self.assertIn("auto_resume_native", response)
+ self.assertFalse(response["auto_resume_native"])
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index 1e8712ff739d8..776002ffcd942 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -21,5 +21,23 @@ llvm::StringRef LLDBServerMockAcceleratorPlugin::GetPluginName() {
std::optional<AcceleratorActions>
LLDBServerMockAcceleratorPlugin::GetInitializeActions() {
- return AcceleratorActions(GetPluginName(), 0);
+ AcceleratorActions actions(GetPluginName(), 1);
+
+ AcceleratorBreakpointInfo bp;
+ bp.identifier = kBreakpointIDInitialize;
+ bp.by_name = AcceleratorBreakpointByName{std::nullopt, "main"};
+ actions.breakpoints.push_back(std::move(bp));
+
+ return actions;
+}
+
+llvm::Expected<AcceleratorBreakpointHitResponse>
+LLDBServerMockAcceleratorPlugin::BreakpointWasHit(
+ AcceleratorBreakpointHitArgs &args) {
+ AcceleratorBreakpointHitResponse response;
+ if (args.breakpoint.identifier == kBreakpointIDInitialize) {
+ response.disable_bp = true;
+ response.auto_resume_native = false;
+ }
+ return response;
}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
index 12d7c45d3155b..d842666371b16 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -20,6 +20,11 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
llvm::StringRef GetPluginName() override;
std::optional<AcceleratorActions> GetInitializeActions() override;
+ llvm::Expected<AcceleratorBreakpointHitResponse>
+ BreakpointWasHit(AcceleratorBreakpointHitArgs &args) override;
+
+private:
+ static constexpr int64_t kBreakpointIDInitialize = 1;
};
} // namespace lldb_server
>From 58bd5235dba4b77448179c618a597ff6ce1f9a98 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Mon, 1 Jun 2026 15:16:44 -0700
Subject: [PATCH 2/6] [lldb-server] Address review feedback for accelerator
plugin breakpoints
Add AcceleratorActions to AcceleratorBreakpointHitResponse so breakpoint
hit responses can request additional breakpoints. Update docs to clarify
that breakpoints can originate from any response containing
AcceleratorActions, and reword priority to "Required for hardware
accelerator debugging support." Add detailed comments to AcceleratorActions
explaining all contexts where it is used. Update mock plugin and test to
exercise the new actions-in-breakpoint-response path.
---
lldb/docs/resources/lldbgdbremote.md | 20 +++++----
.../Utility/AcceleratorGDBRemotePackets.h | 45 +++++++++++++------
.../Utility/AcceleratorGDBRemotePackets.cpp | 32 +++++++------
.../mock/TestMockAcceleratorPlugin.py | 10 +++++
.../Mock/LLDBServerMockAcceleratorPlugin.cpp | 7 +++
.../Mock/LLDBServerMockAcceleratorPlugin.h | 1 +
6 files changed, 79 insertions(+), 36 deletions(-)
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 3c73a87d71545..5d3ebbb7fe51d 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -168,14 +168,15 @@ In future patches, each `accelerator_action` will include additional fields
such as connection info for secondary debug sessions and synchronization
options.
-**Priority To Implement:** Low. Only needed for hardware accelerator
-debugging support.
+**Priority To Implement:** Required for hardware accelerator debugging
+support. Not needed for non-hardware-accelerator debugging.
## jAcceleratorPluginBreakpointHit
Sent by the client when a breakpoint requested by an accelerator plugin
-(via `jAcceleratorPluginInitialize`) is hit in the native process. This
-packet requires the `accelerator-plugins+` feature from `qSupported`.
+is hit in the native process. Breakpoints can originate from any response
+that contains `AcceleratorActions`. This packet requires the
+`accelerator-plugins+` feature from `qSupported`.
```
LLDB SENDS: jAcceleratorPluginBreakpointHit:<json>
@@ -194,17 +195,18 @@ The response JSON has the following fields:
| Key | Type | Description |
|----------------------|------|-------------|
-| `disable_bp` | bool | If true, the client should disable this breakpoint. |
-| `auto_resume_native` | bool | If true, the native process should automatically resume after handling the hit. |
+| `disable_bp` | bool | If true, the client should disable this breakpoint. |
+| `auto_resume_native` | bool | If true, the native process should automatically resume after handling the hit. |
+| `actions` | object | Optional `AcceleratorActions` containing new breakpoints to set and other actions to perform. |
Example:
```
LLDB SENDS: jAcceleratorPluginBreakpointHit:{"plugin_name":"mock","breakpoint":{"identifier":1,"symbol_names":[]},"symbol_values":[]}
-STUB REPLIES: {"disable_bp":true,"auto_resume_native":false}
+STUB REPLIES: {"disable_bp":true,"auto_resume_native":false,"actions":{"plugin_name":"mock","session_name":"","identifier":2,"breakpoints":[{"identifier":2,"by_name":{"function_name":"exit"},"symbol_names":[]}]}}
```
-**Priority To Implement:** Low. Only needed for hardware accelerator
-debugging support.
+**Priority To Implement:** Required for hardware accelerator debugging
+support. Not needed for non-hardware-accelerator debugging.
## jGetDyldProcessState
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index 1618b1d80ea2e..ad9e80924b0e4 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -85,18 +85,22 @@ bool fromJSON(const llvm::json::Value &value,
AcceleratorBreakpointHitArgs &data, llvm::json::Path path);
llvm::json::Value toJSON(const AcceleratorBreakpointHitArgs &data);
-/// Response from the plugin when a breakpoint is hit.
-struct AcceleratorBreakpointHitResponse {
- /// Set to true if this breakpoint should be disabled.
- bool disable_bp = false;
- /// Set to true if the native process should automatically resume.
- bool auto_resume_native = true;
-};
-
-bool fromJSON(const llvm::json::Value &value,
- AcceleratorBreakpointHitResponse &data, llvm::json::Path path);
-llvm::json::Value toJSON(const AcceleratorBreakpointHitResponse &data);
-
+/// Actions to be performed in the native process on behalf of an accelerator
+/// plugin. AcceleratorActions are returned in the following contexts:
+///
+/// - Initialization: in response to the "jAcceleratorPluginInitialize" packet,
+/// each plugin returns an AcceleratorActions describing initial breakpoints
+/// and other setup needed in the native process.
+///
+/// - Breakpoint hits: when a native breakpoint requested by a plugin is hit,
+/// the AcceleratorBreakpointHitResponse contains an AcceleratorActions that
+/// can request additional breakpoints or other actions.
+///
+/// In future patches, AcceleratorActions will also be returned:
+/// - When the native process stops (via NativeProcessIsStopping), allowing
+/// plugins to react to arbitrary stop events.
+/// - Via accelerator stop reply packets, enabling plugins to inject actions
+/// into the native process asynchronously.
struct AcceleratorActions {
AcceleratorActions() = default;
AcceleratorActions(llvm::StringRef plugin_name, int64_t action_id)
@@ -114,9 +118,24 @@ struct AcceleratorActions {
bool fromJSON(const llvm::json::Value &value, AcceleratorActions &data,
llvm::json::Path path);
-
llvm::json::Value toJSON(const AcceleratorActions &data);
+/// Response from the plugin when a breakpoint is hit.
+struct AcceleratorBreakpointHitResponse {
+ /// Set to true if this breakpoint should be disabled.
+ bool disable_bp = false;
+ /// Set to true if the native process should automatically resume after
+ /// the breakpoint is hit. When false, the native process will stop and
+ /// wait for user interaction.
+ bool auto_resume_native = true;
+ /// Optional new actions to perform (e.g. set additional breakpoints).
+ std::optional<AcceleratorActions> actions;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorBreakpointHitResponse &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorBreakpointHitResponse &data);
+
} // namespace lldb_private
#endif // LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
index c05646c26b3bc..34067b1fa64c7 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -86,20 +86,6 @@ AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef symbol_name) const {
return std::nullopt;
}
-bool fromJSON(const Value &value, AcceleratorBreakpointHitResponse &data,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("disable_bp", data.disable_bp) &&
- o.map("auto_resume_native", data.auto_resume_native);
-}
-
-json::Value toJSON(const AcceleratorBreakpointHitResponse &data) {
- return Object{
- {"disable_bp", data.disable_bp},
- {"auto_resume_native", data.auto_resume_native},
- };
-}
-
bool fromJSON(const Value &value, AcceleratorActions &data, Path path) {
ObjectMapper o(value, path);
return o && o.map("plugin_name", data.plugin_name) &&
@@ -117,4 +103,22 @@ json::Value toJSON(const AcceleratorActions &data) {
};
}
+bool fromJSON(const Value &value, AcceleratorBreakpointHitResponse &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("disable_bp", data.disable_bp) &&
+ o.map("auto_resume_native", data.auto_resume_native) &&
+ o.mapOptional("actions", data.actions);
+}
+
+json::Value toJSON(const AcceleratorBreakpointHitResponse &data) {
+ Object obj{
+ {"disable_bp", data.disable_bp},
+ {"auto_resume_native", data.auto_resume_native},
+ };
+ if (data.actions)
+ obj["actions"] = *data.actions;
+ return obj;
+}
+
} // namespace lldb_private
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
index e0dabdf7874dd..8e2bd0d604366 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
@@ -110,3 +110,13 @@ def test_jAcceleratorPluginBreakpointHit_response(self):
self.assertTrue(response["disable_bp"])
self.assertIn("auto_resume_native", response)
self.assertFalse(response["auto_resume_native"])
+
+ # Verify that the response includes new actions with a breakpoint.
+ self.assertIn("actions", response)
+ actions = response["actions"]
+ self.assertEqual(actions["plugin_name"], "mock")
+ self.assertIn("breakpoints", actions)
+ new_bps = actions["breakpoints"]
+ self.assertGreater(len(new_bps), 0)
+ self.assertEqual(new_bps[0]["identifier"], 2)
+ self.assertEqual(new_bps[0]["by_name"]["function_name"], "exit")
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index 776002ffcd942..cb2951919537a 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -38,6 +38,13 @@ LLDBServerMockAcceleratorPlugin::BreakpointWasHit(
if (args.breakpoint.identifier == kBreakpointIDInitialize) {
response.disable_bp = true;
response.auto_resume_native = false;
+
+ AcceleratorActions actions(GetPluginName(), kBreakpointIDExit);
+ AcceleratorBreakpointInfo bp;
+ bp.identifier = kBreakpointIDExit;
+ bp.by_name = AcceleratorBreakpointByName{std::nullopt, "exit"};
+ actions.breakpoints.push_back(std::move(bp));
+ response.actions = std::move(actions);
}
return response;
}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
index d842666371b16..1d3d03121c02e 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -25,6 +25,7 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
private:
static constexpr int64_t kBreakpointIDInitialize = 1;
+ static constexpr int64_t kBreakpointIDExit = 2;
};
} // namespace lldb_server
>From 7525995fa15f14f90a2cf85a536d49e6c36185a1 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Tue, 2 Jun 2026 09:32:06 -0700
Subject: [PATCH 3/6] [lldb-server] Fix accelerator plugin doc and comment
wording
Remove misplaced sentence about breakpoint origins from the
jAcceleratorPluginBreakpointHit section. Update actions description
in response table and breakpoints field comment to match upstream.
---
lldb/docs/resources/lldbgdbremote.md | 5 ++---
lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 5d3ebbb7fe51d..c3bcd03621d61 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -174,8 +174,7 @@ support. Not needed for non-hardware-accelerator debugging.
## jAcceleratorPluginBreakpointHit
Sent by the client when a breakpoint requested by an accelerator plugin
-is hit in the native process. Breakpoints can originate from any response
-that contains `AcceleratorActions`. This packet requires the
+is hit in the native process. This packet requires the
`accelerator-plugins+` feature from `qSupported`.
```
@@ -197,7 +196,7 @@ The response JSON has the following fields:
|----------------------|------|-------------|
| `disable_bp` | bool | If true, the client should disable this breakpoint. |
| `auto_resume_native` | bool | If true, the native process should automatically resume after handling the hit. |
-| `actions` | object | Optional `AcceleratorActions` containing new breakpoints to set and other actions to perform. |
+| `actions` | object | Optional `AcceleratorActions` for the client to perform. |
Example:
```
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index ad9e80924b0e4..9ba36fc540a52 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -112,7 +112,7 @@ struct AcceleratorActions {
std::string session_name;
/// Unique identifier for this action within the plugin.
int64_t identifier = 0;
- /// Breakpoints to set in the native process.
+ /// New breakpoints to set. Nothing to set if this is empty.
std::vector<AcceleratorBreakpointInfo> breakpoints;
};
>From 7fbabb8c73d00dbfb1db325e7a18e36b9df6bb42 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Tue, 2 Jun 2026 14:18:07 -0700
Subject: [PATCH 4/6] [lldb-server] Address JDevlieghere review feedback for
accelerator plugins
Move accelerator packet docs into a separate "Accelerator Packets"
section with subsections, matching the Wasm packets pattern. Replace
auto with explicit type in m_accelerator_plugins loops. Add unit tests
for JSON serialization/deserialization of all accelerator packet types.
---
lldb/docs/resources/lldbgdbremote.md | 180 +++++++++--------
.../GDBRemoteCommunicationServerLLGS.cpp | 6 +-
.../AcceleratorGDBRemotePacketsTest.cpp | 189 ++++++++++++++++++
lldb/unittests/Utility/CMakeLists.txt | 1 +
4 files changed, 287 insertions(+), 89 deletions(-)
create mode 100644 lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index c3bcd03621d61..1fc04e5ce9e11 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -120,93 +120,6 @@ In any case, if we want the normal detach behavior we will just send:
D
```
-## jAcceleratorPluginInitialize
-
-This packet requests initialization data from all accelerator plugins
-installed in lldb-server. Accelerator plugins allow lldb-server to support
-debugging of hardware accelerators (e.g. GPUs, FPGAs) alongside the native
-host process.
-
-This packet requires the `accelerator-plugins+` feature from `qSupported`.
-It should be sent early in the session, after `qSupported` but before
-launching or attaching to an inferior process. If the hardware accelerator
-is not present, launching or attaching to the accelerator debug session
-will fail.
-
-```
-LLDB SENDS: jAcceleratorPluginInitialize
-STUB REPLIES: [<accelerator_action>,...]
-```
-
-Each `accelerator_action` is a JSON object with the following required fields:
-
-| Key | Type | Description |
-|----------------|---------|-------------|
-| `plugin_name` | string | Unique name identifying the accelerator plugin (e.g. `"mock"`, `"amdgpu"`). Each installed plugin has a globally unique name. |
-| `session_name` | string | Human-readable label for the accelerator target, stored on the Target object to distinguish it from the CPU target (e.g. `"AMD GPU Session"`). May be empty. |
-| `identifier` | integer | Identifier for this action, unique within the scope of its `plugin_name`. To refer to a specific action, use the combination of `plugin_name` and `identifier`. |
-
-There can be multiple accelerator plugins installed, each with a globally
-unique `plugin_name`. The response is a JSON array with one entry per
-installed plugin.
-
-Example:
-```
-LLDB SENDS: jAcceleratorPluginInitialize
-STUB REPLIES: [{"plugin_name":"amdgpu","session_name":"AMD GPU Session","identifier":0}]
-```
-
-If no accelerator plugins are installed, the server does not advertise the
-`accelerator-plugins+` feature and this packet should not be sent.
-
-Each `accelerator_action` may include a `breakpoints` array requesting
-breakpoints to be set in the native process. See
-`jAcceleratorPluginBreakpointHit` for the callback when those breakpoints
-are hit.
-
-In future patches, each `accelerator_action` will include additional fields
-such as connection info for secondary debug sessions and synchronization
-options.
-
-**Priority To Implement:** Required for hardware accelerator debugging
-support. Not needed for non-hardware-accelerator debugging.
-
-## jAcceleratorPluginBreakpointHit
-
-Sent by the client when a breakpoint requested by an accelerator plugin
-is hit in the native process. This packet requires the
-`accelerator-plugins+` feature from `qSupported`.
-
-```
-LLDB SENDS: jAcceleratorPluginBreakpointHit:<json>
-STUB REPLIES: <json_response>
-```
-
-The request JSON has the following fields:
-
-| Key | Type | Description |
-|-----------------|--------|-------------|
-| `plugin_name` | string | Name of the plugin that requested the breakpoint. |
-| `breakpoint` | object | The `AcceleratorBreakpointInfo` that was hit, including its `identifier`. |
-| `symbol_values` | array | Array of `{"name": "<name>", "value": <addr>}` for each symbol requested in the breakpoint's `symbol_names`. |
-
-The response JSON has the following fields:
-
-| Key | Type | Description |
-|----------------------|------|-------------|
-| `disable_bp` | bool | If true, the client should disable this breakpoint. |
-| `auto_resume_native` | bool | If true, the native process should automatically resume after handling the hit. |
-| `actions` | object | Optional `AcceleratorActions` for the client to perform. |
-
-Example:
-```
-LLDB SENDS: jAcceleratorPluginBreakpointHit:{"plugin_name":"mock","breakpoint":{"identifier":1,"symbol_names":[]},"symbol_values":[]}
-STUB REPLIES: {"disable_bp":true,"auto_resume_native":false,"actions":{"plugin_name":"mock","session_name":"","identifier":2,"breakpoints":[{"identifier":2,"by_name":{"function_name":"exit"},"symbol_names":[]}]}}
-```
-
-**Priority To Implement:** Required for hardware accelerator debugging
-support. Not needed for non-hardware-accelerator debugging.
-
## jGetDyldProcessState
This packet fetches the process launch state, as reported by libdyld on
@@ -2786,3 +2699,96 @@ omitting them will work fine; these numbers are always base 16.
The length of the payload is not provided. A reliable, 8-bit clean,
transport layer is assumed.
+
+## Accelerator Packets
+
+The packets below support debugging hardware accelerators (e.g. GPUs,
+FPGAs) alongside the native host process via accelerator plugins installed
+in lldb-server.
+
+### jAcceleratorPluginInitialize
+
+This packet requests initialization data from all accelerator plugins
+installed in lldb-server. Accelerator plugins allow lldb-server to support
+debugging of hardware accelerators (e.g. GPUs, FPGAs) alongside the native
+host process.
+
+This packet requires the `accelerator-plugins+` feature from `qSupported`.
+It should be sent early in the session, after `qSupported` but before
+launching or attaching to an inferior process. If the hardware accelerator
+is not present, launching or attaching to the accelerator debug session
+will fail.
+
+```
+LLDB SENDS: jAcceleratorPluginInitialize
+STUB REPLIES: [<accelerator_action>,...]
+```
+
+Each `accelerator_action` is a JSON object with the following required fields:
+
+| Key | Type | Description |
+|----------------|---------|-------------|
+| `plugin_name` | string | Unique name identifying the accelerator plugin (e.g. `"mock"`, `"amdgpu"`). Each installed plugin has a globally unique name. |
+| `session_name` | string | Human-readable label for the accelerator target, stored on the Target object to distinguish it from the CPU target (e.g. `"AMD GPU Session"`). May be empty. |
+| `identifier` | integer | Identifier for this action, unique within the scope of its `plugin_name`. To refer to a specific action, use the combination of `plugin_name` and `identifier`. |
+
+There can be multiple accelerator plugins installed, each with a globally
+unique `plugin_name`. The response is a JSON array with one entry per
+installed plugin.
+
+Example:
+```
+LLDB SENDS: jAcceleratorPluginInitialize
+STUB REPLIES: [{"plugin_name":"amdgpu","session_name":"AMD GPU Session","identifier":0}]
+```
+
+If no accelerator plugins are installed, the server does not advertise the
+`accelerator-plugins+` feature and this packet should not be sent.
+
+Each `accelerator_action` may include a `breakpoints` array requesting
+breakpoints to be set in the native process. See
+`jAcceleratorPluginBreakpointHit` for the callback when those breakpoints
+are hit.
+
+In future patches, each `accelerator_action` will include additional fields
+such as connection info for secondary debug sessions and synchronization
+options.
+
+**Priority To Implement:** Required for hardware accelerator debugging
+support. Not needed for non-hardware-accelerator debugging.
+
+### jAcceleratorPluginBreakpointHit
+
+Sent by the client when a breakpoint requested by an accelerator plugin
+is hit in the native process. This packet requires the
+`accelerator-plugins+` feature from `qSupported`.
+
+```
+LLDB SENDS: jAcceleratorPluginBreakpointHit:<json>
+STUB REPLIES: <json_response>
+```
+
+The request JSON has the following fields:
+
+| Key | Type | Description |
+|-----------------|--------|-------------|
+| `plugin_name` | string | Name of the plugin that requested the breakpoint. |
+| `breakpoint` | object | The `AcceleratorBreakpointInfo` that was hit, including its `identifier`. |
+| `symbol_values` | array | Array of `{"name": "<name>", "value": <addr>}` for each symbol requested in the breakpoint's `symbol_names`. |
+
+The response JSON has the following fields:
+
+| Key | Type | Description |
+|----------------------|------|-------------|
+| `disable_bp` | bool | If true, the client should disable this breakpoint. |
+| `auto_resume_native` | bool | If true, the native process should automatically resume after handling the hit. |
+| `actions` | object | Optional `AcceleratorActions` for the client to perform. |
+
+Example:
+```
+LLDB SENDS: jAcceleratorPluginBreakpointHit:{"plugin_name":"mock","breakpoint":{"identifier":1,"symbol_names":[]},"symbol_values":[]}
+STUB REPLIES: {"disable_bp":true,"auto_resume_native":false,"actions":{"plugin_name":"mock","session_name":"","identifier":2,"breakpoints":[{"identifier":2,"by_name":{"function_name":"exit"},"symbol_names":[]}]}}
+```
+
+**Priority To Implement:** Required for hardware accelerator debugging
+support. Not needed for non-hardware-accelerator debugging.
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 1f3598fe1c2d1..ec80a3e960949 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -4545,7 +4545,8 @@ GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize(
StringExtractorGDBRemote &) {
std::vector<AcceleratorActions> accelerator_actions;
- for (auto &plugin_up : m_accelerator_plugins) {
+ for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
+ m_accelerator_plugins) {
if (auto actions = plugin_up->GetInitializeActions())
accelerator_actions.push_back(std::move(*actions));
}
@@ -4564,7 +4565,8 @@ GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginBreakpointHit(
if (!args)
return SendErrorResponse(args.takeError());
- for (auto &plugin_up : m_accelerator_plugins) {
+ for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
+ m_accelerator_plugins) {
if (plugin_up->GetPluginName() == args->plugin_name) {
llvm::Expected<AcceleratorBreakpointHitResponse> bp_response =
plugin_up->BreakpointWasHit(*args);
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
new file mode 100644
index 0000000000000..e9e74145e618c
--- /dev/null
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -0,0 +1,189 @@
+//===-- AcceleratorGDBRemotePacketsTest.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/AcceleratorGDBRemotePackets.h"
+#include "TestingSupport/TestUtilities.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace llvm;
+
+TEST(AcceleratorGDBRemotePacketsTest, SymbolValue) {
+ SymbolValue sv;
+ sv.name = "my_symbol";
+ sv.value = 0xDEADBEEF;
+
+ Expected<SymbolValue> deserialized = roundtripJSON(sv);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(sv.name, deserialized->name);
+ EXPECT_EQ(sv.value, deserialized->value);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, SymbolValueNullopt) {
+ SymbolValue sv;
+ sv.name = "missing";
+ sv.value = std::nullopt;
+
+ Expected<SymbolValue> deserialized = roundtripJSON(sv);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(sv.name, deserialized->name);
+ EXPECT_EQ(std::nullopt, deserialized->value);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointByName) {
+ AcceleratorBreakpointByName bp;
+ bp.function_name = "main";
+ bp.shlib = "libfoo.so";
+
+ Expected<AcceleratorBreakpointByName> deserialized = roundtripJSON(bp);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(bp.function_name, deserialized->function_name);
+ EXPECT_EQ(bp.shlib, deserialized->shlib);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointByNameNoShlib) {
+ AcceleratorBreakpointByName bp;
+ bp.function_name = "main";
+
+ Expected<AcceleratorBreakpointByName> deserialized = roundtripJSON(bp);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(bp.function_name, deserialized->function_name);
+ EXPECT_EQ(std::nullopt, deserialized->shlib);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointByAddress) {
+ AcceleratorBreakpointByAddress bp;
+ bp.load_address = 0x400000;
+
+ Expected<AcceleratorBreakpointByAddress> deserialized = roundtripJSON(bp);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(bp.load_address, deserialized->load_address);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointInfoByName) {
+ AcceleratorBreakpointInfo info;
+ info.identifier = 42;
+ info.by_name = AcceleratorBreakpointByName{std::nullopt, "main"};
+ info.symbol_names = {"sym1", "sym2"};
+
+ Expected<AcceleratorBreakpointInfo> deserialized = roundtripJSON(info);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(info.identifier, deserialized->identifier);
+ ASSERT_TRUE(deserialized->by_name.has_value());
+ EXPECT_EQ("main", deserialized->by_name->function_name);
+ EXPECT_EQ(std::nullopt, deserialized->by_address);
+ EXPECT_EQ(info.symbol_names, deserialized->symbol_names);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointInfoByAddress) {
+ AcceleratorBreakpointInfo info;
+ info.identifier = 7;
+ info.by_address = AcceleratorBreakpointByAddress{0x1000};
+
+ Expected<AcceleratorBreakpointInfo> deserialized = roundtripJSON(info);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(info.identifier, deserialized->identifier);
+ EXPECT_EQ(std::nullopt, deserialized->by_name);
+ ASSERT_TRUE(deserialized->by_address.has_value());
+ EXPECT_EQ(0x1000u, deserialized->by_address->load_address);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointHitArgs) {
+ AcceleratorBreakpointHitArgs args("mock");
+ args.breakpoint.identifier = 1;
+ args.breakpoint.by_name = AcceleratorBreakpointByName{std::nullopt, "main"};
+ args.symbol_values = {{"sym1", 0x2000}, {"sym2", std::nullopt}};
+
+ Expected<AcceleratorBreakpointHitArgs> deserialized = roundtripJSON(args);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ("mock", deserialized->plugin_name);
+ EXPECT_EQ(1, deserialized->breakpoint.identifier);
+ ASSERT_EQ(2u, deserialized->symbol_values.size());
+ EXPECT_EQ("sym1", deserialized->symbol_values[0].name);
+ EXPECT_EQ(0x2000u, deserialized->symbol_values[0].value);
+ EXPECT_EQ("sym2", deserialized->symbol_values[1].name);
+ EXPECT_EQ(std::nullopt, deserialized->symbol_values[1].value);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorBreakpointHitArgsGetSymbol) {
+ AcceleratorBreakpointHitArgs args("mock");
+ args.symbol_values = {{"sym1", 0x1000}, {"sym2", 0x2000}};
+
+ EXPECT_EQ(0x1000u, args.GetSymbolValue("sym1"));
+ EXPECT_EQ(0x2000u, args.GetSymbolValue("sym2"));
+ EXPECT_EQ(std::nullopt, args.GetSymbolValue("missing"));
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorActions) {
+ AcceleratorActions actions("mock", 1);
+ actions.session_name = "Mock Session";
+ AcceleratorBreakpointInfo bp;
+ bp.identifier = 1;
+ bp.by_name = AcceleratorBreakpointByName{std::nullopt, "main"};
+ actions.breakpoints.push_back(std::move(bp));
+
+ Expected<AcceleratorActions> deserialized = roundtripJSON(actions);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ("mock", deserialized->plugin_name);
+ EXPECT_EQ("Mock Session", deserialized->session_name);
+ EXPECT_EQ(1, deserialized->identifier);
+ ASSERT_EQ(1u, deserialized->breakpoints.size());
+ EXPECT_EQ(1, deserialized->breakpoints[0].identifier);
+ EXPECT_EQ("main", deserialized->breakpoints[0].by_name->function_name);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorActionsEmpty) {
+ AcceleratorActions actions("test", 0);
+
+ Expected<AcceleratorActions> deserialized = roundtripJSON(actions);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ("test", deserialized->plugin_name);
+ EXPECT_TRUE(deserialized->breakpoints.empty());
+}
+
+TEST(AcceleratorGDBRemotePacketsTest,
+ AcceleratorBreakpointHitResponseNoActions) {
+ AcceleratorBreakpointHitResponse response;
+ response.disable_bp = true;
+ response.auto_resume_native = false;
+
+ Expected<AcceleratorBreakpointHitResponse> deserialized =
+ roundtripJSON(response);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_TRUE(deserialized->disable_bp);
+ EXPECT_FALSE(deserialized->auto_resume_native);
+ EXPECT_FALSE(deserialized->actions.has_value());
+}
+
+TEST(AcceleratorGDBRemotePacketsTest,
+ AcceleratorBreakpointHitResponseWithActions) {
+ AcceleratorBreakpointHitResponse response;
+ response.disable_bp = true;
+ response.auto_resume_native = false;
+
+ AcceleratorActions actions("mock", 2);
+ AcceleratorBreakpointInfo bp;
+ bp.identifier = 2;
+ bp.by_name = AcceleratorBreakpointByName{std::nullopt, "exit"};
+ actions.breakpoints.push_back(std::move(bp));
+ response.actions = std::move(actions);
+
+ Expected<AcceleratorBreakpointHitResponse> deserialized =
+ roundtripJSON(response);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_TRUE(deserialized->disable_bp);
+ EXPECT_FALSE(deserialized->auto_resume_native);
+ ASSERT_TRUE(deserialized->actions.has_value());
+ EXPECT_EQ("mock", deserialized->actions->plugin_name);
+ EXPECT_EQ(2, deserialized->actions->identifier);
+ ASSERT_EQ(1u, deserialized->actions->breakpoints.size());
+ EXPECT_EQ("exit",
+ deserialized->actions->breakpoints[0].by_name->function_name);
+}
diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt
index 3a0ef91ea76a5..ed159748838b5 100644
--- a/lldb/unittests/Utility/CMakeLists.txt
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -1,4 +1,5 @@
add_lldb_unittest(UtilityTests
+ AcceleratorGDBRemotePacketsTest.cpp
AnsiTerminalTest.cpp
ArgsTest.cpp
OptionsWithRawTest.cpp
>From 51809a388c3a6158f118d74e9f4e9d44ae26e8cc Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Tue, 2 Jun 2026 14:29:54 -0700
Subject: [PATCH 5/6] [lldb-server] Fix banner length in
AcceleratorGDBRemotePacketsTest.cpp
---
lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
index e9e74145e618c..ea2bdc3038645 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -1,4 +1,4 @@
-//===-- AcceleratorGDBRemotePacketsTest.cpp --------------------------------===//
+//===-- AcceleratorGDBRemotePacketsTest.cpp ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From 5523799b7c98cb1f2aef48df61177498dfa7df60 Mon Sep 17 00:00:00 2001
From: satyanarayana reddy janga <satyajanga at gmail.com>
Date: Tue, 2 Jun 2026 21:12:44 -0500
Subject: [PATCH 6/6] Update
lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
index ea2bdc3038645..fdcb0585fa3c7 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -1,4 +1,4 @@
-//===-- AcceleratorGDBRemotePacketsTest.cpp ---------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
More information about the lldb-commits
mailing list