[Lldb-commits] [lldb] [lldb-server] Add accelerator plugin infrastructure for debugging hardware accelerators like gpus (PR #198907)

satyanarayana reddy janga via lldb-commits lldb-commits at lists.llvm.org
Wed May 27 07:31:51 PDT 2026


https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/198907

>From 1eed527606b1d24de71ad1d3be0c534f0a0974d5 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Thu, 21 May 2026 07:18:21 -0700
Subject: [PATCH] [lldb-server] Add accelerator plugin infrastructure with
 protocol support

Add an extensible plugin system to lldb-server for debugging hardware
accelerators (GPUs, FPGAs, etc.) alongside the native host process.
This is the first step toward upstreaming the server-side plugin
support from the clayborg/llvm-server-plugins branch.

This patch adds:

- LLDBServerAcceleratorPlugin base class with GetPluginName() and
  GetInitializeActions() virtual methods
- AcceleratorActions struct with JSON encode/decode
- accelerator-plugins+ feature in qSupported, only advertised when
  at least one plugin is installed
- jAcceleratorPluginInitialize packet handler that returns a JSON
  array of AcceleratorActions from all installed plugins
- PutAsJSON/PutAsJSONArray template methods on StreamGDBRemote
- InstallPlugin() on GDBRemoteCommunicationServerLLGS for registering
  plugins directly (no PluginManager integration)
- Mock accelerator plugin gated by LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN
  CMake option (default OFF)
- Tests verifying qSupported feature and jAcceleratorPluginInitialize
  JSON response round-trip against a real lldb-server
---
 lldb/docs/resources/lldbgdbremote.md          | 54 +++++++++++++-
 .../lldb/Host/common/NativeProcessProtocol.h  |  3 +-
 .../Utility/AcceleratorGDBRemotePackets.h     | 38 ++++++++++
 lldb/include/lldb/Utility/GDBRemote.h         | 20 +++++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  1 +
 .../tools/lldb-server/gdbremote_testcase.py   |  1 +
 .../Plugins/Process/gdb-remote/CMakeLists.txt |  1 +
 .../GDBRemoteCommunicationServerLLGS.cpp      | 23 ++++++
 .../GDBRemoteCommunicationServerLLGS.h        |  9 +++
 .../LLDBServerAcceleratorPlugin.cpp           | 17 +++++
 .../gdb-remote/LLDBServerAcceleratorPlugin.h  | 43 +++++++++++
 .../Utility/AcceleratorGDBRemotePackets.cpp   | 27 +++++++
 lldb/source/Utility/CMakeLists.txt            |  1 +
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 lldb/test/API/accelerator/mock/Makefile       |  3 +
 .../mock/TestMockAcceleratorPlugin.py         | 74 +++++++++++++++++++
 lldb/test/API/accelerator/mock/main.c         |  1 +
 lldb/test/API/lit.site.cfg.py.in              |  3 +
 lldb/test/CMakeLists.txt                      |  1 +
 lldb/tools/lldb-server/CMakeLists.txt         | 17 +++++
 .../Plugins/Accelerator/CMakeLists.txt        |  1 +
 .../Plugins/Accelerator/Mock/CMakeLists.txt   | 12 +++
 .../Mock/LLDBServerMockAcceleratorPlugin.cpp  | 24 ++++++
 .../Mock/LLDBServerMockAcceleratorPlugin.h    | 28 +++++++
 lldb/tools/lldb-server/Plugins/CMakeLists.txt |  1 +
 lldb/tools/lldb-server/lldb-gdbserver.cpp     | 10 +++
 lldb/utils/lldb-dotest/CMakeLists.txt         |  1 +
 lldb/utils/lldb-dotest/lldb-dotest.in         |  3 +
 28 files changed, 414 insertions(+), 5 deletions(-)
 create mode 100644 lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
 create mode 100644 lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.cpp
 create mode 100644 lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
 create mode 100644 lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
 create mode 100644 lldb/test/API/accelerator/mock/Makefile
 create mode 100644 lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
 create mode 100644 lldb/test/API/accelerator/mock/main.c
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
 create mode 100644 lldb/tools/lldb-server/Plugins/CMakeLists.txt

diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index e93a5ea120d17..74a347b93824d 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -120,6 +120,52 @@ 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.
+
+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.
+
+**Priority To Implement:** Low. Only needed for hardware accelerator
+debugging support.
+
 ## jGetDyldProcessState
 
 This packet fetches the process launch state, as reported by libdyld on
@@ -168,7 +214,7 @@ fetch detailed information in batches, to keep the size of the
 packets from becoming too large.
 
 
-And the second form of jGetLoadedDynamicLibrariesInfos 
+And the second form of jGetLoadedDynamicLibrariesInfos
 requests information about a list of binaries, given their load addresses:
 ```
 jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]}
@@ -1188,7 +1234,7 @@ In the example above, three lldb extensions are shown:
     * A list of compression types that the stub can use to compress packets
     when the QEnableCompression packet is used to request one of them.
   * `SupportedWatchpointTypes=<item,item,...>`
-    * A list of watchpoint types that this stub can manage. Currently defined 
+    * A list of watchpoint types that this stub can manage. Currently defined
       names are:
         * `x86_64` - 64-bit x86-64 watchpoints (1, 2, 4, 8 byte watchpoints
           aligned to those amounts)
@@ -1518,7 +1564,7 @@ tuples to return are:
   region that are "dirty" -- they have been modified.
   Page addresses are in base 16. The size of a page can
   be found from the `qHostInfo`'s `page-size` key-value.
-  
+
   If the stub supports identifying dirty pages within a
   memory region, this key should always be present for all
   `qMemoryRegionInfo` replies.  This key with no pages
@@ -1598,7 +1644,7 @@ The request packet has the fields:
    1. mode bits in base 16
    2. file path in ascii-hex encoding
 
-Reply: 
+Reply:
   * `F<mkdir-return-code>`
     (mkdir called successfully and returned with the given return code)
   * `Exx` (An error occurred)
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 3b133bd218316..f3c0f16502fab 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -287,8 +287,9 @@ class NativeProcessProtocol {
     savecore = (1u << 7),
     siginfo_read = (1u << 8),
     libraries = (1u << 9),
+    accelerator_plugins = (1u << 10),
 
-    LLVM_MARK_AS_BITMASK_ENUM(libraries)
+    LLVM_MARK_AS_BITMASK_ENUM(accelerator_plugins)
   };
 
   class Manager {
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
new file mode 100644
index 0000000000000..479503bf47a3c
--- /dev/null
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
+#define LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
+
+#include "llvm/Support/JSON.h"
+#include <cstdint>
+#include <string>
+
+namespace lldb_private {
+
+struct AcceleratorActions {
+  AcceleratorActions() = default;
+  AcceleratorActions(llvm::StringRef plugin_name, int64_t action_id)
+      : plugin_name(plugin_name), identifier(action_id) {}
+
+  /// Unique name identifying the accelerator plugin.
+  std::string plugin_name;
+  /// Human-readable label for the accelerator target.
+  std::string session_name;
+  /// Unique identifier for this action within the plugin.
+  int64_t identifier = 0;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorActions &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const AcceleratorActions &data);
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/GDBRemote.h b/lldb/include/lldb/Utility/GDBRemote.h
index ab33f02ebb012..888a4dd54124d 100644
--- a/lldb/include/lldb/Utility/GDBRemote.h
+++ b/lldb/include/lldb/Utility/GDBRemote.h
@@ -13,6 +13,7 @@
 #include "lldb/Utility/StreamString.h"
 #include "lldb/lldb-enumerations.h"
 #include "lldb/lldb-public.h"
+#include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <cstddef>
@@ -45,6 +46,25 @@ class StreamGDBRemote : public StreamString {
 
   /// Equivalent to PutEscapedBytes(str.data(), str.size());
   int PutEscapedBytes(llvm::StringRef str);
+
+  template <class T> int PutAsJSON(const T &obj, bool hex_ascii) {
+    std::string json_string;
+    llvm::raw_string_ostream os(json_string);
+    os << llvm::json::Value(toJSON(obj));
+    if (hex_ascii)
+      return PutStringAsRawHex8(json_string);
+    return PutEscapedBytes(json_string.c_str(), json_string.size());
+  }
+
+  template <class T> int PutAsJSONArray(const std::vector<T> &array) {
+    llvm::json::Array json_array;
+    for (const auto &obj : array)
+      json_array.push_back(toJSON(obj));
+    std::string json_string;
+    llvm::raw_string_ostream os(json_string);
+    os << llvm::json::Value(std::move(json_array));
+    return PutEscapedBytes(json_string.data(), json_string.size());
+  }
 };
 
 /// GDB remote packet as used by the GDB remote communication history. Packets
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 9aeac03a54bee..85ec27cac9ac0 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -173,6 +173,7 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_jLLDBTraceGetState,
     eServerPacketType_jLLDBTraceGetBinaryData,
     eServerPacketType_jMultiBreakpoint,
+    eServerPacketType_jAcceleratorPluginInitialize,
 
     eServerPacketType_qMemTags, // read memory tags
     eServerPacketType_QMemTags, // write memory tags
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
index 9eb390f51ac7b..3e89555f24ff3 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -940,6 +940,7 @@ def add_qSupported_packets(self, client_features=[]):
         "SupportedCompressions",
         "MultiMemRead",
         "jMultiBreakpoint",
+        "accelerator-plugins",
     ]
 
     def parse_qSupported_response(self, context):
diff --git a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt
index d805d3930cda3..00a59f8ef2ad4 100644
--- a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt
@@ -31,6 +31,7 @@ add_lldb_library(lldbPluginProcessGDBRemote PLUGIN
   GDBRemoteCommunicationServerCommon.cpp
   GDBRemoteCommunicationServerLLGS.cpp
   GDBRemoteCommunicationServerPlatform.cpp
+  LLDBServerAcceleratorPlugin.cpp
   GDBRemoteRegisterContext.cpp
   GDBRemoteRegisterFallback.cpp
   ProcessGDBRemote.cpp
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index c3c70ad93e9e5..dab08d96adc4a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -31,6 +31,7 @@
 #include "lldb/Host/common/NativeRegisterContext.h"
 #include "lldb/Host/common/NativeThreadProtocol.h"
 #include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/AcceleratorGDBRemotePackets.h"
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/DataBuffer.h"
 #include "lldb/Utility/Endian.h"
@@ -54,6 +55,7 @@
 
 using namespace lldb;
 using namespace lldb_private;
+using namespace lldb_private::lldb_server;
 using namespace lldb_private::process_gdb_remote;
 using namespace llvm;
 
@@ -221,6 +223,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jMultiBreakpoint,
       &GDBRemoteCommunicationServerLLGS::Handle_jMultiBreakpoint);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jAcceleratorPluginInitialize,
+      &GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize);
 
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
                                 &GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -4432,6 +4437,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
     ret.push_back("memory-tagging+");
   if (bool(plugin_features & Extension::savecore))
     ret.push_back("qSaveCore+");
+  if (!m_accelerator_plugins.empty())
+    ret.push_back("accelerator-plugins+");
 
   // check for client features
   m_extensions_supported = {};
@@ -4523,3 +4530,19 @@ lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg,
   return (reverse_connect ? "unix-connect://" : "unix-accept://") +
          url_arg.str();
 }
+
+void GDBRemoteCommunicationServerLLGS::InstallPlugin(
+    std::unique_ptr<LLDBServerAcceleratorPlugin> plugin_up) {
+  m_accelerator_plugins.emplace_back(std::move(plugin_up));
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize(
+    StringExtractorGDBRemote &) {
+  std::vector<AcceleratorActions> accelerator_actions;
+  for (auto &plugin_up : m_accelerator_plugins)
+    accelerator_actions.push_back(plugin_up->GetInitializeActions());
+  StreamGDBRemote response;
+  response.PutAsJSONArray(accelerator_actions);
+  return SendPacketNoLock(response.GetString());
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index ee4372adc0b40..5a088f8de2380 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -20,6 +20,7 @@
 #include "lldb/lldb-private-forward.h"
 
 #include "GDBRemoteCommunicationServerCommon.h"
+#include "LLDBServerAcceleratorPlugin.h"
 
 class StringExtractorGDBRemote;
 
@@ -72,6 +73,9 @@ class GDBRemoteCommunicationServerLLGS
   ///     attach operation.
   Status AttachWaitProcess(llvm::StringRef process_name, bool include_existing);
 
+  void InstallPlugin(
+      std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> plugin_up);
+
   // NativeProcessProtocol::NativeDelegate overrides
   void InitializeDelegate(NativeProcessProtocol *process) override;
 
@@ -104,6 +108,8 @@ class GDBRemoteCommunicationServerLLGS
   MainLoop &m_mainloop;
   MainLoop::ReadHandleUP m_network_handle_up;
   NativeProcessProtocol::Manager &m_process_manager;
+  std::vector<std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin>>
+      m_accelerator_plugins;
   lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID;
   lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID;
   NativeProcessProtocol *m_current_process;
@@ -278,6 +284,9 @@ class GDBRemoteCommunicationServerLLGS
 
   PacketResult Handle_jMultiBreakpoint(StringExtractorGDBRemote &packet);
 
+  PacketResult
+  Handle_jAcceleratorPluginInitialize(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.cpp
new file mode 100644
index 0000000000000..ce3b767832095
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "LLDBServerAcceleratorPlugin.h"
+
+using namespace lldb_private::lldb_server;
+
+LLDBServerAcceleratorPlugin::LLDBServerAcceleratorPlugin(GDBServer &gdb_server,
+                                                         MainLoop &main_loop)
+    : m_gdb_server(gdb_server), m_main_loop(main_loop) {}
+
+LLDBServerAcceleratorPlugin::~LLDBServerAcceleratorPlugin() = default;
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
new file mode 100644
index 0000000000000..baffd30ebb40b
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_LLDBSERVERACCELERATORPLUGIN_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_LLDBSERVERACCELERATORPLUGIN_H
+
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/AcceleratorGDBRemotePackets.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+
+namespace process_gdb_remote {
+class GDBRemoteCommunicationServerLLGS;
+} // namespace process_gdb_remote
+
+namespace lldb_server {
+
+class LLDBServerAcceleratorPlugin {
+public:
+  using GDBServer = process_gdb_remote::GDBRemoteCommunicationServerLLGS;
+
+  LLDBServerAcceleratorPlugin(GDBServer &gdb_server, MainLoop &main_loop);
+  virtual ~LLDBServerAcceleratorPlugin();
+
+  virtual llvm::StringRef GetPluginName() = 0;
+
+  virtual AcceleratorActions GetInitializeActions() = 0;
+
+protected:
+  GDBServer &m_gdb_server;
+  MainLoop &m_main_loop;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_LLDBSERVERACCELERATORPLUGIN_H
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
new file mode 100644
index 0000000000000..8019db4328c78
--- /dev/null
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"
+
+using namespace lldb_private;
+
+llvm::json::Value lldb_private::toJSON(const AcceleratorActions &data) {
+  return llvm::json::Object{
+      {"plugin_name", data.plugin_name},
+      {"session_name", data.session_name},
+      {"identifier", data.identifier},
+  };
+}
+
+bool lldb_private::fromJSON(const llvm::json::Value &value,
+                            AcceleratorActions &data, llvm::json::Path path) {
+  llvm::json::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);
+}
diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt
index 3836ab0ec6c29..080e0d1a0fd96 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -44,6 +44,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
   FileSpec.cpp
   FileSpecList.cpp
   GDBRemote.cpp
+  AcceleratorGDBRemotePackets.cpp
   IOObject.cpp
   LLDBAssert.cpp
   LLDBLog.cpp
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 17e36398fc218..7b46bef933ac2 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -332,6 +332,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jLLDBTraceGetBinaryData;
     if (PACKET_STARTS_WITH("jMultiBreakpoint:"))
       return eServerPacketType_jMultiBreakpoint;
+    if (PACKET_MATCHES("jAcceleratorPluginInitialize"))
+      return eServerPacketType_jAcceleratorPluginInitialize;
     break;
 
   case 'v':
diff --git a/lldb/test/API/accelerator/mock/Makefile b/lldb/test/API/accelerator/mock/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/accelerator/mock/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
new file mode 100644
index 0000000000000..3e9ca174cd533
--- /dev/null
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPlugin.py
@@ -0,0 +1,74 @@
+"""
+Tests for the lldb-server mock accelerator plugin.
+
+Verifies the accelerator-plugins+ feature in qSupported and
+the jAcceleratorPluginInitialize packet response.
+"""
+
+import json
+
+import gdbremote_testcase
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import configuration
+
+
+def get_accelerator_action(actions, plugin_name):
+    for action in actions:
+        if action["plugin_name"] == plugin_name:
+            return action
+    return None
+
+
+class MockAcceleratorPluginTestCase(gdbremote_testcase.GdbRemoteTestCaseBase):
+    def setUp(self):
+        super().setUp()
+        if "mock-accelerator" not in configuration.enabled_plugins:
+            self.skipTest("mock-accelerator plugin is not enabled")
+
+    @add_test_categories(["llgs"])
+    def test_qSupported_reports_accelerator_plugins(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.prep_debug_monitor_and_inferior()
+
+        self.add_qSupported_packets()
+        context = self.expect_gdbremote_sequence()
+        features = self.parse_qSupported_response(context)
+
+        self.assertIn("accelerator-plugins", features)
+        self.assertEqual(features["accelerator-plugins"], "+")
+
+    @add_test_categories(["llgs"])
+    def test_jAcceleratorPluginInitialize_returns_mock_actions(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.prep_debug_monitor_and_inferior()
+
+        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)
+        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)
diff --git a/lldb/test/API/accelerator/mock/main.c b/lldb/test/API/accelerator/mock/main.c
new file mode 100644
index 0000000000000..78f2de106c92b
--- /dev/null
+++ b/lldb/test/API/accelerator/mock/main.c
@@ -0,0 +1 @@
+int main(void) { return 0; }
diff --git a/lldb/test/API/lit.site.cfg.py.in b/lldb/test/API/lit.site.cfg.py.in
index 35d108f53e119..b5b070bb8d60d 100644
--- a/lldb/test/API/lit.site.cfg.py.in
+++ b/lldb/test/API/lit.site.cfg.py.in
@@ -54,6 +54,9 @@ config.clang_module_cache = os.path.join("@LLDB_TEST_MODULE_CACHE_CLANG@", "lldb
 lldb_build_intel_pt = '@LLDB_BUILD_INTEL_PT@'
 if lldb_build_intel_pt == '1':
     config.enabled_plugins.append('intel-pt')
+lldb_build_mock_accelerator = '@LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN@'
+if lldb_build_mock_accelerator == '1':
+    config.enabled_plugins.append('mock-accelerator')
 
 # Additional dotest arguments can be passed to lit by providing a
 # semicolon-separates list: --param dotest-args="arg;arg".
diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt
index ed9dcd11cc6cd..d5e4377648da8 100644
--- a/lldb/test/CMakeLists.txt
+++ b/lldb/test/CMakeLists.txt
@@ -260,6 +260,7 @@ set(LLDB_TEST_USE_LLDB_SERVER OFF CACHE BOOL "If ON, run the test suites against
 # These values are not canonicalized within LLVM.
 llvm_canonicalize_cmake_booleans(
   LLDB_BUILD_INTEL_PT
+  LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN
   LLDB_ENABLE_MTE
   LLDB_ENABLE_PYTHON
   LLDB_ENABLE_LUA
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index fb55c64936121..aedd7ab9f885e 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -34,6 +34,17 @@ else()
   list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF)
 endif()
 
+option(LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN
+  "Enable the mock accelerator plugin for testing" OFF)
+
+add_subdirectory(Plugins)
+
+set(LLDB_SERVER_PLUGINS)
+
+if(LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN)
+  list(APPEND LLDB_SERVER_PLUGINS lldbServerPluginMockAccelerator)
+endif()
+
 if(APPLE_EMBEDDED)
   if(LLDB_CODESIGN_IDENTITY)
     # Use explicit LLDB identity
@@ -61,6 +72,7 @@ add_lldb_tool(lldb-server
       lldbInitialization
       lldbVersion
       ${LLDB_PLUGINS}
+      ${LLDB_SERVER_PLUGINS}
       lldbPluginInstructionARM
       lldbPluginInstructionLoongArch
       lldbPluginInstructionMIPS
@@ -76,3 +88,8 @@ add_dependencies(lldb-server
 )
 target_include_directories(lldb-server PRIVATE "${LLDB_SOURCE_DIR}/source")
 target_link_libraries(lldb-server PRIVATE ${LLDB_SYSTEM_LIBS})
+
+if(LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN)
+  target_compile_definitions(lldb-server
+    PRIVATE LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN)
+endif()
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/Accelerator/CMakeLists.txt
new file mode 100644
index 0000000000000..853788384e141
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(Mock)
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt
new file mode 100644
index 0000000000000..9b5a82a087e3d
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_lldb_library(lldbServerPluginMockAccelerator
+  LLDBServerMockAcceleratorPlugin.cpp
+
+  LINK_LIBS
+    lldbHost
+    lldbUtility
+)
+
+target_include_directories(lldbServerPluginMockAccelerator
+  PRIVATE
+    "${LLDB_SOURCE_DIR}/source"
+)
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
new file mode 100644
index 0000000000000..cade2dfa1231b
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "LLDBServerMockAcceleratorPlugin.h"
+
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+
+LLDBServerMockAcceleratorPlugin::LLDBServerMockAcceleratorPlugin(
+    GDBServer &gdb_server, MainLoop &main_loop)
+    : LLDBServerAcceleratorPlugin(gdb_server, main_loop) {}
+
+llvm::StringRef LLDBServerMockAcceleratorPlugin::GetPluginName() {
+  return "mock";
+}
+
+AcceleratorActions LLDBServerMockAcceleratorPlugin::GetInitializeActions() {
+  return AcceleratorActions(GetPluginName(), 0);
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
new file mode 100644
index 0000000000000..d145a97cffb8c
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_LLDBSERVERMOCKACCELERATORPLUGIN_H
+#define LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_LLDBSERVERMOCKACCELERATORPLUGIN_H
+
+#include "Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h"
+
+namespace lldb_private {
+namespace lldb_server {
+
+class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
+public:
+  LLDBServerMockAcceleratorPlugin(GDBServer &gdb_server, MainLoop &main_loop);
+
+  llvm::StringRef GetPluginName() override;
+  AcceleratorActions GetInitializeActions() override;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_LLDBSERVERMOCKACCELERATORPLUGIN_H
diff --git a/lldb/tools/lldb-server/Plugins/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
new file mode 100644
index 0000000000000..c9f43933b3b55
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(Accelerator)
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index bb758bffad076..2e00c66eaa4dd 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -38,6 +38,10 @@
 #include "llvm/Support/FormatAdapters.h"
 #include "llvm/Support/WithColor.h"
 
+#if defined(LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN)
+#include "Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h"
+#endif
+
 #if defined(__linux__)
 #include "Plugins/Process/Linux/NativeProcessLinux.h"
 #elif defined(__FreeBSD__)
@@ -450,6 +454,12 @@ int main_gdbserver(int argc, char *argv[]) {
   NativeProcessManager manager(mainloop);
   GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
 
+#if defined(LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN)
+  gdb_server.InstallPlugin(
+      std::make_unique<lldb_server::LLDBServerMockAcceleratorPlugin>(gdb_server,
+                                                                     mainloop));
+#endif
+
   llvm::StringRef host_and_port;
   if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {
     host_and_port = Inputs.front();
diff --git a/lldb/utils/lldb-dotest/CMakeLists.txt b/lldb/utils/lldb-dotest/CMakeLists.txt
index 23f1fc23c783d..83c2093230cd0 100644
--- a/lldb/utils/lldb-dotest/CMakeLists.txt
+++ b/lldb/utils/lldb-dotest/CMakeLists.txt
@@ -17,6 +17,7 @@ endif()
 
 llvm_canonicalize_cmake_booleans(
   LLDB_BUILD_INTEL_PT
+  LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN
   LLDB_HAS_LIBCXX
   LLDB_USE_ARM64E_DEBUGSERVER
 )
diff --git a/lldb/utils/lldb-dotest/lldb-dotest.in b/lldb/utils/lldb-dotest/lldb-dotest.in
index 9e0c740efe9f5..dd865bc5246b4 100755
--- a/lldb/utils/lldb-dotest/lldb-dotest.in
+++ b/lldb/utils/lldb-dotest/lldb-dotest.in
@@ -13,6 +13,7 @@ dsymutil = '@LLDB_TEST_DSYMUTIL_CONFIGURED@'
 make = '@LLDB_TEST_MAKE_CONFIGURED@'
 lldb_build_dir = '@LLDB_TEST_BUILD_DIRECTORY_CONFIGURED@'
 lldb_build_intel_pt = "@LLDB_BUILD_INTEL_PT@"
+lldb_build_mock_accelerator = "@LLDB_ENABLE_MOCK_ACCELERATOR_PLUGIN@"
 lldb_framework_dir = "@LLDB_FRAMEWORK_DIR_CONFIGURED@"
 lldb_libs_dir = "@LLDB_LIBS_DIR_CONFIGURED@"
 llvm_tools_dir = "@LLVM_TOOLS_DIR_CONFIGURED@"
@@ -54,6 +55,8 @@ if __name__ == '__main__':
         cmd.extend(['--framework', lldb_framework_dir])
     if lldb_build_intel_pt == "1":
         cmd.extend(['--enable-plugin', 'intel-pt'])
+    if lldb_build_mock_accelerator == "1":
+        cmd.extend(['--enable-plugin', 'mock-accelerator'])
     if lldb_enable_arm64e_debugserver:
         cmd.extend(['--arm64e-debugserver'])
     cmd.extend(['--lldb-obj-root', lldb_obj_root])



More information about the lldb-commits mailing list