[Lldb-commits] [lldb] [lldb-server] Add connection info to accelerator plugin protocol (PR #201449)

satyanarayana reddy janga via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 8 08:29:39 PDT 2026


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

>From 78f5c6f0e0cd5ed28d30d0431be433c29cd02ad1 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Sat, 6 Jun 2026 08:25:45 -0700
Subject: [PATCH 1/2] [lldb] Add connection info to the accelerator plugin
 protocol

Add AcceleratorConnectionInfo, describing how the client should create a
new target and reverse-connect to a separate GDB server that serves an
accelerator's state (e.g. a GPU debug stub). It carries the connect URL
and optional exe path, platform name, triple, and a synchronous flag.

A connect_info field is added to AcceleratorActions so a plugin can ask
the client to establish such a connection alongside (or instead of)
setting breakpoints. This is the wire-format foundation; the client-side
handling that acts on connect_info comes in a follow-up.

Adds JSON serialization, unit tests, and documents the new fields in the
jAcceleratorPluginInitialize packet.
---
 lldb/docs/resources/lldbgdbremote.md          | 16 +++++-
 .../Utility/AcceleratorGDBRemotePackets.h     | 27 ++++++++++
 .../Utility/AcceleratorGDBRemotePackets.cpp   | 25 ++++++++-
 .../AcceleratorGDBRemotePacketsTest.cpp       | 53 +++++++++++++++++++
 4 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index ef42ebd6ae41c..65f16da6bddd8 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -2774,9 +2774,21 @@ packet when one is hit. Each breakpoint object has the following fields:
 Exactly one of `by_name` or `by_address` must be provided for each
 breakpoint.
 
+An `accelerator_action` may also include a `connect_info` object asking the
+client to create a new target and connect to a separate GDB server that
+serves the accelerator's state (for example a GPU debug stub). It has the
+following fields:
+
+| Key             | Type   | Description |
+|-----------------|--------|-------------|
+| `connect_url`   | string | Connection URL to connect to, as used by `process connect <url>`. |
+| `exe_path`      | string | Optional path to the executable to use when creating the accelerator target. If omitted, an empty target is created. |
+| `platform_name` | string | Optional name of the platform to select for the accelerator target. |
+| `triple`        | string | Optional target triple to use as the architecture for the accelerator target. |
+| `synchronous`   | bool   | If true, the client waits for the accelerator process to finish initializing before continuing. |
+
 In future patches, each `accelerator_action` will include additional fields
-such as connection info for secondary debug sessions and synchronization
-options.
+such as synchronization options for the accelerator process.
 
 **Priority To Implement:** Required for hardware accelerator debugging
 support. Not needed for non-hardware-accelerator debugging.
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index 9ba36fc540a52..69cf2bd2e7def 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -85,6 +85,30 @@ bool fromJSON(const llvm::json::Value &value,
               AcceleratorBreakpointHitArgs &data, llvm::json::Path path);
 llvm::json::Value toJSON(const AcceleratorBreakpointHitArgs &data);
 
+/// Information the client needs to create a reverse connection to an
+/// accelerator GDB server (e.g. a separate process serving the accelerator's
+/// register and memory state). When an AcceleratorActions carries this, the
+/// client creates a new target and connects to \a connect_url.
+struct AcceleratorConnectionInfo {
+  /// Path to the executable to use when creating the accelerator target. If
+  /// not set, an empty target is created.
+  std::optional<std::string> exe_path;
+  /// Name of the platform to select for the accelerator target.
+  std::optional<std::string> platform_name;
+  /// Target triple to use as the architecture for the accelerator target.
+  std::optional<std::string> triple;
+  /// Connection URL the client should connect to (as in "process connect
+  /// <url>").
+  std::string connect_url;
+  /// If true, the client waits for the accelerator process to finish
+  /// initializing before continuing.
+  bool synchronous = false;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorConnectionInfo &data,
+              llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorConnectionInfo &data);
+
 /// Actions to be performed in the native process on behalf of an accelerator
 /// plugin. AcceleratorActions are returned in the following contexts:
 ///
@@ -114,6 +138,9 @@ struct AcceleratorActions {
   int64_t identifier = 0;
   /// New breakpoints to set. Nothing to set if this is empty.
   std::vector<AcceleratorBreakpointInfo> breakpoints;
+  /// If set, the client should create a new target and connect to the
+  /// accelerator GDB server described here.
+  std::optional<AcceleratorConnectionInfo> connect_info;
 };
 
 bool fromJSON(const llvm::json::Value &value, AcceleratorActions &data,
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
index 34067b1fa64c7..18282b5ebdfb3 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -86,21 +86,42 @@ AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef symbol_name) const {
   return std::nullopt;
 }
 
+bool fromJSON(const Value &value, AcceleratorConnectionInfo &data, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.mapOptional("exe_path", data.exe_path) &&
+         o.mapOptional("platform_name", data.platform_name) &&
+         o.mapOptional("triple", data.triple) &&
+         o.map("connect_url", data.connect_url) &&
+         o.map("synchronous", data.synchronous);
+}
+
+json::Value toJSON(const AcceleratorConnectionInfo &data) {
+  return Object{
+      {"exe_path", data.exe_path},       {"platform_name", data.platform_name},
+      {"triple", data.triple},           {"connect_url", data.connect_url},
+      {"synchronous", data.synchronous},
+  };
+}
+
 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("breakpoints", data.breakpoints);
+         o.map("breakpoints", data.breakpoints) &&
+         o.mapOptional("connect_info", data.connect_info);
 }
 
 json::Value toJSON(const AcceleratorActions &data) {
-  return Object{
+  Object obj{
       {"plugin_name", data.plugin_name},
       {"session_name", data.session_name},
       {"identifier", data.identifier},
       {"breakpoints", data.breakpoints},
   };
+  if (data.connect_info)
+    obj["connect_info"] = *data.connect_info;
+  return obj;
 }
 
 bool fromJSON(const Value &value, AcceleratorBreakpointHitResponse &data,
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
index fdcb0585fa3c7..f06df0119617a 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -187,3 +187,56 @@ TEST(AcceleratorGDBRemotePacketsTest,
   EXPECT_EQ("exit",
             deserialized->actions->breakpoints[0].by_name->function_name);
 }
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorConnectionInfo) {
+  AcceleratorConnectionInfo conn;
+  conn.exe_path = "/path/to/accel.elf";
+  conn.platform_name = "remote-gdb-server";
+  conn.triple = "amdgcn-amd-amdhsa";
+  conn.connect_url = "connect://localhost:1234";
+  conn.synchronous = true;
+
+  Expected<AcceleratorConnectionInfo> deserialized = roundtripJSON(conn);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ(conn.exe_path, deserialized->exe_path);
+  EXPECT_EQ(conn.platform_name, deserialized->platform_name);
+  EXPECT_EQ(conn.triple, deserialized->triple);
+  EXPECT_EQ(conn.connect_url, deserialized->connect_url);
+  EXPECT_EQ(conn.synchronous, deserialized->synchronous);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorConnectionInfoMinimal) {
+  AcceleratorConnectionInfo conn;
+  conn.connect_url = "connect://localhost:5678";
+
+  Expected<AcceleratorConnectionInfo> deserialized = roundtripJSON(conn);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ(std::nullopt, deserialized->exe_path);
+  EXPECT_EQ(std::nullopt, deserialized->platform_name);
+  EXPECT_EQ(std::nullopt, deserialized->triple);
+  EXPECT_EQ(conn.connect_url, deserialized->connect_url);
+  EXPECT_FALSE(deserialized->synchronous);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorActionsWithConnectInfo) {
+  AcceleratorActions actions("mock", 3);
+  AcceleratorConnectionInfo conn;
+  conn.connect_url = "connect://localhost:9999";
+  conn.synchronous = true;
+  actions.connect_info = std::move(conn);
+
+  Expected<AcceleratorActions> deserialized = roundtripJSON(actions);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  ASSERT_TRUE(deserialized->connect_info.has_value());
+  EXPECT_EQ("connect://localhost:9999",
+            deserialized->connect_info->connect_url);
+  EXPECT_TRUE(deserialized->connect_info->synchronous);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorActionsWithoutConnectInfo) {
+  AcceleratorActions actions("mock", 4);
+
+  Expected<AcceleratorActions> deserialized = roundtripJSON(actions);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_FALSE(deserialized->connect_info.has_value());
+}

>From 9f8baaca0f2328cc2908621765db8aca39ae3ec3 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Mon, 8 Jun 2026 08:01:22 -0700
Subject: [PATCH 2/2] [lldb] Create and connect an accelerator target from
 connect_info

Act on the AcceleratorConnectionInfo returned by an accelerator plugin:
when an AcceleratorActions carries connect_info, ProcessGDBRemote creates
a new target and reverse-connects it to the GDB server the plugin points
at, broadcasting the new target so listeners (e.g. IDEs) can pick it up.

To exercise this end to end, the mock accelerator plugin now stands up an
in-process GDB server backed by a minimal fake accelerator process
(ProcessMockAccelerator + one stopped ThreadMockAccelerator + a tiny
RegisterContextMockAccelerator). The plugin sets a breakpoint on a
dedicated "mock_gpu_accelerator_connect" hook; when hit it listens on a
local port and returns connect_info so the client connects. Using a
dedicated hook keeps the connection isolated to the new connection test
and out of the existing breakpoint test.

Adds an end-to-end API test verifying a second (accelerator) target is
created alongside the original one.
---
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |  55 ++++++++
 .../Process/gdb-remote/ProcessGDBRemote.h     |   4 +
 lldb/test/API/accelerator/connection/Makefile |   3 +
 .../TestMockAcceleratorConnection.py          |  57 ++++++++
 lldb/test/API/accelerator/connection/main.c   |  10 ++
 .../Plugins/Accelerator/Mock/CMakeLists.txt   |   3 +
 .../Mock/LLDBServerMockAcceleratorPlugin.cpp  |  99 +++++++++++++-
 .../Mock/LLDBServerMockAcceleratorPlugin.h    |  30 ++++
 .../Mock/ProcessMockAccelerator.cpp           | 112 +++++++++++++++
 .../Accelerator/Mock/ProcessMockAccelerator.h |  71 ++++++++++
 .../Mock/RegisterContextMockAccelerator.cpp   | 128 ++++++++++++++++++
 .../Mock/RegisterContextMockAccelerator.h     |  49 +++++++
 .../Mock/ThreadMockAccelerator.cpp            |  54 ++++++++
 .../Accelerator/Mock/ThreadMockAccelerator.h  |  48 +++++++
 14 files changed, 722 insertions(+), 1 deletion(-)
 create mode 100644 lldb/test/API/accelerator/connection/Makefile
 create mode 100644 lldb/test/API/accelerator/connection/TestMockAcceleratorConnection.py
 create mode 100644 lldb/test/API/accelerator/connection/main.c
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.h
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.h

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 77506275d3e13..560db50cb4088 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -4186,6 +4186,61 @@ ProcessGDBRemote::HandleAcceleratorActions(const AcceleratorActions &actions) {
       return error;
   }
 
+  if (actions.connect_info) {
+    if (llvm::Error error = HandleAcceleratorConnection(actions))
+      return error;
+  }
+
+  return llvm::Error::success();
+}
+
+llvm::Error ProcessGDBRemote::HandleAcceleratorConnection(
+    const AcceleratorActions &actions) {
+  const AcceleratorConnectionInfo &connect_info = *actions.connect_info;
+  Debugger &debugger = GetTarget().GetDebugger();
+
+  // Create a new (empty) target for the accelerator and connect to the GDB
+  // server the plugin is serving.
+  llvm::StringRef exe_path =
+      connect_info.exe_path ? *connect_info.exe_path : llvm::StringRef();
+  llvm::StringRef triple =
+      connect_info.triple ? *connect_info.triple : llvm::StringRef();
+  TargetSP accelerator_target_sp;
+  Status error = debugger.GetTargetList().CreateTarget(
+      debugger, exe_path, triple, eLoadDependentsNo,
+      /*platform_options=*/nullptr, accelerator_target_sp);
+  if (error.Fail())
+    return error.takeError();
+  if (!accelerator_target_sp)
+    return llvm::createStringError("failed to create accelerator target");
+
+  PlatformSP platform_sp = accelerator_target_sp->GetPlatform();
+  if (!platform_sp)
+    return llvm::createStringError(
+        "no platform for the accelerator target connection");
+
+  ProcessSP process_sp =
+      connect_info.synchronous
+          ? platform_sp->ConnectProcessSynchronous(
+                connect_info.connect_url, GetPluginNameStatic(), debugger,
+                *debugger.GetAsyncOutputStream(), accelerator_target_sp.get(),
+                error)
+          : platform_sp->ConnectProcess(connect_info.connect_url,
+                                        GetPluginNameStatic(), debugger,
+                                        accelerator_target_sp.get(), error);
+  if (error.Fail())
+    return error.takeError();
+  if (!process_sp)
+    return llvm::createStringError("failed to connect to the accelerator");
+
+  accelerator_target_sp->SetTargetSessionName(actions.session_name);
+
+  // Let listeners (e.g. the IDE) know a new target was created.
+  auto event_sp = std::make_shared<Event>(
+      Target::eBroadcastBitNewTargetCreated,
+      new Target::TargetEventData(GetTarget().shared_from_this(),
+                                  accelerator_target_sp));
+  GetTarget().BroadcastEvent(event_sp);
   return llvm::Error::success();
 }
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 0a3386082c388..525a45f7cce21 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -488,6 +488,10 @@ class ProcessGDBRemote : public Process,
   /// breakpoints are still set.
   llvm::Error HandleAcceleratorBreakpoints(const AcceleratorActions &actions);
 
+  /// Create a new target for an accelerator and connect it to the GDB server
+  /// described by the action's connection info.
+  llvm::Error HandleAcceleratorConnection(const AcceleratorActions &actions);
+
   /// Breakpoint callback invoked when an accelerator-plugin-requested
   /// breakpoint is hit. Resolves any requested symbol values, notifies the
   /// plugin via the "jAcceleratorPluginBreakpointHit" packet, and handles the
diff --git a/lldb/test/API/accelerator/connection/Makefile b/lldb/test/API/accelerator/connection/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/accelerator/connection/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/accelerator/connection/TestMockAcceleratorConnection.py b/lldb/test/API/accelerator/connection/TestMockAcceleratorConnection.py
new file mode 100644
index 0000000000000..65b139b037182
--- /dev/null
+++ b/lldb/test/API/accelerator/connection/TestMockAcceleratorConnection.py
@@ -0,0 +1,57 @@
+"""
+End-to-end test for accelerator plugin connections.
+
+Launches a real process against an lldb-server that has the mock accelerator
+plugin enabled. The program calls a dedicated "mock_gpu_accelerator_connect"
+hook; the plugin set a breakpoint there and, when it is hit, asks the client
+(via connect_info) to create a second target and reverse-connect to an
+in-process mock accelerator GDB server. This verifies that a second
+(accelerator) target is created alongside the original one.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import configuration
+
+
+class MockAcceleratorConnectionTestCase(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        super().setUp()
+        if "mock-accelerator" not in configuration.enabled_plugins:
+            self.skipTest("mock-accelerator plugin is not enabled")
+
+    @skipIfRemote
+    @add_test_categories(["llgs"])
+    def test_accelerator_connection_creates_second_target(self):
+        """The mock accelerator plugin drives creation of a second target."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Only the CPU target exists before launch.
+        self.assertEqual(self.dbg.GetNumTargets(), 1)
+
+        # Running to the connection hook causes the plugin to return
+        # connect_info, so the client creates a second target and connects to
+        # the in-process mock accelerator GDB server.
+        process = target.LaunchSimple(None, None, self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+        self.assertState(process.GetState(), lldb.eStateStopped)
+
+        # The accelerator target should now exist alongside the CPU target.
+        self.assertEqual(self.dbg.GetNumTargets(), 2)
+
+        # The accelerator target is a distinct target with a valid process.
+        accelerator_target = None
+        for i in range(self.dbg.GetNumTargets()):
+            candidate = self.dbg.GetTargetAtIndex(i)
+            if candidate != target:
+                accelerator_target = candidate
+                break
+        self.assertTrue(accelerator_target.IsValid())
+        self.assertTrue(accelerator_target.GetProcess().IsValid())
diff --git a/lldb/test/API/accelerator/connection/main.c b/lldb/test/API/accelerator/connection/main.c
new file mode 100644
index 0000000000000..3e31dceafd47d
--- /dev/null
+++ b/lldb/test/API/accelerator/connection/main.c
@@ -0,0 +1,10 @@
+// The mock accelerator plugin sets a connection-trigger breakpoint on this
+// dedicated, uniquely named function. When it is hit the plugin asks the client
+// to create a second target and connect to the mock accelerator GDB server.
+// Only this program defines it, so only this test triggers a connection.
+void mock_gpu_accelerator_connect(void) {}
+
+int main(void) {
+  mock_gpu_accelerator_connect();
+  return 0;
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt
index 9b5a82a087e3d..d6cdc603213e3 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/CMakeLists.txt
@@ -1,5 +1,8 @@
 add_lldb_library(lldbServerPluginMockAccelerator
   LLDBServerMockAcceleratorPlugin.cpp
+  ProcessMockAccelerator.cpp
+  ThreadMockAccelerator.cpp
+  RegisterContextMockAccelerator.cpp
 
   LINK_LIBS
     lldbHost
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index f89fb90e26aa6..45c6183532c46 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -7,13 +7,52 @@
 //===----------------------------------------------------------------------===//
 
 #include "LLDBServerMockAcceleratorPlugin.h"
+#include "ProcessMockAccelerator.h"
 
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Connection.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
 
 LLDBServerMockAcceleratorPlugin::LLDBServerMockAcceleratorPlugin(
     GDBServer &gdb_server, MainLoop &main_loop)
-    : LLDBServerAcceleratorPlugin(gdb_server, main_loop) {}
+    : LLDBServerAcceleratorPlugin(gdb_server, main_loop) {
+  // Stand up an in-process GDB server backed by a fake accelerator process so
+  // the client has something to reverse-connect to. The process is created up
+  // front (and reports itself stopped); CreateConnection() later listens for
+  // the client's reverse connection.
+  m_process_manager_up =
+      std::make_unique<ProcessMockAccelerator::Manager>(m_main_loop);
+  m_gpu_server_up = std::make_unique<GDBRemoteCommunicationServerLLGS>(
+      m_main_loop, *m_process_manager_up);
+
+  ProcessLaunchInfo info;
+  info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug);
+  Args args;
+  args.AppendArgument("mock-accelerator");
+  info.SetArguments(args, /*first_arg_is_executable=*/true);
+  info.GetEnvironment() = Host::GetEnvironment();
+  m_gpu_server_up->SetLaunchInfo(info);
+  if (Status error = m_gpu_server_up->LaunchProcess(); error.Fail())
+    LLDB_LOG(GetLog(GDBRLog::Process),
+             "failed to launch mock accelerator process: {0}",
+             error.AsCString());
+}
+
+LLDBServerMockAcceleratorPlugin::~LLDBServerMockAcceleratorPlugin() = default;
 
 llvm::StringRef LLDBServerMockAcceleratorPlugin::GetPluginName() {
   return "mock";
@@ -36,6 +75,16 @@ LLDBServerMockAcceleratorPlugin::GetInitializeActions() {
   bp.symbol_names.push_back("mock_gpu_accelerator_compute");
   actions.breakpoints.push_back(std::move(bp));
 
+  // Also set a breakpoint on the dedicated connection hook. This only resolves
+  // (and fires) in programs that define "mock_gpu_accelerator_connect"; when it
+  // is hit the plugin asks the client to create a second target and connect to
+  // the mock accelerator GDB server.
+  AcceleratorBreakpointInfo connect_bp;
+  connect_bp.identifier = kBreakpointIDConnect;
+  connect_bp.by_name =
+      AcceleratorBreakpointByName{std::nullopt, "mock_gpu_accelerator_connect"};
+  actions.breakpoints.push_back(std::move(connect_bp));
+
   return actions;
 }
 
@@ -81,7 +130,55 @@ LLDBServerMockAcceleratorPlugin::BreakpointWasHit(
     response.disable_bp = true;
     response.auto_resume_native = false;
     break;
+  case kBreakpointIDConnect: {
+    // The program reached its connection hook. Ask the client to create a
+    // second target and reverse-connect to our in-process mock accelerator
+    // GDB server.
+    response.disable_bp = true;
+    response.auto_resume_native = false;
+    AcceleratorActions actions(GetPluginName(), kBreakpointIDConnect);
+    actions.session_name = "Mock Accelerator Session";
+    actions.connect_info = CreateConnection();
+    response.actions = std::move(actions);
+    break;
+  }
   }
 
   return response;
 }
+
+std::optional<AcceleratorConnectionInfo>
+LLDBServerMockAcceleratorPlugin::CreateConnection() {
+  Log *log = GetLog(GDBRLog::Process);
+
+  // Listen on an ephemeral local port; the client will reverse-connect to it.
+  llvm::Expected<std::unique_ptr<TCPSocket>> sock =
+      Socket::TcpListen("localhost:0");
+  if (!sock) {
+    LLDB_LOG_ERROR(log, sock.takeError(),
+                   "mock accelerator failed to listen: {0}");
+    return std::nullopt;
+  }
+
+  AcceleratorConnectionInfo info;
+  info.connect_url =
+      llvm::formatv("connect://localhost:{0}", (*sock)->GetLocalPortNumber());
+  info.synchronous = true;
+
+  m_listen_socket = std::move(*sock);
+  llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> handles =
+      m_listen_socket->Accept(
+          m_main_loop, [this](std::unique_ptr<Socket> socket) {
+            std::unique_ptr<Connection> connection_up =
+                std::make_unique<ConnectionFileDescriptor>(std::move(socket));
+            m_gpu_server_up->InitializeConnection(std::move(connection_up));
+          });
+  if (!handles) {
+    LLDB_LOG_ERROR(log, handles.takeError(),
+                   "mock accelerator failed to accept: {0}");
+    return std::nullopt;
+  }
+  m_read_handles = std::move(*handles);
+
+  return info;
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
index 7a1b57e0bb0b7..c97f3619eaaae 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -10,13 +10,26 @@
 #define LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_LLDBSERVERMOCKACCELERATORPLUGIN_H
 
 #include "Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+#include <memory>
+#include <vector>
 
 namespace lldb_private {
+
+class TCPSocket;
+
+namespace process_gdb_remote {
+class GDBRemoteCommunicationServerLLGS;
+} // namespace process_gdb_remote
+
 namespace lldb_server {
 
 class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
 public:
   LLDBServerMockAcceleratorPlugin(GDBServer &gdb_server, MainLoop &main_loop);
+  ~LLDBServerMockAcceleratorPlugin() override;
 
   llvm::StringRef GetPluginName() override;
   std::optional<AcceleratorActions> GetInitializeActions() override;
@@ -24,6 +37,10 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
   BreakpointWasHit(AcceleratorBreakpointHitArgs &args) override;
 
 private:
+  // Start listening for a reverse connection to the mock accelerator GDB
+  // server and return the connection info the client should connect to.
+  std::optional<AcceleratorConnectionInfo> CreateConnection();
+
   // Breakpoint set during initialization, by function name with no shared
   // library. Requests the "compute" symbol value when hit.
   static constexpr int64_t kBreakpointIDInitialize = 1;
@@ -32,6 +49,19 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
   static constexpr int64_t kBreakpointIDByAddress = 2;
   // Breakpoint set by function name scoped to a shared library.
   static constexpr int64_t kBreakpointIDByNameShlib = 3;
+  // Breakpoint on the dedicated "mock_gpu_accelerator_connect" hook. When hit,
+  // the plugin asks the client to create a second target and connect to the
+  // mock accelerator GDB server. Only programs that define that function (the
+  // connection test) trigger it.
+  static constexpr int64_t kBreakpointIDConnect = 4;
+
+  // The in-process GDB server (and its fake process) that serves the mock
+  // accelerator connection.
+  std::unique_ptr<NativeProcessProtocol::Manager> m_process_manager_up;
+  std::unique_ptr<process_gdb_remote::GDBRemoteCommunicationServerLLGS>
+      m_gpu_server_up;
+  std::unique_ptr<TCPSocket> m_listen_socket;
+  std::vector<MainLoopBase::ReadHandleUP> m_read_handles;
 };
 
 } // namespace lldb_server
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
new file mode 100644
index 0000000000000..ba5e162d41c83
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ProcessMockAccelerator.h"
+#include "ThreadMockAccelerator.h"
+
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "llvm/Support/Error.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+
+// A fixed, fake pid and tid for the single mock accelerator process/thread.
+static constexpr lldb::pid_t kMockPid = 7777;
+static constexpr lldb::tid_t kMockTid = 1;
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ProcessMockAccelerator::Manager::Launch(ProcessLaunchInfo &launch_info,
+                                        NativeDelegate &native_delegate) {
+  return std::make_unique<ProcessMockAccelerator>(kMockPid, native_delegate);
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ProcessMockAccelerator::Manager::Attach(lldb::pid_t pid,
+                                        NativeDelegate &native_delegate) {
+  return llvm::createStringError("attach is not supported by the mock "
+                                 "accelerator process");
+}
+
+ProcessMockAccelerator::ProcessMockAccelerator(lldb::pid_t pid,
+                                               NativeDelegate &delegate)
+    : NativeProcessProtocol(pid, /*terminal_fd=*/-1, delegate) {
+  m_state = eStateStopped;
+  UpdateThreads();
+}
+
+Status ProcessMockAccelerator::Resume(const ResumeActionList &resume_actions) {
+  // Nothing actually runs; stay stopped.
+  return Status();
+}
+
+Status ProcessMockAccelerator::Halt() { return Status(); }
+
+Status ProcessMockAccelerator::Detach() {
+  SetState(eStateDetached, true);
+  return Status();
+}
+
+Status ProcessMockAccelerator::Signal(int signo) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ProcessMockAccelerator::Kill() { return Status(); }
+
+Status ProcessMockAccelerator::ReadMemory(lldb::addr_t addr, void *buf,
+                                          size_t size, size_t &bytes_read) {
+  bytes_read = 0;
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ProcessMockAccelerator::WriteMemory(lldb::addr_t addr, const void *buf,
+                                           size_t size, size_t &bytes_written) {
+  bytes_written = 0;
+  return Status::FromErrorString("unimplemented");
+}
+
+lldb::addr_t ProcessMockAccelerator::GetSharedLibraryInfoAddress() {
+  return LLDB_INVALID_ADDRESS;
+}
+
+size_t ProcessMockAccelerator::UpdateThreads() {
+  if (m_threads.empty()) {
+    m_threads.push_back(
+        std::make_unique<ThreadMockAccelerator>(*this, kMockTid));
+    SetCurrentThreadID(kMockTid);
+  }
+  return m_threads.size();
+}
+
+const ArchSpec &ProcessMockAccelerator::GetArchitecture() const {
+  if (!m_arch.IsValid())
+    m_arch = HostInfo::GetArchitecture();
+  return m_arch;
+}
+
+Status ProcessMockAccelerator::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                                             bool hardware) {
+  return Status::FromErrorString("unimplemented");
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ProcessMockAccelerator::GetAuxvData() const {
+  return std::error_code(ENOENT, std::generic_category());
+}
+
+Status ProcessMockAccelerator::GetLoadedModuleFileSpec(const char *module_path,
+                                                       FileSpec &file_spec) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status
+ProcessMockAccelerator::GetFileLoadAddress(const llvm::StringRef &file_name,
+                                           lldb::addr_t &load_addr) {
+  return Status::FromErrorString("unimplemented");
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
new file mode 100644
index 0000000000000..07c0c4c6d5ef5
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_PROCESSMOCKACCELERATOR_H
+#define LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_PROCESSMOCKACCELERATOR_H
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+namespace lldb_server {
+
+/// A minimal, always-stopped fake process used to serve a GDB remote
+/// connection for the mock accelerator plugin. It models a single stopped
+/// thread and nothing else; it lets the client create and connect to a second
+/// (accelerator) target without requiring any real hardware.
+class ProcessMockAccelerator : public NativeProcessProtocol {
+public:
+  class Manager : public NativeProcessProtocol::Manager {
+  public:
+    using NativeProcessProtocol::Manager::Manager;
+
+    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+    Launch(ProcessLaunchInfo &launch_info,
+           NativeDelegate &native_delegate) override;
+
+    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+    Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override;
+  };
+
+  ProcessMockAccelerator(lldb::pid_t pid, NativeDelegate &delegate);
+
+  Status Resume(const ResumeActionList &resume_actions) override;
+  Status Halt() override;
+  Status Detach() override;
+  Status Signal(int signo) override;
+  Status Kill() override;
+
+  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                    size_t &bytes_read) override;
+  Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+                     size_t &bytes_written) override;
+
+  lldb::addr_t GetSharedLibraryInfoAddress() override;
+  size_t UpdateThreads() override;
+  const ArchSpec &GetArchitecture() const override;
+
+  Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                       bool hardware) override;
+
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  GetAuxvData() const override;
+
+  Status GetLoadedModuleFileSpec(const char *module_path,
+                                 FileSpec &file_spec) override;
+  Status GetFileLoadAddress(const llvm::StringRef &file_name,
+                            lldb::addr_t &load_addr) override;
+
+private:
+  mutable ArchSpec m_arch;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_PROCESSMOCKACCELERATOR_H
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.cpp
new file mode 100644
index 0000000000000..84c38ea2f12be
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "RegisterContextMockAccelerator.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+
+// LLDB register numbers must start at 0 and be contiguous. This minimal set is
+// just enough for the mock accelerator process to be debugged over GDB remote.
+enum LLDBRegNum : uint32_t {
+  LLDB_R0 = 0,
+  LLDB_R1,
+  LLDB_SP,
+  LLDB_FP,
+  LLDB_PC,
+  LLDB_Flags,
+  kNumRegs
+};
+
+#define DEFINE_REG(name, idx, generic)                                         \
+  {name,                                                                       \
+   nullptr,                                                                    \
+   sizeof(uint64_t),                                                           \
+   idx * sizeof(uint64_t),                                                     \
+   eEncodingUint,                                                              \
+   eFormatHex,                                                                 \
+   {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, generic, LLDB_INVALID_REGNUM,    \
+    idx},                                                                      \
+   nullptr,                                                                    \
+   nullptr,                                                                    \
+   nullptr}
+
+static const RegisterInfo g_register_infos[] = {
+    DEFINE_REG("r0", LLDB_R0, LLDB_INVALID_REGNUM),
+    DEFINE_REG("r1", LLDB_R1, LLDB_INVALID_REGNUM),
+    DEFINE_REG("sp", LLDB_SP, LLDB_REGNUM_GENERIC_SP),
+    DEFINE_REG("fp", LLDB_FP, LLDB_REGNUM_GENERIC_FP),
+    DEFINE_REG("pc", LLDB_PC, LLDB_REGNUM_GENERIC_PC),
+    DEFINE_REG("flags", LLDB_Flags, LLDB_INVALID_REGNUM),
+};
+
+static const RegisterSet g_register_set = {"General Purpose Registers", "gpr",
+                                           kNumRegs, nullptr};
+
+RegisterContextMockAccelerator::RegisterContextMockAccelerator(
+    NativeThreadProtocol &native_thread)
+    : NativeRegisterContext(native_thread) {
+  // Give each register a distinct, constant value so reads are deterministic.
+  for (uint32_t i = 0; i < kNumRegs; ++i)
+    m_regs[i] = 0x1000 + i;
+}
+
+uint32_t RegisterContextMockAccelerator::GetRegisterCount() const {
+  return kNumRegs;
+}
+
+uint32_t RegisterContextMockAccelerator::GetUserRegisterCount() const {
+  return kNumRegs;
+}
+
+const RegisterInfo *
+RegisterContextMockAccelerator::GetRegisterInfoAtIndex(uint32_t reg) const {
+  if (reg < kNumRegs)
+    return &g_register_infos[reg];
+  return nullptr;
+}
+
+uint32_t RegisterContextMockAccelerator::GetRegisterSetCount() const {
+  return 1;
+}
+
+const RegisterSet *
+RegisterContextMockAccelerator::GetRegisterSet(uint32_t set_index) const {
+  if (set_index == 0)
+    return &g_register_set;
+  return nullptr;
+}
+
+Status
+RegisterContextMockAccelerator::ReadRegister(const RegisterInfo *reg_info,
+                                             RegisterValue &reg_value) {
+  if (!reg_info)
+    return Status::FromErrorString("invalid register info");
+  const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+  if (reg >= kNumRegs)
+    return Status::FromErrorString("invalid register number");
+  reg_value.SetUInt64(m_regs[reg]);
+  return Status();
+}
+
+Status
+RegisterContextMockAccelerator::WriteRegister(const RegisterInfo *reg_info,
+                                              const RegisterValue &reg_value) {
+  if (!reg_info)
+    return Status::FromErrorString("invalid register info");
+  const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+  if (reg >= kNumRegs)
+    return Status::FromErrorString("invalid register number");
+  m_regs[reg] = reg_value.GetAsUInt64();
+  return Status();
+}
+
+Status RegisterContextMockAccelerator::ReadAllRegisterValues(
+    lldb::WritableDataBufferSP &data_sp) {
+  data_sp = std::make_shared<DataBufferHeap>(
+      reinterpret_cast<const uint8_t *>(m_regs.data()),
+      m_regs.size() * sizeof(uint64_t));
+  return Status();
+}
+
+Status RegisterContextMockAccelerator::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  if (!data_sp || data_sp->GetByteSize() != m_regs.size() * sizeof(uint64_t))
+    return Status::FromErrorString("invalid register data");
+  ::memcpy(m_regs.data(), data_sp->GetBytes(),
+           m_regs.size() * sizeof(uint64_t));
+  return Status();
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.h
new file mode 100644
index 0000000000000..b26a5d454826c
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/RegisterContextMockAccelerator.h
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_REGISTERCONTEXTMOCKACCELERATOR_H
+#define LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_REGISTERCONTEXTMOCKACCELERATOR_H
+
+#include "lldb/Host/common/NativeRegisterContext.h"
+
+#include <array>
+
+namespace lldb_private {
+namespace lldb_server {
+
+/// A minimal register context for the mock accelerator process. It exposes a
+/// small, fixed register set with constant values; it exists only so the mock
+/// accelerator process can be debugged over the GDB remote protocol, not to
+/// model any real hardware.
+class RegisterContextMockAccelerator : public NativeRegisterContext {
+public:
+  RegisterContextMockAccelerator(NativeThreadProtocol &native_thread);
+
+  uint32_t GetRegisterCount() const override;
+  uint32_t GetUserRegisterCount() const override;
+  const RegisterInfo *GetRegisterInfoAtIndex(uint32_t reg) const override;
+  uint32_t GetRegisterSetCount() const override;
+  const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+  Status ReadRegister(const RegisterInfo *reg_info,
+                      RegisterValue &reg_value) override;
+  Status WriteRegister(const RegisterInfo *reg_info,
+                       const RegisterValue &reg_value) override;
+
+  Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override;
+  Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+private:
+  /// The registers, indexed by LLDB register number. Each one is 64 bits.
+  std::array<uint64_t, 6> m_regs;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_REGISTERCONTEXTMOCKACCELERATOR_H
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.cpp
new file mode 100644
index 0000000000000..69b7408ef3b56
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ThreadMockAccelerator.h"
+#include "ProcessMockAccelerator.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+
+ThreadMockAccelerator::ThreadMockAccelerator(ProcessMockAccelerator &process,
+                                             lldb::tid_t tid)
+    : NativeThreadProtocol(process, tid), m_reg_context(*this) {
+  m_stop_info.reason = lldb::eStopReasonTrace;
+}
+
+std::string ThreadMockAccelerator::GetName() {
+  return "Mock Accelerator Thread";
+}
+
+lldb::StateType ThreadMockAccelerator::GetState() {
+  return lldb::eStateStopped;
+}
+
+bool ThreadMockAccelerator::GetStopReason(ThreadStopInfo &stop_info,
+                                          std::string &description) {
+  stop_info = m_stop_info;
+  description = "mock accelerator thread stopped";
+  return true;
+}
+
+Status ThreadMockAccelerator::SetWatchpoint(lldb::addr_t addr, size_t size,
+                                            uint32_t watch_flags,
+                                            bool hardware) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadMockAccelerator::RemoveWatchpoint(lldb::addr_t addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadMockAccelerator::SetHardwareBreakpoint(lldb::addr_t addr,
+                                                    size_t size) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadMockAccelerator::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+  return Status::FromErrorString("unimplemented");
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.h
new file mode 100644
index 0000000000000..5f9dec9483511
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ThreadMockAccelerator.h
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_THREADMOCKACCELERATOR_H
+#define LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_THREADMOCKACCELERATOR_H
+
+#include "RegisterContextMockAccelerator.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include <string>
+
+namespace lldb_private {
+namespace lldb_server {
+
+class ProcessMockAccelerator;
+
+/// A single, always-stopped thread for the mock accelerator process.
+class ThreadMockAccelerator : public NativeThreadProtocol {
+public:
+  ThreadMockAccelerator(ProcessMockAccelerator &process, lldb::tid_t tid);
+
+  std::string GetName() override;
+  lldb::StateType GetState() override;
+  bool GetStopReason(ThreadStopInfo &stop_info,
+                     std::string &description) override;
+  RegisterContextMockAccelerator &GetRegisterContext() override {
+    return m_reg_context;
+  }
+
+  Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+                       bool hardware) override;
+  Status RemoveWatchpoint(lldb::addr_t addr) override;
+  Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+  Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+
+private:
+  RegisterContextMockAccelerator m_reg_context;
+  ThreadStopInfo m_stop_info;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_TOOLS_LLDB_SERVER_PLUGINS_ACCELERATOR_MOCK_THREADMOCKACCELERATOR_H



More information about the lldb-commits mailing list