[Lldb-commits] [lldb] [llvm] AMD GPU plugin implementation v0 (PR #143464)

via lldb-commits lldb-commits at lists.llvm.org
Thu Jun 12 11:12:44 PDT 2025


https://github.com/jeffreytan81 updated https://github.com/llvm/llvm-project/pull/143464

>From 5a6179a366598f57618713347e2986533be10ae2 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 19 Mar 2025 17:02:26 -0700
Subject: [PATCH 01/34] lldb-server with GPU plugins.

---
 .../GDBRemoteCommunicationServerLLGS.cpp      | 66 +++++++++----
 .../GDBRemoteCommunicationServerLLGS.h        | 11 ++-
 lldb/tools/lldb-server/CMakeLists.txt         |  5 +
 lldb/tools/lldb-server/Plugins/CMakeLists.txt |  2 +
 .../Plugins/MockGPU/CMakeLists.txt            |  6 ++
 .../Plugins/MockGPU/MockGPUPlugin.cpp         |  8 ++
 .../Plugins/MockGPU/MockGPUPlugin.h           |  0
 .../Plugins/PluginInterface/CMakeLists.txt    |  4 +
 .../LLDBServerNativeProcess.cpp               | 23 +++++
 .../PluginInterface/LLDBServerNativeProcess.h | 46 +++++++++
 .../PluginInterface/LLDBServerPlugin.h        | 94 +++++++++++++++++++
 lldb/tools/lldb-server/lldb-gdbserver.cpp     | 48 ++++++++--
 12 files changed, 288 insertions(+), 25 deletions(-)
 create mode 100644 lldb/tools/lldb-server/Plugins/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.h
 create mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
 create mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h

diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 89d2730cfccd0..c198344b991fc 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -41,6 +41,7 @@
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/UnimplementedError.h"
 #include "lldb/Utility/UriParser.h"
+#include "llvm/Support/DynamicLibrary.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/TargetParser/Triple.h"
@@ -54,6 +55,7 @@ using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
 using namespace llvm;
 
+sys::DynamicLibrary g_gpu_plugin;
 // GDBRemote Errors
 
 namespace {
@@ -253,7 +255,8 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
       &GDBRemoteCommunicationServerLLGS::Handle_vCtrlC);
 }
 
-void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) {
+void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(
+    const ProcessLaunchInfo &info) {
   m_process_launch_info = info;
 }
 
@@ -289,6 +292,9 @@ Status GDBRemoteCommunicationServerLLGS::LaunchProcess() {
     if (!process_or)
       return Status::FromError(process_or.takeError());
     m_continue_process = m_current_process = process_or->get();
+    // Notify anyone wanting to know when a NativeProcessProtocol is created.
+    if (m_process_created_callback)
+      m_process_created_callback(process_or->get());
     m_debugged_processes.emplace(
         m_current_process->GetID(),
         DebuggedProcess{std::move(*process_or), DebuggedProcess::Flag{}});
@@ -362,6 +368,9 @@ Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) {
     return status;
   }
   m_continue_process = m_current_process = process_or->get();
+  // Notify anyone wanting to know when a NativeProcessProtocol is created.
+  if (m_process_created_callback)
+    m_process_created_callback(process_or->get());
   m_debugged_processes.emplace(
       m_current_process->GetID(),
       DebuggedProcess{std::move(*process_or), DebuggedProcess::Flag{}});
@@ -614,10 +623,11 @@ static void CollectRegNums(const uint32_t *reg_num, StreamString &response,
   }
 }
 
-static void WriteRegisterValueInHexFixedWidth(
-    StreamString &response, NativeRegisterContext &reg_ctx,
-    const RegisterInfo &reg_info, const RegisterValue *reg_value_p,
-    lldb::ByteOrder byte_order) {
+static void WriteRegisterValueInHexFixedWidth(StreamString &response,
+                                              NativeRegisterContext &reg_ctx,
+                                              const RegisterInfo &reg_info,
+                                              const RegisterValue *reg_value_p,
+                                              lldb::ByteOrder byte_order) {
   RegisterValue reg_value;
   if (!reg_value_p) {
     Status error = reg_ctx.ReadRegister(&reg_info, reg_value);
@@ -643,7 +653,7 @@ static std::optional<json::Object>
 GetRegistersAsJSON(NativeThreadProtocol &thread) {
   Log *log = GetLog(LLDBLog::Thread);
 
-  NativeRegisterContext& reg_ctx = thread.GetRegisterContext();
+  NativeRegisterContext &reg_ctx = thread.GetRegisterContext();
 
   json::Object register_object;
 
@@ -682,8 +692,8 @@ GetRegistersAsJSON(NativeThreadProtocol &thread) {
     }
 
     StreamString stream;
-    WriteRegisterValueInHexFixedWidth(stream, reg_ctx, *reg_info_p,
-                                      &reg_value, lldb::eByteOrderBig);
+    WriteRegisterValueInHexFixedWidth(stream, reg_ctx, *reg_info_p, &reg_value,
+                                      lldb::eByteOrderBig);
 
     register_object.try_emplace(llvm::to_string(reg_num),
                                 stream.GetString().str());
@@ -794,8 +804,7 @@ GetJSONThreadsInfo(NativeProcessProtocol &process, bool abridged) {
   return threads_array;
 }
 
-StreamString
-GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread(
+StreamString GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread(
     NativeThreadProtocol &thread) {
   Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread);
 
@@ -1379,10 +1388,37 @@ GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetBinaryData(
     return SendErrorResponse(bytes.takeError());
 }
 
+void PrintSomething() { puts(__PRETTY_FUNCTION__); }
+
+typedef void (*LLDBServerPluginInitialize)();
+
+template <class T> static T FuncPtr(void *Ptr) {
+  union {
+    T F;
+    void *P;
+  } Tmp;
+  Tmp.P = Ptr;
+  return Tmp.F;
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo(
     StringExtractorGDBRemote &packet) {
   // Fail if we don't have a current process.
+  static std::once_flag OnceFlag;
+
+  std::call_once(OnceFlag, []() {
+    const char *plugin_path =
+        "/data/users/gclayton/github/Debug/lib/liblldbServerPluginMockGPU.so";
+    std::string error;
+    g_gpu_plugin =
+        sys::DynamicLibrary::getPermanentLibrary(plugin_path, &error);
+    LLDBServerPluginInitialize plugin_initialize =
+        FuncPtr<LLDBServerPluginInitialize>(
+            g_gpu_plugin.getAddressOfSymbol("LLDBServerPluginInitialize"));
+    plugin_initialize();
+  });
+
   if (!m_current_process ||
       (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
     return SendErrorResponse(68);
@@ -3104,8 +3140,7 @@ GDBRemoteCommunicationServerLLGS::BuildTargetXml() {
     }
 
     response.Indent();
-    response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32
-                    "\" regnum=\"%d\" ",
+    response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32 "\" regnum=\"%d\" ",
                     reg_info->name, reg_info->byte_size * 8, reg_index);
 
     if (!reg_context.RegisterOffsetIsDynamic())
@@ -3316,7 +3351,7 @@ GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState(
   }
 
   // Grab the register context for the thread.
-  NativeRegisterContext& reg_context = thread->GetRegisterContext();
+  NativeRegisterContext &reg_context = thread->GetRegisterContext();
 
   // Save registers to a buffer.
   WritableDataBufferSP register_data_sp;
@@ -3577,8 +3612,7 @@ GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) {
   for (auto it = m_debugged_processes.begin();
        it != m_debugged_processes.end();) {
     if (pid == LLDB_INVALID_PROCESS_ID || pid == it->first) {
-      LLDB_LOGF(log,
-                "GDBRemoteCommunicationServerLLGS::%s detaching %" PRId64,
+      LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s detaching %" PRId64,
                 __FUNCTION__, it->first);
       if (llvm::Error e = it->second.process_up->Detach().ToError())
         detach_error = llvm::joinErrors(std::move(detach_error), std::move(e));
@@ -3727,7 +3761,7 @@ GDBRemoteCommunicationServerLLGS::Handle_QPassSignals(
       break; // End of string
     if (separator != ';')
       return SendIllFormedResponse(packet, "Invalid separator,"
-                                            " expected semicolon.");
+                                           " expected semicolon.");
   }
 
   // Fail if we don't have a current process.
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 646b6a102abf6..beed8e00e79d9 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -35,8 +35,7 @@ class GDBRemoteCommunicationServerLLGS
 public:
   // Constructors and Destructors
   GDBRemoteCommunicationServerLLGS(
-      MainLoop &mainloop,
-      NativeProcessProtocol::Manager &process_manager);
+      MainLoop &mainloop, NativeProcessProtocol::Manager &process_manager);
 
   void SetLaunchInfo(const ProcessLaunchInfo &info);
 
@@ -86,6 +85,12 @@ class GDBRemoteCommunicationServerLLGS
 
   Status InitializeConnection(std::unique_ptr<Connection> connection);
 
+  using ProcessCreatedCallback = std::function<void(NativeProcessProtocol *)>;
+
+  void SetProcessCreatedCallback(ProcessCreatedCallback callback) {
+    m_process_created_callback = callback;
+  }
+
   struct DebuggedProcess {
     enum class Flag {
       vkilled = (1u << 0),
@@ -124,6 +129,8 @@ class GDBRemoteCommunicationServerLLGS
 
   NativeProcessProtocol::Extension m_extensions_supported = {};
 
+  ProcessCreatedCallback m_process_created_callback = nullptr;
+
   // Typically we would use a SmallVector for this data but in this context we
   // don't know how much data we're recieving so we would have to heap allocate
   // a lot, or have a very large stack frame. So it's a member instead.
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index 0135b367fcc21..b20c8990c5ce9 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -60,15 +60,20 @@ add_lldb_tool(lldb-server
       lldbPluginInstructionMIPS64
       lldbPluginInstructionRISCV
       ${LLDB_SYSTEM_LIBS}
+      lldbServerPluginInterface
 
     LINK_COMPONENTS
       Option
       Support
 )
 
+set_target_properties(lldb-server PROPERTIES ENABLE_EXPORTS 1)
+
 add_dependencies(lldb-server
   LLGSOptionsTableGen
   ${tablegen_deps}
 )
 target_include_directories(lldb-server PRIVATE "${LLDB_SOURCE_DIR}/source")
 target_link_libraries(lldb-server PRIVATE ${LLDB_SYSTEM_LIBS})
+
+add_subdirectory(Plugins)
diff --git a/lldb/tools/lldb-server/Plugins/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
new file mode 100644
index 0000000000000..a0275332a32ce
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(MockGPU)
+add_subdirectory(PluginInterface)
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
new file mode 100644
index 0000000000000..4349ba7c3f14e
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+add_lldb_library(lldbServerPluginMockGPU SHARED
+  MockGPUPlugin.cpp
+)
+
+target_link_libraries(lldbServerPluginMockGPU lldbServerPluginInterface)
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp
new file mode 100644
index 0000000000000..7d490b5584fbe
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp
@@ -0,0 +1,8 @@
+#include <stdio.h>
+extern void PrintSomething();
+extern "C" {
+void LLDBServerPluginInitialize() {
+  puts("LLDBServerPluginInitialize");
+  // PrintSomething();
+}
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.h b/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.h
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
new file mode 100644
index 0000000000000..85bc3d5613508
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
@@ -0,0 +1,4 @@
+
+add_lldb_library(lldbServerPluginInterface SHARED
+  LLDBServerNativeProcess.cpp
+)
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
new file mode 100644
index 0000000000000..2e265ae119a00
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
@@ -0,0 +1,23 @@
+//===-- LLDBServerNativeProcess.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBServerNativeProcess.h"
+
+using namespace lldb_private;
+using namespace lldb_server;
+
+static LLDBServerNativeProcess *g_native_process = nullptr;
+
+LLDBServerNativeProcess *LLDBServerNativeProcess::GetNativeProcess() {
+  return g_native_process;
+}
+
+void LLDBServerNativeProcess::SetNativeProcess(
+    LLDBServerNativeProcess *process) {
+  g_native_process = process;
+}
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
new file mode 100644
index 0000000000000..d15a36ef164cf
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
@@ -0,0 +1,46 @@
+//===-- LLDBServerNativeProcess.h -------------------------------*- C++ -*-===//
+//
+// 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_LLDBSERVERPLUGIN_H
+#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
+
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-types.h"
+#include <memory>
+
+namespace lldb_private {
+namespace lldb_server {
+
+/// A class that interfaces back to the lldb-server native process for
+/// LLDBServerNativeProcess objects.
+class LLDBServerNativeProcess {
+public:
+  static LLDBServerNativeProcess *GetNativeProcess();
+  // lldb-server will call this function to set the native process object prior
+  // to any plug-ins being loaded.
+  static void SetNativeProcess(LLDBServerNativeProcess *process);
+
+  virtual ~LLDBServerNativeProcess();
+  /// Set a breakpoint in the native process.
+  ///
+  /// When the breakpoints gets hit, lldb-server will call
+  /// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
+  /// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in the
+  /// native process.
+  virtual lldb::user_id_t SetBreakpoint(lldb::addr_t address) = 0;
+
+  virtual Status RegisterSignalCatcher(int signo) = 0;
+
+  virtual Status HaltProcess() = 0;
+  virtual Status ContinueProcess() = 0;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
new file mode 100644
index 0000000000000..ec5e0cd65db10
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
@@ -0,0 +1,94 @@
+//===-- LLDBServerPlugin.h --------------------------------------*- C++ -*-===//
+//
+// 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_LLDBSERVERPLUGIN_H
+#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
+
+#include "lldb/lldb-types.h"
+#include <functional>
+#include <stdint.h>
+#include <string>
+namespace lldb_private {
+namespace lldb_server {
+
+class LLDBServerNativeProcess;
+
+typedef std::function<std::string(const char *)>
+    CStrArgWithReturnStringCallback;
+typedef std::function<std::string()> NoArgsReturnStringCallback;
+
+// lldb-server will decode all packets and any packets and for any packets that
+// have handlers in this structure, the functions will be called. This removes
+// the need for plug-ins to have to parse the packets and args.
+struct GDBRemotePacketCallbacks {
+  // Handle any "general query packets" here.
+  // Handle the "qSupported" query.
+
+  CStrArgWithReturnStringCallback qSupported = nullptr;
+  NoArgsReturnStringCallback qHostInfo = nullptr;
+  NoArgsReturnStringCallback qProcessInfo = nullptr;
+  CStrArgWithReturnStringCallback qXfer = nullptr;
+
+  // Handle "vCont?" packet
+  NoArgsReturnStringCallback vContQuery = nullptr;
+};
+
+class LLDBServerPlugin {
+  // Add a version field to allow the APIs to change over time.
+  const uint32_t m_version = 1;
+  LLDBServerNativeProcess &m_process;
+
+public:
+  LLDBServerPlugin(LLDBServerNativeProcess &process) : m_process(process) {}
+
+  virtual LLDBServerPlugin() = default;
+
+  virtual void GetCallbacks(GDBRemotePacketCallbacks &callbacks);
+
+  /// Get a file descriptor to listen for in the ptrace epoll loop.
+  ///
+  /// When polling for process ptrace events, plug-ins can supply extra file
+  /// descriptors that should be listened to. When a file descriptor has
+  /// events, the LLDBServerPlugin::HandleFileDescriptorEvent(...) function
+  /// will get called synchronously from the event loop listening for events.
+  /// This allows synchronization with the ptrace event loop.
+  ///
+  /// \param idx The index of the file descriptor to add.
+  ///
+  /// \return A valid file descriptor if \a idx is a valid index, or -1.
+  virtual int GetEventFileDescriptorAtIndex(size_t idx) { return -1; }
+
+  /// Handle a file descriptor event that was registered with the lldb-server
+  /// from previous calls to LLDBServerPlugin::GetEventFileDescriptorAtIndex()
+  ///
+  /// \param fd The file descriptor event to handle.
+  virtual bool HandleEventFileDescriptorEvent(int fd) { return false; }
+
+  /// Handle a received a GDB remote packet that doesn't have a callback
+  /// specified in the GDBRemotePacketCallbacks structure after a call to
+  /// LLDBServerPlugin::GetCallbacks(...).
+  ///
+  /// \return
+  ///   The resonse packet to send. If the empty string is returned, this will
+  ///   cause an unimplemented packet ($#00) to be sent signaling this packet
+  ///   is not supported.
+  virtual std::string HandlePacket(const uint8_t *payload, size_t payload_size);
+
+  /// Called when a breakpoint is hit in the native process.
+  ///
+  /// LLDBServerPlugin objects can set breakpoints in the native process by
+  /// calling m_process.SetBreakpoint(...) to help implement funcionality,
+  /// such as dynamic library loading in GPUs or to synchronize in any other
+  /// way with the native process.
+  virtual void BreakpointWasHit(lldb::addr_t address) {}
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 843354147f2da..54212ccd53545 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -18,6 +18,7 @@
 #endif
 
 #include "LLDBServerUtilities.h"
+#include "Plugins/PluginInterface/LLDBServerNativeProcess.h"
 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
 #include "lldb/Host/Config.h"
@@ -89,7 +90,37 @@ class NativeProcessManager : public NativeProcessProtocol::Manager {
   }
 };
 #endif
-}
+
+class LLDBServerNativeProcessImpl : public LLDBServerNativeProcess {
+  NativeProcessProtocol *m_native_process = nullptr;
+
+public:
+  LLDBServerNativeProcessImpl(NativeProcessProtocol *native_process)
+      : m_native_process(native_process) {}
+
+  ~LLDBServerNativeProcessImpl() override {}
+  /// Set a breakpoint in the native process.
+  ///
+  /// When the breakpoints gets hit, lldb-server will call
+  /// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
+  /// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in the
+  /// native process.
+  lldb::user_id_t SetBreakpoint(lldb::addr_t address) override {
+    return LLDB_INVALID_BREAK_ID;
+  }
+
+  Status RegisterSignalCatcher(int signo) override {
+    return Status::FromErrorString("unimplemented");
+  }
+
+  Status HaltProcess() override { return m_native_process->Halt(); }
+  Status ContinueProcess() override {
+    ResumeActionList resume_actions;
+    return m_native_process->Resume(resume_actions);
+  }
+};
+
+} // namespace
 
 #ifndef _WIN32
 // Watch for signals
@@ -132,9 +163,8 @@ void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,
   const long int pid = strtol(attach_target.c_str(), &end_p, 10);
 
   // We'll call it a match if the entire argument is consumed.
-  if (end_p &&
-      static_cast<size_t>(end_p - attach_target.c_str()) ==
-          attach_target.size())
+  if (end_p && static_cast<size_t>(end_p - attach_target.c_str()) ==
+                   attach_target.size())
     handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));
   else
     handle_attach_to_process_name(gdb_server, attach_target);
@@ -452,7 +482,11 @@ int main_gdbserver(int argc, char *argv[]) {
 
   NativeProcessManager manager(mainloop);
   GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
-
+  gdb_server.SetProcessCreatedCallback([](NativeProcessProtocol *process) {
+    assert(process);
+    LLDBServerNativeProcess::SetNativeProcess(
+        new LLDBServerNativeProcessImpl(process));
+  });
   llvm::StringRef host_and_port;
   if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {
     host_and_port = Inputs.front();
@@ -475,8 +509,8 @@ int main_gdbserver(int argc, char *argv[]) {
   printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);
 
   ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port,
-                  progname, subcommand, named_pipe_path.c_str(),
-                  unnamed_pipe, connection_fd);
+                  progname, subcommand, named_pipe_path.c_str(), unnamed_pipe,
+                  connection_fd);
 
   if (!gdb_server.IsConnected()) {
     fprintf(stderr, "no connection information provided, unable to run\n");

>From 677a0eb42ab721ba4ae3ea9be400a95994eb39a4 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 2 Apr 2025 10:33:15 -0700
Subject: [PATCH 02/34] Made the mock GPU plug-in functional.

This patch implements that changes needed to allow a GPU plug-in to
detect it is loadable and return a connection URL back to LLDB. LLDB
will attach to the GPU process automatically by having lldb-server
listen on port 0, figure out the real port and send it back to LLDB.
ProcessGDBRemote will create a new target and attach to it
automatically.
---
 .../lldb/Host/common/NativeProcessProtocol.h  |   3 +
 .../Host/common/NativeProcessProtocol.cpp     |   4 +
 .../GDBRemoteCommunicationClient.cpp          |  13 +-
 .../gdb-remote/GDBRemoteCommunicationClient.h |  25 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      |  53 +-
 .../GDBRemoteCommunicationServerLLGS.h        |  21 +-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |  30 +
 .../gdb-remote/ProcessGDBRemoteLog.cpp        |   1 +
 .../Process/gdb-remote/ProcessGDBRemoteLog.h  |   1 +
 .../Plugins/MockGPU/CMakeLists.txt            |   8 +-
 .../MockGPU/LLDBServerPluginMockGPU.cpp       | 171 +++++
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  38 ++
 .../Plugins/MockGPU/MockGPUPlugin.cpp         |   8 -
 .../Plugins/MockGPU/MockGPUPlugin.h           |   0
 .../Plugins/MockGPU/ProcessMockGPU.cpp        | 145 ++++
 .../Plugins/MockGPU/ProcessMockGPU.h          | 101 +++
 .../MockGPU/RegisterContextMockGPU.cpp        | 626 ++++++++++++++++++
 .../Plugins/MockGPU/RegisterContextMockGPU.h  |  83 +++
 .../Plugins/MockGPU/ThreadMockGPU.cpp         |  55 ++
 .../Plugins/MockGPU/ThreadMockGPU.h           |  64 ++
 .../Plugins/PluginInterface/CMakeLists.txt    |   6 +-
 .../LLDBServerNativeProcess.cpp               |  30 +-
 .../PluginInterface/LLDBServerNativeProcess.h |  32 +-
 .../PluginInterface/LLDBServerPlugin.h        |  78 +--
 lldb/tools/lldb-server/lldb-gdbserver.cpp     |  55 +-
 25 files changed, 1495 insertions(+), 156 deletions(-)
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
 delete mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp
 delete mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.h
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.h

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 1d5fecfcd5c27..b8a59cd25d84f 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -179,6 +179,9 @@ class NativeProcessProtocol {
   // Accessors
   lldb::pid_t GetID() const { return m_pid; }
 
+  // Get process info
+  virtual bool GetProcessInfo(ProcessInstanceInfo &proc_info);
+
   lldb::StateType GetState() const;
 
   bool IsRunning() const {
diff --git a/lldb/source/Host/common/NativeProcessProtocol.cpp b/lldb/source/Host/common/NativeProcessProtocol.cpp
index e36eefaa6f4a4..5e03f5ca0bec4 100644
--- a/lldb/source/Host/common/NativeProcessProtocol.cpp
+++ b/lldb/source/Host/common/NativeProcessProtocol.cpp
@@ -766,4 +766,8 @@ void NativeProcessProtocol::DoStopIDBumped(uint32_t /* newBumpId */) {
   // Default implementation does nothing.
 }
 
+bool NativeProcessProtocol::GetProcessInfo(ProcessInstanceInfo &proc_info) {
+  return Host::GetProcessInfo(m_pid, proc_info);
+}
+
 NativeProcessProtocol::Manager::~Manager() = default;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 501670d62e75b..45f05483f9de1 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -1238,8 +1238,8 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
   Log *log = GetLog(GDBRLog::Process);
 
   if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) {
-    // host info computation can require DNS traffic and shelling out to external processes.
-    // Increase the timeout to account for that.
+    // host info computation can require DNS traffic and shelling out to
+    // external processes. Increase the timeout to account for that.
     ScopedTimeout timeout(*this, seconds(10));
     m_qHostInfo_is_valid = eLazyBoolNo;
     StringExtractorGDBRemote response;
@@ -2262,7 +2262,7 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
                  !vendor_name.empty()) {
         llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name);
         if (!environment.empty())
-            triple.setEnvironmentName(environment);
+          triple.setEnvironmentName(environment);
 
         assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat);
         assert(triple.getObjectFormat() != llvm::Triple::Wasm);
@@ -2297,7 +2297,8 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
         }
         m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
         m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name));
-        m_process_arch.GetTriple().setEnvironmentName(llvm::StringRef(environment));
+        m_process_arch.GetTriple().setEnvironmentName(
+            llvm::StringRef(environment));
       }
       return true;
     }
@@ -2925,7 +2926,7 @@ GDBRemoteCommunicationClient::GetCurrentProcessAndThreadIDs(
 
           ids.push_back(*pid_tid);
           ch = response.GetChar(); // Skip the command separator
-        } while (ch == ',');       // Make sure we got a comma separator
+        } while (ch == ','); // Make sure we got a comma separator
       }
     }
 
@@ -4378,3 +4379,5 @@ llvm::Expected<int> GDBRemoteCommunicationClient::KillProcess(lldb::pid_t pid) {
                                  "unexpected response to k packet: %s",
                                  response.GetStringRef().str().c_str());
 }
+
+void GDBRemoteCommunicationClient::SetFilePassingFD(int fd) {}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index a765e95bf9814..437155815d1ea 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -405,10 +405,10 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   llvm::ErrorOr<llvm::MD5::MD5Result> CalculateMD5(const FileSpec &file_spec);
 
-  lldb::DataBufferSP ReadRegister(
-      lldb::tid_t tid,
-      uint32_t
-          reg_num); // Must be the eRegisterKindProcessPlugin register number
+  lldb::DataBufferSP
+  ReadRegister(lldb::tid_t tid,
+               uint32_t reg_num); // Must be the eRegisterKindProcessPlugin
+                                  // register number
 
   lldb::DataBufferSP ReadAllRegisters(lldb::tid_t tid);
 
@@ -530,6 +530,13 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   llvm::Expected<int> KillProcess(lldb::pid_t pid);
 
+  // If this function is called with a valid file descriptor, a thread will be
+  // started that will listen for messages on fd from the socket pair. This
+  // allows us to pass file descriptors from the lldb-server to this client to
+  // allow another GDB remote connection to be started. This is used for GPU
+  // debugging on a local machine.
+  void SetFilePassingFD(int fd);
+
 protected:
   LazyBool m_supports_not_sending_acks = eLazyBoolCalculate;
   LazyBool m_supports_thread_suffix = eLazyBoolCalculate;
@@ -583,8 +590,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
       m_supports_qSymbol : 1, m_qSymbol_requests_done : 1,
       m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1,
       m_supports_jModulesInfo : 1, m_supports_vFileSize : 1,
-      m_supports_vFileMode : 1, m_supports_vFileExists : 1,
-      m_supports_vRun : 1;
+      m_supports_vFileMode : 1, m_supports_vFileExists : 1, m_supports_vRun : 1;
 
   /// Current gdb remote protocol process identifier for all other operations
   lldb::pid_t m_curr_pid = LLDB_INVALID_PROCESS_ID;
@@ -619,8 +625,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
       UINT32_MAX; // from reply to qGDBServerVersion, zero if
                   // qGDBServerVersion is not supported
   std::chrono::seconds m_default_packet_timeout;
-  int m_target_vm_page_size = 0; // target system VM page size; 0 unspecified
-  uint64_t m_max_packet_size = 0;    // as returned by qSupported
+  int m_target_vm_page_size = 0;  // target system VM page size; 0 unspecified
+  uint64_t m_max_packet_size = 0; // as returned by qSupported
   std::string m_qSupported_response; // the complete response to qSupported
 
   bool m_supported_async_json_packets_is_valid = false;
@@ -628,7 +634,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   std::vector<MemoryRegionInfo> m_qXfer_memory_map;
   bool m_qXfer_memory_map_loaded = false;
-
+  // Used to pass a file descriptor from the GDB server back to the client.
+  std::optional<int> m_file_passing_fd;
   bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
 
   bool GetGDBServerVersion();
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index c198344b991fc..39d04c032678d 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -292,9 +292,6 @@ Status GDBRemoteCommunicationServerLLGS::LaunchProcess() {
     if (!process_or)
       return Status::FromError(process_or.takeError());
     m_continue_process = m_current_process = process_or->get();
-    // Notify anyone wanting to know when a NativeProcessProtocol is created.
-    if (m_process_created_callback)
-      m_process_created_callback(process_or->get());
     m_debugged_processes.emplace(
         m_current_process->GetID(),
         DebuggedProcess{std::move(*process_or), DebuggedProcess::Flag{}});
@@ -368,9 +365,6 @@ Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) {
     return status;
   }
   m_continue_process = m_current_process = process_or->get();
-  // Notify anyone wanting to know when a NativeProcessProtocol is created.
-  if (m_process_created_callback)
-    m_process_created_callback(process_or->get());
   m_debugged_processes.emplace(
       m_current_process->GetID(),
       DebuggedProcess{std::move(*process_or), DebuggedProcess::Flag{}});
@@ -1008,7 +1002,8 @@ StreamString GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread(
 
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
-    NativeProcessProtocol &process, lldb::tid_t tid, bool force_synchronous) {
+    NativeProcessProtocol &process, lldb::tid_t tid, bool force_synchronous,
+    llvm::StringRef extra_stop_reply_args) {
   // Ensure we can get info on the given thread.
   NativeThreadProtocol *thread = process.GetThreadByID(tid);
   if (!thread)
@@ -1018,6 +1013,9 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
   if (response.Empty())
     return SendErrorResponse(42);
 
+  if (!extra_stop_reply_args.empty())
+    response.PutCString(extra_stop_reply_args);
+
   if (m_non_stop && !force_synchronous) {
     PacketResult ret = SendNotificationPacketNoLock(
         "Stop", m_stop_notification_queue, response.GetString());
@@ -1092,8 +1090,17 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
   Log *log = GetLog(LLDBLog::Process);
   LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
 
+  std::string extra_stop_reply_args;
+  // Call the state changed callback
+  if (m_process_stopped_callback) {
+    extra_stop_reply_args = m_process_stopped_callback(*this, process);
+    // LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called, m_process_stopped_callback returned \"%s\"", __FUNCTION__, extra_stop_reply_args.str().c_str());
+
+  }
+
   PacketResult result = SendStopReasonForState(
-      *process, StateType::eStateStopped, /*force_synchronous=*/false);
+      *process, StateType::eStateStopped, /*force_synchronous=*/false, 
+      extra_stop_reply_args);
   if (result != PacketResult::Success) {
     LLDB_LOGF(log,
               "GDBRemoteCommunicationServerLLGS::%s failed to send stop "
@@ -1113,6 +1120,7 @@ void GDBRemoteCommunicationServerLLGS::ProcessStateChanged(
               __FUNCTION__, process->GetID(), StateAsCString(state));
   }
 
+
   switch (state) {
   case StateType::eStateRunning:
     break;
@@ -1405,31 +1413,13 @@ GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo(
     StringExtractorGDBRemote &packet) {
   // Fail if we don't have a current process.
-  static std::once_flag OnceFlag;
-
-  std::call_once(OnceFlag, []() {
-    const char *plugin_path =
-        "/data/users/gclayton/github/Debug/lib/liblldbServerPluginMockGPU.so";
-    std::string error;
-    g_gpu_plugin =
-        sys::DynamicLibrary::getPermanentLibrary(plugin_path, &error);
-    LLDBServerPluginInitialize plugin_initialize =
-        FuncPtr<LLDBServerPluginInitialize>(
-            g_gpu_plugin.getAddressOfSymbol("LLDBServerPluginInitialize"));
-    plugin_initialize();
-  });
 
   if (!m_current_process ||
       (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
     return SendErrorResponse(68);
 
-  lldb::pid_t pid = m_current_process->GetID();
-
-  if (pid == LLDB_INVALID_PROCESS_ID)
-    return SendErrorResponse(1);
-
   ProcessInstanceInfo proc_info;
-  if (!Host::GetProcessInfo(pid, proc_info))
+  if (!m_current_process->GetProcessInfo(proc_info))
     return SendErrorResponse(1);
 
   StreamString response;
@@ -1923,7 +1913,6 @@ GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_stop_reason(
     StringExtractorGDBRemote &packet) {
   // Handle the $? gdbremote command.
-
   if (m_non_stop) {
     // Clear the notification queue first, except for pending exit
     // notifications.
@@ -1965,7 +1954,7 @@ GDBRemoteCommunicationServerLLGS::Handle_stop_reason(
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::SendStopReasonForState(
     NativeProcessProtocol &process, lldb::StateType process_state,
-    bool force_synchronous) {
+    bool force_synchronous, llvm::StringRef extra_stop_reply_args) {
   Log *log = GetLog(LLDBLog::Process);
 
   if (m_disabling_non_stop) {
@@ -1999,7 +1988,8 @@ GDBRemoteCommunicationServerLLGS::SendStopReasonForState(
     // Make sure we set the current thread so g and p packets return the data
     // the gdb will expect.
     SetCurrentThreadID(tid);
-    return SendStopReplyPacketForThread(process, tid, force_synchronous);
+    return SendStopReplyPacketForThread(process, tid, force_synchronous, 
+                                        extra_stop_reply_args);
   }
 
   case eStateInvalid:
@@ -3656,7 +3646,8 @@ GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo(
     return SendErrorResponse(0x15);
   }
   return SendStopReplyPacketForThread(*m_current_process, tid,
-                                      /*force_synchronous=*/true);
+                                      /*force_synchronous=*/true, 
+                                      llvm::StringRef());
 }
 
 GDBRemoteCommunication::PacketResult
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index beed8e00e79d9..564b6784ad8d6 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -85,10 +85,15 @@ class GDBRemoteCommunicationServerLLGS
 
   Status InitializeConnection(std::unique_ptr<Connection> connection);
 
-  using ProcessCreatedCallback = std::function<void(NativeProcessProtocol *)>;
-
-  void SetProcessCreatedCallback(ProcessCreatedCallback callback) {
-    m_process_created_callback = callback;
+  using ProcessStoppedChangedCallback = 
+      std::function<std::string (GDBRemoteCommunicationServerLLGS &server,
+                                 NativeProcessProtocol *process)>;
+
+  bool SetProcessStoppedCallback(ProcessStoppedChangedCallback callback) {
+    if (m_process_stopped_callback)
+      return false;
+    m_process_stopped_callback = callback;
+    return true;
   }
 
   struct DebuggedProcess {
@@ -129,7 +134,7 @@ class GDBRemoteCommunicationServerLLGS
 
   NativeProcessProtocol::Extension m_extensions_supported = {};
 
-  ProcessCreatedCallback m_process_created_callback = nullptr;
+  ProcessStoppedChangedCallback m_process_stopped_callback = nullptr;
 
   // Typically we would use a SmallVector for this data but in this context we
   // don't know how much data we're recieving so we would have to heap allocate
@@ -144,11 +149,13 @@ class GDBRemoteCommunicationServerLLGS
 
   PacketResult SendStopReplyPacketForThread(NativeProcessProtocol &process,
                                             lldb::tid_t tid,
-                                            bool force_synchronous);
+                                            bool force_synchronous,
+                                            llvm::StringRef extra_stop_reply_args = {});
 
   PacketResult SendStopReasonForState(NativeProcessProtocol &process,
                                       lldb::StateType process_state,
-                                      bool force_synchronous);
+                                      bool force_synchronous, 
+                                      llvm::StringRef extra_stop_reply_args = {});
 
   void EnqueueStopReplyPackets(lldb::tid_t thread_to_skip);
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 68360788c96e6..a83972f55692c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2390,6 +2390,36 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
         if (!value.getAsInteger(0, addressing_bits)) {
           addressable_bits.SetHighmemAddressableBits(addressing_bits);
         }
+      } else if (key.compare("gpu-url") == 0) {
+        Log *log = GetLog(GDBRLog::Plugin);
+        LLDB_LOG(log, "gpu-url: url = \"{0}\"", value); 
+        auto &debugger = GetTarget().GetDebugger();
+        TargetSP gpu_target_sp;
+        // Create an empty target for our GPU.
+        Status error(debugger.GetTargetList().CreateTarget(
+          debugger, llvm::StringRef(), llvm::StringRef(), eLoadDependentsNo, 
+          nullptr, gpu_target_sp));
+        if (error.Fail()) {
+          LLDB_LOG(log, "gpu-url: error creating target: \"{0}\"", error); 
+        } else if (gpu_target_sp) {
+          PlatformSP platform_sp = gpu_target_sp->GetPlatform();
+          if (platform_sp) {
+            ProcessSP process_sp = platform_sp->ConnectProcess(
+                        value, GetPluginNameStatic(), debugger,
+                        gpu_target_sp.get(), error);
+            if (error.Fail()) {
+              LLDB_LOG(log, "gpu-url: error connecting to process: \"{0}\"", error); 
+            } else if (!process_sp) {
+              LLDB_LOG(log, "gpu-url: invalid process"); 
+            } else {
+              LLDB_LOG(log, "gpu-url: successfully created process!!!");
+            }
+          } else {
+            LLDB_LOG(log, "gpu-url: invalid platform");
+          }
+        } else {
+          LLDB_LOG(log, "gpu-url: invalid target");
+        }
       } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
         uint32_t reg = UINT32_MAX;
         if (!key.getAsInteger(16, reg))
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
index 3322f6b8048ab..76e1fb196424e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
@@ -31,6 +31,7 @@ static constexpr Log::Category g_categories[] = {
     {{"step"}, {"log step related activities"}, GDBRLog::Step},
     {{"thread"}, {"log thread events and activities"}, GDBRLog::Thread},
     {{"watch"}, {"log watchpoint related activities"}, GDBRLog::Watchpoints},
+    {{"plugin"}, {"log internal lldb-server plugin messages"}, GDBRLog::Plugin},
 };
 
 static Log::Channel g_channel(g_categories, GDBRLog::Packets);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
index 66b2f00f1ea96..db129022eb32f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
@@ -27,6 +27,7 @@ enum class GDBRLog : Log::MaskType {
   Step = Log::ChannelFlag<8>,
   Thread = Log::ChannelFlag<9>,
   Watchpoints = Log::ChannelFlag<10>,
+  Plugin = Log::ChannelFlag<11>,
   LLVM_MARK_AS_BITMASK_ENUM(Watchpoints)
 };
 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
index 4349ba7c3f14e..0b827f3c027b3 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
@@ -1,6 +1,10 @@
 
-add_lldb_library(lldbServerPluginMockGPU SHARED
-  MockGPUPlugin.cpp
+add_lldb_library(lldbServerPluginMockGPU
+  LLDBServerPluginMockGPU.cpp
+  ProcessMockGPU.cpp
+  RegisterContextMockGPU.cpp
+  ThreadMockGPU.cpp
 )
 
 target_link_libraries(lldbServerPluginMockGPU lldbServerPluginInterface)
+target_include_directories(lldbServerPluginMockGPU PRIVATE "${LLDB_SOURCE_DIR}/source" "../..")
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
new file mode 100644
index 0000000000000..cc153f6a816a8
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -0,0 +1,171 @@
+//===-- LLDBServerPluginMockGPU.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBServerPluginMockGPU.h"
+#include "ProcessMockGPU.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "llvm/Support/Error.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <thread>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+
+LLDBServerPluginMockGPU::LLDBServerPluginMockGPU(
+  LLDBServerPlugin::GDBServer &native_process)
+    : LLDBServerPlugin(native_process) {
+  m_process_manager_up.reset(new ProcessManagerMockGPU(m_main_loop));
+  m_gdb_server.reset(
+      new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up));
+
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::LLDBServerPluginMockGPU() faking launch...");
+  ProcessLaunchInfo info;
+  info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |
+                      eLaunchFlagDisableASLR);
+  Args args;
+  args.AppendArgument("/pretend/path/to/mockgpu");
+  args.AppendArgument("--option1");
+  args.AppendArgument("--option2");
+  args.AppendArgument("--option3");
+  info.SetArguments(args, true);
+  info.GetEnvironment() = Host::GetEnvironment();
+  m_gdb_server->SetLaunchInfo(info);
+  Status error = m_gdb_server->LaunchProcess();
+  if (error.Fail()) {
+    LLDB_LOGF(log, "LLDBServerPluginMockGPU::LLDBServerPluginMockGPU() failed to launch: %s", error.AsCString());
+  } else {
+    LLDB_LOGF(log, "LLDBServerPluginMockGPU::LLDBServerPluginMockGPU() launched successfully");
+  }
+}
+
+LLDBServerPluginMockGPU::~LLDBServerPluginMockGPU() {
+  CloseFDs();
+}
+
+void LLDBServerPluginMockGPU::CloseFDs() {
+  if (m_fds[0] != -1) {
+    close(m_fds[0]);
+    m_fds[0] = -1;
+  }
+  if (m_fds[1] != -1) {
+    close(m_fds[1]);
+    m_fds[1] = -1;
+  }
+}
+
+int LLDBServerPluginMockGPU::GetEventFileDescriptorAtIndex(size_t idx) {
+  if (idx != 0)
+    return -1;
+  if (m_fds[0] == -1) {
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, m_fds) == -1) {
+      m_fds[0] = -1;
+      m_fds[1] = -1;
+    }
+  }
+  return m_fds[0];
+}
+
+
+bool LLDBServerPluginMockGPU::HandleEventFileDescriptorEvent(int fd) { 
+  if (fd == m_fds[0]) {
+    char buf[1];
+    // Read 1 bytes from the fd
+    read(m_fds[0], buf, sizeof(buf));
+    return true;
+  }
+  return false;
+}
+
+#if 0
+TEST_P(SocketTest, TCPAcceptTimeout) {
+  if (!HostSupportsProtocol())
+    return;
+
+  const bool child_processes_inherit = false;
+  auto listen_socket_up =
+      std::make_unique<TCPSocket>(true, child_processes_inherit);
+  Status error = listen_socket_up->Listen(
+      llvm::formatv("[{0}]:0", GetParam().localhost_ip).str(), 5);
+  ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
+  ASSERT_TRUE(listen_socket_up->IsValid());
+
+  Socket *socket;
+  ASSERT_THAT_ERROR(
+      listen_socket_up->Accept(std::chrono::milliseconds(10), socket)
+          .takeError(),
+      llvm::Failed<llvm::ErrorInfoBase>(
+          testing::Property(&llvm::ErrorInfoBase::convertToErrorCode,
+                            std::make_error_code(std::errc::timed_out))));
+}
+#endif
+
+void LLDBServerPluginMockGPU::AcceptAndMainLoopThread(
+    std::unique_ptr<TCPSocket> listen_socket_up) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() spawned");
+
+  if (!listen_socket_up)
+    return;
+  Socket *socket = nullptr;
+  Status error = listen_socket_up->Accept(std::chrono::seconds(30), socket);
+  if (error.Fail()) {
+    LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() error "
+              "returned from Accept(): %s", error.AsCString());  
+    return;
+  }
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() initializing connection");
+  std::unique_ptr<Connection> connection_up(new ConnectionFileDescriptor(socket));
+  m_gdb_server->InitializeConnection(std::move(connection_up));
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() running main loop");
+  m_main_loop_status = m_main_loop.Run();
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() main loop exited!");
+  if (m_main_loop_status.Fail()) {
+    LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() main "
+              "loop exited with an error: %s", m_main_loop_status.AsCString());
+
+  }
+}
+
+std::optional<std::string> LLDBServerPluginMockGPU::GetConnectionURL() {
+  static int g_counter = 0;
+  Log *log = GetLog(GDBRLog::Plugin);
+  ++g_counter;
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::GetConnectionURL() g_counter = %i", 
+            g_counter);
+  if (++g_counter == 2) {
+    LLDB_LOGF(log, "LLDBServerPluginMockGPU::GetConnectionURL() trying to "
+              "listen on port 0");
+    llvm::Expected<std::unique_ptr<TCPSocket>> sock = 
+        Socket::TcpListen("localhost:0", 5);
+    if (sock) {
+      const uint16_t listen_port = (*sock)->GetLocalPortNumber();
+      auto extra_args = llvm::formatv("gpu-url:connect://localhost:{};", 
+                                      listen_port);
+      LLDB_LOGF(log, 
+                "LLDBServerPluginMockGPU::GetConnectionURL() listening to %u", 
+                listen_port);
+      std::thread t(&LLDBServerPluginMockGPU::AcceptAndMainLoopThread, this, 
+                    std::move(*sock));
+      t.detach();
+      return extra_args.str();
+    } else {
+      LLDB_LOGF(log, "LLDBServerPluginMockGPU::GetConnectionURL() failed to "
+                "listen to localhost:0");      
+    }
+  }
+  return std::nullopt;
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
new file mode 100644
index 0000000000000..67cfc45480ee1
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -0,0 +1,38 @@
+//===-- LLDBServerPluginMockGPU.h -------------------------------*- C++ -*-===//
+//
+// 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_LLDBSERVERPLUGINMOCKGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINMOCKGPU_H
+
+#include "Plugins/PluginInterface/LLDBServerPlugin.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+namespace lldb_server {
+
+class LLDBServerPluginMockGPU : public lldb_private::lldb_server::LLDBServerPlugin {
+public:
+  LLDBServerPluginMockGPU(lldb_private::lldb_server::LLDBServerPlugin::GDBServer &native_process);
+  ~LLDBServerPluginMockGPU() override;
+  int GetEventFileDescriptorAtIndex(size_t idx) override;
+  bool HandleEventFileDescriptorEvent(int fd) override;
+  std::optional<std::string> GetConnectionURL() override;
+
+private:
+  void CloseFDs();
+  void AcceptAndMainLoopThread(std::unique_ptr<TCPSocket> listen_socket_up);
+
+  // Used with a socketpair to get events on the native ptrace event queue.
+  int m_fds[2] = {-1, -1};
+  Status m_main_loop_status;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINMOCKGPU_H
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp
deleted file mode 100644
index 7d490b5584fbe..0000000000000
--- a/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <stdio.h>
-extern void PrintSomething();
-extern "C" {
-void LLDBServerPluginInitialize() {
-  puts("LLDBServerPluginInitialize");
-  // PrintSomething();
-}
-}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.h b/lldb/tools/lldb-server/Plugins/MockGPU/MockGPUPlugin.h
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
new file mode 100644
index 0000000000000..13a97eb9b587c
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
@@ -0,0 +1,145 @@
+//===-- ProcessMockGPU.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessMockGPU.h"
+#include "ThreadMockGPU.h"
+
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UnimplementedError.h"
+#include "llvm/Support/Error.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+
+ProcessMockGPU::ProcessMockGPU(lldb::pid_t pid, NativeDelegate &delegate)
+    : NativeProcessProtocol(pid, -1, delegate) {
+  m_state = eStateStopped;
+  UpdateThreads();
+}
+
+Status ProcessMockGPU::Resume(const ResumeActionList &resume_actions) {
+  SetState(StateType::eStateRunning, true);
+  return Status();
+}
+
+Status ProcessMockGPU::Halt() {
+  SetState(StateType::eStateStopped, true);
+  return Status();
+}
+
+Status ProcessMockGPU::Detach() {
+  SetState(StateType::eStateDetached, true);
+  return Status();
+}
+
+/// Sends a process a UNIX signal \a signal.
+///
+/// \return
+///     Returns an error object.
+Status ProcessMockGPU::Signal(int signo) {
+  return Status::FromErrorString("unimplemented");
+}
+
+/// Tells a process to interrupt all operations as if by a Ctrl-C.
+///
+/// The default implementation will send a local host's equivalent of
+/// a SIGSTOP to the process via the NativeProcessProtocol::Signal()
+/// operation.
+///
+/// \return
+///     Returns an error object.
+Status ProcessMockGPU::Interrupt() { return Status(); }
+
+Status ProcessMockGPU::Kill() { return Status(); }
+
+Status ProcessMockGPU::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                                  size_t &bytes_read) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ProcessMockGPU::WriteMemory(lldb::addr_t addr, const void *buf,
+                                   size_t size, size_t &bytes_written) {
+  return Status::FromErrorString("unimplemented");
+}
+
+lldb::addr_t ProcessMockGPU::GetSharedLibraryInfoAddress() {
+  return LLDB_INVALID_ADDRESS;
+}
+
+size_t ProcessMockGPU::UpdateThreads() {
+  if (m_threads.empty()) {
+    lldb::tid_t tid = 3456;
+    m_threads.push_back(std::make_unique<ThreadMockGPU>(*this, 3456));
+    // ThreadMockGPU &thread = static_cast<ThreadMockGPU &>(*m_threads.back());
+    SetCurrentThreadID(tid);
+  }
+  return m_threads.size();
+}
+
+const ArchSpec &ProcessMockGPU::GetArchitecture() const {
+  m_arch = ArchSpec("mockgpu");
+  return m_arch;
+}
+
+// Breakpoint functions
+Status ProcessMockGPU::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                                     bool hardware) {
+  return Status::FromErrorString("unimplemented");
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ProcessMockGPU::GetAuxvData() const {
+  return nullptr; // TODO: try to return
+                  // llvm::make_error<UnimplementedError>();
+}
+
+Status ProcessMockGPU::GetLoadedModuleFileSpec(const char *module_path,
+                                               FileSpec &file_spec) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ProcessMockGPU::GetFileLoadAddress(const llvm::StringRef &file_name,
+                                          lldb::addr_t &load_addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+void ProcessMockGPU::SetLaunchInfo(ProcessLaunchInfo &launch_info) {
+  static_cast<ProcessInfo &>(m_process_info) =
+      static_cast<ProcessInfo &>(launch_info);
+}
+
+bool ProcessMockGPU::GetProcessInfo(ProcessInstanceInfo &proc_info) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "ProcessMockGPU::%s() entered", __FUNCTION__);
+  m_process_info.SetProcessID(m_pid);
+  m_process_info.SetArchitecture(GetArchitecture());
+  proc_info = m_process_info;
+  return true;
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ProcessManagerMockGPU::Launch(
+    ProcessLaunchInfo &launch_info,
+    NativeProcessProtocol::NativeDelegate &native_delegate) {
+  lldb::pid_t pid = 1234;
+  auto proc_up = std::make_unique<ProcessMockGPU>(pid, native_delegate);
+  proc_up->SetLaunchInfo(launch_info);
+  return proc_up;
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ProcessManagerMockGPU::Attach(
+    lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) {
+  return llvm::createStringError("Unimplemented function");
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h
new file mode 100644
index 0000000000000..e772556a609b7
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h
@@ -0,0 +1,101 @@
+//===-- ProcessMockGPU.h ----------------------------------------*- C++ -*-===//
+//
+// 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_PROCESSMOCKGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_PROCESSMOCKGPU_H
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Utility/ProcessInfo.h"
+
+namespace lldb_private {
+namespace lldb_server {
+
+/// \class ProcessMockGPU
+/// Abstract class that extends \a NativeProcessProtocol for a mock GPU. This
+/// class is used to unit testing the GPU plugins in lldb-server.
+class ProcessMockGPU : public NativeProcessProtocol {
+  // TODO: change NativeProcessProtocol::GetArchitecture() to return by value
+  mutable ArchSpec m_arch;
+  ProcessInstanceInfo m_process_info;
+
+public:
+  ProcessMockGPU(lldb::pid_t pid, NativeDelegate &delegate);
+
+  Status Resume(const ResumeActionList &resume_actions) override;
+
+  Status Halt() override;
+
+  Status Detach() override;
+
+  /// Sends a process a UNIX signal \a signal.
+  ///
+  /// \return
+  ///     Returns an error object.
+  Status Signal(int signo) override;
+
+  /// Tells a process to interrupt all operations as if by a Ctrl-C.
+  ///
+  /// The default implementation will send a local host's equivalent of
+  /// a SIGSTOP to the process via the NativeProcessProtocol::Signal()
+  /// operation.
+  ///
+  /// \return
+  ///     Returns an error object.
+  Status Interrupt() 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;
+
+  // Breakpoint functions
+  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;
+
+  bool GetProcessInfo(ProcessInstanceInfo &info) override;
+
+  // Custom accessors
+  void SetLaunchInfo(ProcessLaunchInfo &launch_info);
+};
+
+class ProcessManagerMockGPU : public NativeProcessProtocol::Manager {
+public:
+  ProcessManagerMockGPU(MainLoop &mainloop)
+      : NativeProcessProtocol::Manager(mainloop) {}
+
+  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+  Launch(ProcessLaunchInfo &launch_info,
+         NativeProcessProtocol::NativeDelegate &native_delegate) override;
+
+  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+  Attach(lldb::pid_t pid,
+         NativeProcessProtocol::NativeDelegate &native_delegate) override;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
new file mode 100644
index 0000000000000..cc74acc9f6dba
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
@@ -0,0 +1,626 @@
+//===-- RegisterContextMockGPU.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 "RegisterContextMockGPU.h"
+
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+
+/// LLDB register numbers must start at 0 and be contiguous with no gaps.
+enum LLDBRegNum : uint32_t {
+  LLDB_R0 = 0,
+  LLDB_R1,
+  LLDB_R2,
+  LLDB_R3,
+  LLDB_R4,
+  LLDB_R5,
+  LLDB_R6,
+  LLDB_R7,
+  LLDB_SP,
+  LLDB_FP,
+  LLDB_PC,
+  LLDB_Flags,
+  LLDB_V0,
+  LLDB_V1,
+  LLDB_V2,
+  LLDB_V3,
+  LLDB_V4,
+  LLDB_V5,
+  LLDB_V6,
+  LLDB_V7,
+  kNumRegs
+};
+
+/// DWARF register numbers should match the register numbers that the compiler
+/// uses in the DWARF debug info. They can be any number and do not need to
+/// be in increasing order or consective, there can be gaps. The compiler has
+/// dedicated register numbers for any DWARF that references registers, like
+/// location expressions and .debug_frame unwind info.
+enum DWARFRegNum : uint32_t {
+  DWARF_R0 = 128,
+  DWARF_R1,
+  DWARF_R2,
+  DWARF_R3,
+  DWARF_R4,
+  DWARF_R5,
+  DWARF_R6,
+  DWARF_R7,
+  DWARF_SP,
+  DWARF_FP,
+  DWARF_PC,
+  DWARF_Flags = LLDB_INVALID_REGNUM, // This register does not exist in DWARF
+  DWARF_V0 = 256,
+  DWARF_V1,
+  DWARF_V2,
+  DWARF_V3,
+  DWARF_V4,
+  DWARF_V5,
+  DWARF_V6,
+  DWARF_V7,
+};
+
+/// Compiler registers should match the register numbers that the compiler
+/// uses in runtime information. They can be any number and do not need to
+/// be in increasing order or consective, there can be gaps. The compiler has
+/// dedicated register numbers for any runtime information that references
+/// registers, like .eh_frame unwind info. Many times these numbers match the
+/// DWARF register numbers, but not always.
+enum CompilerRegNum : uint32_t {
+  EH_FRAME_R0 = 1000,
+  EH_FRAME_R1,
+  EH_FRAME_R2,
+  EH_FRAME_R3,
+  EH_FRAME_R4,
+  EH_FRAME_R5,
+  EH_FRAME_R6,
+  EH_FRAME_R7,
+  EH_FRAME_SP,
+  EH_FRAME_FP,
+  EH_FRAME_PC,
+  EH_FRAME_Flags = LLDB_INVALID_REGNUM, // Not accessed by runtime info.
+  EH_FRAME_V0 = 2000,
+  EH_FRAME_V1,
+  EH_FRAME_V2,
+  EH_FRAME_V3,
+  EH_FRAME_V4,
+  EH_FRAME_V5,
+  EH_FRAME_V6,
+  EH_FRAME_V7,
+};
+
+uint32_t g_gpr_regnums[32] = {LLDB_R0, LLDB_R1, LLDB_R2, LLDB_R3,
+                              LLDB_R4, LLDB_R5, LLDB_R6, LLDB_R7,
+                              LLDB_SP, LLDB_FP, LLDB_PC, LLDB_Flags};
+uint32_t g_vec_regnums[32] = {LLDB_V0, LLDB_V1, LLDB_V2, LLDB_V3,
+                              LLDB_V4, LLDB_V5, LLDB_V6, LLDB_V7};
+
+static const RegisterSet g_reg_sets[] = {
+    {"General Purpose Registers", "gpr",
+     sizeof(g_gpr_regnums) / sizeof(g_gpr_regnums[0]), g_gpr_regnums},
+    {"Vector Registers", "vector",
+     sizeof(g_vec_regnums) / sizeof(g_vec_regnums[0]), g_vec_regnums},
+};
+
+/// Define all of the information about all registers. The register info structs
+/// are accessed by the LLDB register numbers, which are defined above.
+static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
+    {
+        "R0",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        0,             // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R0, // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R0,    // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_ARG1, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R0, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R0  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R1",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        8,             // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R1, // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R1,    // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_ARG2, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R1, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R1  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R2",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        16,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R2, // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R2,    // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_ARG3, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R2, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R2  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R3",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        24,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R3, // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R3,    // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_ARG4, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R3, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R3  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R4",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        32,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R4,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R4,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R4, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R4  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R5",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        40,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R5,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R5,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R5, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R5  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R6",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        48,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R6,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R6,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R6, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R6  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "R7",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        56,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_R7,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_R7,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_R7, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_R7  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "SP",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        64,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_SP,            // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_SP,               // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_SP, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_SP, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_SP  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "FP",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        72,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_FP,            // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_FP,               // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_FP, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_FP, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_FP  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "PC",          // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        80,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_PC,            // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_PC,               // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_PC, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_PC, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_PC  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "Flags",       // RegisterInfo::name
+        nullptr,       // RegisterInfo::alt_name
+        8,             // RegisterInfo::byte_size
+        88,            // RegisterInfo::byte_offset
+        eEncodingUint, // RegisterInfo::encoding
+        eFormatHex,    // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_Flags, // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_Flags,    // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_REGNUM_GENERIC_FLAGS, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_Flags, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_Flags  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V0",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        96,                    // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V0,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V0,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V0, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V0  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V1",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        104,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V1,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V1,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V1, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V1  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V2",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        112,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V2,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V2,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V2, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V2  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V3",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        120,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V3,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V3,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V3, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V3  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V4",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        128,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V4,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V4,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V4, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V4  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V5",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        136,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V5,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V5,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V5, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V5  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V6",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        144,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V6,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V6,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V6, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V6  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+    {
+        "V7",                  // RegisterInfo::name
+        nullptr,               // RegisterInfo::alt_name
+        8,                     // RegisterInfo::byte_size
+        152,                   // RegisterInfo::byte_offset
+        eEncodingVector,       // RegisterInfo::encoding
+        eFormatVectorOfUInt32, // RegisterInfo::format
+        {
+            // RegisterInfo::kinds[]
+            EH_FRAME_V7,         // RegisterInfo::kinds[eRegisterKindEHFrame]
+            DWARF_V7,            // RegisterInfo::kinds[eRegisterKindDWARF]
+            LLDB_INVALID_REGNUM, // RegisterInfo::kinds[eRegisterKindGeneric]
+            LLDB_V7, // RegisterInfo::kinds[eRegisterKindProcessPlugin]
+            LLDB_V7  // RegisterInfo::kinds[eRegisterKindLLDB]
+        },
+        nullptr, // RegisterInfo::value_regs
+        nullptr, // RegisterInfo::invalidate_regs
+        nullptr, // RegisterInfo::flags_type
+    },
+};
+RegisterContextMockGPU::RegisterContextMockGPU(
+    NativeThreadProtocol &native_thread)
+    : NativeRegisterContext(native_thread) {
+  InitRegisters();
+  // Only doing this for the Mock GPU class, don't do this in real GPU classes.
+  ReadRegs();
+}
+
+void RegisterContextMockGPU::InitRegisters() {
+  for (size_t i = 0; i < kNumRegs; ++i)
+    m_regs.data[i] = 0;
+  m_regs_valid.resize(kNumRegs, false);
+}
+
+void RegisterContextMockGPU::InvalidateAllRegisters() {
+  // Do what ever book keeping we need to do to indicate that all register
+  // values are now invalid.
+  for (uint32_t i = 0; i < kNumRegs; ++i)
+    m_regs_valid[i] = false;
+}
+
+Status RegisterContextMockGPU::ReadRegs() {
+  // Fill all registers with unique values.
+  for (uint32_t i = 0; i < kNumRegs; ++i) {
+    m_regs_valid[i] = true;
+    m_regs.data[i] = i;
+  }
+  return Status();
+}
+
+uint32_t RegisterContextMockGPU::GetRegisterSetCount() const {
+  return sizeof(g_reg_sets) / sizeof(g_reg_sets[0]);
+}
+
+uint32_t RegisterContextMockGPU::GetRegisterCount() const { return kNumRegs; }
+
+uint32_t RegisterContextMockGPU::GetUserRegisterCount() const {
+  return GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextMockGPU::GetRegisterInfoAtIndex(uint32_t reg) const {
+  if (reg < kNumRegs)
+    return &g_reg_infos[reg];
+  return nullptr;
+}
+
+const RegisterSet *
+RegisterContextMockGPU::GetRegisterSet(uint32_t set_index) const {
+  if (set_index < GetRegisterSetCount())
+    return &g_reg_sets[set_index];
+  return nullptr;
+}
+
+Status RegisterContextMockGPU::ReadRegister(const RegisterInfo *reg_info,
+                                            RegisterValue &reg_value) {
+  Status error;
+  const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB];
+  if (!m_regs_valid[lldb_reg_num])
+    error = ReadRegs();
+  if (error.Fail())
+    return error;
+  reg_value.SetUInt64(m_regs.data[lldb_reg_num]);
+  return Status();
+}
+
+Status RegisterContextMockGPU::WriteRegister(const RegisterInfo *reg_info,
+                                             const RegisterValue &reg_value) {
+  const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB];
+  bool success = false;
+  uint64_t new_value = reg_value.GetAsUInt64(UINT64_MAX, &success);
+  if (!success)
+    return Status::FromErrorString("register write failed");
+  m_regs.data[lldb_reg_num] = new_value;
+  m_regs_valid[lldb_reg_num] = true;
+  return Status();
+}
+
+Status RegisterContextMockGPU::ReadAllRegisterValues(
+    lldb::WritableDataBufferSP &data_sp) {
+  ReadRegs(); // Read all registers first
+  const size_t regs_byte_size = sizeof(m_regs);
+  data_sp.reset(new DataBufferHeap(regs_byte_size, 0));
+  uint8_t *dst = data_sp->GetBytes();
+  memcpy(dst, &m_regs.data[0], regs_byte_size);
+  return Status();
+}
+
+Status RegisterContextMockGPU::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  const size_t regs_byte_size = sizeof(m_regs);
+
+  if (!data_sp) {
+    return Status::FromErrorStringWithFormat(
+        "RegisterContextMockGPU::%s invalid data_sp provided", __FUNCTION__);
+  }
+
+  if (data_sp->GetByteSize() != regs_byte_size) {
+    return Status::FromErrorStringWithFormat(
+        "RegisterContextMockGPU::%s data_sp contained mismatched "
+        "data size, expected %" PRIu64 ", actual %" PRIu64,
+        __FUNCTION__, regs_byte_size, data_sp->GetByteSize());
+  }
+
+  const uint8_t *src = data_sp->GetBytes();
+  if (src == nullptr) {
+    return Status::FromErrorStringWithFormat(
+        "RegisterContextMockGPU::%s "
+        "DataBuffer::GetBytes() returned a null "
+        "pointer",
+        __FUNCTION__);
+  }
+  memcpy(&m_regs.data[0], src, regs_byte_size);
+  return Status();
+}
+
+std::vector<uint32_t>
+RegisterContextMockGPU::GetExpeditedRegisters(ExpeditedRegs expType) const {
+  static std::vector<uint32_t> g_expedited_regs;
+  if (g_expedited_regs.empty()) {
+    g_expedited_regs.push_back(LLDB_PC);
+    g_expedited_regs.push_back(LLDB_SP);
+    g_expedited_regs.push_back(LLDB_FP);
+  }
+  return g_expedited_regs;
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h
new file mode 100644
index 0000000000000..8992fe0e02205
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h
@@ -0,0 +1,83 @@
+//===-- RegisterContextMockGPU.h --------------------------------*- C++ -*-===//
+//
+// 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_REGISTERCONTEXTMOCKGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTMOCKGPU_H
+
+// #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/lldb-forward.h"
+
+namespace lldb_private {
+namespace lldb_server {
+
+class RegisterContextMockGPU : public NativeRegisterContext {
+public:
+  RegisterContextMockGPU(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;
+
+  std::vector<uint32_t>
+  GetExpeditedRegisters(ExpeditedRegs expType) const override;
+
+private:
+  void InitRegisters();
+  void InvalidateAllRegisters();
+  Status ReadRegs();
+
+  // All mock GPU registers are contained in this buffer.
+  union {
+    uint64_t data[20]; // Allow for indexed access to each register value.
+    struct {           // Define a struct for each register value.
+      uint64_t R0;
+      uint64_t R1;
+      uint64_t R2;
+      uint64_t R3;
+      uint64_t R4;
+      uint64_t R5;
+      uint64_t R6;
+      uint64_t R7;
+      uint64_t SP;
+      uint64_t FP;
+      uint64_t PC;
+      uint64_t Flags;
+      uint64_t V0;
+      uint64_t V1;
+      uint64_t V2;
+      uint64_t V3;
+      uint64_t V4;
+      uint64_t V5;
+      uint64_t V6;
+      uint64_t V7;
+    } regs;
+  } m_regs;
+  std::vector<bool> m_regs_valid;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTMOCKGPU_H
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
new file mode 100644
index 0000000000000..238b02936d928
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
@@ -0,0 +1,55 @@
+//===-- ThreadMockGPU.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "ThreadMockGPU.h"
+#include "ProcessMockGPU.h"
+
+using namespace lldb_private;
+using namespace lldb_server;
+
+ThreadMockGPU::ThreadMockGPU(ProcessMockGPU &process, lldb::tid_t tid)
+    : NativeThreadProtocol(process, tid), m_reg_context(*this) {
+  m_stop_info.reason = lldb::eStopReasonSignal;
+  m_stop_info.signo = SIGTRAP;
+}
+
+// NativeThreadProtocol Interface
+std::string ThreadMockGPU::GetName() { return "Mock GPU Thread Name"; }
+
+lldb::StateType ThreadMockGPU::GetState() { return lldb::eStateStopped; }
+
+bool ThreadMockGPU::GetStopReason(ThreadStopInfo &stop_info,
+                                  std::string &description) {
+  stop_info = m_stop_info;
+  description = "Mock GPU Thread Stop Reason";
+  return true;
+}
+
+Status ThreadMockGPU::SetWatchpoint(lldb::addr_t addr, size_t size,
+                                    uint32_t watch_flags, bool hardware) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadMockGPU::RemoveWatchpoint(lldb::addr_t addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadMockGPU::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadMockGPU::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+ProcessMockGPU &ThreadMockGPU::GetProcess() {
+  return static_cast<ProcessMockGPU &>(m_process);
+}
+
+const ProcessMockGPU &ThreadMockGPU::GetProcess() const {
+  return static_cast<const ProcessMockGPU &>(m_process);
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.h
new file mode 100644
index 0000000000000..1b46706611c22
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.h
@@ -0,0 +1,64 @@
+//===-- ThreadMockGPU.h --------------------------------------- -*- C++ -*-===//
+//
+// 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_THREADMOCKGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_THREADMOCKGPU_H
+
+#include "RegisterContextMockGPU.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/lldb-private-forward.h"
+#include <string>
+
+namespace lldb_private {
+namespace lldb_server {
+class ProcessMockGPU;
+
+class NativeProcessLinux;
+
+class ThreadMockGPU : public NativeThreadProtocol {
+  friend class ProcessMockGPU;
+
+public:
+  ThreadMockGPU(ProcessMockGPU &process, lldb::tid_t tid);
+
+  // NativeThreadProtocol Interface
+  std::string GetName() override;
+
+  lldb::StateType GetState() override;
+
+  bool GetStopReason(ThreadStopInfo &stop_info,
+                     std::string &description) override;
+
+  RegisterContextMockGPU &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;
+
+  ProcessMockGPU &GetProcess();
+
+  const ProcessMockGPU &GetProcess() const;
+
+private:
+  // Member Variables
+  lldb::StateType m_state;
+  ThreadStopInfo m_stop_info;
+  RegisterContextMockGPU m_reg_context;
+  std::string m_stop_description;
+};
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_THREADMOCKGPU_H
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
index 85bc3d5613508..8eb4cfddec53b 100644
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
@@ -1,4 +1,8 @@
 
-add_lldb_library(lldbServerPluginInterface SHARED
+add_lldb_library(lldbServerPluginInterface
   LLDBServerNativeProcess.cpp
+  LLDBServerPlugin.cpp
 )
+
+target_link_libraries(lldbServerPluginInterface lldbServerPluginMockGPU)
+target_include_directories(lldbServerPluginInterface PRIVATE "${LLDB_SOURCE_DIR}/source")
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
index 2e265ae119a00..85c318acc17d6 100644
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
@@ -7,17 +7,35 @@
 //===----------------------------------------------------------------------===//
 
 #include "LLDBServerNativeProcess.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
 
 using namespace lldb_private;
 using namespace lldb_server;
 
-static LLDBServerNativeProcess *g_native_process = nullptr;
+LLDBServerNativeProcess::LLDBServerNativeProcess(
+    NativeProcessProtocol *native_process)
+    : m_native_process(native_process) {}
 
-LLDBServerNativeProcess *LLDBServerNativeProcess::GetNativeProcess() {
-  return g_native_process;
+LLDBServerNativeProcess::~LLDBServerNativeProcess() {}
+/// Set a breakpoint in the native process.
+///
+/// When the breakpoints gets hit, lldb-server will call
+/// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
+/// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in the
+/// native process.
+lldb::user_id_t LLDBServerNativeProcess::SetBreakpoint(lldb::addr_t address) {
+  return LLDB_INVALID_BREAK_ID;
 }
 
-void LLDBServerNativeProcess::SetNativeProcess(
-    LLDBServerNativeProcess *process) {
-  g_native_process = process;
+Status LLDBServerNativeProcess::RegisterSignalCatcher(int signo) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status LLDBServerNativeProcess::HaltProcess() {
+  return m_native_process->Halt();
+}
+
+Status LLDBServerNativeProcess::ContinueProcess() {
+  ResumeActionList resume_actions;
+  return m_native_process->Resume(resume_actions);
 }
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
index d15a36ef164cf..d40139892cde6 100644
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
@@ -6,41 +6,41 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
-#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
+#ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERNATIVEPROCESS_H
+#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERNATIVEPROCESS_H
 
 #include "lldb/Utility/Status.h"
 #include "lldb/lldb-types.h"
 #include <memory>
 
 namespace lldb_private {
+
+class NativeProcessProtocol;
+
 namespace lldb_server {
 
 /// A class that interfaces back to the lldb-server native process for
 /// LLDBServerNativeProcess objects.
 class LLDBServerNativeProcess {
+  NativeProcessProtocol *m_native_process;
+
 public:
-  static LLDBServerNativeProcess *GetNativeProcess();
-  // lldb-server will call this function to set the native process object prior
-  // to any plug-ins being loaded.
-  static void SetNativeProcess(LLDBServerNativeProcess *process);
+  LLDBServerNativeProcess(NativeProcessProtocol *native_process);
+  ~LLDBServerNativeProcess();
 
-  virtual ~LLDBServerNativeProcess();
   /// Set a breakpoint in the native process.
   ///
   /// When the breakpoints gets hit, lldb-server will call
   /// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
-  /// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in the
-  /// native process.
-  virtual lldb::user_id_t SetBreakpoint(lldb::addr_t address) = 0;
-
-  virtual Status RegisterSignalCatcher(int signo) = 0;
-
-  virtual Status HaltProcess() = 0;
-  virtual Status ContinueProcess() = 0;
+  /// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in
+  /// the native process.
+  lldb::user_id_t SetBreakpoint(lldb::addr_t address);
+  Status RegisterSignalCatcher(int signo);
+  Status HaltProcess();
+  Status ContinueProcess();
 };
 
 } // namespace lldb_server
 } // namespace lldb_private
 
-#endif
+#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERNATIVEPROCESS_H
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
index ec5e0cd65db10..7eb8d65d56f0d 100644
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
@@ -9,46 +9,60 @@
 #ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
 #define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
 
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
 #include "lldb/lldb-types.h"
+
 #include <functional>
+#include <optional>
 #include <stdint.h>
 #include <string>
 namespace lldb_private {
-namespace lldb_server {
-
-class LLDBServerNativeProcess;
 
-typedef std::function<std::string(const char *)>
-    CStrArgWithReturnStringCallback;
-typedef std::function<std::string()> NoArgsReturnStringCallback;
-
-// lldb-server will decode all packets and any packets and for any packets that
-// have handlers in this structure, the functions will be called. This removes
-// the need for plug-ins to have to parse the packets and args.
-struct GDBRemotePacketCallbacks {
-  // Handle any "general query packets" here.
-  // Handle the "qSupported" query.
+namespace process_gdb_remote {
+  class GDBRemoteCommunicationServerLLGS;
+}
+  
+namespace lldb_server {
 
-  CStrArgWithReturnStringCallback qSupported = nullptr;
-  NoArgsReturnStringCallback qHostInfo = nullptr;
-  NoArgsReturnStringCallback qProcessInfo = nullptr;
-  CStrArgWithReturnStringCallback qXfer = nullptr;
 
-  // Handle "vCont?" packet
-  NoArgsReturnStringCallback vContQuery = nullptr;
-};
+class LLDBServerNativeProcess;
 
 class LLDBServerPlugin {
+protected:
   // Add a version field to allow the APIs to change over time.
-  const uint32_t m_version = 1;
-  LLDBServerNativeProcess &m_process;
+  using GDBServer = process_gdb_remote::GDBRemoteCommunicationServerLLGS;
+  using Manager = NativeProcessProtocol::Manager;
+  GDBServer &m_native_process;
+  MainLoop m_main_loop;
+  std::unique_ptr<Manager> m_process_manager_up;
+  std::unique_ptr<GDBServer> m_gdb_server;
+  bool m_is_connected = false;
 
 public:
-  LLDBServerPlugin(LLDBServerNativeProcess &process) : m_process(process) {}
-
-  virtual LLDBServerPlugin() = default;
-
-  virtual void GetCallbacks(GDBRemotePacketCallbacks &callbacks);
+  using CreateCallback = llvm::function_ref<LLDBServerPlugin *()>;
+  static void RegisterCreatePlugin(CreateCallback callback);
+  static size_t GetNumCreateCallbacks();
+  static CreateCallback GetCreateCallbackAtIndex(size_t i);
+  LLDBServerPlugin(GDBServer &native_process) : 
+      m_native_process(native_process) {}
+
+  virtual ~LLDBServerPlugin();
+
+  /// Check if we are already connected.
+  bool IsConnected() const { return m_is_connected; }
+  /// Get an connection URL to connect to this plug-in.
+  ///
+  /// This function will get called each time native process stops if this
+  /// object is not connected already. If the plug-in is ready to be activated,
+  /// return a valid URL to use with "process connect" that can connect to this
+  /// plug-in. Execution should wait for a connection to be made before trying
+  /// to do any blocking code. The plug-in should assume the users do not want
+  /// to use any features unless a connection is made.
+  virtual std::optional<std::string> GetConnectionURL() {
+    return std::nullopt;
+  };
 
   /// Get a file descriptor to listen for in the ptrace epoll loop.
   ///
@@ -69,16 +83,6 @@ class LLDBServerPlugin {
   /// \param fd The file descriptor event to handle.
   virtual bool HandleEventFileDescriptorEvent(int fd) { return false; }
 
-  /// Handle a received a GDB remote packet that doesn't have a callback
-  /// specified in the GDBRemotePacketCallbacks structure after a call to
-  /// LLDBServerPlugin::GetCallbacks(...).
-  ///
-  /// \return
-  ///   The resonse packet to send. If the empty string is returned, this will
-  ///   cause an unimplemented packet ($#00) to be sent signaling this packet
-  ///   is not supported.
-  virtual std::string HandlePacket(const uint8_t *payload, size_t payload_size);
-
   /// Called when a breakpoint is hit in the native process.
   ///
   /// LLDBServerPlugin objects can set breakpoints in the native process by
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 54212ccd53545..a42489e518174 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -19,6 +19,7 @@
 
 #include "LLDBServerUtilities.h"
 #include "Plugins/PluginInterface/LLDBServerNativeProcess.h"
+#include "Plugins/MockGPU/LLDBServerPluginMockGPU.h"
 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
 #include "lldb/Host/Config.h"
@@ -48,6 +49,8 @@
 #include "Plugins/Process/Windows/Common/NativeProcessWindows.h"
 #endif
 
+#include "Plugins/MockGPU/ProcessMockGPU.h"
+
 #ifndef LLGS_PROGRAM_NAME
 #define LLGS_PROGRAM_NAME "lldb-server"
 #endif
@@ -91,35 +94,6 @@ class NativeProcessManager : public NativeProcessProtocol::Manager {
 };
 #endif
 
-class LLDBServerNativeProcessImpl : public LLDBServerNativeProcess {
-  NativeProcessProtocol *m_native_process = nullptr;
-
-public:
-  LLDBServerNativeProcessImpl(NativeProcessProtocol *native_process)
-      : m_native_process(native_process) {}
-
-  ~LLDBServerNativeProcessImpl() override {}
-  /// Set a breakpoint in the native process.
-  ///
-  /// When the breakpoints gets hit, lldb-server will call
-  /// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
-  /// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in the
-  /// native process.
-  lldb::user_id_t SetBreakpoint(lldb::addr_t address) override {
-    return LLDB_INVALID_BREAK_ID;
-  }
-
-  Status RegisterSignalCatcher(int signo) override {
-    return Status::FromErrorString("unimplemented");
-  }
-
-  Status HaltProcess() override { return m_native_process->Halt(); }
-  Status ContinueProcess() override {
-    ResumeActionList resume_actions;
-    return m_native_process->Resume(resume_actions);
-  }
-};
-
 } // namespace
 
 #ifndef _WIN32
@@ -465,7 +439,7 @@ int main_gdbserver(int argc, char *argv[]) {
   if (!LLDBServerUtilities::SetupLogging(
           log_file, log_channels,
           LLDB_LOG_OPTION_PREPEND_TIMESTAMP |
-              LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION))
+          LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
     return -1;
 
   std::vector<llvm::StringRef> Inputs;
@@ -482,11 +456,24 @@ int main_gdbserver(int argc, char *argv[]) {
 
   NativeProcessManager manager(mainloop);
   GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
-  gdb_server.SetProcessCreatedCallback([](NativeProcessProtocol *process) {
-    assert(process);
-    LLDBServerNativeProcess::SetNativeProcess(
-        new LLDBServerNativeProcessImpl(process));
+
+
+  LLDBServerPluginMockGPU mockGPUPlugin(gdb_server);
+
+  gdb_server.SetProcessStoppedCallback([&](
+      GDBRemoteCommunicationServerLLGS &gdb_server,
+      NativeProcessProtocol *native_process) -> std::string {
+    if (!mockGPUPlugin.IsConnected()) {
+      if (std::optional<std::string> opt_url = mockGPUPlugin.GetConnectionURL())
+        return opt_url.value();
+    } 
+    return "";
   });
+
+  // MockGPUProcessManager manager(mainloop);
+  // GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
+
+
   llvm::StringRef host_and_port;
   if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {
     host_and_port = Inputs.front();

>From 35a4a86fd9b9686789f88b93d4860f970db7b020 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 2 Apr 2025 10:35:55 -0700
Subject: [PATCH 03/34] Add missing file.

---
 .../PluginInterface/LLDBServerPlugin.cpp      | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp

diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp
new file mode 100644
index 0000000000000..4c60c8a8cec0d
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp
@@ -0,0 +1,32 @@
+//===-- LLDBServerPlugin.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBServerPlugin.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb_server;
+
+static std::vector<LLDBServerPlugin::CreateCallback> g_create_callbacks;
+
+void LLDBServerPlugin::RegisterCreatePlugin(CreateCallback callback) {
+  g_create_callbacks.push_back(callback);
+}
+
+size_t LLDBServerPlugin::GetNumCreateCallbacks() {
+  return g_create_callbacks.size();
+}
+
+LLDBServerPlugin::CreateCallback
+LLDBServerPlugin::GetCreateCallbackAtIndex(size_t i) {
+  if (i < g_create_callbacks.size())
+    return g_create_callbacks[i];
+  return nullptr;
+}
+
+LLDBServerPlugin::~LLDBServerPlugin() {}

>From 5c60a00672d8cebb8c6693d56ba7b6fd7fb7a2e1 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 9 Apr 2025 14:54:30 -0700
Subject: [PATCH 04/34] Moved LLDBServerPlugin into LLDB library.

This patch formalizes the plug-ins and moves the code into the LLDB
library lldbPluginProcessGDBRemote. This allows the sever objects
GDBRemoteCommunicationServerLLGS to own a list of GPU plug-ins.

Removed the process stopped callback and we now iterate over all of the
installed GPU plug-ins in the GDBRemoteCommunicationServerLLGS class
itself. This will allow more functionality to be implemented for GPU
plugins.
---
 .../lldb/Utility/StringExtractorGDBRemote.h   |  3 ++
 .../Plugins/Process/gdb-remote/CMakeLists.txt |  1 +
 .../GDBRemoteCommunicationClient.cpp          | 30 ++++++++++-
 .../gdb-remote/GDBRemoteCommunicationClient.h |  4 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      | 52 ++++++++++++++++---
 .../GDBRemoteCommunicationServerLLGS.h        | 19 +++----
 .../Process/gdb-remote}/LLDBServerPlugin.cpp  | 19 ++-----
 .../Process/gdb-remote}/LLDBServerPlugin.h    | 12 +----
 lldb/tools/lldb-server/CMakeLists.txt         |  2 +-
 lldb/tools/lldb-server/Plugins/CMakeLists.txt |  1 -
 .../Plugins/MockGPU/CMakeLists.txt            |  1 -
 .../MockGPU/LLDBServerPluginMockGPU.cpp       |  1 +
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  5 +-
 .../Plugins/PluginInterface/CMakeLists.txt    |  8 ---
 .../LLDBServerNativeProcess.cpp               | 41 ---------------
 .../PluginInterface/LLDBServerNativeProcess.h | 46 ----------------
 lldb/tools/lldb-server/lldb-gdbserver.cpp     | 19 +------
 17 files changed, 102 insertions(+), 162 deletions(-)
 rename lldb/{tools/lldb-server/Plugins/PluginInterface => source/Plugins/Process/gdb-remote}/LLDBServerPlugin.cpp (51%)
 rename lldb/{tools/lldb-server/Plugins/PluginInterface => source/Plugins/Process/gdb-remote}/LLDBServerPlugin.h (88%)
 delete mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
 delete mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
 delete mode 100644 lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h

diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index dd468ef5bddef..6b4b38ac5f0fe 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -181,6 +181,9 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_vStopped,
     eServerPacketType_vCtrlC,
     eServerPacketType_vStdio,
+
+    // GPU plug-in packets.
+    eServerPacketType_jGPUPluginInitialize
   };
 
   ServerPacketType GetServerPacketType() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt
index 6755999b18185..7cf55a7b41671 100644
--- a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt
@@ -29,6 +29,7 @@ add_lldb_library(lldbPluginProcessGDBRemote PLUGIN
   GDBRemoteCommunicationServerPlatform.cpp
   GDBRemoteRegisterContext.cpp
   GDBRemoteRegisterFallback.cpp
+  LLDBServerPlugin.cpp
   ProcessGDBRemote.cpp
   ProcessGDBRemoteLog.cpp
   ThreadGDBRemote.cpp
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 45f05483f9de1..083d68be5f401 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -365,7 +365,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
   m_x_packet_state.reset();
   m_supports_reverse_continue = eLazyBoolNo;
   m_supports_reverse_step = eLazyBoolNo;
-
+  m_supports_gpu_plugins =  eLazyBoolNo;
   m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
                                   // not, we assume no limit
 
@@ -420,10 +420,13 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
         m_uses_native_signals = eLazyBoolYes;
       else if (x == "binary-upload+")
         m_x_packet_state = xPacketState::Prefixed;
+      else if (x == "gpu-plugins")
+        m_supports_gpu_plugins = eLazyBoolYes;
       else if (x == "ReverseContinue+")
         m_supports_reverse_continue = eLazyBoolYes;
       else if (x == "ReverseStep+")
         m_supports_reverse_step = eLazyBoolYes;
+      
       // Look for a list of compressions in the features list e.g.
       // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
       // deflate,lzma
@@ -599,6 +602,31 @@ StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
   return object_sp;
 }
 
+StructuredData::ObjectSP GDBRemoteCommunicationClient::GetGPUPluginInfo() {
+  // Get JSON information containing any breakpoints and other information
+  // required by any GPU plug-ins using the "jGPUPluginInitialize" packet.
+  //
+  // GPU plug-ins might require breakpoints to be set in the native program
+  // that controls the GPUs. This packet allows the GPU plug-ins to set one or
+  // more breakpoints in the native program and get a callback when those 
+  // breakpoints get hit.
+  StructuredData::ObjectSP object_sp;
+
+  if (m_supports_gpu_plugins == eLazyBoolYes) {
+    StringExtractorGDBRemote response;
+    response.SetResponseValidatorToJSON();
+    if (SendPacketAndWaitForResponse("jGPUPluginInitialize", response) ==
+        PacketResult::Success) {
+      if (response.IsUnsupportedResponse()) {
+        m_supports_gpu_plugins = eLazyBoolNo;
+      } else if (!response.Empty()) {
+        object_sp = StructuredData::ParseJSON(response.GetStringRef());
+      }
+    }
+  }
+  return object_sp;
+}
+
 bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
   if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) {
     StringExtractorGDBRemote response;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 437155815d1ea..3220164091b07 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -433,6 +433,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   StructuredData::ObjectSP GetThreadsInfo();
 
+  StructuredData::ObjectSP GetGPUPluginInfo();
+
   bool GetThreadExtendedInfoSupported();
 
   bool GetLoadedDynamicLibrariesInfosSupported();
@@ -581,7 +583,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   std::optional<xPacketState> m_x_packet_state;
   LazyBool m_supports_reverse_continue = eLazyBoolCalculate;
   LazyBool m_supports_reverse_step = eLazyBoolCalculate;
-
+  LazyBool m_supports_gpu_plugins = eLazyBoolCalculate;
   bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
       m_supports_qUserName : 1, m_supports_qGroupName : 1,
       m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1,
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 39d04c032678d..7295705fad369 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -17,6 +17,7 @@
 #include <thread>
 
 #include "GDBRemoteCommunicationServerLLGS.h"
+#include "LLDBServerPlugin.h"
 #include "lldb/Host/ConnectionFileDescriptor.h"
 #include "lldb/Host/Debug.h"
 #include "lldb/Host/File.h"
@@ -253,7 +254,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_vCtrlC,
       &GDBRemoteCommunicationServerLLGS::Handle_vCtrlC);
-}
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jGPUPluginInitialize,
+      &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize);
+  }
 
 void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(
     const ProcessLaunchInfo &info) {
@@ -1090,12 +1094,13 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
   Log *log = GetLog(LLDBLog::Process);
   LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
 
+  // Check if any plug-ins have new connections
   std::string extra_stop_reply_args;
-  // Call the state changed callback
-  if (m_process_stopped_callback) {
-    extra_stop_reply_args = m_process_stopped_callback(*this, process);
-    // LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called, m_process_stopped_callback returned \"%s\"", __FUNCTION__, extra_stop_reply_args.str().c_str());
-
+  for (auto &plugin_up : m_plugins) {
+    if (!plugin_up->IsConnected()) {
+      if (std::optional<std::string> opt_url = plugin_up->GetConnectionURL())
+        extra_stop_reply_args += opt_url.value();
+    } 
   }
 
   PacketResult result = SendStopReasonForState(
@@ -3678,6 +3683,35 @@ GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo(
   return SendPacketNoLock(escaped_response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize(
+    StringExtractorGDBRemote &) {
+
+  // // Ensure we have a debugged process.
+  // if (!m_current_process ||
+  //     (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
+  //     return SendErrorResponse(llvm::make_error<StringError>(
+  //       inconvertibleErrorCode(), "No current process and no PID provided"));
+
+  // StreamString response;
+  // const bool threads_with_valid_stop_info_only = false;
+  // llvm::Expected<json::Value> threads_info =
+  //     GetJSONThreadsInfo(*m_current_process, threads_with_valid_stop_info_only);
+  // if (!threads_info) {
+  //   LLDB_LOG_ERROR(log, threads_info.takeError(),
+  //                  "failed to prepare a packet for pid {1}: {0}",
+  //                  m_current_process->GetID());
+  //   return SendErrorResponse(52);
+  // }
+
+  // response.AsRawOstream() << *threads_info;
+  // StreamGDBRemote escaped_response;
+  // escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+  // return SendPacketNoLock(escaped_response.GetString());
+  return SendErrorResponse(99);
+}
+
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo(
     StringExtractorGDBRemote &packet) {
@@ -4246,6 +4280,7 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
                             "QListThreadsInStopReply+",
                             "qXfer:features:read+",
                             "QNonStop+",
+                            "gpu-plugins"
                         });
 
   // report server-only features
@@ -4354,3 +4389,8 @@ 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<lldb_server::LLDBServerPlugin> plugin_up) {
+  m_plugins.emplace_back(std::move(plugin_up));
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 564b6784ad8d6..09d79d9090cf9 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -25,6 +25,10 @@ class StringExtractorGDBRemote;
 
 namespace lldb_private {
 
+namespace lldb_server {
+  class LLDBServerPlugin;
+}
+
 namespace process_gdb_remote {
 
 class ProcessGDBRemote;
@@ -85,16 +89,7 @@ class GDBRemoteCommunicationServerLLGS
 
   Status InitializeConnection(std::unique_ptr<Connection> connection);
 
-  using ProcessStoppedChangedCallback = 
-      std::function<std::string (GDBRemoteCommunicationServerLLGS &server,
-                                 NativeProcessProtocol *process)>;
-
-  bool SetProcessStoppedCallback(ProcessStoppedChangedCallback callback) {
-    if (m_process_stopped_callback)
-      return false;
-    m_process_stopped_callback = callback;
-    return true;
-  }
+  void InstallPlugin(std::unique_ptr<lldb_server::LLDBServerPlugin> plugin_up);
 
   struct DebuggedProcess {
     enum class Flag {
@@ -134,7 +129,7 @@ class GDBRemoteCommunicationServerLLGS
 
   NativeProcessProtocol::Extension m_extensions_supported = {};
 
-  ProcessStoppedChangedCallback m_process_stopped_callback = nullptr;
+  std::vector<std::unique_ptr<lldb_server::LLDBServerPlugin>> m_plugins;
 
   // Typically we would use a SmallVector for this data but in this context we
   // don't know how much data we're recieving so we would have to heap allocate
@@ -285,6 +280,8 @@ class GDBRemoteCommunicationServerLLGS
 
   PacketResult Handle_T(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jGPUPluginInitialize(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
similarity index 51%
rename from lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp
rename to lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
index 4c60c8a8cec0d..72a5eb629be7a 100644
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
@@ -7,26 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "LLDBServerPlugin.h"
-#include <vector>
+#include "GDBRemoteCommunicationServerLLGS.h"
 
 using namespace lldb_private;
 using namespace lldb_server;
 
-static std::vector<LLDBServerPlugin::CreateCallback> g_create_callbacks;
 
-void LLDBServerPlugin::RegisterCreatePlugin(CreateCallback callback) {
-  g_create_callbacks.push_back(callback);
-}
-
-size_t LLDBServerPlugin::GetNumCreateCallbacks() {
-  return g_create_callbacks.size();
-}
-
-LLDBServerPlugin::CreateCallback
-LLDBServerPlugin::GetCreateCallbackAtIndex(size_t i) {
-  if (i < g_create_callbacks.size())
-    return g_create_callbacks[i];
-  return nullptr;
-}
+LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process) :
+    m_native_process(native_process) {}
 
 LLDBServerPlugin::~LLDBServerPlugin() {}
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
similarity index 88%
rename from lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
rename to lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index 7eb8d65d56f0d..831967cd91ec2 100644
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -9,7 +9,6 @@
 #ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
 #define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
 
-#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
 #include "lldb/Host/MainLoop.h"
 #include "lldb/Host/common/NativeProcessProtocol.h"
 #include "lldb/lldb-types.h"
@@ -26,9 +25,6 @@ namespace process_gdb_remote {
   
 namespace lldb_server {
 
-
-class LLDBServerNativeProcess;
-
 class LLDBServerPlugin {
 protected:
   // Add a version field to allow the APIs to change over time.
@@ -41,13 +37,7 @@ class LLDBServerPlugin {
   bool m_is_connected = false;
 
 public:
-  using CreateCallback = llvm::function_ref<LLDBServerPlugin *()>;
-  static void RegisterCreatePlugin(CreateCallback callback);
-  static size_t GetNumCreateCallbacks();
-  static CreateCallback GetCreateCallbackAtIndex(size_t i);
-  LLDBServerPlugin(GDBServer &native_process) : 
-      m_native_process(native_process) {}
-
+  LLDBServerPlugin(GDBServer &native_process);
   virtual ~LLDBServerPlugin();
 
   /// Check if we are already connected.
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index b20c8990c5ce9..b842e8fd8632e 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -60,7 +60,7 @@ add_lldb_tool(lldb-server
       lldbPluginInstructionMIPS64
       lldbPluginInstructionRISCV
       ${LLDB_SYSTEM_LIBS}
-      lldbServerPluginInterface
+      lldbServerPluginMockGPU
 
     LINK_COMPONENTS
       Option
diff --git a/lldb/tools/lldb-server/Plugins/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
index a0275332a32ce..45f42a7afd1fa 100644
--- a/lldb/tools/lldb-server/Plugins/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
@@ -1,2 +1 @@
 add_subdirectory(MockGPU)
-add_subdirectory(PluginInterface)
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
index 0b827f3c027b3..6edfdc259ac67 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/CMakeLists.txt
@@ -6,5 +6,4 @@ add_lldb_library(lldbServerPluginMockGPU
   ThreadMockGPU.cpp
 )
 
-target_link_libraries(lldbServerPluginMockGPU lldbServerPluginInterface)
 target_include_directories(lldbServerPluginMockGPU PRIVATE "${LLDB_SOURCE_DIR}/source" "../..")
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index cc153f6a816a8..bac89a43f260a 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -11,6 +11,7 @@
 #include "lldb/Host/common/TCPSocket.h"
 #include "llvm/Support/Error.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
 #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
 
 #include <sys/socket.h>
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index 67cfc45480ee1..2a1c9e752bb3d 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -9,10 +9,13 @@
 #ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINMOCKGPU_H
 #define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINMOCKGPU_H
 
-#include "Plugins/PluginInterface/LLDBServerPlugin.h"
+#include "Plugins/Process/gdb-remote/LLDBServerPlugin.h"
 #include "lldb/Utility/Status.h"
 
 namespace lldb_private {
+  
+  class TCPSocket;
+
 namespace lldb_server {
 
 class LLDBServerPluginMockGPU : public lldb_private::lldb_server::LLDBServerPlugin {
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
deleted file mode 100644
index 8eb4cfddec53b..0000000000000
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-add_lldb_library(lldbServerPluginInterface
-  LLDBServerNativeProcess.cpp
-  LLDBServerPlugin.cpp
-)
-
-target_link_libraries(lldbServerPluginInterface lldbServerPluginMockGPU)
-target_include_directories(lldbServerPluginInterface PRIVATE "${LLDB_SOURCE_DIR}/source")
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
deleted file mode 100644
index 85c318acc17d6..0000000000000
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===-- LLDBServerNativeProcess.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.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "LLDBServerNativeProcess.h"
-#include "lldb/Host/common/NativeProcessProtocol.h"
-
-using namespace lldb_private;
-using namespace lldb_server;
-
-LLDBServerNativeProcess::LLDBServerNativeProcess(
-    NativeProcessProtocol *native_process)
-    : m_native_process(native_process) {}
-
-LLDBServerNativeProcess::~LLDBServerNativeProcess() {}
-/// Set a breakpoint in the native process.
-///
-/// When the breakpoints gets hit, lldb-server will call
-/// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
-/// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in the
-/// native process.
-lldb::user_id_t LLDBServerNativeProcess::SetBreakpoint(lldb::addr_t address) {
-  return LLDB_INVALID_BREAK_ID;
-}
-
-Status LLDBServerNativeProcess::RegisterSignalCatcher(int signo) {
-  return Status::FromErrorString("unimplemented");
-}
-
-Status LLDBServerNativeProcess::HaltProcess() {
-  return m_native_process->Halt();
-}
-
-Status LLDBServerNativeProcess::ContinueProcess() {
-  ResumeActionList resume_actions;
-  return m_native_process->Resume(resume_actions);
-}
diff --git a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h b/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
deleted file mode 100644
index d40139892cde6..0000000000000
--- a/lldb/tools/lldb-server/Plugins/PluginInterface/LLDBServerNativeProcess.h
+++ /dev/null
@@ -1,46 +0,0 @@
-//===-- LLDBServerNativeProcess.h -------------------------------*- C++ -*-===//
-//
-// 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_LLDBSERVERNATIVEPROCESS_H
-#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERNATIVEPROCESS_H
-
-#include "lldb/Utility/Status.h"
-#include "lldb/lldb-types.h"
-#include <memory>
-
-namespace lldb_private {
-
-class NativeProcessProtocol;
-
-namespace lldb_server {
-
-/// A class that interfaces back to the lldb-server native process for
-/// LLDBServerNativeProcess objects.
-class LLDBServerNativeProcess {
-  NativeProcessProtocol *m_native_process;
-
-public:
-  LLDBServerNativeProcess(NativeProcessProtocol *native_process);
-  ~LLDBServerNativeProcess();
-
-  /// Set a breakpoint in the native process.
-  ///
-  /// When the breakpoints gets hit, lldb-server will call
-  /// LLDBServerPlugin::BreakpointWasHit with this address. This will allow
-  /// LLDBServerPlugin plugins to synchronously handle a breakpoint hit in
-  /// the native process.
-  lldb::user_id_t SetBreakpoint(lldb::addr_t address);
-  Status RegisterSignalCatcher(int signo);
-  Status HaltProcess();
-  Status ContinueProcess();
-};
-
-} // namespace lldb_server
-} // namespace lldb_private
-
-#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERNATIVEPROCESS_H
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index a42489e518174..caebc98b43928 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -18,7 +18,6 @@
 #endif
 
 #include "LLDBServerUtilities.h"
-#include "Plugins/PluginInterface/LLDBServerNativeProcess.h"
 #include "Plugins/MockGPU/LLDBServerPluginMockGPU.h"
 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
@@ -457,22 +456,8 @@ int main_gdbserver(int argc, char *argv[]) {
   NativeProcessManager manager(mainloop);
   GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
 
-
-  LLDBServerPluginMockGPU mockGPUPlugin(gdb_server);
-
-  gdb_server.SetProcessStoppedCallback([&](
-      GDBRemoteCommunicationServerLLGS &gdb_server,
-      NativeProcessProtocol *native_process) -> std::string {
-    if (!mockGPUPlugin.IsConnected()) {
-      if (std::optional<std::string> opt_url = mockGPUPlugin.GetConnectionURL())
-        return opt_url.value();
-    } 
-    return "";
-  });
-
-  // MockGPUProcessManager manager(mainloop);
-  // GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
-
+  // Install the mock GPU plugin.
+  gdb_server.InstallPlugin(std::make_unique<LLDBServerPluginMockGPU>(gdb_server));
 
   llvm::StringRef host_and_port;
   if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {

>From cf005a895ed4bca7154110e61157cf6830b78a97 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Fri, 11 Apr 2025 01:53:45 -0700
Subject: [PATCH 05/34] Flesh out plugin system.

GPU plugins can now intialize a GPUPluginInfo info structure in
LLDBServerPlugin::InitializePluginInfo() by overriding this function.
This info will be sent to LLDB. LLDB will now set any breakpoints that
are requested in this structure and the plugins can now specify a
breakpoint by name with a shared library basename and specify any
symbols that are desired when the breakpoint gets hit. When the
breakpoints get hit, the function:
void LLDBServerPlugin::BreakpointWasHit(GPUPluginBreakpointHitArgs &args)
will get called with any symbol values that were requested.
---
 .../lldb/Utility/GPUGDBRemotePackets.h        | 67 ++++++++++++++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  3 +-
 .../GDBRemoteCommunicationClient.cpp          | 30 +++++-
 .../gdb-remote/GDBRemoteCommunicationClient.h |  8 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      | 61 +++++++-----
 .../GDBRemoteCommunicationServerLLGS.h        |  2 +
 .../Process/gdb-remote/LLDBServerPlugin.cpp   |  8 +-
 .../Process/gdb-remote/LLDBServerPlugin.h     | 33 ++++++-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 92 ++++++++++++++++++-
 .../Process/gdb-remote/ProcessGDBRemote.h     |  9 ++
 lldb/source/Utility/CMakeLists.txt            |  1 +
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 78 ++++++++++++++++
 .../Utility/StringExtractorGDBRemote.cpp      |  5 +-
 .../MockGPU/LLDBServerPluginMockGPU.cpp       | 36 ++++++++
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  3 +
 15 files changed, 403 insertions(+), 33 deletions(-)
 create mode 100644 lldb/include/lldb/Utility/GPUGDBRemotePackets.h
 create mode 100644 lldb/source/Utility/GPUGDBRemotePackets.cpp

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
new file mode 100644
index 0000000000000..1a481e23e73be
--- /dev/null
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -0,0 +1,67 @@
+//===-- GPUGDBRemotePackets.h -----------------------------------*- C++ -*-===//
+//
+// 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_GPUGDBREMOTEPACKETS_H
+#define LLDB_UTILITY_GPUGDBREMOTEPACKETS_H
+
+#include "llvm/Support/JSON.h"
+#include <string>
+#include <vector>
+
+/// See docs/lldb-gdb-remote.txt for more information.
+namespace lldb_private {
+
+/// A class that represents a symbol value
+struct SymbolValue {
+  std::string name;
+  uint64_t value;
+};
+bool fromJSON(const llvm::json::Value &value, SymbolValue &info, 
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const SymbolValue &packet);
+
+struct GPUBreakpointInfo {
+  std::string identifier;
+  std::string shlib;
+  std::string function_name;
+  /// Names of symbols that should be supplied when the breakpoint is hit.
+  std::vector<std::string> symbol_names;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &info,
+  llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUBreakpointInfo &packet);
+
+
+struct GPUPluginInfo {
+  std::string name;
+  std::string description;
+  std::vector<GPUBreakpointInfo> breakpoints;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &info,
+  llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUPluginInfo &packet);
+
+struct GPUPluginBreakpointHitArgs {
+  std::string plugin_name;
+  GPUBreakpointInfo breakpoint;
+  std::vector<SymbolValue> symbol_values;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &info,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUPluginBreakpointHitArgs &packet);
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_GPUGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 6b4b38ac5f0fe..a1894a3939593 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -183,7 +183,8 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_vStdio,
 
     // GPU plug-in packets.
-    eServerPacketType_jGPUPluginInitialize
+    eServerPacketType_jGPUPluginInitialize,
+    eServerPacketType_jGPUPluginBreakpointHit
   };
 
   ServerPacketType GetServerPacketType() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 083d68be5f401..b603638ff320e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -34,8 +34,10 @@
 #include "ProcessGDBRemote.h"
 #include "ProcessGDBRemoteLog.h"
 #include "lldb/Host/Config.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/StringExtractorGDBRemote.h"
 
+
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB
@@ -602,7 +604,8 @@ StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
   return object_sp;
 }
 
-StructuredData::ObjectSP GDBRemoteCommunicationClient::GetGPUPluginInfo() {
+std::optional<std::vector<GPUPluginInfo>> 
+GDBRemoteCommunicationClient::GetGPUPluginInfos() {
   // Get JSON information containing any breakpoints and other information
   // required by any GPU plug-ins using the "jGPUPluginInitialize" packet.
   //
@@ -610,7 +613,6 @@ StructuredData::ObjectSP GDBRemoteCommunicationClient::GetGPUPluginInfo() {
   // that controls the GPUs. This packet allows the GPU plug-ins to set one or
   // more breakpoints in the native program and get a callback when those 
   // breakpoints get hit.
-  StructuredData::ObjectSP object_sp;
 
   if (m_supports_gpu_plugins == eLazyBoolYes) {
     StringExtractorGDBRemote response;
@@ -620,11 +622,31 @@ StructuredData::ObjectSP GDBRemoteCommunicationClient::GetGPUPluginInfo() {
       if (response.IsUnsupportedResponse()) {
         m_supports_gpu_plugins = eLazyBoolNo;
       } else if (!response.Empty()) {
-        object_sp = StructuredData::ParseJSON(response.GetStringRef());
+        if (llvm::Expected<std::vector<GPUPluginInfo>> info = 
+                llvm::json::parse<std::vector<GPUPluginInfo>>(response.Peek(), 
+                                                              "GPUPluginInfo"))
+          return std::move(*info);
+        else
+          llvm::consumeError(info.takeError());
       }
     }
   }
-  return object_sp;
+  return std::nullopt;
+}
+
+bool GDBRemoteCommunicationClient::GPUBreakpointHit(
+  const GPUPluginBreakpointHitArgs &args) {
+  StreamGDBRemote packet;
+  packet.PutCString("jGPUPluginBreakpointHit:");
+  std::string json_string;
+  llvm::raw_string_ostream os(json_string);
+  os << toJSON(args);
+  packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
+      PacketResult::Success)
+    return response.IsOKResponse();
+  return false;
 }
 
 bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 3220164091b07..fe9e662adac25 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -33,6 +33,10 @@
 #include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
+
+  struct GPUPluginBreakpointHitArgs;
+  struct GPUPluginInfo;
+
 namespace process_gdb_remote {
 
 /// The offsets used by the target when relocating the executable. Decoded from
@@ -433,7 +437,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   StructuredData::ObjectSP GetThreadsInfo();
 
-  StructuredData::ObjectSP GetGPUPluginInfo();
+  std::optional<std::vector<GPUPluginInfo>> GetGPUPluginInfos();
+
+  bool GPUBreakpointHit(const GPUPluginBreakpointHitArgs &args);
 
   bool GetThreadExtendedInfoSupported();
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 7295705fad369..ff1793a936f23 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -35,6 +35,7 @@
 #include "lldb/Utility/DataBuffer.h"
 #include "lldb/Utility/Endian.h"
 #include "lldb/Utility/GDBRemote.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/LLDBAssert.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
@@ -257,7 +258,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jGPUPluginInitialize,
       &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize);
-  }
+      RegisterMemberFunctionHandler(
+        StringExtractorGDBRemote::eServerPacketType_jGPUPluginBreakpointHit,
+        &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit);
+    }
 
 void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(
     const ProcessLaunchInfo &info) {
@@ -3686,29 +3690,42 @@ GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo(
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize(
     StringExtractorGDBRemote &) {
+  std::vector<GPUPluginInfo> infos;
+  std::string json_string;
+  llvm::raw_string_ostream os(json_string);
+  bool first = true;
+  os << "[";
+  for (auto &plugin_up: m_plugins) {
+    if (first)
+      first = false;
+    else
+      os << ",";
+    os << toJSON(plugin_up->GetPluginInfo());
+  }
+  os << "]";
+  StreamGDBRemote response;
+  response.PutEscapedBytes(json_string.data(), json_string.size());
+  return SendPacketNoLock(response.GetString());
+}
 
-  // // Ensure we have a debugged process.
-  // if (!m_current_process ||
-  //     (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
-  //     return SendErrorResponse(llvm::make_error<StringError>(
-  //       inconvertibleErrorCode(), "No current process and no PID provided"));
-
-  // StreamString response;
-  // const bool threads_with_valid_stop_info_only = false;
-  // llvm::Expected<json::Value> threads_info =
-  //     GetJSONThreadsInfo(*m_current_process, threads_with_valid_stop_info_only);
-  // if (!threads_info) {
-  //   LLDB_LOG_ERROR(log, threads_info.takeError(),
-  //                  "failed to prepare a packet for pid {1}: {0}",
-  //                  m_current_process->GetID());
-  //   return SendErrorResponse(52);
-  // }
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit(
+    StringExtractorGDBRemote &packet) {
+
+  packet.ConsumeFront("jGPUPluginBreakpointHit:");
+  Expected<GPUPluginBreakpointHitArgs> args =
+      json::parse<GPUPluginBreakpointHitArgs>(packet.Peek(), 
+                                              "GPUPluginBreakpointHitArgs");
+  if (!args)
+    return SendErrorResponse(args.takeError());
 
-  // response.AsRawOstream() << *threads_info;
-  // StreamGDBRemote escaped_response;
-  // escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
-  // return SendPacketNoLock(escaped_response.GetString());
-  return SendErrorResponse(99);
+  for (auto &plugin_up: m_plugins) {
+    if (plugin_up->GetPluginName() == args->plugin_name) {
+      plugin_up->BreakpointWasHit(*args);
+      return SendOKResponse();
+    }
+  }
+  return SendErrorResponse(Status::FromErrorString("Invalid plugin name."));
 }
 
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 09d79d9090cf9..ffb6cc542ed58 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -281,6 +281,8 @@ class GDBRemoteCommunicationServerLLGS
   PacketResult Handle_T(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_jGPUPluginInitialize(StringExtractorGDBRemote &packet);
+  
+  PacketResult Handle_jGPUPluginBreakpointHit(StringExtractorGDBRemote &packet);
 
   void SetCurrentThreadID(lldb::tid_t tid);
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
index 72a5eb629be7a..4f4072082160b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
@@ -14,6 +14,12 @@ using namespace lldb_server;
 
 
 LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process) :
-    m_native_process(native_process) {}
+  m_native_process(native_process) {}
 
 LLDBServerPlugin::~LLDBServerPlugin() {}
+
+const GPUPluginInfo &LLDBServerPlugin::GetPluginInfo() {
+  if (m_info.name.empty())
+    InitializePluginInfo();
+  return m_info;
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index 831967cd91ec2..e7037c2f22172 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -11,7 +11,9 @@
 
 #include "lldb/Host/MainLoop.h"
 #include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/lldb-types.h"
+#include "llvm/Support/JSON.h"
 
 #include <functional>
 #include <optional>
@@ -42,6 +44,9 @@ class LLDBServerPlugin {
 
   /// Check if we are already connected.
   bool IsConnected() const { return m_is_connected; }
+
+  virtual llvm::StringRef GetPluginName() = 0;
+
   /// Get an connection URL to connect to this plug-in.
   ///
   /// This function will get called each time native process stops if this
@@ -54,6 +59,29 @@ class LLDBServerPlugin {
     return std::nullopt;
   };
 
+  /// Get the GPU plug-in information.
+  ///
+  /// Each GPU plugin can return a structure that describes the GPU plug-in and 
+  /// the breakpoints it requires in the native process. GPU plug-ins might want
+  /// to set breakpoints in the native process to know when the GPU has been 
+  /// initialized, or when the GPU has shared libraries that get loaded.
+  /// They can do this by populating the LLDBServerPlugin::m_info structure 
+  /// when this function gets called. The contents of this structure will be
+  /// converted to JSON and sent to the LLDB client. The structure allows 
+  /// plug-ins to set breakpoints by name and can also request symbol values
+  /// that should be sent when the breakpoint gets hit.
+  /// When the breakpoint is hit, the LLDBServerPlugin::BreakpointWasHit(...)
+  /// method will get called with a structure that identifies the plugin,
+  /// breakpoint and it will supply any requested symbol values.
+  virtual void InitializePluginInfo() = 0;
+
+  /// Get the plugin info.
+  ///
+  /// This function will call InitializePluginInfo() one time to intialize the
+  /// information. Plug-ins must override InitializePluginInfo() and fill in
+  /// the structure in the LLDBServerPlugin::m_info instance variable.
+  const GPUPluginInfo &GetPluginInfo();
+
   /// Get a file descriptor to listen for in the ptrace epoll loop.
   ///
   /// When polling for process ptrace events, plug-ins can supply extra file
@@ -79,7 +107,10 @@ class LLDBServerPlugin {
   /// calling m_process.SetBreakpoint(...) to help implement funcionality,
   /// such as dynamic library loading in GPUs or to synchronize in any other
   /// way with the native process.
-  virtual void BreakpointWasHit(lldb::addr_t address) {}
+  virtual void BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {}
+
+  protected:
+    GPUPluginInfo m_info;
 };
 
 } // namespace lldb_server
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index a83972f55692c..139c5ab51b807 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -23,6 +23,7 @@
 #include <ctime>
 #include <sys/types.h>
 
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
 #include "lldb/Breakpoint/Watchpoint.h"
 #include "lldb/Breakpoint/WatchpointAlgorithms.h"
 #include "lldb/Breakpoint/WatchpointResource.h"
@@ -83,8 +84,10 @@
 #include "ProcessGDBRemoteLog.h"
 #include "ThreadGDBRemote.h"
 #include "lldb/Host/Host.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/StringExtractorGDBRemote.h"
 
+
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringSwitch.h"
@@ -612,7 +615,6 @@ Status ProcessGDBRemote::WillLaunchOrAttach() {
   m_stdio_communication.Clear();
   return error;
 }
-
 // Process Control
 Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
                                   ProcessLaunchInfo &launch_info) {
@@ -805,6 +807,60 @@ Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
   return error;
 }
 
+class GPUBreakpointCallbackBaton : public TypedBaton<GPUPluginBreakpointHitArgs> {
+  public:
+  explicit GPUBreakpointCallbackBaton(std::unique_ptr<GPUPluginBreakpointHitArgs> Data)
+  : TypedBaton(std::move(Data)) {}
+};
+
+typedef std::shared_ptr<GPUBreakpointCallbackBaton> GPUBreakpointCallbackBatonSP;
+
+bool ProcessGDBRemote::GPUBreakpointHitCallback(
+    void *baton, 
+    StoppointCallbackContext *context,
+    lldb::user_id_t break_id, 
+    lldb::user_id_t break_loc_id) {
+  ProcessSP process_sp = context->exe_ctx_ref.GetProcessSP();
+  ProcessGDBRemote *process = static_cast<ProcessGDBRemote *>(process_sp.get());
+  return process->GPUBreakpointHit(baton, context, break_id, break_loc_id);
+}
+
+bool ProcessGDBRemote::GPUBreakpointHit(void *baton, 
+                                        StoppointCallbackContext *context,
+                                        lldb::user_id_t break_id, 
+                                        lldb::user_id_t break_loc_id) {
+  GPUPluginBreakpointHitArgs *callback_data = 
+      static_cast<GPUPluginBreakpointHitArgs *>(baton);
+  // Make a copy of the args so we can fill in any needed symbol values prior
+  // to notifying lldb-server.
+  GPUPluginBreakpointHitArgs args = *callback_data;
+  Target &target = GetTarget();
+  const size_t num_symbols = args.breakpoint.symbol_names.size();
+  if (num_symbols > 0) {
+    args.symbol_values.resize(num_symbols);
+    for (size_t i=0; i<num_symbols; ++i) {
+      const auto &symbol_name = args.breakpoint.symbol_names[i];
+      SymbolContextList sc_list;
+      target.GetImages().FindSymbolsWithNameAndType(ConstString(symbol_name),
+                                                    lldb::eSymbolTypeAny,
+                                                    sc_list);
+      SymbolContext sc;
+      args.symbol_values[i].name = symbol_name;
+      args.symbol_values[i].value = LLDB_INVALID_ADDRESS;
+      size_t num_symbols = sc_list.GetSize();
+      for (size_t sc_idx=0; sc_idx<num_symbols; ++sc_idx) {
+        if (sc_list.GetContextAtIndex(sc_idx, sc)) {
+          args.symbol_values[i].value = sc.symbol->GetAddress().GetLoadAddress(&target);
+          if (args.symbol_values[i].value != LLDB_INVALID_ADDRESS)
+            break;
+        }
+      }
+    }
+  }
+  m_gdb_comm.GPUBreakpointHit(args);
+  return false; // Don't stop, auto continue.
+}
+
 Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
   Status error;
   // Only connect if we have a valid connect URL
@@ -858,7 +914,39 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
   m_gdb_comm.GetVContSupported('c');
   m_gdb_comm.GetVAttachOrWaitSupported();
   m_gdb_comm.EnableErrorStringInPacket();
-
+  Target &target = GetTarget();
+  if (auto infos = m_gdb_comm.GetGPUPluginInfos()) {
+    for (const auto &info: *infos) {
+      for (const auto &bp_info: info.breakpoints) {
+        auto args_up = std::make_unique<GPUPluginBreakpointHitArgs>();
+        args_up->plugin_name = info.name;
+        args_up->breakpoint = bp_info;
+        FileSpecList bp_modules;
+        if (!bp_info.shlib.empty())
+          bp_modules.Append(FileSpec(bp_info.shlib, 
+                                     llvm::sys::path::Style::native));
+        BreakpointSP bp_sp = target.CreateBreakpoint(
+            &bp_modules, // Containing modules.
+            nullptr, // Containing source files.
+            bp_info.function_name.c_str(), // Function name.
+            eFunctionNameTypeFull, // Function name type.
+            eLanguageTypeUnknown, // Language type
+            0, // Byte offset.
+            eLazyBoolNo, // Skip prologue.
+            true, // Internal breakpoint.
+            false); // Request hardware.
+        if (bp_sp) {
+          // Create some JSON we can send back to the lldb-server
+          // that identifies the plug-in and the breakpoint for when
+          // the breakpoint gets hit.
+          auto baton_sp = 
+              std::make_shared<GPUBreakpointCallbackBaton>(std::move(args_up));
+          bp_sp->SetCallback(GPUBreakpointHitCallback, baton_sp,
+                             /*is_synchronous=*/true);
+        }
+      }
+    }
+  }
   // First dispatch any commands from the platform:
   auto handle_cmds = [&] (const Args &args) ->  void {
     for (const Args::ArgEntry &entry : args) {
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 1cbd1e82b381d..8502d16ff0837 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -282,6 +282,7 @@ class ProcessGDBRemote : public Process,
                                               // registers and memory for all
                                               // threads if "jThreadsInfo"
                                               // packet is supported
+  StructuredData::ObjectSP m_plugin_metadata_sp;
   tid_collection m_continue_c_tids;           // 'c' for continue
   tid_sig_collection m_continue_C_tids;       // 'C' for continue with signal
   tid_collection m_continue_s_tids;           // 's' for step
@@ -436,6 +437,14 @@ class ProcessGDBRemote : public Process,
                                            lldb::user_id_t break_id,
                                            lldb::user_id_t break_loc_id);
 
+  static bool GPUBreakpointHitCallback(void *baton,
+                                       StoppointCallbackContext *context,
+                                       lldb::user_id_t break_id, 
+                                       lldb::user_id_t break_loc_id);
+                    
+  bool GPUBreakpointHit(void *baton, StoppointCallbackContext *context, 
+                        lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+                    
   // ContinueDelegate interface
   void HandleAsyncStdout(llvm::StringRef out) override;
   void HandleAsyncMisc(llvm::StringRef data) override;
diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt
index 6954a2508ffe1..12ecc68eed7c3 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -45,6 +45,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
   FileSpec.cpp
   FileSpecList.cpp
   GDBRemote.cpp
+  GPUGDBRemotePackets.cpp
   IOObject.cpp
   LLDBAssert.cpp
   LLDBLog.cpp
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
new file mode 100644
index 0000000000000..3fd43dce7fa12
--- /dev/null
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -0,0 +1,78 @@
+//===-- GPUGDBRemotePackets.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/GPUGDBRemotePackets.h"
+
+using namespace llvm;
+using namespace llvm::json;
+
+namespace lldb_private {
+
+bool fromJSON(const json::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 json::Value(Object{{"name", data.name}, {"value", data.value}});
+}
+
+
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("identifier", data.identifier) &&
+         o.map("shlib", data.shlib) &&
+         o.map("function_name", data.function_name) &&
+         o.map("symbol_names", data.symbol_names);
+}
+
+llvm::json::Value toJSON(const GPUBreakpointInfo &data) {
+  return json::Value(
+    Object{{"identifier", data.identifier}, 
+           {"shlib", data.shlib},
+           {"function_name", data.function_name},
+           {"symbol_names", data.symbol_names},
+          });
+}
+
+bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("name", data.name) &&
+         o.map("description", data.description) &&
+         o.map("breakpoints", data.breakpoints);      
+}
+
+llvm::json::Value toJSON(const GPUPluginInfo &data) {
+  return json::Value(
+    Object{{"name", data.name}, 
+           {"description", data.description},
+           {"breakpoints", data.breakpoints},
+          });
+}
+
+bool fromJSON(const json::Value &value, GPUPluginBreakpointHitArgs &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 GPUPluginBreakpointHitArgs &data) {
+  return json::Value(
+      Object{{"plugin_name", data.plugin_name}, 
+             {"breakpoint", data.breakpoint},
+             {"symbol_values", data.symbol_values},
+            });
+}
+} // namespace lldb_private
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index c5755b2733605..f2b7053cd0d45 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -320,7 +320,10 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jSignalsInfo;
     if (PACKET_MATCHES("jThreadsInfo"))
       return eServerPacketType_jThreadsInfo;
-
+    if (PACKET_MATCHES("jGPUPluginInitialize"))
+      return eServerPacketType_jGPUPluginInitialize;
+    if (PACKET_STARTS_WITH("jGPUPluginBreakpointHit:"))
+      return eServerPacketType_jGPUPluginBreakpointHit;
     if (PACKET_MATCHES("jLLDBTraceSupported"))
       return eServerPacketType_jLLDBTraceSupported;
     if (PACKET_STARTS_WITH("jLLDBTraceStop:"))
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index bac89a43f260a..0d69d35f506a9 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -57,6 +57,10 @@ LLDBServerPluginMockGPU::~LLDBServerPluginMockGPU() {
   CloseFDs();
 }
 
+llvm::StringRef LLDBServerPluginMockGPU::GetPluginName() {
+  return "mock-gpu";
+}
+
 void LLDBServerPluginMockGPU::CloseFDs() {
   if (m_fds[0] != -1) {
     close(m_fds[0]);
@@ -170,3 +174,35 @@ std::optional<std::string> LLDBServerPluginMockGPU::GetConnectionURL() {
   }
   return std::nullopt;
 }
+
+void LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  std::string json_string;
+  llvm::raw_string_ostream os(json_string);
+  os << toJSON(args);
+
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\")", 
+            json_string.c_str());
+}
+
+void LLDBServerPluginMockGPU::InitializePluginInfo() {
+  m_info.name = GetPluginName();
+  m_info.description = "Mock GPU plugin";
+  
+  GPUBreakpointInfo bp1;
+  bp1.identifier = "gpu_initialize";
+  bp1.shlib = "a.out";
+  bp1.function_name = "gpu_initialize";
+  bp1.symbol_names.push_back("printf");
+  bp1.symbol_names.push_back("puts");
+
+  GPUBreakpointInfo bp2;
+  bp2.identifier = "gpu_shlib_load";
+  bp2.shlib = "a.out";
+  bp2.function_name = "gpu_shlib_load";
+  bp2.symbol_names.push_back("g_shlib_list");
+  bp2.symbol_names.push_back("invalid_symbol");
+
+  m_info.breakpoints.emplace_back(std::move(bp1));
+  m_info.breakpoints.emplace_back(std::move(bp2));
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index 2a1c9e752bb3d..b57f0ae35a999 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -22,9 +22,12 @@ class LLDBServerPluginMockGPU : public lldb_private::lldb_server::LLDBServerPlug
 public:
   LLDBServerPluginMockGPU(lldb_private::lldb_server::LLDBServerPlugin::GDBServer &native_process);
   ~LLDBServerPluginMockGPU() override;
+  llvm::StringRef GetPluginName() override;
   int GetEventFileDescriptorAtIndex(size_t idx) override;
   bool HandleEventFileDescriptorEvent(int fd) override;
   std::optional<std::string> GetConnectionURL() override;
+  void InitializePluginInfo() override;
+  void BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
 
 private:
   void CloseFDs();

>From fbe1914b570b60014c56fdb294c9ec69803a856f Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Fri, 11 Apr 2025 09:27:40 -0700
Subject: [PATCH 06/34] Add a JSON response to jGPUPluginBreakpointHit

The response to jGPUPluginBreakpointHit is now JSON and can:
- disable the breakpoint if disable_bp is set to true
- set new breakpoints if there is an array of new breakpoints to set
- return a connect URL to allow reverse connection

The functionality for disabling the breakpoint, setting new breakpoints
and connecting to the URL is still TODO
---
 .../lldb/Utility/GPUGDBRemotePackets.h        | 32 ++++++++++++++-----
 .../GDBRemoteCommunicationClient.cpp          | 25 +++++++++++++--
 .../GDBRemoteCommunicationServerLLGS.cpp      | 10 ++++--
 .../Process/gdb-remote/LLDBServerPlugin.h     |  3 +-
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 19 +++++++++++
 .../MockGPU/LLDBServerPluginMockGPU.cpp       | 18 ++++++++---
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  3 +-
 7 files changed, 91 insertions(+), 19 deletions(-)

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 1a481e23e73be..4cdd96bb2d651 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -21,10 +21,10 @@ struct SymbolValue {
   std::string name;
   uint64_t value;
 };
-bool fromJSON(const llvm::json::Value &value, SymbolValue &info, 
+bool fromJSON(const llvm::json::Value &value, SymbolValue &data, 
               llvm::json::Path path);
 
-llvm::json::Value toJSON(const SymbolValue &packet);
+llvm::json::Value toJSON(const SymbolValue &data);
 
 struct GPUBreakpointInfo {
   std::string identifier;
@@ -34,10 +34,10 @@ struct GPUBreakpointInfo {
   std::vector<std::string> symbol_names;
 };
 
-bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &info,
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
   llvm::json::Path path);
 
-llvm::json::Value toJSON(const GPUBreakpointInfo &packet);
+llvm::json::Value toJSON(const GPUBreakpointInfo &data);
 
 
 struct GPUPluginInfo {
@@ -46,10 +46,10 @@ struct GPUPluginInfo {
   std::vector<GPUBreakpointInfo> breakpoints;
 };
 
-bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &info,
+bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &data,
   llvm::json::Path path);
 
-llvm::json::Value toJSON(const GPUPluginInfo &packet);
+llvm::json::Value toJSON(const GPUPluginInfo &data);
 
 struct GPUPluginBreakpointHitArgs {
   std::string plugin_name;
@@ -57,10 +57,26 @@ struct GPUPluginBreakpointHitArgs {
   std::vector<SymbolValue> symbol_values;
 };
 
-bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &info,
+bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &data,
               llvm::json::Path path);
 
-llvm::json::Value toJSON(const GPUPluginBreakpointHitArgs &packet);
+llvm::json::Value toJSON(const GPUPluginBreakpointHitArgs &data);
+
+struct GPUPluginBreakpointHitResponse {
+  ///< Set to true if this berakpoint should be disabled.
+  bool disable_bp = false; 
+  /// Optional new breakpoints to set.
+  std::optional<std::vector<GPUBreakpointInfo>> breakpoints;
+  /// If a GPU connection is available return a connect URL to use to reverse
+  /// connect to the GPU GDB server.
+  std::optional<std::string> connect_url;
+};
+
+bool fromJSON(const llvm::json::Value &value, 
+              GPUPluginBreakpointHitResponse &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data);
 
 } // namespace lldb_private
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index b603638ff320e..1da4ceaf36656 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -643,10 +643,29 @@ bool GDBRemoteCommunicationClient::GPUBreakpointHit(
   os << toJSON(args);
   packet.PutEscapedBytes(json_string.c_str(), json_string.size());
   StringExtractorGDBRemote response;
+  
   if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
-      PacketResult::Success)
-    return response.IsOKResponse();
-  return false;
+      PacketResult::Success) {
+    if (!response.Empty()) {
+      if (llvm::Expected<GPUPluginBreakpointHitResponse> info = 
+          llvm::json::parse<GPUPluginBreakpointHitResponse>(response.Peek(), 
+              "GPUPluginBreakpointHitResponse")) {
+        if (info->disable_bp) {
+          // TODO: disable BP
+        }
+        if (info->breakpoints) {
+          // TODO: set new breakpoints
+        }
+        if (info->connect_url) {
+          // TODO: connect to URL
+        }
+        return true; // Auto continue
+      } else {
+        llvm::consumeError(info.takeError());
+      }
+    }
+  }
+  return false; // Stop at BP
 }
 
 bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index ff1793a936f23..9d5306ad044e2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -3721,8 +3721,14 @@ GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit(
 
   for (auto &plugin_up: m_plugins) {
     if (plugin_up->GetPluginName() == args->plugin_name) {
-      plugin_up->BreakpointWasHit(*args);
-      return SendOKResponse();
+      GPUPluginBreakpointHitResponse bp_response = 
+          plugin_up->BreakpointWasHit(*args);
+      std::string json_string;
+      llvm::raw_string_ostream os(json_string);
+      os << toJSON(bp_response);
+      StreamGDBRemote response;
+      response.PutEscapedBytes(json_string.data(), json_string.size());
+      return SendPacketNoLock(response.GetString());
     }
   }
   return SendErrorResponse(Status::FromErrorString("Invalid plugin name."));
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index e7037c2f22172..3c22e7501e09a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -107,7 +107,8 @@ class LLDBServerPlugin {
   /// calling m_process.SetBreakpoint(...) to help implement funcionality,
   /// such as dynamic library loading in GPUs or to synchronize in any other
   /// way with the native process.
-  virtual void BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {}
+  virtual GPUPluginBreakpointHitResponse 
+  BreakpointWasHit(GPUPluginBreakpointHitArgs &args) = 0;
 
   protected:
     GPUPluginInfo m_info;
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 3fd43dce7fa12..fe89b0d716537 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -75,4 +75,23 @@ json::Value toJSON(const GPUPluginBreakpointHitArgs &data) {
              {"symbol_values", data.symbol_values},
             });
 }
+
+bool fromJSON(const llvm::json::Value &value, 
+              GPUPluginBreakpointHitResponse &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("disable_bp", data.disable_bp) &&
+         o.mapOptional("breakpoints", data.breakpoints) &&
+         o.mapOptional("connect_url", data.connect_url);
+}
+
+llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data) {
+  return json::Value(
+    Object{{"disable_bp", data.disable_bp}, 
+           {"breakpoints", data.breakpoints},
+           {"connect_url", data.connect_url},
+          });
+}
+
 } // namespace lldb_private
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index 0d69d35f506a9..80e8aa09915fe 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -175,14 +175,24 @@ std::optional<std::string> LLDBServerPluginMockGPU::GetConnectionURL() {
   return std::nullopt;
 }
 
-void LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
+GPUPluginBreakpointHitResponse 
+LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
   Log *log = GetLog(GDBRLog::Plugin);
   std::string json_string;
+  std::string &bp_identifier = args.breakpoint.identifier;
   llvm::raw_string_ostream os(json_string);
   os << toJSON(args);
-
-  LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\")", 
-            json_string.c_str());
+  LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\"):\nJSON:\n%s", 
+            bp_identifier.c_str(), json_string.c_str());
+
+  GPUPluginBreakpointHitResponse response;
+  if (bp_identifier == "gpu_initialize") {
+    response.disable_bp = true;
+    LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\") disabling breakpoint", 
+              bp_identifier.c_str());
+    // response.connect_url = GetConnectionURL();
+  }
+  return response;
 }
 
 void LLDBServerPluginMockGPU::InitializePluginInfo() {
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index b57f0ae35a999..35093f2853138 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -27,7 +27,8 @@ class LLDBServerPluginMockGPU : public lldb_private::lldb_server::LLDBServerPlug
   bool HandleEventFileDescriptorEvent(int fd) override;
   std::optional<std::string> GetConnectionURL() override;
   void InitializePluginInfo() override;
-  void BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
+  GPUPluginBreakpointHitResponse 
+  BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
 
 private:
   void CloseFDs();

>From a152eff00a9bead9ddf8bc480bb6232d3a2528df Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Fri, 11 Apr 2025 12:57:21 -0700
Subject: [PATCH 07/34] Add StreamGDBRemote methods to facilitate sending JSON.

Many GDB remote packets send JSON. This patch add new Put methods to
StreamGDBRemote to facilitate sendings objects that are converted to
JSON and sent as escaped JSON text.
---
 lldb/include/lldb/Utility/GDBRemote.h         | 51 ++++++++++++++++++-
 .../lldb/Utility/GPUGDBRemotePackets.h        | 16 ++++++
 .../GDBRemoteCommunicationClient.cpp          |  5 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      | 35 ++++++-------
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 42 ++++++++++++++-
 5 files changed, 126 insertions(+), 23 deletions(-)

diff --git a/lldb/include/lldb/Utility/GDBRemote.h b/lldb/include/lldb/Utility/GDBRemote.h
index ab700c00474ae..c091d53fc4a7b 100644
--- a/lldb/include/lldb/Utility/GDBRemote.h
+++ b/lldb/include/lldb/Utility/GDBRemote.h
@@ -14,11 +14,11 @@
 #include "lldb/lldb-enumerations.h"
 #include "lldb/lldb-public.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/JSON.h"
 
 #include <cstddef>
 #include <cstdint>
 #include <string>
-#include <vector>
 
 namespace lldb_private {
 
@@ -43,6 +43,55 @@ class StreamGDBRemote : public StreamString {
   ///     Number of bytes written.
   // TODO: Convert this function to take ArrayRef<uint8_t>
   int PutEscapedBytes(const void *s, size_t src_len);
+
+  /// Convert an object into JSON and add the JSON text to the packet.
+  ///
+  /// Any special characters in the JSON will be escaped to make sure it doesn't
+  /// interfere with the GDB remote protocol packet format.
+  ///
+  /// \param[in] obj
+  ///     The object to convert to JSON which must have a method written that
+  ///     converts the object to a llvm::json::Value:
+  ///
+  ///     \code llvm::json::Value toJSON(const T &obj);
+  ///
+  /// \return
+  ///     Number of bytes written.
+  template<class T> int PutAsJSON(const T &obj) {
+    std::string json_string;
+    llvm::raw_string_ostream os(json_string);
+    os << toJSON(obj);
+    return PutEscapedBytes(json_string.c_str(), json_string.size());  
+  }
+  /// Convert an array of objects into JSON and add the JSON text to the packet.
+  ///
+  /// Any special characters in the JSON will be escaped to make sure it doesn't
+  /// interfere with the GDB remote protocol packet format.
+  ///
+  /// \param[in] array
+  ///     An array of objects to convert to JSON. The object't type must have a 
+  ///     method written that converts the object to a llvm::json::Value:
+  ///
+  ///     \code llvm::json::Value toJSON(const T &obj);
+  ///
+  /// \return
+  ///     Number of bytes written.
+  template<class T> int PutAsJSONArray(const std::vector<T> &array) {
+    std::string json_string;
+    llvm::raw_string_ostream os(json_string);
+    bool first = true;
+    os << "[";
+    for (auto &obj: array) {
+      if (first)
+        first = false;
+      else
+        os << ",";
+      os << toJSON(obj);
+    }
+    os << "]";
+    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/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 4cdd96bb2d651..48454bf203354 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -62,6 +62,22 @@ bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &data,
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitArgs &data);
 
+/// A structure that contains all of the information needed for LLDB to create
+/// a reverse connection to a GPU GDB server
+struct GPUPluginConnectionInfo {
+  /// The name of the platform to select when creating the target.
+  std::optional<std::string> platform_name;
+  /// The target triple to use as the architecture when creating the target.
+  std::optional<std::string> triple;
+  /// The connection URL to use with "process connect <url>". 
+  std::string connect_url;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUPluginConnectionInfo &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUPluginConnectionInfo &data);
+
 struct GPUPluginBreakpointHitResponse {
   ///< Set to true if this berakpoint should be disabled.
   bool disable_bp = false; 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 1da4ceaf36656..468882490cd32 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -638,10 +638,7 @@ bool GDBRemoteCommunicationClient::GPUBreakpointHit(
   const GPUPluginBreakpointHitArgs &args) {
   StreamGDBRemote packet;
   packet.PutCString("jGPUPluginBreakpointHit:");
-  std::string json_string;
-  llvm::raw_string_ostream os(json_string);
-  os << toJSON(args);
-  packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+  packet.PutAsJSON(args);
   StringExtractorGDBRemote response;
   
   if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 9d5306ad044e2..12ceaf51bf8d4 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -3691,20 +3691,24 @@ GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize(
     StringExtractorGDBRemote &) {
   std::vector<GPUPluginInfo> infos;
-  std::string json_string;
-  llvm::raw_string_ostream os(json_string);
-  bool first = true;
-  os << "[";
-  for (auto &plugin_up: m_plugins) {
-    if (first)
-      first = false;
-    else
-      os << ",";
-    os << toJSON(plugin_up->GetPluginInfo());
-  }
-  os << "]";
+  for (auto &plugin_up: m_plugins)
+      infos.push_back(plugin_up->GetPluginInfo());
   StreamGDBRemote response;
-  response.PutEscapedBytes(json_string.data(), json_string.size());
+  response.PutAsJSONArray(infos);
+
+  // std::string json_string;
+  // llvm::raw_string_ostream os(json_string);
+  // bool first = true;
+  // os << "[";
+  // for (auto &plugin_up: m_plugins) {
+  //   if (first)
+  //     first = false;
+  //   else
+  //     os << ",";
+  //   os << toJSON(plugin_up->GetPluginInfo());
+  // }
+  // os << "]";
+  // response.PutEscapedBytes(json_string.data(), json_string.size());
   return SendPacketNoLock(response.GetString());
 }
 
@@ -3723,11 +3727,8 @@ GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit(
     if (plugin_up->GetPluginName() == args->plugin_name) {
       GPUPluginBreakpointHitResponse bp_response = 
           plugin_up->BreakpointWasHit(*args);
-      std::string json_string;
-      llvm::raw_string_ostream os(json_string);
-      os << toJSON(bp_response);
       StreamGDBRemote response;
-      response.PutEscapedBytes(json_string.data(), json_string.size());
+      response.PutAsJSON(bp_response);
       return SendPacketNoLock(response.GetString());
     }
   }
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index fe89b0d716537..21fe5831aae8d 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -13,6 +13,10 @@ using namespace llvm::json;
 
 namespace lldb_private {
 
+//------------------------------------------------------------------------------
+// SymbolValue
+//------------------------------------------------------------------------------
+
 bool fromJSON(const json::Value &value, SymbolValue &data, Path path) {
   ObjectMapper o(value, path);
   return o && o.map("name", data.name) && o.map("value", data.value);
@@ -22,7 +26,9 @@ json::Value toJSON(const SymbolValue &data) {
   return json::Value(Object{{"name", data.name}, {"value", data.value}});
 }
 
-
+//------------------------------------------------------------------------------
+// GPUBreakpointInfo
+//------------------------------------------------------------------------------
 bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
@@ -42,6 +48,10 @@ llvm::json::Value toJSON(const GPUBreakpointInfo &data) {
           });
 }
 
+//------------------------------------------------------------------------------
+// GPUPluginInfo
+//------------------------------------------------------------------------------
+
 bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
@@ -59,6 +69,30 @@ llvm::json::Value toJSON(const GPUPluginInfo &data) {
           });
 }
 
+//------------------------------------------------------------------------------
+// GPUPluginConnectionInfo
+//------------------------------------------------------------------------------
+bool fromJSON(const llvm::json::Value &value, GPUPluginConnectionInfo &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+          o.mapOptional("platform_name", data.platform_name) &&
+          o.mapOptional("triple", data.triple) &&
+          o.map("connect_url", data.connect_url);
+}
+
+llvm::json::Value toJSON(const GPUPluginConnectionInfo &data) {
+  return json::Value(
+      Object{{"platform_name", data.platform_name}, 
+             {"triple", data.triple},
+             {"connect_url", data.connect_url},
+            });
+}
+
+
+//------------------------------------------------------------------------------
+// GPUPluginBreakpointHitArgs
+//------------------------------------------------------------------------------
 bool fromJSON(const json::Value &value, GPUPluginBreakpointHitArgs &data,
               Path path) {
   ObjectMapper o(value, path);
@@ -76,6 +110,12 @@ json::Value toJSON(const GPUPluginBreakpointHitArgs &data) {
             });
 }
 
+
+//------------------------------------------------------------------------------
+// GPUPluginBreakpointHitResponse
+//------------------------------------------------------------------------------
+
+
 bool fromJSON(const llvm::json::Value &value, 
               GPUPluginBreakpointHitResponse &data,
               llvm::json::Path path) {

>From 3c1c238cee4ef6e61b3c660c725a8e50020ad0c3 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Fri, 11 Apr 2025 18:06:10 -0700
Subject: [PATCH 08/34] Many fixes listed below.

- Added the ability to encode JSON as hex ascii so JSON can be used in
  stop reply packets
- Added exe_path to GPUPluginConnectionInfo
- Added StringExtractorGDBRemote::GetFromJSONText to decode objects from
  JSON text.
- Added StringExtractorGDBRemote::GetFromJSONHexASCII to decode objects from
  JSON HEX ASCII.
- Change GDBRemoteCommunicationClient::GPUBreakpointHit to return an
  optional GPUPluginBreakpointHitResponse and moved TODO work over into
  caller function where it makes more sense.
- Change stop reply GPU connections to use JSON encoding of a
  GPUPluginConnectionInfo object.
- Remove LLDBServerPlugin::GetConnectionURL() and replace with a
  function that is designed to always create a connection.
- Added LLDBServerPlugin::NativeProcessIsStopping() to let the GPU
  plug-in know that the native process is stopping in case the plug-in
  wants to start a connection on stop. The Mock GPU plug-in isn't using
  this feature anymore.
- Handle disabling the breakpoint in ProcessGDBRemote::GPUBreakpointHit
  and also setting any new breakpoints and connecting to LLDB if the
  breakpoint hit response struct GPUPluginBreakpointHitResponse asks for
  it.
- Rename "gpu-url" in the stop reply packet to "gpu-connection" and
  convert it to use hex ASCII encoded JSON version of GPUPluginConnectionInfo
- Added ProcessGDBRemote::HandleGPUBreakpoints() to handle any
  breakpoints requested by the GPU plug-ins since those requests can now
  be done by the plugin info or a breakpoint hit response
- Added ProcessGDBRemote::HandleConnectionRequest() to handle reverse
  connecting from stop reply or from breakpoint hit response.
- Fix the size of RegisterContextMockGPU::g_gpr_regnums and g_vec_regnums
---
 lldb/include/lldb/Utility/GDBRemote.h         |  11 +-
 .../lldb/Utility/GPUGDBRemotePackets.h        |  14 +-
 .../lldb/Utility/StringExtractorGDBRemote.h   |  20 +++
 .../GDBRemoteCommunicationClient.cpp          |  19 +-
 .../gdb-remote/GDBRemoteCommunicationClient.h |   8 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      |  28 +--
 .../Process/gdb-remote/LLDBServerPlugin.h     |  31 +++-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 167 +++++++++++-------
 .../Process/gdb-remote/ProcessGDBRemote.h     |   6 +
 lldb/source/Utility/GPUGDBRemotePackets.cpp   |  14 +-
 .../MockGPU/LLDBServerPluginMockGPU.cpp       | 117 ++++++------
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  39 +++-
 .../MockGPU/RegisterContextMockGPU.cpp        |  10 +-
 13 files changed, 295 insertions(+), 189 deletions(-)

diff --git a/lldb/include/lldb/Utility/GDBRemote.h b/lldb/include/lldb/Utility/GDBRemote.h
index c091d53fc4a7b..6f979404e1335 100644
--- a/lldb/include/lldb/Utility/GDBRemote.h
+++ b/lldb/include/lldb/Utility/GDBRemote.h
@@ -55,13 +55,20 @@ class StreamGDBRemote : public StreamString {
   ///
   ///     \code llvm::json::Value toJSON(const T &obj);
   ///
+  /// \param[in] hex_ascii
+  ///     If \a true then encode JSON as hex ASCII bytes. If \a false, then
+  ///     encode as an escaped string value.
+  ///
   /// \return
   ///     Number of bytes written.
-  template<class T> int PutAsJSON(const T &obj) {
+  template<class T> int PutAsJSON(const T &obj, bool hex_ascii) {
     std::string json_string;
     llvm::raw_string_ostream os(json_string);
     os << toJSON(obj);
-    return PutEscapedBytes(json_string.c_str(), json_string.size());  
+    if (hex_ascii)
+      return PutStringAsRawHex8(json_string);
+    else
+      return PutEscapedBytes(json_string.c_str(), json_string.size());  
   }
   /// Convert an array of objects into JSON and add the JSON text to the packet.
   ///
diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 48454bf203354..240a58758be77 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -62,9 +62,15 @@ bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &data,
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitArgs &data);
 
+///-----------------------------------------------------------------------------
+/// GPUPluginConnectionInfo
+///
 /// A structure that contains all of the information needed for LLDB to create
 /// a reverse connection to a GPU GDB server
+///-----------------------------------------------------------------------------
 struct GPUPluginConnectionInfo {
+  /// A target executable path to use when creating the target.
+  std::optional<std::string> exe_path;
   /// The name of the platform to select when creating the target.
   std::optional<std::string> platform_name;
   /// The target triple to use as the architecture when creating the target.
@@ -78,6 +84,12 @@ bool fromJSON(const llvm::json::Value &value, GPUPluginConnectionInfo &data,
 
 llvm::json::Value toJSON(const GPUPluginConnectionInfo &data);
 
+///-----------------------------------------------------------------------------
+/// GPUPluginBreakpointHitResponse
+///
+/// A response structure from the GPU plugin from hitting a native breakpoint
+/// set by the GPU plugin.
+///-----------------------------------------------------------------------------
 struct GPUPluginBreakpointHitResponse {
   ///< Set to true if this berakpoint should be disabled.
   bool disable_bp = false; 
@@ -85,7 +97,7 @@ struct GPUPluginBreakpointHitResponse {
   std::optional<std::vector<GPUBreakpointInfo>> breakpoints;
   /// If a GPU connection is available return a connect URL to use to reverse
   /// connect to the GPU GDB server.
-  std::optional<std::string> connect_url;
+  std::optional<GPUPluginConnectionInfo> connect_info;
 };
 
 bool fromJSON(const llvm::json::Value &value, 
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index a1894a3939593..3f05e29ca04c9 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -12,6 +12,7 @@
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/StringExtractor.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
 
 #include <optional>
 #include <string>
@@ -218,6 +219,25 @@ class StringExtractorGDBRemote : public StringExtractor {
   std::optional<std::pair<lldb::pid_t, lldb::tid_t>>
   GetPidTid(lldb::pid_t default_pid);
 
+
+  template<class T> std::optional<T> GetFromJSONText() {
+    llvm::Expected<T> info = llvm::json::parse<T>(Peek(), "");
+    if (info)
+      return *info;
+    llvm::consumeError(info.takeError());
+    return std::nullopt;
+  }
+
+  template<class T> std::optional<T> GetFromJSONHexASCII() {
+    std::string json;
+    GetHexByteString(json);
+    llvm::Expected<T> info = llvm::json::parse<T>(json.c_str(), "");
+    if (info)
+      return *info;
+    llvm::consumeError(info.takeError());
+    return std::nullopt;
+  }
+
 protected:
   ResponseValidatorCallback m_validator = nullptr;
   void *m_validator_baton = nullptr;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 468882490cd32..4c938b50e47b3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -634,35 +634,26 @@ GDBRemoteCommunicationClient::GetGPUPluginInfos() {
   return std::nullopt;
 }
 
-bool GDBRemoteCommunicationClient::GPUBreakpointHit(
+std::optional<GPUPluginBreakpointHitResponse> 
+GDBRemoteCommunicationClient::GPUBreakpointHit(
   const GPUPluginBreakpointHitArgs &args) {
   StreamGDBRemote packet;
   packet.PutCString("jGPUPluginBreakpointHit:");
-  packet.PutAsJSON(args);
+  packet.PutAsJSON(args, /*hex_ascii=*/false);
   StringExtractorGDBRemote response;
-  
   if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
       PacketResult::Success) {
     if (!response.Empty()) {
       if (llvm::Expected<GPUPluginBreakpointHitResponse> info = 
           llvm::json::parse<GPUPluginBreakpointHitResponse>(response.Peek(), 
               "GPUPluginBreakpointHitResponse")) {
-        if (info->disable_bp) {
-          // TODO: disable BP
-        }
-        if (info->breakpoints) {
-          // TODO: set new breakpoints
-        }
-        if (info->connect_url) {
-          // TODO: connect to URL
-        }
-        return true; // Auto continue
+        return *info;
       } else {
         llvm::consumeError(info.takeError());
       }
     }
   }
-  return false; // Stop at BP
+  return std::nullopt;
 }
 
 bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index fe9e662adac25..0fae435b0a741 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -22,6 +22,7 @@
 #include "lldb/Utility/AddressableBits.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/GDBRemote.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/ProcessInfo.h"
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/TraceGDBRemotePackets.h"
@@ -33,10 +34,6 @@
 #include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
-
-  struct GPUPluginBreakpointHitArgs;
-  struct GPUPluginInfo;
-
 namespace process_gdb_remote {
 
 /// The offsets used by the target when relocating the executable. Decoded from
@@ -439,7 +436,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   std::optional<std::vector<GPUPluginInfo>> GetGPUPluginInfos();
 
-  bool GPUBreakpointHit(const GPUPluginBreakpointHitArgs &args);
+  std::optional<GPUPluginBreakpointHitResponse> 
+  GPUBreakpointHit(const GPUPluginBreakpointHitArgs &args);
 
   bool GetThreadExtendedInfoSupported();
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 12ceaf51bf8d4..a403f1f8e0a8c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -1099,17 +1099,19 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
   LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
 
   // Check if any plug-ins have new connections
-  std::string extra_stop_reply_args;
+  StreamGDBRemote extra_stop_reply_args;
   for (auto &plugin_up : m_plugins) {
-    if (!plugin_up->IsConnected()) {
-      if (std::optional<std::string> opt_url = plugin_up->GetConnectionURL())
-        extra_stop_reply_args += opt_url.value();
+    if (std::optional<GPUPluginConnectionInfo> connection_info = 
+          plugin_up->NativeProcessIsStopping()) {
+      extra_stop_reply_args.PutCString("gpu-connection:");
+      extra_stop_reply_args.PutAsJSON(*connection_info, /*hex_ascii=*/true);
+      extra_stop_reply_args.PutChar(';');
     } 
   }
 
   PacketResult result = SendStopReasonForState(
       *process, StateType::eStateStopped, /*force_synchronous=*/false, 
-      extra_stop_reply_args);
+      extra_stop_reply_args.GetData());
   if (result != PacketResult::Success) {
     LLDB_LOGF(log,
               "GDBRemoteCommunicationServerLLGS::%s failed to send stop "
@@ -3695,20 +3697,6 @@ GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize(
       infos.push_back(plugin_up->GetPluginInfo());
   StreamGDBRemote response;
   response.PutAsJSONArray(infos);
-
-  // std::string json_string;
-  // llvm::raw_string_ostream os(json_string);
-  // bool first = true;
-  // os << "[";
-  // for (auto &plugin_up: m_plugins) {
-  //   if (first)
-  //     first = false;
-  //   else
-  //     os << ",";
-  //   os << toJSON(plugin_up->GetPluginInfo());
-  // }
-  // os << "]";
-  // response.PutEscapedBytes(json_string.data(), json_string.size());
   return SendPacketNoLock(response.GetString());
 }
 
@@ -3728,7 +3716,7 @@ GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit(
       GPUPluginBreakpointHitResponse bp_response = 
           plugin_up->BreakpointWasHit(*args);
       StreamGDBRemote response;
-      response.PutAsJSON(bp_response);
+      response.PutAsJSON(bp_response, /*hex_ascii=*/false);
       return SendPacketNoLock(response.GetString());
     }
   }
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index 3c22e7501e09a..a8673e2e5abaa 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -19,6 +19,8 @@
 #include <optional>
 #include <stdint.h>
 #include <string>
+#include <mutex>
+
 namespace lldb_private {
 
 namespace process_gdb_remote {
@@ -36,6 +38,7 @@ class LLDBServerPlugin {
   MainLoop m_main_loop;
   std::unique_ptr<Manager> m_process_manager_up;
   std::unique_ptr<GDBServer> m_gdb_server;
+  bool m_is_listening = false;
   bool m_is_connected = false;
 
 public:
@@ -47,18 +50,29 @@ class LLDBServerPlugin {
 
   virtual llvm::StringRef GetPluginName() = 0;
 
-  /// Get an connection URL to connect to this plug-in.
+  /// Get notified when the process is stopping.
   ///
-  /// This function will get called each time native process stops if this
-  /// object is not connected already. If the plug-in is ready to be activated,
-  /// return a valid URL to use with "process connect" that can connect to this
-  /// plug-in. Execution should wait for a connection to be made before trying
-  /// to do any blocking code. The plug-in should assume the users do not want
-  /// to use any features unless a connection is made.
-  virtual std::optional<std::string> GetConnectionURL() {
+  /// This function will get called each time native process stops as the stop
+  /// reply packet is being created. If the plug-in is ready to be activated,
+  /// return a GPUPluginConnectionInfo with a value connection URL to use with 
+  /// "process connect" which can connect to this plug-in. Plug-ins should wait 
+  /// for a connection to be made before trying to do any blocking code. The 
+  /// plug-in should assume the users do not want to use any features unless a 
+  /// connection is made.
+  virtual std::optional<GPUPluginConnectionInfo> NativeProcessIsStopping() {
     return std::nullopt;
   };
 
+  /// Get an connection URL to connect to this plug-in.
+  ///
+  /// This function will get called when the plug-in is ready to be activated
+  /// and connected to LLDB. The plug-in should create a connection that listens
+  /// for a connection from LLDB and it should fill in the connection URL 
+  /// with a valid URL that can be used with "process connect". Typical plug-ins
+  /// will listen on a TCP port or a Unix domain socket and spin up a thread
+  /// that will accept the connection and run the main loop. 
+  virtual std::optional<GPUPluginConnectionInfo> CreateConnection() = 0;
+
   /// Get the GPU plug-in information.
   ///
   /// Each GPU plugin can return a structure that describes the GPU plug-in and 
@@ -111,6 +125,7 @@ class LLDBServerPlugin {
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) = 0;
 
   protected:
+    std::mutex m_connect_mutex;
     GPUPluginInfo m_info;
 };
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 139c5ab51b807..94b0e62cdbfcc 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -84,7 +84,6 @@
 #include "ProcessGDBRemoteLog.h"
 #include "ThreadGDBRemote.h"
 #include "lldb/Host/Host.h"
-#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/StringExtractorGDBRemote.h"
 
 
@@ -857,10 +856,103 @@ bool ProcessGDBRemote::GPUBreakpointHit(void *baton,
       }
     }
   }
-  m_gdb_comm.GPUBreakpointHit(args);
+  std::optional<GPUPluginBreakpointHitResponse> response =
+      m_gdb_comm.GPUBreakpointHit(args);
+  if (response) {
+    // Disable the breakpoint if requested, but leave it around so we can see
+    // the hit count and other stats on the breakpoint.
+    if (response->disable_bp) {
+      BreakpointSP bp_sp = target.GetBreakpointByID(break_id);
+      if (bp_sp) 
+        bp_sp->SetEnabled(false);
+    }
+    //  Set any new breakpoints if requested.
+    if (response->breakpoints)
+      HandleGPUBreakpoints(args.plugin_name, response->breakpoints.value());
+    if (response->connect_info)
+      HandleConnectionRequest(*response->connect_info);
+  }
   return false; // Don't stop, auto continue.
 }
 
+Status ProcessGDBRemote::HandleConnectionRequest(
+    const GPUPluginConnectionInfo &connection_info) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOG(log, "ProcessGDBRemote::HandleConnectionRequest()"); 
+  auto &debugger = GetTarget().GetDebugger();
+  TargetSP gpu_target_sp;
+  llvm::StringRef exe_path;
+  llvm::StringRef triple;
+  OptionGroupPlatform *platform_options = nullptr;
+
+  if (connection_info.exe_path)
+      exe_path = *connection_info.exe_path;
+  if (connection_info.triple)
+    triple = *connection_info.triple;
+  // Create an empty target for our GPU.
+  Status error(debugger.GetTargetList().CreateTarget(
+    debugger, exe_path, triple, eLoadDependentsNo, platform_options, 
+    gpu_target_sp));
+  if (error.Fail())
+    return error;
+  if (!gpu_target_sp)
+    return Status::FromErrorString("failed to create target");
+
+  PlatformSP platform_sp = gpu_target_sp->GetPlatform();
+  if (!platform_sp)
+    return Status::FromErrorString("invalid platform for target needed for "
+                                   "connecting to process");
+
+  ProcessSP process_sp = platform_sp->ConnectProcess(
+      connection_info.connect_url, GetPluginNameStatic(), debugger,
+      gpu_target_sp.get(), error);
+  if (error.Fail())
+    return error;
+  if (!process_sp)
+    return Status::FromErrorString("invalid process after conneting");
+
+  LLDB_LOG(log, "ProcessGDBRemote::HandleConnectionRequest(): successfully "
+           "created process!!!");
+  return Status();
+}
+
+void ProcessGDBRemote::HandleGPUBreakpoints(
+    const std::string plugin_name,
+    const std::vector<GPUBreakpointInfo> &breakpoints) {
+  Target &target = GetTarget();
+  for (const auto &bp: breakpoints) {
+    // Create data that will live with the breakpoint so when we hit the 
+    // breakpoint and the GPUBreakpointHitCallback is called, we can use this
+    // data.
+    auto args_up = std::make_unique<GPUPluginBreakpointHitArgs>();
+    args_up->plugin_name = plugin_name;
+    args_up->breakpoint = bp;
+    FileSpecList bp_modules;
+    if (!bp.shlib.empty())
+      bp_modules.Append(FileSpec(bp.shlib, 
+                                  llvm::sys::path::Style::native));
+    BreakpointSP bp_sp = target.CreateBreakpoint(
+        &bp_modules, // Containing modules.
+        nullptr, // Containing source files.
+        bp.function_name.c_str(), // Function name.
+        eFunctionNameTypeFull, // Function name type.
+        eLanguageTypeUnknown, // Language type
+        0, // Byte offset.
+        eLazyBoolNo, // Skip prologue.
+        true, // Internal breakpoint.
+        false); // Request hardware.
+    if (bp_sp) {
+      // Create some JSON we can send back to the lldb-server
+      // that identifies the plug-in and the breakpoint for when
+      // the breakpoint gets hit.
+      auto baton_sp = 
+          std::make_shared<GPUBreakpointCallbackBaton>(std::move(args_up));
+      bp_sp->SetCallback(GPUBreakpointHitCallback, baton_sp,
+                          /*is_synchronous=*/true);
+    }
+  }
+}
+
 Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
   Status error;
   // Only connect if we have a valid connect URL
@@ -914,38 +1006,9 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
   m_gdb_comm.GetVContSupported('c');
   m_gdb_comm.GetVAttachOrWaitSupported();
   m_gdb_comm.EnableErrorStringInPacket();
-  Target &target = GetTarget();
-  if (auto infos = m_gdb_comm.GetGPUPluginInfos()) {
-    for (const auto &info: *infos) {
-      for (const auto &bp_info: info.breakpoints) {
-        auto args_up = std::make_unique<GPUPluginBreakpointHitArgs>();
-        args_up->plugin_name = info.name;
-        args_up->breakpoint = bp_info;
-        FileSpecList bp_modules;
-        if (!bp_info.shlib.empty())
-          bp_modules.Append(FileSpec(bp_info.shlib, 
-                                     llvm::sys::path::Style::native));
-        BreakpointSP bp_sp = target.CreateBreakpoint(
-            &bp_modules, // Containing modules.
-            nullptr, // Containing source files.
-            bp_info.function_name.c_str(), // Function name.
-            eFunctionNameTypeFull, // Function name type.
-            eLanguageTypeUnknown, // Language type
-            0, // Byte offset.
-            eLazyBoolNo, // Skip prologue.
-            true, // Internal breakpoint.
-            false); // Request hardware.
-        if (bp_sp) {
-          // Create some JSON we can send back to the lldb-server
-          // that identifies the plug-in and the breakpoint for when
-          // the breakpoint gets hit.
-          auto baton_sp = 
-              std::make_shared<GPUBreakpointCallbackBaton>(std::move(args_up));
-          bp_sp->SetCallback(GPUBreakpointHitCallback, baton_sp,
-                             /*is_synchronous=*/true);
-        }
-      }
-    }
+  if (auto plugin_infos = m_gdb_comm.GetGPUPluginInfos()) {
+    for (const auto &plugin_info: *plugin_infos)
+      HandleGPUBreakpoints(plugin_info.name, plugin_info.breakpoints);
   }
   // First dispatch any commands from the platform:
   auto handle_cmds = [&] (const Args &args) ->  void {
@@ -2478,36 +2541,12 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
         if (!value.getAsInteger(0, addressing_bits)) {
           addressable_bits.SetHighmemAddressableBits(addressing_bits);
         }
-      } else if (key.compare("gpu-url") == 0) {
-        Log *log = GetLog(GDBRLog::Plugin);
-        LLDB_LOG(log, "gpu-url: url = \"{0}\"", value); 
-        auto &debugger = GetTarget().GetDebugger();
-        TargetSP gpu_target_sp;
-        // Create an empty target for our GPU.
-        Status error(debugger.GetTargetList().CreateTarget(
-          debugger, llvm::StringRef(), llvm::StringRef(), eLoadDependentsNo, 
-          nullptr, gpu_target_sp));
-        if (error.Fail()) {
-          LLDB_LOG(log, "gpu-url: error creating target: \"{0}\"", error); 
-        } else if (gpu_target_sp) {
-          PlatformSP platform_sp = gpu_target_sp->GetPlatform();
-          if (platform_sp) {
-            ProcessSP process_sp = platform_sp->ConnectProcess(
-                        value, GetPluginNameStatic(), debugger,
-                        gpu_target_sp.get(), error);
-            if (error.Fail()) {
-              LLDB_LOG(log, "gpu-url: error connecting to process: \"{0}\"", error); 
-            } else if (!process_sp) {
-              LLDB_LOG(log, "gpu-url: invalid process"); 
-            } else {
-              LLDB_LOG(log, "gpu-url: successfully created process!!!");
-            }
-          } else {
-            LLDB_LOG(log, "gpu-url: invalid platform");
-          }
-        } else {
-          LLDB_LOG(log, "gpu-url: invalid target");
-        }
+      } else if (key.compare("gpu-connection") == 0) {
+        StringExtractorGDBRemote extractor(value);
+        std::optional<GPUPluginConnectionInfo> connect_info = 
+            extractor.GetFromJSONHexASCII<GPUPluginConnectionInfo>();
+        if (connect_info)
+          HandleConnectionRequest(*connect_info);
       } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
         uint32_t reg = UINT32_MAX;
         if (!key.getAsInteger(16, reg))
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 8502d16ff0837..9d58d453e4b5e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -27,11 +27,13 @@
 #include "lldb/Utility/Broadcaster.h"
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/GDBRemote.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/StringExtractor.h"
 #include "lldb/Utility/StringList.h"
 #include "lldb/Utility/StructuredData.h"
+
 #include "lldb/lldb-private-forward.h"
 
 #include "GDBRemoteCommunicationClient.h"
@@ -445,6 +447,10 @@ class ProcessGDBRemote : public Process,
   bool GPUBreakpointHit(void *baton, StoppointCallbackContext *context, 
                         lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
                     
+  void HandleGPUBreakpoints(const std::string plugin_name,
+                            const std::vector<GPUBreakpointInfo> &breakpoints);
+
+  Status HandleConnectionRequest(const GPUPluginConnectionInfo &connection_info);
   // ContinueDelegate interface
   void HandleAsyncStdout(llvm::StringRef out) override;
   void HandleAsyncMisc(llvm::StringRef data) override;
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 21fe5831aae8d..c7cd0a63d116a 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -76,14 +76,16 @@ bool fromJSON(const llvm::json::Value &value, GPUPluginConnectionInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
   return o && 
-          o.mapOptional("platform_name", data.platform_name) &&
-          o.mapOptional("triple", data.triple) &&
-          o.map("connect_url", data.connect_url);
+         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);
 }
 
 llvm::json::Value toJSON(const GPUPluginConnectionInfo &data) {
   return json::Value(
-      Object{{"platform_name", data.platform_name}, 
+      Object{{"exe_path", data.exe_path}, 
+             {"platform_name", data.platform_name}, 
              {"triple", data.triple},
              {"connect_url", data.connect_url},
             });
@@ -123,14 +125,14 @@ bool fromJSON(const llvm::json::Value &value,
   return o && 
          o.map("disable_bp", data.disable_bp) &&
          o.mapOptional("breakpoints", data.breakpoints) &&
-         o.mapOptional("connect_url", data.connect_url);
+         o.mapOptional("connect_info", data.connect_info);
 }
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data) {
   return json::Value(
     Object{{"disable_bp", data.disable_bp}, 
            {"breakpoints", data.breakpoints},
-           {"connect_url", data.connect_url},
+           {"connect_info", data.connect_info},
           });
 }
 
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index 80e8aa09915fe..dc37ca731b84b 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -95,83 +95,74 @@ bool LLDBServerPluginMockGPU::HandleEventFileDescriptorEvent(int fd) {
   return false;
 }
 
-#if 0
-TEST_P(SocketTest, TCPAcceptTimeout) {
-  if (!HostSupportsProtocol())
-    return;
-
-  const bool child_processes_inherit = false;
-  auto listen_socket_up =
-      std::make_unique<TCPSocket>(true, child_processes_inherit);
-  Status error = listen_socket_up->Listen(
-      llvm::formatv("[{0}]:0", GetParam().localhost_ip).str(), 5);
-  ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
-  ASSERT_TRUE(listen_socket_up->IsValid());
-
-  Socket *socket;
-  ASSERT_THAT_ERROR(
-      listen_socket_up->Accept(std::chrono::milliseconds(10), socket)
-          .takeError(),
-      llvm::Failed<llvm::ErrorInfoBase>(
-          testing::Property(&llvm::ErrorInfoBase::convertToErrorCode,
-                            std::make_error_code(std::errc::timed_out))));
-}
-#endif
-
 void LLDBServerPluginMockGPU::AcceptAndMainLoopThread(
     std::unique_ptr<TCPSocket> listen_socket_up) {
   Log *log = GetLog(GDBRLog::Plugin);
-  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() spawned");
-
-  if (!listen_socket_up)
-    return;
+  LLDB_LOGF(log, "%s spawned", __PRETTY_FUNCTION__);
   Socket *socket = nullptr;
   Status error = listen_socket_up->Accept(std::chrono::seconds(30), socket);
-  if (error.Fail()) {
-    LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() error "
-              "returned from Accept(): %s", error.AsCString());  
-    return;
+  // Scope for lock guard.
+  {
+    // Protect access to m_is_listening and m_is_connected.
+    std::lock_guard<std::mutex> guard(m_connect_mutex);
+    m_is_listening = false;
+    if (error.Fail()) {
+      LLDB_LOGF(log, "%s error returned from Accept(): %s", __PRETTY_FUNCTION__, 
+                error.AsCString());  
+      return;
+    }
+    m_is_connected = true;
   }
-  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() initializing connection");
-  std::unique_ptr<Connection> connection_up(new ConnectionFileDescriptor(socket));
+
+  LLDB_LOGF(log, "%s initializing connection", __PRETTY_FUNCTION__);
+  std::unique_ptr<Connection> connection_up(
+      new ConnectionFileDescriptor(socket));
   m_gdb_server->InitializeConnection(std::move(connection_up));
-  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() running main loop");
+  LLDB_LOGF(log, "%s running main loop", __PRETTY_FUNCTION__);
   m_main_loop_status = m_main_loop.Run();
-  LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() main loop exited!");
+  LLDB_LOGF(log, "%s main loop exited!", __PRETTY_FUNCTION__);
   if (m_main_loop_status.Fail()) {
-    LLDB_LOGF(log, "LLDBServerPluginMockGPU::AcceptAndMainLoopThread() main "
-              "loop exited with an error: %s", m_main_loop_status.AsCString());
+    LLDB_LOGF(log, "%s main loop exited with an error: %s", __PRETTY_FUNCTION__,
+              m_main_loop_status.AsCString());
 
   }
+  // Protect access to m_is_connected.
+  std::lock_guard<std::mutex> guard(m_connect_mutex);
+  m_is_connected = false;
 }
 
-std::optional<std::string> LLDBServerPluginMockGPU::GetConnectionURL() {
-  static int g_counter = 0;
+std::optional<GPUPluginConnectionInfo> LLDBServerPluginMockGPU::CreateConnection() {
+  std::lock_guard<std::mutex> guard(m_connect_mutex);
   Log *log = GetLog(GDBRLog::Plugin);
-  ++g_counter;
-  LLDB_LOGF(log, "LLDBServerPluginMockGPU::GetConnectionURL() g_counter = %i", 
-            g_counter);
-  if (++g_counter == 2) {
-    LLDB_LOGF(log, "LLDBServerPluginMockGPU::GetConnectionURL() trying to "
-              "listen on port 0");
-    llvm::Expected<std::unique_ptr<TCPSocket>> sock = 
-        Socket::TcpListen("localhost:0", 5);
-    if (sock) {
-      const uint16_t listen_port = (*sock)->GetLocalPortNumber();
-      auto extra_args = llvm::formatv("gpu-url:connect://localhost:{};", 
-                                      listen_port);
-      LLDB_LOGF(log, 
-                "LLDBServerPluginMockGPU::GetConnectionURL() listening to %u", 
-                listen_port);
-      std::thread t(&LLDBServerPluginMockGPU::AcceptAndMainLoopThread, this, 
-                    std::move(*sock));
-      t.detach();
-      return extra_args.str();
-    } else {
-      LLDB_LOGF(log, "LLDBServerPluginMockGPU::GetConnectionURL() failed to "
-                "listen to localhost:0");      
-    }
+  LLDB_LOGF(log, "%s called", __PRETTY_FUNCTION__);
+  if (m_is_connected) {
+    LLDB_LOGF(log, "%s already connected", __PRETTY_FUNCTION__);
+    return std::nullopt;
+  }
+  if (m_is_listening) {
+    LLDB_LOGF(log, "%s already listening", __PRETTY_FUNCTION__);
+    return std::nullopt;
+  }
+  m_is_listening = true;
+  LLDB_LOGF(log, "%s trying to listen on port 0", __PRETTY_FUNCTION__);
+  llvm::Expected<std::unique_ptr<TCPSocket>> sock = 
+      Socket::TcpListen("localhost:0", 5);
+  if (sock) {
+    GPUPluginConnectionInfo connection_info;
+    const uint16_t listen_port = (*sock)->GetLocalPortNumber();
+    connection_info.connect_url = llvm::formatv("connect://localhost:{}", 
+                                                listen_port);
+    LLDB_LOGF(log, "%s listening to %u", __PRETTY_FUNCTION__, listen_port);
+    std::thread t(&LLDBServerPluginMockGPU::AcceptAndMainLoopThread, this, 
+                  std::move(*sock));
+    t.detach();
+    return connection_info;
+  } else {
+    std::string error = llvm::toString(sock.takeError());
+    LLDB_LOGF(log, "%s failed to listen to localhost:0: %s", 
+              __PRETTY_FUNCTION__, error.c_str());
   }
+  m_is_listening = false;
   return std::nullopt;
 }
 
@@ -190,7 +181,7 @@ LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
     response.disable_bp = true;
     LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\") disabling breakpoint", 
               bp_identifier.c_str());
-    // response.connect_url = GetConnectionURL();
+    response.connect_info = CreateConnection();
   }
   return response;
 }
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index 35093f2853138..f1d4dbbe29001 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -12,6 +12,43 @@
 #include "Plugins/Process/gdb-remote/LLDBServerPlugin.h"
 #include "lldb/Utility/Status.h"
 
+// This is a mock GPU plugin that is used for testing the LLDBServerPlugin. It
+// should be run with the following code as the main binary:
+/*
+
+$ cat main.cpp
+#include <stdio.h>
+
+struct ShlibInfo {
+  const char *path = nullptr;
+  ShlibInfo *next = nullptr;
+};
+
+ShlibInfo g_shlib_list = { "/tmp/a.out", nullptr};
+
+int gpu_initialize() {
+  return puts(__FUNCTION__);
+}
+int gpu_shlib_load() {
+  return puts(__FUNCTION__);
+}
+int main(int argc, const char **argv) {
+  gpu_initialize();
+  gpu_shlib_load();
+  return 0; // Break here
+}
+
+$ clang++ -g -O0 -o a.out main.cpp
+$ ninja lldb lldb-server
+$ ./bin/lldb a.out -o 'b /Break here/ -o run
+
+*/
+// If the above code is run, you will be stopped at the breakpoint and the Mock
+// GPU target will be selected. Try doing a "reg read --all" to see the state
+// of the GPU registers. Then you can select the native process target with 
+// "target select 0" and issue commands to the native process, and then select
+// the GPU target with "target select 1" and issue commands to the GPU target.
+
 namespace lldb_private {
   
   class TCPSocket;
@@ -25,7 +62,7 @@ class LLDBServerPluginMockGPU : public lldb_private::lldb_server::LLDBServerPlug
   llvm::StringRef GetPluginName() override;
   int GetEventFileDescriptorAtIndex(size_t idx) override;
   bool HandleEventFileDescriptorEvent(int fd) override;
-  std::optional<std::string> GetConnectionURL() override;
+  std::optional<GPUPluginConnectionInfo> CreateConnection() override;
   void InitializePluginInfo() override;
   GPUPluginBreakpointHitResponse 
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
index cc74acc9f6dba..06cba735736e1 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
@@ -101,11 +101,11 @@ enum CompilerRegNum : uint32_t {
   EH_FRAME_V7,
 };
 
-uint32_t g_gpr_regnums[32] = {LLDB_R0, LLDB_R1, LLDB_R2, LLDB_R3,
-                              LLDB_R4, LLDB_R5, LLDB_R6, LLDB_R7,
-                              LLDB_SP, LLDB_FP, LLDB_PC, LLDB_Flags};
-uint32_t g_vec_regnums[32] = {LLDB_V0, LLDB_V1, LLDB_V2, LLDB_V3,
-                              LLDB_V4, LLDB_V5, LLDB_V6, LLDB_V7};
+uint32_t g_gpr_regnums[] = {LLDB_R0, LLDB_R1, LLDB_R2, LLDB_R3,
+                            LLDB_R4, LLDB_R5, LLDB_R6, LLDB_R7,
+                            LLDB_SP, LLDB_FP, LLDB_PC, LLDB_Flags};
+uint32_t g_vec_regnums[] = {LLDB_V0, LLDB_V1, LLDB_V2, LLDB_V3,
+                            LLDB_V4, LLDB_V5, LLDB_V6, LLDB_V7};
 
 static const RegisterSet g_reg_sets[] = {
     {"General Purpose Registers", "gpr",

>From 9523096ba8b658f24c1cce03609d5a0b1073c7ce Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 16 Apr 2025 11:41:08 -0700
Subject: [PATCH 09/34] Add arch and target triple support for AMD and NVidia
 GPUs.

---
 lldb/include/lldb/Utility/ArchSpec.h |  6 ++++++
 lldb/source/Utility/ArchSpec.cpp     | 21 ++++++++++++++++++++-
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h
index 7e9bc23a75acb..b6d6e4801302c 100644
--- a/lldb/include/lldb/Utility/ArchSpec.h
+++ b/lldb/include/lldb/Utility/ArchSpec.h
@@ -236,6 +236,12 @@ class ArchSpec {
 
     eCore_wasm32,
 
+    eCore_amd_gpu_r600,
+    eCore_amd_gpu_gcn,
+
+    eCore_nvidia_nvptx,
+    eCore_nvidia_nvptx64,
+
     kNumCores,
 
     kCore_invalid,
diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp
index 495215459336a..3cdde2f1f822e 100644
--- a/lldb/source/Utility/ArchSpec.cpp
+++ b/lldb/source/Utility/ArchSpec.cpp
@@ -248,7 +248,15 @@ static const CoreDefinition g_core_definitions[] = {
 
     {eByteOrderLittle, 4, 1, 4, llvm::Triple::wasm32, ArchSpec::eCore_wasm32,
      "wasm32"},
-};
+    {eByteOrderLittle, 4, 4, 4, llvm::Triple::r600, 
+      ArchSpec::eCore_amd_gpu_r600,"r600"},
+    {eByteOrderLittle, 8, 4, 4, llvm::Triple::amdgcn, 
+      ArchSpec::eCore_amd_gpu_gcn, "amdgcn"},
+    {eByteOrderLittle, 4, 4, 4, llvm::Triple::nvptx, 
+      ArchSpec::eCore_nvidia_nvptx,"nvptx"},
+    {eByteOrderLittle, 8, 4, 4, llvm::Triple::nvptx64, 
+      ArchSpec::eCore_nvidia_nvptx64, "nvptx64"},
+  };
 
 // Ensure that we have an entry in the g_core_definitions for each core. If you
 // comment out an entry above, you will need to comment out the corresponding
@@ -432,6 +440,14 @@ static const ArchDefinitionEntry g_elf_arch_entries[] = {
     {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH,
      ArchSpec::eLoongArchSubType_loongarch64, 0xFFFFFFFFu,
      0xFFFFFFFFu}, // loongarch64
+    {ArchSpec::eCore_amd_gpu_r600, llvm::ELF::EM_AMDGPU, LLDB_INVALID_CPUTYPE,
+     0xFFFFFFFFu, 0xFFFFFFFFu},
+    {ArchSpec::eCore_amd_gpu_gcn, llvm::ELF::EM_AMDGPU, LLDB_INVALID_CPUTYPE,
+     0xFFFFFFFFu, 0xFFFFFFFFu},
+    {ArchSpec::eCore_nvidia_nvptx, llvm::ELF::EM_CUDA, LLDB_INVALID_CPUTYPE,
+     0xFFFFFFFFu, 0xFFFFFFFFu},
+    {ArchSpec::eCore_nvidia_nvptx64, llvm::ELF::EM_CUDA, LLDB_INVALID_CPUTYPE,
+     0xFFFFFFFFu, 0xFFFFFFFFu},
 };
 
 static const ArchDefinition g_elf_arch_def = {
@@ -920,6 +936,9 @@ bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu,
           case llvm::ELF::ELFOSABI_STANDALONE:
             m_triple.setOS(llvm::Triple::OSType::UnknownOS);
             break;
+          case llvm::ELF::ELFOSABI_AMDGPU_HSA:
+            m_triple.setOS(llvm::Triple::OSType::AMDHSA);
+            break;            
           }
         } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) {
           m_triple.setVendor(llvm::Triple::PC);

>From 0fc9010d9afc11aef80b9873b4e12e68506871b4 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 22 Apr 2025 22:26:00 -0700
Subject: [PATCH 10/34] Changes to the plug-in models.

This current patch allows GPU plug-ins to return GPUActions structs at
various times:
- Each plugin must return a GPUActions to response to the function
  GPUActions LLDBServerPlugin::GetInitializeActions() which replaces
  the old and deprecated void LLDBServerPlugin::InitializePluginInfo()
- Optionally to a LLDBServerPlugin::NativeProcessIsStopping() call each
  time the process stops
- In a breakpoint was hit response.

This allows many ways to set breakpoints at various times. The
GPUActions struct also allows a connection to be made at all three of
the above times for GPUActions.

Changes:
- The SymbolValue class now has its "value" as an optional uint64_t. If
the symbol value that is sent to the GPU plugin during a breakpoint was
hit method call doesn't have a value, then the symbol is not avaiable or
can't be resolved to a load address.
- Removed the GPUPluginInfo class, plugins now return a GPUActions
  object.
- Added a GPUActions object that has the plug-in name, any breakpoints
  that need to be set, and an optional connection info struct. This
  allows plug-ins to return a GPUActions in response to the initializing
  of the plugin, breakpoint was hit or each time the native process stops.
- Added GDBRemoteCommunicationServerLLGS::GetCurrentProcess() to allow
  plug-ins to access the current NativeProcessProtocol.
- Removed LLDBServerPlugin::GetPluginInfo() and
  LLDBServerPlugin::InitializePluginInfo(). Clients now must implement
  the GPUActions LLDBServerPlugin::GetInitializeActions()
- Removed the virtual CreateConnection() from LLDBServerPlugin.
- Updated the Mock GPU to set a new breakpoint on the third stop to test
  that we can return a GPUActions from a native process stop.
---
 .../lldb/Utility/GPUGDBRemotePackets.h        | 43 ++++++++++-------
 .../GDBRemoteCommunicationClient.cpp          | 10 ++--
 .../gdb-remote/GDBRemoteCommunicationClient.h |  2 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      | 10 ++--
 .../GDBRemoteCommunicationServerLLGS.h        |  4 ++
 .../Process/gdb-remote/LLDBServerPlugin.cpp   |  6 ---
 .../Process/gdb-remote/LLDBServerPlugin.h     | 44 ++++++------------
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 38 ++++++++-------
 .../Process/gdb-remote/ProcessGDBRemote.h     |  3 ++
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 46 +++++++++----------
 .../MockGPU/LLDBServerPluginMockGPU.cpp       | 34 +++++++++++---
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  9 ++--
 12 files changed, 134 insertions(+), 115 deletions(-)

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 240a58758be77..97a66eb48c54f 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -18,8 +18,10 @@ namespace lldb_private {
 
 /// A class that represents a symbol value
 struct SymbolValue {
+  /// Name of the symbol.
   std::string name;
-  uint64_t value;
+  /// If the optional doesn't have a value, then the symbol was not available.
+  std::optional<uint64_t> value;
 };
 bool fromJSON(const llvm::json::Value &value, SymbolValue &data, 
               llvm::json::Path path);
@@ -39,18 +41,6 @@ bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
 
 llvm::json::Value toJSON(const GPUBreakpointInfo &data);
 
-
-struct GPUPluginInfo {
-  std::string name;
-  std::string description;
-  std::vector<GPUBreakpointInfo> breakpoints;
-};
-
-bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &data,
-  llvm::json::Path path);
-
-llvm::json::Value toJSON(const GPUPluginInfo &data);
-
 struct GPUPluginBreakpointHitArgs {
   std::string plugin_name;
   GPUBreakpointInfo breakpoint;
@@ -84,6 +74,28 @@ bool fromJSON(const llvm::json::Value &value, GPUPluginConnectionInfo &data,
 
 llvm::json::Value toJSON(const GPUPluginConnectionInfo &data);
 
+///-----------------------------------------------------------------------------
+/// GPUActions
+///
+/// A structure that contains action to be taken after a stop or breakpoint hit
+/// event.
+///-----------------------------------------------------------------------------
+struct GPUActions {
+  /// The name of the plugin.
+  std::string plugin_name;
+  /// Optional new breakpoints to set.
+  std::optional<std::vector<GPUBreakpointInfo>> breakpoints;
+  /// If a GPU connection is available return a connect URL to use to reverse
+  /// connect to the GPU GDB server.
+  std::optional<GPUPluginConnectionInfo> connect_info;
+};
+
+bool fromJSON(const llvm::json::Value &value, 
+  GPUActions &data,
+  llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUActions &data);
+
 ///-----------------------------------------------------------------------------
 /// GPUPluginBreakpointHitResponse
 ///
@@ -94,10 +106,7 @@ struct GPUPluginBreakpointHitResponse {
   ///< Set to true if this berakpoint should be disabled.
   bool disable_bp = false; 
   /// Optional new breakpoints to set.
-  std::optional<std::vector<GPUBreakpointInfo>> breakpoints;
-  /// If a GPU connection is available return a connect URL to use to reverse
-  /// connect to the GPU GDB server.
-  std::optional<GPUPluginConnectionInfo> connect_info;
+  GPUActions actions;
 };
 
 bool fromJSON(const llvm::json::Value &value, 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 4c938b50e47b3..2287fa2b1e93f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -604,8 +604,8 @@ StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
   return object_sp;
 }
 
-std::optional<std::vector<GPUPluginInfo>> 
-GDBRemoteCommunicationClient::GetGPUPluginInfos() {
+std::optional<std::vector<GPUActions>> 
+GDBRemoteCommunicationClient::GetGPUInitializeActions() {
   // Get JSON information containing any breakpoints and other information
   // required by any GPU plug-ins using the "jGPUPluginInitialize" packet.
   //
@@ -622,9 +622,9 @@ GDBRemoteCommunicationClient::GetGPUPluginInfos() {
       if (response.IsUnsupportedResponse()) {
         m_supports_gpu_plugins = eLazyBoolNo;
       } else if (!response.Empty()) {
-        if (llvm::Expected<std::vector<GPUPluginInfo>> info = 
-                llvm::json::parse<std::vector<GPUPluginInfo>>(response.Peek(), 
-                                                              "GPUPluginInfo"))
+        if (llvm::Expected<std::vector<GPUActions>> info = 
+                llvm::json::parse<std::vector<GPUActions>>(response.Peek(), 
+                                                            "GPUActions"))
           return std::move(*info);
         else
           llvm::consumeError(info.takeError());
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 0fae435b0a741..6666cdd730c58 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -434,7 +434,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   StructuredData::ObjectSP GetThreadsInfo();
 
-  std::optional<std::vector<GPUPluginInfo>> GetGPUPluginInfos();
+  std::optional<std::vector<GPUActions>> GetGPUInitializeActions();
 
   std::optional<GPUPluginBreakpointHitResponse> 
   GPUBreakpointHit(const GPUPluginBreakpointHitArgs &args);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index a403f1f8e0a8c..914be713feb08 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -1101,9 +1101,9 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
   // Check if any plug-ins have new connections
   StreamGDBRemote extra_stop_reply_args;
   for (auto &plugin_up : m_plugins) {
-    if (std::optional<GPUPluginConnectionInfo> connection_info = 
+    if (std::optional<GPUActions> connection_info = 
           plugin_up->NativeProcessIsStopping()) {
-      extra_stop_reply_args.PutCString("gpu-connection:");
+      extra_stop_reply_args.PutCString("gpu-actions:");
       extra_stop_reply_args.PutAsJSON(*connection_info, /*hex_ascii=*/true);
       extra_stop_reply_args.PutChar(';');
     } 
@@ -3692,11 +3692,11 @@ GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo(
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize(
     StringExtractorGDBRemote &) {
-  std::vector<GPUPluginInfo> infos;
+  std::vector<GPUActions> gpu_actions;
   for (auto &plugin_up: m_plugins)
-      infos.push_back(plugin_up->GetPluginInfo());
+    gpu_actions.push_back(plugin_up->GetInitializeActions());
   StreamGDBRemote response;
-  response.PutAsJSONArray(infos);
+  response.PutAsJSONArray(gpu_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 ffb6cc542ed58..bd3ec4ba96b35 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -102,6 +102,10 @@ class GDBRemoteCommunicationServerLLGS
     Flag flags;
   };
 
+  NativeProcessProtocol *GetCurrentProcess() {
+    return m_current_process;
+  }
+
 protected:
   MainLoop &m_mainloop;
   MainLoop::ReadHandleUP m_network_handle_up;
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
index 4f4072082160b..6db7120a245a2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
@@ -17,9 +17,3 @@ LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process) :
   m_native_process(native_process) {}
 
 LLDBServerPlugin::~LLDBServerPlugin() {}
-
-const GPUPluginInfo &LLDBServerPlugin::GetPluginInfo() {
-  if (m_info.name.empty())
-    InitializePluginInfo();
-  return m_info;
-}
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index a8673e2e5abaa..6bafce998ea95 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -59,42 +59,27 @@ class LLDBServerPlugin {
   /// for a connection to be made before trying to do any blocking code. The 
   /// plug-in should assume the users do not want to use any features unless a 
   /// connection is made.
-  virtual std::optional<GPUPluginConnectionInfo> NativeProcessIsStopping() {
+  virtual std::optional<GPUActions> NativeProcessIsStopping() {
     return std::nullopt;
   };
 
-  /// Get an connection URL to connect to this plug-in.
-  ///
-  /// This function will get called when the plug-in is ready to be activated
-  /// and connected to LLDB. The plug-in should create a connection that listens
-  /// for a connection from LLDB and it should fill in the connection URL 
-  /// with a valid URL that can be used with "process connect". Typical plug-ins
-  /// will listen on a TCP port or a Unix domain socket and spin up a thread
-  /// that will accept the connection and run the main loop. 
-  virtual std::optional<GPUPluginConnectionInfo> CreateConnection() = 0;
-
-  /// Get the GPU plug-in information.
+  /// Get the GPU plug-in initialization actions.
   ///
   /// Each GPU plugin can return a structure that describes the GPU plug-in and 
-  /// the breakpoints it requires in the native process. GPU plug-ins might want
+  /// any actions that should be performed right away .Actions include setting
+  /// any breakpoints it requires in the native process. GPU plug-ins might want
   /// to set breakpoints in the native process to know when the GPU has been 
   /// initialized, or when the GPU has shared libraries that get loaded.
-  /// They can do this by populating the LLDBServerPlugin::m_info structure 
-  /// when this function gets called. The contents of this structure will be
-  /// converted to JSON and sent to the LLDB client. The structure allows 
-  /// plug-ins to set breakpoints by name and can also request symbol values
-  /// that should be sent when the breakpoint gets hit.
-  /// When the breakpoint is hit, the LLDBServerPlugin::BreakpointWasHit(...)
-  /// method will get called with a structure that identifies the plugin,
+  /// They can do this by populating returning any actions needed when this
+  /// function is called.
+  /// 
+  /// The contents of this structure will be converted to JSON and sent to the 
+  /// LLDB client. The structure allows plug-ins to set breakpoints by name 
+  /// and to also request symbol values that should be sent when the breakpoint
+  /// gets hit. When the breakpoint is hit, the BreakpointWasHit(...) method
+  /// will get called with a structure that identifies the plugin,
   /// breakpoint and it will supply any requested symbol values.
-  virtual void InitializePluginInfo() = 0;
-
-  /// Get the plugin info.
-  ///
-  /// This function will call InitializePluginInfo() one time to intialize the
-  /// information. Plug-ins must override InitializePluginInfo() and fill in
-  /// the structure in the LLDBServerPlugin::m_info instance variable.
-  const GPUPluginInfo &GetPluginInfo();
+  virtual GPUActions GetInitializeActions() = 0;
 
   /// Get a file descriptor to listen for in the ptrace epoll loop.
   ///
@@ -126,10 +111,9 @@ class LLDBServerPlugin {
 
   protected:
     std::mutex m_connect_mutex;
-    GPUPluginInfo m_info;
 };
 
 } // namespace lldb_server
 } // namespace lldb_private
 
-#endif
+#endif // LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGIN_H
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 94b0e62cdbfcc..f26f25349d83c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -845,13 +845,14 @@ bool ProcessGDBRemote::GPUBreakpointHit(void *baton,
                                                     sc_list);
       SymbolContext sc;
       args.symbol_values[i].name = symbol_name;
-      args.symbol_values[i].value = LLDB_INVALID_ADDRESS;
       size_t num_symbols = sc_list.GetSize();
       for (size_t sc_idx=0; sc_idx<num_symbols; ++sc_idx) {
         if (sc_list.GetContextAtIndex(sc_idx, sc)) {
-          args.symbol_values[i].value = sc.symbol->GetAddress().GetLoadAddress(&target);
-          if (args.symbol_values[i].value != LLDB_INVALID_ADDRESS)
+          auto load_addr = sc.symbol->GetAddress().GetLoadAddress(&target);
+          if (load_addr != LLDB_INVALID_ADDRESS) {
+            args.symbol_values[i].value = load_addr;
             break;
+          }
         }
       }
     }
@@ -866,15 +867,20 @@ bool ProcessGDBRemote::GPUBreakpointHit(void *baton,
       if (bp_sp) 
         bp_sp->SetEnabled(false);
     }
-    //  Set any new breakpoints if requested.
-    if (response->breakpoints)
-      HandleGPUBreakpoints(args.plugin_name, response->breakpoints.value());
-    if (response->connect_info)
-      HandleConnectionRequest(*response->connect_info);
+    HandleGPUActions(response->actions);
   }
   return false; // Don't stop, auto continue.
 }
 
+Status ProcessGDBRemote::HandleGPUActions(const GPUActions &gpu_action) {
+  Status error;
+  if (gpu_action.breakpoints)
+    HandleGPUBreakpoints(gpu_action.plugin_name, *gpu_action.breakpoints);
+  if (gpu_action.connect_info)
+    error = HandleConnectionRequest(*gpu_action.connect_info);
+  return error;
+}
+
 Status ProcessGDBRemote::HandleConnectionRequest(
     const GPUPluginConnectionInfo &connection_info) {
   Log *log = GetLog(GDBRLog::Plugin);
@@ -1006,9 +1012,9 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
   m_gdb_comm.GetVContSupported('c');
   m_gdb_comm.GetVAttachOrWaitSupported();
   m_gdb_comm.EnableErrorStringInPacket();
-  if (auto plugin_infos = m_gdb_comm.GetGPUPluginInfos()) {
-    for (const auto &plugin_info: *plugin_infos)
-      HandleGPUBreakpoints(plugin_info.name, plugin_info.breakpoints);
+  if (auto init_actions = m_gdb_comm.GetGPUInitializeActions()) {
+    for (const auto &init_action: *init_actions)
+      HandleGPUActions(init_action);
   }
   // First dispatch any commands from the platform:
   auto handle_cmds = [&] (const Args &args) ->  void {
@@ -2541,12 +2547,12 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
         if (!value.getAsInteger(0, addressing_bits)) {
           addressable_bits.SetHighmemAddressableBits(addressing_bits);
         }
-      } else if (key.compare("gpu-connection") == 0) {
+      } else if (key.compare("gpu-actions") == 0) {
         StringExtractorGDBRemote extractor(value);
-        std::optional<GPUPluginConnectionInfo> connect_info = 
-            extractor.GetFromJSONHexASCII<GPUPluginConnectionInfo>();
-        if (connect_info)
-          HandleConnectionRequest(*connect_info);
+        if (std::optional<GPUActions> gpu_actions = 
+            extractor.GetFromJSONHexASCII<GPUActions>()) {
+          HandleGPUActions(*gpu_actions);
+        }
       } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
         uint32_t reg = UINT32_MAX;
         if (!key.getAsInteger(16, reg))
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 9d58d453e4b5e..d2f6ed57c31a8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -451,6 +451,9 @@ class ProcessGDBRemote : public Process,
                             const std::vector<GPUBreakpointInfo> &breakpoints);
 
   Status HandleConnectionRequest(const GPUPluginConnectionInfo &connection_info);
+
+  Status HandleGPUActions(const GPUActions &gpu_action);
+
   // ContinueDelegate interface
   void HandleAsyncStdout(llvm::StringRef out) override;
   void HandleAsyncMisc(llvm::StringRef data) override;
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index c7cd0a63d116a..987aae827f834 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -48,27 +48,6 @@ llvm::json::Value toJSON(const GPUBreakpointInfo &data) {
           });
 }
 
-//------------------------------------------------------------------------------
-// GPUPluginInfo
-//------------------------------------------------------------------------------
-
-bool fromJSON(const llvm::json::Value &value, GPUPluginInfo &data,
-              llvm::json::Path path) {
-  ObjectMapper o(value, path);
-  return o && 
-         o.map("name", data.name) &&
-         o.map("description", data.description) &&
-         o.map("breakpoints", data.breakpoints);      
-}
-
-llvm::json::Value toJSON(const GPUPluginInfo &data) {
-  return json::Value(
-    Object{{"name", data.name}, 
-           {"description", data.description},
-           {"breakpoints", data.breakpoints},
-          });
-}
-
 //------------------------------------------------------------------------------
 // GPUPluginConnectionInfo
 //------------------------------------------------------------------------------
@@ -112,6 +91,25 @@ json::Value toJSON(const GPUPluginBreakpointHitArgs &data) {
             });
 }
 
+//------------------------------------------------------------------------------
+// GPUActions
+//------------------------------------------------------------------------------
+bool fromJSON(const llvm::json::Value &value, GPUActions &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("plugin_name", data.plugin_name) &&
+         o.mapOptional("breakpoints", data.breakpoints) &&
+         o.mapOptional("connect_info", data.connect_info);
+}
+
+llvm::json::Value toJSON(const GPUActions &data) {
+  return json::Value(
+    Object{{"plugin_name", data.plugin_name},
+           {"breakpoints", data.breakpoints},
+           {"connect_info", data.connect_info},
+          });
+}
 
 //------------------------------------------------------------------------------
 // GPUPluginBreakpointHitResponse
@@ -124,15 +122,13 @@ bool fromJSON(const llvm::json::Value &value,
   ObjectMapper o(value, path);
   return o && 
          o.map("disable_bp", data.disable_bp) &&
-         o.mapOptional("breakpoints", data.breakpoints) &&
-         o.mapOptional("connect_info", data.connect_info);
+         o.map("actions", data.actions);
 }
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data) {
   return json::Value(
     Object{{"disable_bp", data.disable_bp}, 
-           {"breakpoints", data.breakpoints},
-           {"connect_info", data.connect_info},
+           {"actions", data.actions},
           });
 }
 
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index dc37ca731b84b..b530cb751cc5d 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -166,6 +166,24 @@ std::optional<GPUPluginConnectionInfo> LLDBServerPluginMockGPU::CreateConnection
   return std::nullopt;
 }
 
+std::optional<GPUActions> LLDBServerPluginMockGPU::NativeProcessIsStopping() {
+  NativeProcessProtocol *native_process = m_native_process.GetCurrentProcess();
+  // Show that we can return a valid GPUActions object from a stop event.
+  if (native_process->GetStopID() == 3) {
+    GPUActions actions;
+    actions.plugin_name = GetPluginName();
+    GPUBreakpointInfo bp;
+    bp.identifier = "3rd stop breakpoint";
+    bp.shlib = "a.out";
+    bp.function_name = "gpu_third_stop";
+    std::vector<GPUBreakpointInfo> breakpoints;
+    breakpoints.emplace_back(std::move(bp));
+    actions.breakpoints = std::move(breakpoints);
+    return actions;
+  }
+  return std::nullopt;
+}
+
 GPUPluginBreakpointHitResponse 
 LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
   Log *log = GetLog(GDBRLog::Plugin);
@@ -177,18 +195,19 @@ LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
             bp_identifier.c_str(), json_string.c_str());
 
   GPUPluginBreakpointHitResponse response;
+  response.actions.plugin_name = GetPluginName();
   if (bp_identifier == "gpu_initialize") {
     response.disable_bp = true;
     LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\") disabling breakpoint", 
               bp_identifier.c_str());
-    response.connect_info = CreateConnection();
+    response.actions.connect_info = CreateConnection();
   }
   return response;
 }
 
-void LLDBServerPluginMockGPU::InitializePluginInfo() {
-  m_info.name = GetPluginName();
-  m_info.description = "Mock GPU plugin";
+GPUActions LLDBServerPluginMockGPU::GetInitializeActions() {
+  GPUActions init_actions;
+  init_actions.plugin_name = GetPluginName();
   
   GPUBreakpointInfo bp1;
   bp1.identifier = "gpu_initialize";
@@ -204,6 +223,9 @@ void LLDBServerPluginMockGPU::InitializePluginInfo() {
   bp2.symbol_names.push_back("g_shlib_list");
   bp2.symbol_names.push_back("invalid_symbol");
 
-  m_info.breakpoints.emplace_back(std::move(bp1));
-  m_info.breakpoints.emplace_back(std::move(bp2));
+  std::vector<GPUBreakpointInfo> breakpoints;
+  breakpoints.emplace_back(std::move(bp1));
+  breakpoints.emplace_back(std::move(bp2));
+  init_actions.breakpoints = std::move(breakpoints);
+  return init_actions;
 }
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index f1d4dbbe29001..3dcee3b6f3bb7 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -55,19 +55,20 @@ namespace lldb_private {
 
 namespace lldb_server {
 
-class LLDBServerPluginMockGPU : public lldb_private::lldb_server::LLDBServerPlugin {
+class LLDBServerPluginMockGPU : public LLDBServerPlugin {
 public:
-  LLDBServerPluginMockGPU(lldb_private::lldb_server::LLDBServerPlugin::GDBServer &native_process);
+  LLDBServerPluginMockGPU(LLDBServerPlugin::GDBServer &native_process);
   ~LLDBServerPluginMockGPU() override;
   llvm::StringRef GetPluginName() override;
   int GetEventFileDescriptorAtIndex(size_t idx) override;
   bool HandleEventFileDescriptorEvent(int fd) override;
-  std::optional<GPUPluginConnectionInfo> CreateConnection() override;
-  void InitializePluginInfo() override;
+  GPUActions GetInitializeActions() override;
+  std::optional<struct GPUActions> NativeProcessIsStopping() override;  
   GPUPluginBreakpointHitResponse 
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
 
 private:
+  std::optional<GPUPluginConnectionInfo> CreateConnection();
   void CloseFDs();
   void AcceptAndMainLoopThread(std::unique_ptr<TCPSocket> listen_socket_up);
 

>From 6b18f90f1063e59903cad19a6e2fafdfceb71680 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 29 Apr 2025 15:38:31 -0700
Subject: [PATCH 11/34] Clean up the register context code for the Mock GPU.

Cleaned up the register context code to auto generate the register
offset in the RegisterContext class. Also rename m_regs_valid to
m_reg_value_is_valid to be more clear that this represents if the
value of the register is valid in the data structures.
---
 .../MockGPU/RegisterContextMockGPU.cpp        | 51 ++++++++++---------
 .../Plugins/MockGPU/RegisterContextMockGPU.h  | 28 ++++++----
 2 files changed, 43 insertions(+), 36 deletions(-)

diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
index 06cba735736e1..18a3ffc35fa07 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.cpp
@@ -116,12 +116,13 @@ static const RegisterSet g_reg_sets[] = {
 
 /// Define all of the information about all registers. The register info structs
 /// are accessed by the LLDB register numbers, which are defined above.
+#define REG_OFFSET(Reg) offsetof(RegisterContextMockGPU::RegisterContext, Reg)
 static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
     {
         "R0",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        0,             // RegisterInfo::byte_offset
+        REG_OFFSET(R0),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -140,7 +141,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R1",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        8,             // RegisterInfo::byte_offset
+        REG_OFFSET(R1),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -159,7 +160,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R2",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        16,            // RegisterInfo::byte_offset
+        REG_OFFSET(R2),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -178,7 +179,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R3",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        24,            // RegisterInfo::byte_offset
+        REG_OFFSET(R3),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -197,7 +198,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R4",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        32,            // RegisterInfo::byte_offset
+        REG_OFFSET(R4),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -216,7 +217,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R5",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        40,            // RegisterInfo::byte_offset
+        REG_OFFSET(R5),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -235,7 +236,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R6",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        48,            // RegisterInfo::byte_offset
+        REG_OFFSET(R6),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -254,7 +255,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "R7",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        56,            // RegisterInfo::byte_offset
+        REG_OFFSET(R7),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -273,7 +274,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "SP",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        64,            // RegisterInfo::byte_offset
+        REG_OFFSET(SP),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -292,7 +293,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "FP",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        72,            // RegisterInfo::byte_offset
+        REG_OFFSET(FP),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -311,7 +312,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "PC",          // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        80,            // RegisterInfo::byte_offset
+        REG_OFFSET(PC),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -330,7 +331,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "Flags",       // RegisterInfo::name
         nullptr,       // RegisterInfo::alt_name
         8,             // RegisterInfo::byte_size
-        88,            // RegisterInfo::byte_offset
+        REG_OFFSET(Flags),// RegisterInfo::byte_offset
         eEncodingUint, // RegisterInfo::encoding
         eFormatHex,    // RegisterInfo::format
         {
@@ -349,7 +350,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V0",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        96,                    // RegisterInfo::byte_offset
+        REG_OFFSET(V0),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -368,7 +369,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V1",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        104,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V1),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -387,7 +388,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V2",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        112,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V2),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -406,7 +407,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V3",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        120,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V3),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -425,7 +426,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V4",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        128,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V4),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -444,7 +445,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V5",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        136,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V5),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -463,7 +464,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V6",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        144,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V6),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -482,7 +483,7 @@ static const RegisterInfo g_reg_infos[LLDBRegNum::kNumRegs] = {
         "V7",                  // RegisterInfo::name
         nullptr,               // RegisterInfo::alt_name
         8,                     // RegisterInfo::byte_size
-        152,                   // RegisterInfo::byte_offset
+        REG_OFFSET(V7),        // RegisterInfo::byte_offset
         eEncodingVector,       // RegisterInfo::encoding
         eFormatVectorOfUInt32, // RegisterInfo::format
         {
@@ -509,20 +510,20 @@ RegisterContextMockGPU::RegisterContextMockGPU(
 void RegisterContextMockGPU::InitRegisters() {
   for (size_t i = 0; i < kNumRegs; ++i)
     m_regs.data[i] = 0;
-  m_regs_valid.resize(kNumRegs, false);
+  m_reg_value_is_valid.resize(kNumRegs, false);
 }
 
 void RegisterContextMockGPU::InvalidateAllRegisters() {
   // Do what ever book keeping we need to do to indicate that all register
   // values are now invalid.
   for (uint32_t i = 0; i < kNumRegs; ++i)
-    m_regs_valid[i] = false;
+    m_reg_value_is_valid[i] = false;
 }
 
 Status RegisterContextMockGPU::ReadRegs() {
   // Fill all registers with unique values.
   for (uint32_t i = 0; i < kNumRegs; ++i) {
-    m_regs_valid[i] = true;
+    m_reg_value_is_valid[i] = true;
     m_regs.data[i] = i;
   }
   return Status();
@@ -556,7 +557,7 @@ Status RegisterContextMockGPU::ReadRegister(const RegisterInfo *reg_info,
                                             RegisterValue &reg_value) {
   Status error;
   const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB];
-  if (!m_regs_valid[lldb_reg_num])
+  if (!m_reg_value_is_valid[lldb_reg_num])
     error = ReadRegs();
   if (error.Fail())
     return error;
@@ -572,7 +573,7 @@ Status RegisterContextMockGPU::WriteRegister(const RegisterInfo *reg_info,
   if (!success)
     return Status::FromErrorString("register write failed");
   m_regs.data[lldb_reg_num] = new_value;
-  m_regs_valid[lldb_reg_num] = true;
+  m_reg_value_is_valid[lldb_reg_num] = true;
   return Status();
 }
 
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h
index 8992fe0e02205..e80b7fec263a3 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/RegisterContextMockGPU.h
@@ -43,15 +43,8 @@ class RegisterContextMockGPU : public NativeRegisterContext {
   std::vector<uint32_t>
   GetExpeditedRegisters(ExpeditedRegs expType) const override;
 
-private:
-  void InitRegisters();
-  void InvalidateAllRegisters();
-  Status ReadRegs();
-
-  // All mock GPU registers are contained in this buffer.
-  union {
-    uint64_t data[20]; // Allow for indexed access to each register value.
-    struct {           // Define a struct for each register value.
+    // A storage stucture for all registers;
+    struct RegisterContext {
       uint64_t R0;
       uint64_t R1;
       uint64_t R2;
@@ -72,9 +65,22 @@ class RegisterContextMockGPU : public NativeRegisterContext {
       uint64_t V5;
       uint64_t V6;
       uint64_t V7;
-    } regs;
+    };
+  
+private:
+  void InitRegisters();
+  void InvalidateAllRegisters();
+  Status ReadRegs();
+
+
+  // All mock GPU registers are contained in this buffer.
+  union {
+    /// Allow for indexed access to each register value.
+    uint64_t data[sizeof(RegisterContext)/sizeof(uint64_t)];
+    /// Allow for direct access to the register values by name.
+    RegisterContext regs;
   } m_regs;
-  std::vector<bool> m_regs_valid;
+  std::vector<bool> m_reg_value_is_valid;
 };
 
 } // namespace lldb_server

>From 3133713a5c9e78e930cc8801130adba65bfea07e Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 29 Apr 2025 22:13:33 -0700
Subject: [PATCH 12/34] Added the ability for GPU plug-ins to return shared
 libraries.

Added a "jGPUPluginGetDynamicLoaderLibraryInfo" packet that gets GPU
dynamic loader information from a GPU. It does this when the stop reason
for a thread is set to eStopReasonDynammicLoader. The
NativeProcessProtocol::GetGPUDynamicLoaderLibraryInfos() will get called
with the arguments to get the full list of shared libraries or a partial
list.

The shared libraries can be specified in many ways:
- path to a library on disk for self contains object files
- path to a file on disk that contains an object file at a file offset
  and file size
- native process memory location where the object file is loaded and
  should be read from

Shared libraries can then specify how to get loaded by:
- Specify a load address where all sections will be slid evenly
- Specify each section within an ELF file and exactly where they should
  be loaded, this can include subsections being loaded
---
 .../lldb/Host/common/NativeProcessProtocol.h  |  9 +-
 .../lldb/Utility/GPUGDBRemotePackets.h        | 80 +++++++++++++++++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  3 +-
 lldb/include/lldb/lldb-enumerations.h         |  5 ++
 lldb/source/API/SBThread.cpp                  |  2 +
 .../GDBRemoteCommunicationClient.cpp          | 24 ++++-
 .../gdb-remote/GDBRemoteCommunicationClient.h |  3 +
 .../GDBRemoteCommunicationServerLLGS.cpp      | 38 +++++++-
 .../GDBRemoteCommunicationServerLLGS.h        |  3 +
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 15 ++++
 lldb/source/Target/Process.cpp                |  1 +
 lldb/source/Target/Thread.cpp                 |  4 +-
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 80 +++++++++++++++++
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 .../MockGPU/LLDBServerPluginMockGPU.cpp       |  2 +-
 .../Plugins/MockGPU/ProcessMockGPU.cpp        | 89 ++++++++++++++++++-
 .../Plugins/MockGPU/ProcessMockGPU.h          | 33 ++++---
 .../Plugins/MockGPU/ThreadMockGPU.cpp         |  3 +-
 18 files changed, 370 insertions(+), 26 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index b8a59cd25d84f..e0c78158ec81e 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -17,6 +17,7 @@
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Iterable.h"
 #include "lldb/Utility/Status.h"
+#include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/TraceGDBRemotePackets.h"
 #include "lldb/Utility/UnimplementedError.h"
 #include "lldb/lldb-private-forward.h"
@@ -255,6 +256,11 @@ class NativeProcessProtocol {
   virtual Status GetFileLoadAddress(const llvm::StringRef &file_name,
                                     lldb::addr_t &load_addr) = 0;
 
+  virtual std::optional<GPUDynamicLoaderResponse> 
+  GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) {
+    return std::nullopt;
+  }
+
   /// Extension flag constants, returned by Manager::GetSupportedExtensions()
   /// and passed to SetEnabledExtension()
   enum class Extension {
@@ -267,8 +273,9 @@ class NativeProcessProtocol {
     memory_tagging = (1u << 6),
     savecore = (1u << 7),
     siginfo_read = (1u << 8),
+    json_dynamic_loader = (1u << 9),
 
-    LLVM_MARK_AS_BITMASK_ENUM(siginfo_read)
+    LLVM_MARK_AS_BITMASK_ENUM(json_dynamic_loader)
   };
 
   class Manager {
diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 97a66eb48c54f..626d73eedaa4f 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_UTILITY_GPUGDBREMOTEPACKETS_H
 #define LLDB_UTILITY_GPUGDBREMOTEPACKETS_H
 
+#include "lldb/lldb-types.h"
 #include "llvm/Support/JSON.h"
 #include <string>
 #include <vector>
@@ -115,6 +116,85 @@ bool fromJSON(const llvm::json::Value &value,
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data);
 
+struct GPUSectionInfo {
+  std::string name;
+  /// The load address of this section only. If this value is valid, then this
+  /// section is loaded at this address, else child sections can be loaded 
+  /// individually.
+  std::optional<lldb::addr_t> load_address;
+  /// Child sections that have individual load addresses can be specified.
+  std::vector<GPUSectionInfo> children;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUSectionInfo &data);
+
+struct GPUDynamicLoaderLibraryInfo {
+  std::string pathname;
+  /// Set to true if this shared library is being loaded, false if the library
+  /// is being unloaded.
+  bool load;
+  /// The address where the object file is loaded. If this member has a value
+  /// the object file is loaded at an address and all sections should be slid to
+  /// match this base address. If this member doesn't have a value, then 
+  /// individual section's load address must be specified individually if
+  /// \a loaded_sections has a value. If this doesn't have a value and the
+  /// \a loaded_Section doesn't have a value, this library will be unloaded.
+  std::optional<lldb::addr_t> load_address;
+
+  /// If this library is only available as an in memory image of an object file
+  /// in the native process, then this address holds the address from which the 
+  /// image can be read.
+  std::optional<lldb::addr_t> native_memory_address;
+  /// If this library is only available as an in memory image of an object file
+  /// in the native process, then this size of the in memory image that starts
+  /// at \a native_memory_address.
+  std::optional<lldb::addr_t> native_memory_size;
+  /// If the library exists inside of a file at an offset, \a file_offset will 
+  /// have a value that is the offset in bytes from the start of the file 
+  /// specified by \a pathname.
+  std::optional<uint64_t> file_offset;
+  /// If the library exists inside of a file at an offset, \a file_size will 
+  /// have a value that indicates the size in bytes of the object file.
+  std::optional<uint64_t> file_size;
+  /// If the object file specified by this structure has sections that get 
+  /// loaded at different times then this will not be empty. If it is empty
+  /// the \a load_address must be specified if \a load is true.
+  std::vector<GPUSectionInfo> loaded_sections;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data);
+
+
+struct GPUDynamicLoaderArgs {
+  /// Set to true to get all shared library information. Set to false to get
+  /// only the libraries that were updated since the last call to 
+  /// the "jGPUPluginGetDynamicLoaderLibraryInfo" packet.
+  bool full;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderArgs &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUDynamicLoaderArgs &data);
+
+struct GPUDynamicLoaderResponse {
+  /// Set to true to get all shared library information. Set to false to get
+  /// only the libraries that were updated since the last call to 
+  /// the "jGPUPluginGetDynamicLoaderLibraryInfo" packet.
+  std::vector<GPUDynamicLoaderLibraryInfo> library_infos;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderResponse &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUDynamicLoaderResponse &data);
+
 } // namespace lldb_private
 
 #endif // LLDB_UTILITY_GPUGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 3f05e29ca04c9..b6c03b91afb97 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -185,7 +185,8 @@ class StringExtractorGDBRemote : public StringExtractor {
 
     // GPU plug-in packets.
     eServerPacketType_jGPUPluginInitialize,
-    eServerPacketType_jGPUPluginBreakpointHit
+    eServerPacketType_jGPUPluginBreakpointHit,
+    eServerPacketType_jGPUPluginGetDynamicLoaderLibraryInfo,
   };
 
   ServerPacketType GetServerPacketType() const;
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 8e962428260f8..cbc97d71d50a9 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -260,6 +260,11 @@ enum StopReason {
   // Indicates that execution stopped because the debugger backend relies
   // on recorded data and we reached the end of that data.
   eStopReasonHistoryBoundary,
+  /// Inicates that the program stopped for a dynamic loader plugin to do some
+  /// work. The process will auto continue after the plugin is done loading or
+  /// unloading some shared libraries unless the target setting named
+  /// "target.process.stop-on-sharedlibrary-events" it set to true.
+  eStopReasonDynammicLoader
 };
 
 /// Command Return Status Types.
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index d9469fc1390d6..73f4145749349 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -173,6 +173,7 @@ size_t SBThread::GetStopReasonDataCount() {
         case eStopReasonProcessorTrace:
         case eStopReasonVForkDone:
         case eStopReasonHistoryBoundary:
+        case lldb::eStopReasonDynammicLoader:
           // There is no data for these stop reasons.
           return 0;
 
@@ -235,6 +236,7 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
         case eStopReasonProcessorTrace:
         case eStopReasonVForkDone:
         case eStopReasonHistoryBoundary:
+        case lldb::eStopReasonDynammicLoader:        
           // There is no data for these stop reasons.
           return 0;
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 2287fa2b1e93f..7be52894dc0dc 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -636,7 +636,7 @@ GDBRemoteCommunicationClient::GetGPUInitializeActions() {
 
 std::optional<GPUPluginBreakpointHitResponse> 
 GDBRemoteCommunicationClient::GPUBreakpointHit(
-  const GPUPluginBreakpointHitArgs &args) {
+    const GPUPluginBreakpointHitArgs &args) {
   StreamGDBRemote packet;
   packet.PutCString("jGPUPluginBreakpointHit:");
   packet.PutAsJSON(args, /*hex_ascii=*/false);
@@ -656,6 +656,28 @@ GDBRemoteCommunicationClient::GPUBreakpointHit(
   return std::nullopt;
 }
 
+std::optional<GPUDynamicLoaderResponse> 
+GDBRemoteCommunicationClient::GetGPUDynamicLoaderLibraryInfos(
+    const GPUDynamicLoaderArgs &args) {
+  StreamGDBRemote packet;
+  packet.PutCString("jGPUPluginGetDynamicLoaderLibraryInfo:");
+  packet.PutAsJSON(args, /*hex_ascii=*/false);
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
+      PacketResult::Success) {
+    if (!response.Empty()) {
+      if (llvm::Expected<GPUDynamicLoaderResponse> info = 
+          llvm::json::parse<GPUDynamicLoaderResponse>(response.Peek(), 
+              "GPUDynamicLoaderResponse")) {
+        return *info;
+      } else {
+        llvm::consumeError(info.takeError());
+      }
+    }
+  }
+  return std::nullopt;
+}
+
 bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
   if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) {
     StringExtractorGDBRemote response;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 6666cdd730c58..ef9eaa31a451f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -439,6 +439,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   std::optional<GPUPluginBreakpointHitResponse> 
   GPUBreakpointHit(const GPUPluginBreakpointHitArgs &args);
 
+  std::optional<GPUDynamicLoaderResponse> 
+  GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args);
+
   bool GetThreadExtendedInfoSupported();
 
   bool GetLoadedDynamicLibrariesInfosSupported();
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 914be713feb08..59557b8c981ab 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -258,10 +258,13 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jGPUPluginInitialize,
       &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginInitialize);
-      RegisterMemberFunctionHandler(
-        StringExtractorGDBRemote::eServerPacketType_jGPUPluginBreakpointHit,
-        &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit);
-    }
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jGPUPluginBreakpointHit,
+      &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jGPUPluginGetDynamicLoaderLibraryInfo,
+      &GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginGetDynamicLoaderLibraryInfo);
+  }
 
 void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(
     const ProcessLaunchInfo &info) {
@@ -728,6 +731,8 @@ static const char *GetStopReasonString(StopReason stop_reason) {
     return "vforkdone";
   case eStopReasonInterrupt:
     return "async interrupt";
+  case eStopReasonDynammicLoader:
+    return "dyld";
   case eStopReasonHistoryBoundary:
   case eStopReasonInstrumentation:
   case eStopReasonInvalid:
@@ -3724,6 +3729,29 @@ GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit(
 }
 
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginGetDynamicLoaderLibraryInfo(
+    StringExtractorGDBRemote &packet) {
+
+  packet.ConsumeFront("jGPUPluginGetDynamicLoaderLibraryInfo:");
+  Expected<GPUDynamicLoaderArgs> args =
+      json::parse<GPUDynamicLoaderArgs>(packet.Peek(), "GPUDynamicLoaderArgs");
+  if (!args)
+    return SendErrorResponse(args.takeError());
+
+
+  if (!m_current_process)
+    return SendErrorResponse(Status::FromErrorString("invalid process"));
+  std::optional<GPUDynamicLoaderResponse> libraries_response = 
+      m_current_process->GetGPUDynamicLoaderLibraryInfos(*args);
+  if (!libraries_response)
+    return SendErrorResponse(Status::FromErrorString(
+        "jGPUPluginGetDynamicLoaderLibraryInfo not supported"));
+  StreamGDBRemote response;
+  response.PutAsJSON(*libraries_response, /*hex_ascii=*/false);
+  return SendPacketNoLock(response.GetString());
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo(
     StringExtractorGDBRemote &packet) {
@@ -4310,6 +4338,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
     ret.push_back("memory-tagging+");
   if (bool(plugin_features & Extension::savecore))
     ret.push_back("qSaveCore+");
+  if (bool(plugin_features & Extension::json_dynamic_loader))
+    ret.push_back("qJsonDynamicLoader+");
 
   // check for client features
   m_extensions_supported = {};
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index bd3ec4ba96b35..a2ac100ccdde0 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -288,6 +288,9 @@ class GDBRemoteCommunicationServerLLGS
   
   PacketResult Handle_jGPUPluginBreakpointHit(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jGPUPluginGetDynamicLoaderLibraryInfo(
+      StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index f26f25349d83c..a0b220d693b50 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2083,6 +2083,21 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
           thread_sp->SetStopInfo(
               StopInfo::CreateStopReasonVForkDone(*thread_sp));
           handled = true;
+        } else if (reason == "dyld") {
+          Log *log(GetLog(GDBRLog::Process));
+          LLDB_LOG(log, "got dyld stop reason");
+          GPUDynamicLoaderArgs args;
+          args.full = true;
+          std::optional<GPUDynamicLoaderResponse> infos = 
+              m_gdb_comm.GetGPUDynamicLoaderLibraryInfos(args);
+          if (infos) {
+            for (const GPUDynamicLoaderLibraryInfo &info : (*infos).library_infos) {
+              LLDB_LOG(log, "library: %s", info.pathname.c_str());
+            }
+          }
+          // TODO: create dyld stop reason
+          thread_sp->SetStopInfo(StopInfo::CreateStopReasonToTrace(*thread_sp));
+          handled = true;
         }
       }
 
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 335991018070c..c962af98542cf 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -872,6 +872,7 @@ bool Process::HandleProcessStateChangedEvent(
             case eStopReasonInstrumentation:
             case eStopReasonProcessorTrace:
             case eStopReasonInterrupt:
+            case eStopReasonDynammicLoader:
               if (!other_thread)
                 other_thread = thread;
               break;
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index accc4708c24e1..8cbfedc048928 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1755,8 +1755,10 @@ std::string Thread::StopReasonAsString(lldb::StopReason reason) {
     return "processor trace";
   case eStopReasonInterrupt:
     return "async interrupt";
-  case eStopReasonHistoryBoundary:
+    case eStopReasonHistoryBoundary:
     return "history boundary";
+  case eStopReasonDynammicLoader:
+    return "dynamic loader";
   }
 
   return "StopReason = " + std::to_string(reason);
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 987aae827f834..b5bb5223d4588 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -132,4 +132,84 @@ llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data) {
           });
 }
 
+//------------------------------------------------------------------------------
+// GPUSectionInfo
+//------------------------------------------------------------------------------
+
+bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("name", data.name) &&
+         o.mapOptional("load_address", data.load_address),
+         o.map("children", data.children);
+}
+
+llvm::json::Value toJSON(const GPUSectionInfo &data) {
+  return json::Value(
+    Object{{"name", data.name}, 
+           {"load_address", data.load_address},
+           {"children", data.children}
+          });
+}
+
+//------------------------------------------------------------------------------
+// GPUDynamicLoaderLibraryInfo
+//------------------------------------------------------------------------------
+
+bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
+  llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("pathname", data.pathname) &&
+         o.map("load", data.load) &&
+         o.mapOptional("load_address", data.load_address),
+         o.mapOptional("native_memory_address", data.native_memory_address),
+         o.mapOptional("native_memory_size", data.native_memory_size),
+         o.mapOptional("file_offset", data.file_offset),
+         o.mapOptional("file_size", data.file_size),
+         o.map("loaded_sections", data.loaded_sections);
+}
+
+llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data) {
+return json::Value(
+Object{{"pathname", data.pathname}, 
+       {"load", data.load},
+       {"native_memory_address", data.native_memory_address},
+       {"native_memory_size", data.native_memory_size},
+       {"file_offset", data.file_offset},
+       {"file_size", data.file_size},
+       {"loaded_sections", data.loaded_sections}
+      });
+}
+
+//------------------------------------------------------------------------------
+// GPUDynamicLoaderArgs
+//------------------------------------------------------------------------------
+
+bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderArgs &data,
+    llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("full", data.full);
+}
+
+llvm::json::Value toJSON(const GPUDynamicLoaderArgs &data) {
+  return json::Value(Object{{"full", data.full}});
+}
+
+//------------------------------------------------------------------------------
+// GPUDynamicLoaderResponse
+//------------------------------------------------------------------------------
+bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderResponse &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+         o.map("library_infos", data.library_infos);
+}
+
+llvm::json::Value toJSON(const GPUDynamicLoaderResponse &data) {
+  return json::Value(Object{{"library_infos", data.library_infos}});
+}
+
 } // namespace lldb_private
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index f2b7053cd0d45..09c94a5975565 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -324,6 +324,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jGPUPluginInitialize;
     if (PACKET_STARTS_WITH("jGPUPluginBreakpointHit:"))
       return eServerPacketType_jGPUPluginBreakpointHit;
+    if (PACKET_STARTS_WITH("jGPUPluginGetDynamicLoaderLibraryInfo:"))
+      return eServerPacketType_jGPUPluginGetDynamicLoaderLibraryInfo;
     if (PACKET_MATCHES("jLLDBTraceSupported"))
       return eServerPacketType_jLLDBTraceSupported;
     if (PACKET_STARTS_WITH("jLLDBTraceStop:"))
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index b530cb751cc5d..e91ccce1bc41e 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -28,7 +28,7 @@ using namespace lldb_private::process_gdb_remote;
 LLDBServerPluginMockGPU::LLDBServerPluginMockGPU(
   LLDBServerPlugin::GDBServer &native_process)
     : LLDBServerPlugin(native_process) {
-  m_process_manager_up.reset(new ProcessManagerMockGPU(m_main_loop));
+  m_process_manager_up.reset(new ProcessMockGPU::Manager(m_main_loop));
   m_gdb_server.reset(
       new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up));
 
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
index 13a97eb9b587c..cba3651e967ce 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
@@ -128,8 +128,87 @@ bool ProcessMockGPU::GetProcessInfo(ProcessInstanceInfo &proc_info) {
   return true;
 }
 
+std::optional<GPUDynamicLoaderResponse> 
+ProcessMockGPU::GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) {
+  GPUDynamicLoaderResponse response;
+  // First example of a shared library. This is for cases where there is a file
+  // on disk that contains an object file that can be loaded into the process
+  // and everything should be slid to the load address. All sections within this
+  // file will be loaded at their file address + 0x20000. This is very typical
+  // for ELF files.
+  GPUDynamicLoaderLibraryInfo lib1;
+  lib1.pathname = "/usr/lib/lib1.so";
+  lib1.load_address = 0x20000;
+  response.library_infos.push_back(lib1);
+  // Second example of a shared library. This is for cases where there is an
+  // object file contained within another object file at some file offset with
+  // a file size. This one is slid to 0x30000, and all sections will get slid
+  // by the same amount.
+  GPUDynamicLoaderLibraryInfo lib2;
+  lib2.pathname = "/tmp/a.out";
+  lib2.load_address = 0x30000;
+  lib2.file_offset = 0x1000;
+  lib2.file_size = 0x500;
+  response.library_infos.push_back(lib2);
+  /// Third example of a shared library. This is for cases where there the 
+  /// object file is loaded into the memory of the native process. LLDB will 
+  /// need create an in memory object file using the data in this info.
+  GPUDynamicLoaderLibraryInfo lib3;
+  lib3.pathname = "/usr/lib/lib3.so";
+  lib3.native_memory_address = 0x4500000;
+  lib3.native_memory_size = 0x2000;
+  response.library_infos.push_back(lib3);
+
+  /// Fourth example of a shared library where we load each of the top level
+  /// sections of an object file at different addresses. 
+  GPUDynamicLoaderLibraryInfo lib4;
+  lib4.pathname = "/usr/lib/lib4.so";
+  lib4.loaded_sections.push_back({"PT_LOAD[0]", 0x0e0000, {}});
+  lib4.loaded_sections.push_back({"PT_LOAD[1]", 0x100000, {}});
+  lib4.loaded_sections.push_back({"PT_LOAD[2]", 0x0f0000, {}});
+  lib4.loaded_sections.push_back({"PT_LOAD[3]", 0x020000, {}});
+  response.library_infos.push_back(lib4);
+
+  /// Fifth example of a shared library. This is for cases where there the 
+  /// object file is loaded individual sections are loaded at different 
+  /// addresses instead of having a single load address for the entire object 
+  /// file. This allows GPU plug-ins to load sections at different addresses 
+  /// as they are loaded by the GPU driver. Sections can be created for 
+  /// functions in the ObjectFileELF plug-in when parsing the GPU ELF file so
+  /// that individual functions can be loaded at different addresses as the 
+  /// driver loads them.
+  GPUDynamicLoaderLibraryInfo lib5;
+  lib5.pathname = "/usr/lib/lib5.so";
+  /// Here we are going to assume that the .text section has functions that 
+  /// create sections for each function in the object file. Then each function 
+  /// can be loaded at a different address as the driver loads them.
+
+  /// Create the same section hierarchy as found in the ELF file by creating
+  /// a "PT_LOAD[0]" section that contains a ".text" section. We don't give
+  /// either a load address. We will add sections for each function and set the
+  /// load addresses for each function section in the text.children array.
+  GPUSectionInfo PT_LOAD1;
+  PT_LOAD1.name = "PT_LOAD[1]";
+  GPUSectionInfo text_section;
+  text_section.name = ".text";
+  GPUSectionInfo func_foo_section;
+  func_foo_section.name = "foo";
+  func_foo_section.load_address = 0x80000;
+  text_section.children.push_back(func_foo_section); 
+
+  GPUSectionInfo func_bar_section;
+  func_bar_section.name = "bar";
+  func_bar_section.load_address = 0x80200;
+  text_section.children.push_back(func_bar_section); 
+  PT_LOAD1.children.push_back(text_section);
+  lib5.loaded_sections.push_back(PT_LOAD1);
+  response.library_infos.push_back(lib5);
+  return response;
+}
+
+
 llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
-ProcessManagerMockGPU::Launch(
+ProcessMockGPU::Manager::Launch(
     ProcessLaunchInfo &launch_info,
     NativeProcessProtocol::NativeDelegate &native_delegate) {
   lldb::pid_t pid = 1234;
@@ -139,7 +218,13 @@ ProcessManagerMockGPU::Launch(
 }
 
 llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
-ProcessManagerMockGPU::Attach(
+ProcessMockGPU::Manager::Attach(
     lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) {
   return llvm::createStringError("Unimplemented function");
 }
+
+
+ProcessMockGPU::Extension
+ProcessMockGPU::Manager::GetSupportedExtensions() const {
+  return Extension::json_dynamic_loader;
+}
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h
index e772556a609b7..d1326b83c309e 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.h
@@ -24,6 +24,23 @@ class ProcessMockGPU : public NativeProcessProtocol {
   ProcessInstanceInfo m_process_info;
 
 public:
+
+class Manager : public NativeProcessProtocol::Manager {
+  public:
+    Manager(MainLoop &mainloop)
+        : NativeProcessProtocol::Manager(mainloop) {}
+  
+    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+    Launch(ProcessLaunchInfo &launch_info,
+           NativeProcessProtocol::NativeDelegate &native_delegate) override;
+  
+    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+    Attach(lldb::pid_t pid,
+           NativeProcessProtocol::NativeDelegate &native_delegate) override;
+  
+    Extension GetSupportedExtensions() const override;
+  };
+  
   ProcessMockGPU(lldb::pid_t pid, NativeDelegate &delegate);
 
   Status Resume(const ResumeActionList &resume_actions) override;
@@ -77,23 +94,13 @@ class ProcessMockGPU : public NativeProcessProtocol {
 
   bool GetProcessInfo(ProcessInstanceInfo &info) override;
 
+  std::optional<GPUDynamicLoaderResponse> 
+  GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) override;
+
   // Custom accessors
   void SetLaunchInfo(ProcessLaunchInfo &launch_info);
 };
 
-class ProcessManagerMockGPU : public NativeProcessProtocol::Manager {
-public:
-  ProcessManagerMockGPU(MainLoop &mainloop)
-      : NativeProcessProtocol::Manager(mainloop) {}
-
-  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
-  Launch(ProcessLaunchInfo &launch_info,
-         NativeProcessProtocol::NativeDelegate &native_delegate) override;
-
-  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
-  Attach(lldb::pid_t pid,
-         NativeProcessProtocol::NativeDelegate &native_delegate) override;
-};
 
 } // namespace lldb_server
 } // namespace lldb_private
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
index 238b02936d928..9b30ce4f0605c 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
@@ -13,8 +13,7 @@ using namespace lldb_server;
 
 ThreadMockGPU::ThreadMockGPU(ProcessMockGPU &process, lldb::tid_t tid)
     : NativeThreadProtocol(process, tid), m_reg_context(*this) {
-  m_stop_info.reason = lldb::eStopReasonSignal;
-  m_stop_info.signo = SIGTRAP;
+  m_stop_info.reason = lldb::eStopReasonDynammicLoader;
 }
 
 // NativeThreadProtocol Interface

>From 51a42b975d203282b513688045321d313402ba75 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 30 Apr 2025 09:26:59 -0700
Subject: [PATCH 13/34] Add dynamic loader support for GPUs.

Dymamic loader for GPUs is implemented by the NativeProcessProtocol
function:

std::optional<GPUDynamicLoaderResponse>
NativeProcessProtocol::GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args);

Most of the functionality is here, still need to make the sections load
correctly.
---
 lldb/include/lldb/Target/DynamicLoader.h      |  11 ++
 .../lldb/Utility/GPUGDBRemotePackets.h        |   3 +
 .../Plugins/DynamicLoader/CMakeLists.txt      |   1 +
 .../DynamicLoader/GDBRemoteGPU/CMakeLists.txt |  10 ++
 .../DynamicLoaderGDBRemoteGPU.cpp             | 165 ++++++++++++++++++
 .../GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h  |  71 ++++++++
 .../GDBRemoteCommunicationClient.cpp          |   2 +
 .../gdb-remote/GDBRemoteCommunicationClient.h |   5 +
 .../GDBRemoteCommunicationServerLLGS.cpp      |   2 +-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |  23 ++-
 lldb/source/Utility/GPUGDBRemotePackets.cpp   |  14 +-
 .../Plugins/MockGPU/ProcessMockGPU.cpp        |   2 +
 12 files changed, 289 insertions(+), 20 deletions(-)
 create mode 100644 lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/CMakeLists.txt
 create mode 100644 lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
 create mode 100644 lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h

diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h
index 75bb6cb6bb907..14730b8373dac 100644
--- a/lldb/include/lldb/Target/DynamicLoader.h
+++ b/lldb/include/lldb/Target/DynamicLoader.h
@@ -90,6 +90,17 @@ class DynamicLoader : public PluginInterface {
   /// loader often knows what the program entry point is. So the process and
   /// the dynamic loader can work together to detect this.
   virtual bool ProcessDidExec() { return false; }
+
+  /// A function that allows dynamic loader to handle eStopReasonDynammicLoader
+  /// stop reasons. This is intended for dynamic loaders that aren't able to
+  /// set a breakpoint in the process, but rely on being notified by a driver or
+  /// debug services that shared libraries are available.
+  ///
+  /// \returns The value of GetStopWhenImagesChange()
+  virtual bool HandleStopReasonDynammicLoader() { 
+    return GetStopWhenImagesChange();
+  }
+
   /// Get whether the process should stop when images change.
   ///
   /// When images (executables and shared libraries) get loaded or unloaded,
diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 626d73eedaa4f..988ca6d18556f 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -132,7 +132,10 @@ bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
 llvm::json::Value toJSON(const GPUSectionInfo &data);
 
 struct GPUDynamicLoaderLibraryInfo {
+  /// The path to the shared library object file on disk.
   std::string pathname;
+  /// The UUID of the shared library if it is known.
+  std::optional<std::string> uuid_str;
   /// Set to true if this shared library is being loaded, false if the library
   /// is being unloaded.
   bool load;
diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
index 30607159acdc0..5202750995fd0 100644
--- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
+++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
@@ -6,3 +6,4 @@ add_subdirectory(Static)
 add_subdirectory(Hexagon-DYLD)
 add_subdirectory(Windows-DYLD)
 add_subdirectory(wasm-DYLD)
+add_subdirectory(GDBRemoteGPU)
diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/CMakeLists.txt
new file mode 100644
index 0000000000000..5a421999d1bcf
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_lldb_library(lldbPluginDynamicLoaderGDBRemoteGPU PLUGIN
+  DynamicLoaderGDBRemoteGPU.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbHost
+    lldbSymbol
+    lldbTarget
+    lldbUtility
+  )
diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
new file mode 100644
index 0000000000000..2e891fa0d6c0c
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
@@ -0,0 +1,165 @@
+//===-- DynamicLoaderGDBRemoteGPU.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/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+
+#include "DynamicLoaderGDBRemoteGPU.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+LLDB_PLUGIN_DEFINE(DynamicLoaderGDBRemoteGPU)
+
+// Create an instance of this class. This function is filled into the plugin
+// info class that gets handed out by the plugin factory and allows the lldb to
+// instantiate an instance of this class.
+DynamicLoader *DynamicLoaderGDBRemoteGPU::CreateInstance(Process *process,
+                                                         bool force) {
+  // Only create an instance if the clients ask for this plugin by name. This
+  // plugin will be created by the ProcessGDBRemote class by asking for it by
+  // name.
+  if (force)
+    return new DynamicLoaderGDBRemoteGPU(process);
+  return nullptr;
+}
+
+// Constructor
+DynamicLoaderGDBRemoteGPU::DynamicLoaderGDBRemoteGPU(Process *process)
+    : DynamicLoader(process) {}
+
+/// Called after attaching a process.
+///
+/// Allow DynamicLoader plug-ins to execute some code after
+/// attaching to a process.
+void DynamicLoaderGDBRemoteGPU::DidAttach() { LoadModules(true); }
+
+/// Called after attaching a process.
+///
+/// Allow DynamicLoader plug-ins to execute some code after
+/// attaching to a process.
+void DynamicLoaderGDBRemoteGPU::DidLaunch() { LoadModules(true); }
+
+bool DynamicLoaderGDBRemoteGPU::HandleStopReasonDynammicLoader() { 
+  LoadModules(false);
+  return GetStopWhenImagesChange();
+}
+
+void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
+  
+  ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(m_process);
+  ModuleList loaded_module_list;
+  GPUDynamicLoaderArgs args;
+  args.full = full;
+  std::optional<GPUDynamicLoaderResponse> response =
+      gdb_process->GetGDBRemote().GetGPUDynamicLoaderLibraryInfos(args);
+  if (response) {
+    for (const GPUDynamicLoaderLibraryInfo &info : response->library_infos) {
+      UUID uuid;
+      std::shared_ptr<DataBufferHeap> data_sp;
+      if (info.native_memory_address && info.native_memory_size) {
+        data_sp = std::make_shared<DataBufferHeap>(*info.native_memory_size, 0);
+        Status error;
+        // TODO: we are assuming we can read the memory from the GPU process
+        // since the memory is shared with the host process.
+        const size_t bytes_read = m_process->ReadMemory(
+            *info.native_memory_address, data_sp->GetBytes(), 
+            data_sp->GetByteSize(), error);
+        if (bytes_read != *info.native_memory_size)
+          data_sp.reset();
+      }
+        
+      if (info.uuid_str)
+        uuid.SetFromStringRef(*info.uuid_str);
+      ModuleSpec module_spec(FileSpec(info.pathname), uuid, data_sp);
+      if (info.file_offset)
+        module_spec.SetObjectOffset(*info.file_offset);
+      if (info.file_size)
+        module_spec.SetObjectSize(*info.file_size);
+
+      ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, 
+                                                    /*notify=*/true);
+      if (module_sp) {
+      }
+    }
+  }
+#if 0
+  ModuleList loaded_module_list;
+
+  Target &target = m_process->GetTarget();
+  for (ModuleSP module_sp : module_list.Modules()) {
+    if (module_sp) {
+      bool changed = false;
+      bool no_load_addresses = true;
+      // If this module has a section with a load address set in
+      // the target, assume all necessary work is already done. There
+      // may be sections without a load address set intentionally
+      // and we don't want to mutate that.
+      // For a module with no load addresses set, set the load addresses
+      // to slide == 0, the same as the file addresses, in the target.
+      ObjectFile *image_object_file = module_sp->GetObjectFile();
+      if (image_object_file) {
+        SectionList *section_list = image_object_file->GetSectionList();
+        if (section_list) {
+          const size_t num_sections = section_list->GetSize();
+          for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+            SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+            if (section_sp) {
+              if (target.GetSectionLoadAddress(section_sp) !=
+                  LLDB_INVALID_ADDRESS) {
+                no_load_addresses = false;
+                break;
+              }
+            }
+          }
+        }
+      }
+      if (no_load_addresses)
+        module_sp->SetLoadAddress(target, 0, true /*value_is_offset*/, changed);
+
+      if (changed)
+        loaded_module_list.AppendIfNeeded(module_sp);
+    }
+  }
+
+  target.ModulesDidLoad(loaded_module_list);
+  #endif
+}
+
+ThreadPlanSP
+DynamicLoaderGDBRemoteGPU::GetStepThroughTrampolinePlan(Thread &thread,
+                                                  bool stop_others) {
+  return ThreadPlanSP();
+}
+
+Status DynamicLoaderGDBRemoteGPU::CanLoadImage() {
+  return Status::FromErrorString(
+      "can't load images on GPU targets");
+}
+
+void DynamicLoaderGDBRemoteGPU::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderGDBRemoteGPU::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+llvm::StringRef DynamicLoaderGDBRemoteGPU::GetPluginDescriptionStatic() {
+  return "Dynamic loader plug-in for GPU targets that uses GDB remote packets "
+         "tailored for GPUs to get the library load and unload information from"
+         " the lldb-server GPU plug-in GDB server connection.";
+}
diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h
new file mode 100644
index 0000000000000..11e3e377b3c99
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h
@@ -0,0 +1,71 @@
+//===-- DynamicLoaderGDBRemoteGPU.h -----------------------------*- C++ -*-===//
+//
+// 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_DYNAMICLOADER_STATIC_DYNAMICLOADERGDBREMOTEGPU_H
+#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_STATIC_DYNAMICLOADERGDBREMOTEGPU_H
+
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
+
+/// A dynamic loader class for lldb-server GPU plug-ins.
+///
+/// GPUs have special requirements for loading and unloading shared libraries
+/// and this class implements the DynamicLoader interface to support these
+/// targets. The lldb-server GPU plug-ins implement functions that return the
+/// information needed to load and unload shared libraries by handling the
+/// "jGPUPluginGetDynamicLoaderLibraryInfo" packet. Many GPUs have drivers that
+/// coordinate the loading and unloading of shared libraries, but they don't use
+/// the standard method of setting a breakpoint in the target and handle the
+/// breakpoint callback in the dynamic loader plug-in. Instead, the drivers 
+/// have callbacks or notifications that tell the lldb-server GPU plug-in when
+/// a shared library is loaded or unloaded. 
+class DynamicLoaderGDBRemoteGPU : public lldb_private::DynamicLoader {
+public:
+  DynamicLoaderGDBRemoteGPU(lldb_private::Process *process);
+
+  // Static Functions
+  static void Initialize();
+
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() { return "gdb-remote-gpu"; }
+
+  static llvm::StringRef GetPluginDescriptionStatic();
+
+  static lldb_private::DynamicLoader *
+  CreateInstance(lldb_private::Process *process, bool force);
+
+  /// Called after attaching a process.
+  ///
+  /// Allow DynamicLoader plug-ins to execute some code after
+  /// attaching to a process.
+  void DidAttach() override;
+
+  void DidLaunch() override;
+
+  bool HandleStopReasonDynammicLoader() override;
+  
+  lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+                                                  bool stop_others) override;
+
+  lldb_private::Status CanLoadImage() override;
+
+  // PluginInterface protocol
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+private:
+  /// Load all modules by calling sending a packet via GDB remote.
+  ///
+  /// \param[in] full
+  ///     If true, load all modules. If false, load or unload only new modules.
+  void LoadModules(bool full);
+};
+
+#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_STATIC_DYNAMICLOADERGDBREMOTEGPU_H
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 7be52894dc0dc..26bb6785ff1df 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -418,6 +418,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
         m_supports_memory_tagging = eLazyBoolYes;
       else if (x == "qSaveCore+")
         m_supports_qSaveCore = eLazyBoolYes;
+      else if (x == "jGPUPluginGetDynamicLoaderLibraryInfo+")
+        m_supports_jGPUPluginGetDynamicLoaderLibraryInfo = eLazyBoolYes;
       else if (x == "native-signals+")
         m_uses_native_signals = eLazyBoolYes;
       else if (x == "binary-upload+")
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index ef9eaa31a451f..e029bae4f5fc7 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -546,6 +546,10 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   // debugging on a local machine.
   void SetFilePassingFD(int fd);
 
+  bool SupportsGPUDynamicLoader() const {
+    return m_supports_jGPUPluginGetDynamicLoaderLibraryInfo == eLazyBoolYes;
+  }
+  
 protected:
   LazyBool m_supports_not_sending_acks = eLazyBoolCalculate;
   LazyBool m_supports_thread_suffix = eLazyBoolCalculate;
@@ -586,6 +590,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   LazyBool m_supports_multiprocess = eLazyBoolCalculate;
   LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
   LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
+  LazyBool m_supports_jGPUPluginGetDynamicLoaderLibraryInfo = eLazyBoolCalculate;
   LazyBool m_uses_native_signals = eLazyBoolCalculate;
   std::optional<xPacketState> m_x_packet_state;
   LazyBool m_supports_reverse_continue = eLazyBoolCalculate;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 59557b8c981ab..51bafe391f243 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -4339,7 +4339,7 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
   if (bool(plugin_features & Extension::savecore))
     ret.push_back("qSaveCore+");
   if (bool(plugin_features & Extension::json_dynamic_loader))
-    ret.push_back("qJsonDynamicLoader+");
+    ret.push_back("jGPUPluginGetDynamicLoaderLibraryInfo+");
 
   // check for client features
   m_extensions_supported = {};
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index a0b220d693b50..79fec6a2baedb 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -80,6 +80,7 @@
 #include "Plugins/Process/Utility/GDBRemoteSignals.h"
 #include "Plugins/Process/Utility/InferiorCallPOSIX.h"
 #include "Plugins/Process/Utility/StopInfoMachException.h"
+#include "Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h"
 #include "ProcessGDBRemote.h"
 #include "ProcessGDBRemoteLog.h"
 #include "ThreadGDBRemote.h"
@@ -2084,17 +2085,9 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
               StopInfo::CreateStopReasonVForkDone(*thread_sp));
           handled = true;
         } else if (reason == "dyld") {
-          Log *log(GetLog(GDBRLog::Process));
-          LLDB_LOG(log, "got dyld stop reason");
-          GPUDynamicLoaderArgs args;
-          args.full = true;
-          std::optional<GPUDynamicLoaderResponse> infos = 
-              m_gdb_comm.GetGPUDynamicLoaderLibraryInfos(args);
-          if (infos) {
-            for (const GPUDynamicLoaderLibraryInfo &info : (*infos).library_infos) {
-              LLDB_LOG(log, "library: %s", info.pathname.c_str());
-            }
-          }
+          // Let the dynamic loader handle the dynamic loader stop reason.
+          if (m_dyld_up)
+            m_dyld_up->HandleStopReasonDynammicLoader();
           // TODO: create dyld stop reason
           thread_sp->SetStopInfo(StopInfo::CreateStopReasonToTrace(*thread_sp));
           handled = true;
@@ -4121,8 +4114,12 @@ bool ProcessGDBRemote::StopNoticingNewThreads() {
 }
 
 DynamicLoader *ProcessGDBRemote::GetDynamicLoader() {
-  if (m_dyld_up.get() == nullptr)
-    m_dyld_up.reset(DynamicLoader::FindPlugin(this, ""));
+  if (m_dyld_up.get() == nullptr) {
+    llvm::StringRef dyld_plugin_name;
+    if (m_gdb_comm.SupportsGPUDynamicLoader())
+      dyld_plugin_name = DynamicLoaderGDBRemoteGPU::GetPluginNameStatic();
+    m_dyld_up.reset(DynamicLoader::FindPlugin(this, dyld_plugin_name));
+  }
   return m_dyld_up.get();
 }
 
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index b5bb5223d4588..438aed869a828 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -141,7 +141,7 @@ bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
   ObjectMapper o(value, path);
   return o && 
          o.map("name", data.name) &&
-         o.mapOptional("load_address", data.load_address),
+         o.mapOptional("load_address", data.load_address) &&
          o.map("children", data.children);
 }
 
@@ -162,18 +162,20 @@ bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
   ObjectMapper o(value, path);
   return o && 
          o.map("pathname", data.pathname) &&
+         o.mapOptional("uuid", data.uuid_str) &&
          o.map("load", data.load) &&
-         o.mapOptional("load_address", data.load_address),
-         o.mapOptional("native_memory_address", data.native_memory_address),
-         o.mapOptional("native_memory_size", data.native_memory_size),
-         o.mapOptional("file_offset", data.file_offset),
-         o.mapOptional("file_size", data.file_size),
+         o.mapOptional("load_address", data.load_address) &&
+         o.mapOptional("native_memory_address", data.native_memory_address) &&
+         o.mapOptional("native_memory_size", data.native_memory_size) &&
+         o.mapOptional("file_offset", data.file_offset) &&
+         o.mapOptional("file_size", data.file_size) &&
          o.map("loaded_sections", data.loaded_sections);
 }
 
 llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data) {
 return json::Value(
 Object{{"pathname", data.pathname}, 
+       {"uuid", data.uuid_str},
        {"load", data.load},
        {"native_memory_address", data.native_memory_address},
        {"native_memory_size", data.native_memory_size},
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
index cba3651e967ce..2ccbd7eef2e20 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
@@ -138,6 +138,7 @@ ProcessMockGPU::GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args
   // for ELF files.
   GPUDynamicLoaderLibraryInfo lib1;
   lib1.pathname = "/usr/lib/lib1.so";
+  lib1.uuid_str = "A5D69E75-92DE-3FAB-BD95-5171EAE860CC";
   lib1.load_address = 0x20000;
   response.library_infos.push_back(lib1);
   // Second example of a shared library. This is for cases where there is an
@@ -146,6 +147,7 @@ ProcessMockGPU::GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args
   // by the same amount.
   GPUDynamicLoaderLibraryInfo lib2;
   lib2.pathname = "/tmp/a.out";
+  lib1.uuid_str = "9F6F8018-B2D8-3946-8F38-38B0B890CC31";
   lib2.load_address = 0x30000;
   lib2.file_offset = 0x1000;
   lib2.file_size = 0x500;

>From f0519717d008b2ec5fea636e899fdf1ff7f8a24d Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Thu, 1 May 2025 10:54:03 -0700
Subject: [PATCH 14/34] Get dynamic loader working and hooked up.

Changed the definition of GPUSectionInfo to not have children, it now
can have an array of section names that define the hiearchy to use when
finding the section by names. If there is only one section name, then we
find it regardless of the hiearchy.

Hooked up the loading of sections and the loading of the entire file.
---
 .../lldb/Utility/GPUGDBRemotePackets.h        |  21 ++--
 .../DynamicLoaderGDBRemoteGPU.cpp             | 118 +++++++++---------
 lldb/source/Utility/GPUGDBRemotePackets.cpp   |  10 +-
 .../Plugins/MockGPU/ProcessMockGPU.cpp        |  30 +----
 4 files changed, 80 insertions(+), 99 deletions(-)

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 988ca6d18556f..0734752ccd036 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -117,13 +117,17 @@ bool fromJSON(const llvm::json::Value &value,
 llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data);
 
 struct GPUSectionInfo {
-  std::string name;
+  /// Name of the section to load. If there are multiple sections, each section
+  /// will be looked up and then a child section within the previous section
+  /// will be looked up. This allows plug-ins to specify a hiearchy of sections
+  /// in the case where section names are not unique. A valid example looks 
+  /// like: ["PT_LOAD[0]", ".text"]. If there is only one section name, LLDB
+  /// will find the first section that matches that name.
+  std::vector<std::string> names;
   /// The load address of this section only. If this value is valid, then this
   /// section is loaded at this address, else child sections can be loaded 
   /// individually.
-  std::optional<lldb::addr_t> load_address;
-  /// Child sections that have individual load addresses can be specified.
-  std::vector<GPUSectionInfo> children;
+  lldb::addr_t load_address;
 };
 
 bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
@@ -147,6 +151,11 @@ struct GPUDynamicLoaderLibraryInfo {
   /// \a loaded_Section doesn't have a value, this library will be unloaded.
   std::optional<lldb::addr_t> load_address;
 
+  /// If the object file specified by this structure has sections that get 
+  /// loaded at different times then this will not be empty. If it is empty
+  /// the \a load_address must be specified if \a load is true.
+  std::vector<GPUSectionInfo> loaded_sections;
+
   /// If this library is only available as an in memory image of an object file
   /// in the native process, then this address holds the address from which the 
   /// image can be read.
@@ -162,10 +171,6 @@ struct GPUDynamicLoaderLibraryInfo {
   /// If the library exists inside of a file at an offset, \a file_size will 
   /// have a value that indicates the size in bytes of the object file.
   std::optional<uint64_t> file_size;
-  /// If the object file specified by this structure has sections that get 
-  /// loaded at different times then this will not be empty. If it is empty
-  /// the \a load_address must be specified if \a load is true.
-  std::vector<GPUSectionInfo> loaded_sections;
 };
 
 bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
index 2e891fa0d6c0c..99a598958988d 100644
--- a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
@@ -63,79 +63,75 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
   ModuleList loaded_module_list;
   GPUDynamicLoaderArgs args;
   args.full = full;
+  Target &target = m_process->GetTarget();
   std::optional<GPUDynamicLoaderResponse> response =
       gdb_process->GetGDBRemote().GetGPUDynamicLoaderLibraryInfos(args);
-  if (response) {
-    for (const GPUDynamicLoaderLibraryInfo &info : response->library_infos) {
-      UUID uuid;
-      std::shared_ptr<DataBufferHeap> data_sp;
-      if (info.native_memory_address && info.native_memory_size) {
-        data_sp = std::make_shared<DataBufferHeap>(*info.native_memory_size, 0);
-        Status error;
-        // TODO: we are assuming we can read the memory from the GPU process
-        // since the memory is shared with the host process.
-        const size_t bytes_read = m_process->ReadMemory(
-            *info.native_memory_address, data_sp->GetBytes(), 
-            data_sp->GetByteSize(), error);
-        if (bytes_read != *info.native_memory_size)
-          data_sp.reset();
-      }
-        
-      if (info.uuid_str)
-        uuid.SetFromStringRef(*info.uuid_str);
-      ModuleSpec module_spec(FileSpec(info.pathname), uuid, data_sp);
-      if (info.file_offset)
-        module_spec.SetObjectOffset(*info.file_offset);
-      if (info.file_size)
-        module_spec.SetObjectSize(*info.file_size);
-
-      ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, 
-                                                    /*notify=*/true);
-      if (module_sp) {
-      }
+  if (!response)
+    return;
+  for (const GPUDynamicLoaderLibraryInfo &info : response->library_infos) {
+    std::shared_ptr<DataBufferHeap> data_sp;
+    // Read the object file from memory if requested.
+    if (info.native_memory_address && info.native_memory_size) {
+      data_sp = std::make_shared<DataBufferHeap>(*info.native_memory_size, 0);
+      Status error;
+      // TODO: we are assuming we can read the memory from the GPU process
+      // since the memory is shared with the host process.
+      const size_t bytes_read = m_process->ReadMemory(
+          *info.native_memory_address, data_sp->GetBytes(), 
+          data_sp->GetByteSize(), error);
+      if (bytes_read != *info.native_memory_size)
+        data_sp.reset();
     }
-  }
-#if 0
-  ModuleList loaded_module_list;
-
-  Target &target = m_process->GetTarget();
-  for (ModuleSP module_sp : module_list.Modules()) {
+    // Extract the UUID if available.
+    UUID uuid;
+    if (info.uuid_str)
+      uuid.SetFromStringRef(*info.uuid_str);
+    // Create a module specification from the info we got.
+    ModuleSpec module_spec(FileSpec(info.pathname), uuid, data_sp);
+    if (info.file_offset)
+      module_spec.SetObjectOffset(*info.file_offset);
+    if (info.file_size)
+      module_spec.SetObjectSize(*info.file_size);
+    // Get or create the module from the module spec.
+    ModuleSP module_sp = target.GetOrCreateModule(module_spec, 
+                                                  /*notify=*/true);
     if (module_sp) {
       bool changed = false;
-      bool no_load_addresses = true;
-      // If this module has a section with a load address set in
-      // the target, assume all necessary work is already done. There
-      // may be sections without a load address set intentionally
-      // and we don't want to mutate that.
-      // For a module with no load addresses set, set the load addresses
-      // to slide == 0, the same as the file addresses, in the target.
-      ObjectFile *image_object_file = module_sp->GetObjectFile();
-      if (image_object_file) {
-        SectionList *section_list = image_object_file->GetSectionList();
-        if (section_list) {
-          const size_t num_sections = section_list->GetSize();
-          for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
-            SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
-            if (section_sp) {
-              if (target.GetSectionLoadAddress(section_sp) !=
-                  LLDB_INVALID_ADDRESS) {
-                no_load_addresses = false;
-                break;
-              }
-            }
+      if (info.load_address)
+        module_sp->SetLoadAddress(target, *info.load_address, 
+                                  /*value_is_offset=*/true , changed);
+      else if (!info.loaded_sections.empty()) {    
+        
+        // Set the load address of the module to the first loaded section.
+        bool warn_multiple = true;
+        for (const GPUSectionInfo &sect : info.loaded_sections) {
+          if (sect.names.empty())
+            continue;
+          // Find the section by name using the names specified. If there is 
+          // only on name, them find it. If there are multiple names, the top
+          // most section names comes first and then we find child sections
+          // by name within the previous section.
+          SectionSP section_sp;
+          for (uint32_t i=0; i<sect.names.size(); ++i) {
+            ConstString name(sect.names[i]);
+            if (section_sp)
+              section_sp = section_sp->GetChildren().FindSectionByName(name);
+            else
+              section_sp = module_sp->GetSectionList()->FindSectionByName(name);
+            if (!section_sp)
+              break;
           }
+          if (section_sp)
+            changed = target.SetSectionLoadAddress(section_sp, 
+                                                   sect.load_address, 
+                                                   warn_multiple);
         }
       }
-      if (no_load_addresses)
-        module_sp->SetLoadAddress(target, 0, true /*value_is_offset*/, changed);
-
       if (changed)
-        loaded_module_list.AppendIfNeeded(module_sp);
+        loaded_module_list.AppendIfNeeded(module_sp);            
     }
   }
-
   target.ModulesDidLoad(loaded_module_list);
-  #endif
 }
 
 ThreadPlanSP
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 438aed869a828..31129bcc4dd69 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -140,16 +140,14 @@ bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
   return o && 
-         o.map("name", data.name) &&
-         o.mapOptional("load_address", data.load_address) &&
-         o.map("children", data.children);
+         o.map("names", data.names) &&
+         o.map("load_address", data.load_address);
 }
 
 llvm::json::Value toJSON(const GPUSectionInfo &data) {
   return json::Value(
-    Object{{"name", data.name}, 
-           {"load_address", data.load_address},
-           {"children", data.children}
+    Object{{"names", data.names}, 
+           {"load_address", data.load_address}
           });
 }
 
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
index 2ccbd7eef2e20..3421211947601 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
@@ -165,10 +165,10 @@ ProcessMockGPU::GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args
   /// sections of an object file at different addresses. 
   GPUDynamicLoaderLibraryInfo lib4;
   lib4.pathname = "/usr/lib/lib4.so";
-  lib4.loaded_sections.push_back({"PT_LOAD[0]", 0x0e0000, {}});
-  lib4.loaded_sections.push_back({"PT_LOAD[1]", 0x100000, {}});
-  lib4.loaded_sections.push_back({"PT_LOAD[2]", 0x0f0000, {}});
-  lib4.loaded_sections.push_back({"PT_LOAD[3]", 0x020000, {}});
+  lib4.loaded_sections.push_back({{"PT_LOAD[0]"}, 0x0e0000});
+  lib4.loaded_sections.push_back({{"PT_LOAD[1]"}, 0x100000});
+  lib4.loaded_sections.push_back({{"PT_LOAD[2]"}, 0x0f0000});
+  lib4.loaded_sections.push_back({{"PT_LOAD[3]"}, 0x020000});
   response.library_infos.push_back(lib4);
 
   /// Fifth example of a shared library. This is for cases where there the 
@@ -184,26 +184,8 @@ ProcessMockGPU::GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args
   /// Here we are going to assume that the .text section has functions that 
   /// create sections for each function in the object file. Then each function 
   /// can be loaded at a different address as the driver loads them.
-
-  /// Create the same section hierarchy as found in the ELF file by creating
-  /// a "PT_LOAD[0]" section that contains a ".text" section. We don't give
-  /// either a load address. We will add sections for each function and set the
-  /// load addresses for each function section in the text.children array.
-  GPUSectionInfo PT_LOAD1;
-  PT_LOAD1.name = "PT_LOAD[1]";
-  GPUSectionInfo text_section;
-  text_section.name = ".text";
-  GPUSectionInfo func_foo_section;
-  func_foo_section.name = "foo";
-  func_foo_section.load_address = 0x80000;
-  text_section.children.push_back(func_foo_section); 
-
-  GPUSectionInfo func_bar_section;
-  func_bar_section.name = "bar";
-  func_bar_section.load_address = 0x80200;
-  text_section.children.push_back(func_bar_section); 
-  PT_LOAD1.children.push_back(text_section);
-  lib5.loaded_sections.push_back(PT_LOAD1);
+  lib5.loaded_sections.push_back({{"PT_LOAD[1]", ".text", "foo"}, 0x80000}); 
+  lib5.loaded_sections.push_back({{"PT_LOAD[1]", ".text", "bar"}, 0x80200}); 
   response.library_infos.push_back(lib5);
   return response;
 }

>From 2de87a2e60ef38db302e4d06893eddcfa9e30873 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Fri, 2 May 2025 16:05:20 -0700
Subject: [PATCH 15/34] Added logging to GPU dynamic loader and fixed load
 address to be sent.

---
 .../DynamicLoaderGDBRemoteGPU.cpp             | 38 +++++++++++++++----
 lldb/source/Utility/GPUGDBRemotePackets.cpp   |  1 +
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
index 99a598958988d..2057bc9f67946 100644
--- a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
@@ -13,6 +13,8 @@
 #include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
 
 #include "DynamicLoaderGDBRemoteGPU.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
@@ -58,7 +60,8 @@ bool DynamicLoaderGDBRemoteGPU::HandleStopReasonDynammicLoader() {
 }
 
 void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
-  
+  Log *log = GetLog(LLDBLog::DynamicLoader);
+
   ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(m_process);
   ModuleList loaded_module_list;
   GPUDynamicLoaderArgs args;
@@ -66,12 +69,16 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
   Target &target = m_process->GetTarget();
   std::optional<GPUDynamicLoaderResponse> response =
       gdb_process->GetGDBRemote().GetGPUDynamicLoaderLibraryInfos(args);
-  if (!response)
+  if (!response) {
+    LLDB_LOG(log, "Failed to get dynamic loading info from GDB server");
     return;
+  }
   for (const GPUDynamicLoaderLibraryInfo &info : response->library_infos) {
     std::shared_ptr<DataBufferHeap> data_sp;
     // Read the object file from memory if requested.
     if (info.native_memory_address && info.native_memory_size) {
+      LLDB_LOG(log, "Reading \"{0}\" from memory at {1:x}", info.pathname, 
+               *info.native_memory_address);
       data_sp = std::make_shared<DataBufferHeap>(*info.native_memory_size, 0);
       Status error;
       // TODO: we are assuming we can read the memory from the GPU process
@@ -79,8 +86,11 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
       const size_t bytes_read = m_process->ReadMemory(
           *info.native_memory_address, data_sp->GetBytes(), 
           data_sp->GetByteSize(), error);
-      if (bytes_read != *info.native_memory_size)
+      if (bytes_read != *info.native_memory_size) {
+        LLDB_LOG(log, "Failed to read \"{0}\" from memory at {1:x}: {2}", 
+                 info.pathname, *info.native_memory_address, error);
         data_sp.reset();
+      }
     }
     // Extract the UUID if available.
     UUID uuid;
@@ -96,11 +106,16 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
     ModuleSP module_sp = target.GetOrCreateModule(module_spec, 
                                                   /*notify=*/true);
     if (module_sp) {
+      LLDB_LOG(log, "Created module for \"{0}\": {1:x}", 
+               info.pathname, module_sp.get());
       bool changed = false;
-      if (info.load_address)
+      if (info.load_address) {
+        LLDB_LOG(log, "Setting load address for module \"{0}\" to {1:x}", 
+                 info.pathname, *info.load_address);
+
         module_sp->SetLoadAddress(target, *info.load_address, 
                                   /*value_is_offset=*/true , changed);
-      else if (!info.loaded_sections.empty()) {    
+      } else if (!info.loaded_sections.empty()) {    
         
         // Set the load address of the module to the first loaded section.
         bool warn_multiple = true;
@@ -121,14 +136,23 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
             if (!section_sp)
               break;
           }
-          if (section_sp)
+          if (section_sp) {
+            LLDB_LOG(log, "Loading module \"{0}\" section \"{1} to {2:x}", 
+                     info.pathname, section_sp->GetName(), sect.load_address);
             changed = target.SetSectionLoadAddress(section_sp, 
                                                    sect.load_address, 
                                                    warn_multiple);
+          } else {
+            LLDB_LOG(log, "Failed to find section \"{0}\"", 
+                     section_sp->GetName());
+          }
         }
       }
-      if (changed)
+      if (changed) {
+        LLDB_LOG(log, "Module \"{0}\" was loaded, notifying target", 
+                 info.pathname);
         loaded_module_list.AppendIfNeeded(module_sp);            
+      }
     }
   }
   target.ModulesDidLoad(loaded_module_list);
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 31129bcc4dd69..d3a3b0b6f399f 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -175,6 +175,7 @@ return json::Value(
 Object{{"pathname", data.pathname}, 
        {"uuid", data.uuid_str},
        {"load", data.load},
+       {"load_address", data.load_address},
        {"native_memory_address", data.native_memory_address},
        {"native_memory_size", data.native_memory_size},
        {"file_offset", data.file_offset},

>From 6eb4b235c389c698290579834af08e7bc686e221 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Mon, 5 May 2025 10:39:08 -0700
Subject: [PATCH 16/34] Add the ability to set breakpoints by address.

---
 .../lldb/Utility/GPUGDBRemotePackets.h        | 52 ++++++++++++++++++-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 33 +++++++-----
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 48 +++++++++++++++--
 .../MockGPU/LLDBServerPluginMockGPU.cpp       |  9 ++--
 4 files changed, 117 insertions(+), 25 deletions(-)

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 0734752ccd036..19a1638116514 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -29,10 +29,58 @@ bool fromJSON(const llvm::json::Value &value, SymbolValue &data,
 
 llvm::json::Value toJSON(const SymbolValue &data);
 
+///-----------------------------------------------------------------------------
+/// GPUBreakpointByName
+///
+/// A structure that contains information on how to set a breakpoint by function
+/// name with optional shared library name.
+///-----------------------------------------------------------------------------
+
+struct GPUBreakpointByName {
+  /// An optional breakpoint shared library name to limit the scope of the
+  /// breakpoint to a specific shared library.
+  std::optional<std::string> shlib;
+  /// The name of the function to set a breakpoint at.
+  std::string function_name;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointByName &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUBreakpointByName &data);
+
+///-----------------------------------------------------------------------------
+/// GPUBreakpointByAddress
+///
+/// A structure that contains information on how to set a breakpoint by address.
+///-----------------------------------------------------------------------------
+struct GPUBreakpointByAddress {
+  /// A valid load address in the current native debug target.
+  lldb::addr_t load_address;
+};
+
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointByAddress &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUBreakpointByAddress &data);
+
+///-----------------------------------------------------------------------------
+/// GPUBreakpointInfo
+///
+/// A breakpoint definition structure.
+///
+/// Clients should either fill in the \a name_info or the \a addr_info. If the
+/// breakpoint callback needs some symbols from the native process, they can
+/// fill in the array of symbol names with any symbol names that are needed. 
+/// These symbol values will be delivered in the breakpoint callback to the GPU
+/// plug-in.
+///-----------------------------------------------------------------------------
 struct GPUBreakpointInfo {
   std::string identifier;
-  std::string shlib;
-  std::string function_name;
+  /// An optional breakpoint by name info.
+  std::optional<GPUBreakpointByName> name_info;
+  /// An optional load address to set a breakpoint at in the native process.
+  std::optional<GPUBreakpointByAddress> addr_info;
   /// Names of symbols that should be supplied when the breakpoint is hit.
   std::vector<std::string> symbol_names;
 };
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 79fec6a2baedb..d72c3c212c169 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -935,19 +935,26 @@ void ProcessGDBRemote::HandleGPUBreakpoints(
     args_up->plugin_name = plugin_name;
     args_up->breakpoint = bp;
     FileSpecList bp_modules;
-    if (!bp.shlib.empty())
-      bp_modules.Append(FileSpec(bp.shlib, 
-                                  llvm::sys::path::Style::native));
-    BreakpointSP bp_sp = target.CreateBreakpoint(
-        &bp_modules, // Containing modules.
-        nullptr, // Containing source files.
-        bp.function_name.c_str(), // Function name.
-        eFunctionNameTypeFull, // Function name type.
-        eLanguageTypeUnknown, // Language type
-        0, // Byte offset.
-        eLazyBoolNo, // Skip prologue.
-        true, // Internal breakpoint.
-        false); // Request hardware.
+    BreakpointSP bp_sp;
+    if (bp.name_info) {
+      if (bp.name_info->shlib && !bp.name_info->shlib->empty())
+        bp_modules.Append(FileSpec(*bp.name_info->shlib, 
+                                   llvm::sys::path::Style::native));
+      bp_sp = target.CreateBreakpoint(
+          &bp_modules, // Containing modules.
+          nullptr, // Containing source files.
+          bp.name_info->function_name.c_str(), // Function name.
+          eFunctionNameTypeFull, // Function name type.
+          eLanguageTypeUnknown, // Language type
+          0, // Byte offset.
+          eLazyBoolNo, // Skip prologue.
+          true, // Internal breakpoint.
+          false); // Request hardware.
+    } else if (bp.addr_info) {
+      bp_sp = target.CreateBreakpoint(bp.addr_info->load_address, 
+                                      /*internal=*/true,
+                                      /*request_hardware=*/false);
+    }
     if (bp_sp) {
       // Create some JSON we can send back to the lldb-server
       // that identifies the plug-in and the breakpoint for when
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index d3a3b0b6f399f..9abd04fc68c70 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -26,6 +26,46 @@ json::Value toJSON(const SymbolValue &data) {
   return json::Value(Object{{"name", data.name}, {"value", data.value}});
 }
 
+///-----------------------------------------------------------------------------
+/// GPUBreakpointByName
+///
+/// A structure that contains information on how to set a breakpoint by function
+/// name with optional shared library name.
+///-----------------------------------------------------------------------------
+
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointByName &data,
+              llvm::json::Path path) {
+  ObjectMapper o(value, path);
+  return o && 
+          o.mapOptional("shlib", data.shlib) &&
+          o.map("function_name", data.function_name);              
+}
+
+llvm::json::Value toJSON(const GPUBreakpointByName &data) {
+  return json::Value(
+    Object{{"shlib", data.shlib},
+           {"function_name", data.function_name}
+          });
+}
+
+///-----------------------------------------------------------------------------
+/// GPUBreakpointByAddress
+///
+/// A structure that contains information on how to set a breakpoint by address.
+///-----------------------------------------------------------------------------
+
+bool fromJSON(const llvm::json::Value &value, GPUBreakpointByAddress &data,
+              llvm::json::Path path){
+  ObjectMapper o(value, path);
+  return o && o.map("load_address", data.load_address);              
+}
+
+llvm::json::Value toJSON(const GPUBreakpointByAddress &data) {
+  return json::Value(
+    Object{{"load_address", data.load_address}});
+}
+
+
 //------------------------------------------------------------------------------
 // GPUBreakpointInfo
 //------------------------------------------------------------------------------
@@ -34,16 +74,16 @@ bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
   ObjectMapper o(value, path);
   return o && 
          o.map("identifier", data.identifier) &&
-         o.map("shlib", data.shlib) &&
-         o.map("function_name", data.function_name) &&
+         o.mapOptional("name_info", data.name_info) &&
+         o.mapOptional("addr_info", data.addr_info) &&
          o.map("symbol_names", data.symbol_names);
 }
 
 llvm::json::Value toJSON(const GPUBreakpointInfo &data) {
   return json::Value(
     Object{{"identifier", data.identifier}, 
-           {"shlib", data.shlib},
-           {"function_name", data.function_name},
+           {"name_info", data.name_info},
+           {"addr_info", data.addr_info},
            {"symbol_names", data.symbol_names},
           });
 }
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index e91ccce1bc41e..f320b0ef89a9d 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -174,8 +174,7 @@ std::optional<GPUActions> LLDBServerPluginMockGPU::NativeProcessIsStopping() {
     actions.plugin_name = GetPluginName();
     GPUBreakpointInfo bp;
     bp.identifier = "3rd stop breakpoint";
-    bp.shlib = "a.out";
-    bp.function_name = "gpu_third_stop";
+    bp.name_info = {"a.out", "gpu_third_stop"};
     std::vector<GPUBreakpointInfo> breakpoints;
     breakpoints.emplace_back(std::move(bp));
     actions.breakpoints = std::move(breakpoints);
@@ -211,15 +210,13 @@ GPUActions LLDBServerPluginMockGPU::GetInitializeActions() {
   
   GPUBreakpointInfo bp1;
   bp1.identifier = "gpu_initialize";
-  bp1.shlib = "a.out";
-  bp1.function_name = "gpu_initialize";
+  bp1.name_info = {"a.out", "gpu_initialize"};
   bp1.symbol_names.push_back("printf");
   bp1.symbol_names.push_back("puts");
 
   GPUBreakpointInfo bp2;
   bp2.identifier = "gpu_shlib_load";
-  bp2.shlib = "a.out";
-  bp2.function_name = "gpu_shlib_load";
+  bp2.name_info = {"a.out", "gpu_shlib_load"};
   bp2.symbol_names.push_back("g_shlib_list");
   bp2.symbol_names.push_back("invalid_symbol");
 

>From 65cc3e409d11d1d89e09c550c533a366ab355480 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Mon, 5 May 2025 22:33:43 -0700
Subject: [PATCH 17/34] Added "bool load_libraries;" to the GPUActions
 structure.

This patch makes it so that a breakpoint in the native process can cause
shared libraries to be loaded in the GPU process. When GPU plug-ins
respond to the LLDBServerPlugin method:

  GPUPluginBreakpointHitResponse
  LLDBServerPlugin::BreakpointWasHit(GPUPluginBreakpointHitArgs &args);

They can now set the GPUPluginBreakpointHitResponse.load_libraries to
true. The GPU plug-in should already hvae notified LLDB that it is
stopped prior to sending this to ensure that no progress is made on the
GPU while shared libraries are loaded and breakpoints get resolved.

Code was added to Target.h that allows a target to know about all of the
installed GPU plug-ins. This allows a native process to make calls on
the GPU process, and also for the GPU process to get the native target.
This will allow code to resume both targets when one gets resumed if
that is the preferred methodology for the native process and GPU. It
also allows us to stop the native target from the GPU target and vice
versa.

The LLDBServerPluginMockGPU now tests that breakpoints by address work
by setting a brekapoint by address from the "gdb_shlib_load" symbol that
is requested by the "gpu_initialize" breakpoint.
---
 lldb/include/lldb/Target/DynamicLoader.h      |  4 +-
 lldb/include/lldb/Target/Target.h             | 23 ++++++
 .../lldb/Utility/GPUGDBRemotePackets.h        | 80 +++++++++++++------
 .../DynamicLoaderGDBRemoteGPU.cpp             | 12 +--
 .../GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h  |  8 +-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 70 ++++++++++++----
 .../Process/gdb-remote/ProcessGDBRemote.h     |  5 +-
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 14 +++-
 .../MockGPU/LLDBServerPluginMockGPU.cpp       | 38 +++++----
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |  6 +-
 .../Plugins/MockGPU/ThreadMockGPU.cpp         |  2 +-
 11 files changed, 191 insertions(+), 71 deletions(-)

diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h
index 14730b8373dac..6f18a6747e3dd 100644
--- a/lldb/include/lldb/Target/DynamicLoader.h
+++ b/lldb/include/lldb/Target/DynamicLoader.h
@@ -96,9 +96,9 @@ class DynamicLoader : public PluginInterface {
   /// set a breakpoint in the process, but rely on being notified by a driver or
   /// debug services that shared libraries are available.
   ///
-  /// \returns The value of GetStopWhenImagesChange()
+  /// \returns True if handled, false otherwise.
   virtual bool HandleStopReasonDynammicLoader() { 
-    return GetStopWhenImagesChange();
+    return false;
   }
 
   /// Get whether the process should stop when images change.
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 80ce5f013344c..db5f35a3c11b9 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1546,6 +1546,21 @@ class Target : public std::enable_shared_from_this<Target>,
   /// Print all the signals set in this target.
   void PrintDummySignals(Stream &strm, Args &signals);
 
+
+  lldb::TargetSP GetGPUPluginTarget(llvm::StringRef plugin_name) {
+    return m_gpu_plugin_targets.lookup(plugin_name).lock();
+  }
+
+  void SetGPUPluginTarget(llvm::StringRef plugin_name, 
+                          lldb::TargetSP target_sp) {
+    m_native_target_gpu_wp = shared_from_this();
+    m_gpu_plugin_targets[plugin_name] = target_sp;
+  }
+
+  lldb::TargetSP GetNativeTargetForGPU() {
+    return m_native_target_gpu_wp.lock();
+  }
+
 protected:
   /// Implementing of ModuleList::Notifier.
 
@@ -1634,6 +1649,14 @@ class Target : public std::enable_shared_from_this<Target>,
   /// signals you will have.
   llvm::StringMap<DummySignalValues> m_dummy_signals;
 
+  /// If a process spawns another target for a GPU plug-in, this map tracks the
+  /// associated plug-in targets so they can be accessed.
+  llvm::StringMap<lldb::TargetWP> m_gpu_plugin_targets;
+  /// If a target has a parent target, this can be used to synchronize the two
+  /// targets. For example if a GPU target wants to resume but it requires its
+  /// native target to resume as well, we can use this to make this happen.
+  lldb::TargetWP m_native_target_gpu_wp;
+
   static void ImageSearchPathsChanged(const PathMappingList &path_list,
                                       void *baton);
 
diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 19a1638116514..1094c3addcf98 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -94,6 +94,8 @@ struct GPUPluginBreakpointHitArgs {
   std::string plugin_name;
   GPUBreakpointInfo breakpoint;
   std::vector<SymbolValue> symbol_values;
+
+  std::optional<uint64_t> GetSymbolValue(llvm::StringRef symbol_name);
 };
 
 bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &data,
@@ -126,17 +128,47 @@ llvm::json::Value toJSON(const GPUPluginConnectionInfo &data);
 ///-----------------------------------------------------------------------------
 /// GPUActions
 ///
-/// A structure that contains action to be taken after a stop or breakpoint hit
-/// event.
+/// A structure used by the native process that is debugging the GPU that
+/// contains actions to be performed after:
+///
+/// - GPU Initilization in response to the "jGPUPluginInitialize" packet sent to
+///   the native process' lldb-server that contains GPU plugins. This packet is
+///   sent to the ProcessGDBRemote for the native process one time when a native
+///   process is being attached or launched.
+///
+/// - When a native breakpoint that was requested by the GPU plugin is hit, the
+///   native process in LLDB will call into the native process' GDB server and
+///   have it call the GPU plug-in method:
+///
+///     GPUPluginBreakpointHitResponse 
+///     LLDBServerPlugin::BreakpointWasHit(GPUPluginBreakpointHitArgs &args);
+///
+///   The GPUPluginBreakpointHitResponse contains a GPUActions member that will
+///   be encoded and sent back to the ProcessGDBRemote for the native process. 
+///
+/// - Anytime the native process stops, the native process' GDB server will ask
+///   each GPU plug-in if there are any actions it would like to report, the
+///   native process' lldb-server will call the GPU plug-in method:
+///
+///     std::optional<GPUActions> LLDBServerPlugin::NativeProcessIsStopping();
+///
+///   If GPUActions are returned from this method, they will be encoded into the
+///   native process' stop reply packet and handled in ProcessGDBRemote for the
+///   native process.
 ///-----------------------------------------------------------------------------
 struct GPUActions {
   /// The name of the plugin.
   std::string plugin_name;
-  /// Optional new breakpoints to set.
-  std::optional<std::vector<GPUBreakpointInfo>> breakpoints;
+  /// New breakpoints to set. Nothing to set if this is empty.
+  std::vector<GPUBreakpointInfo> breakpoints;
   /// If a GPU connection is available return a connect URL to use to reverse
-  /// connect to the GPU GDB server.
+  /// connect to the GPU GDB server as a separate process.
   std::optional<GPUPluginConnectionInfo> connect_info;
+  /// Set this to true if the native plug-in should tell the ProcessGDBRemote
+  /// in LLDB for the GPU process to load libraries. This allows the native 
+  /// process to be notified that it should query for the shared libraries on 
+  /// the GPU connection.
+  bool load_libraries = false;
 };
 
 bool fromJSON(const llvm::json::Value &value, 
@@ -145,24 +177,6 @@ bool fromJSON(const llvm::json::Value &value,
 
 llvm::json::Value toJSON(const GPUActions &data);
 
-///-----------------------------------------------------------------------------
-/// GPUPluginBreakpointHitResponse
-///
-/// A response structure from the GPU plugin from hitting a native breakpoint
-/// set by the GPU plugin.
-///-----------------------------------------------------------------------------
-struct GPUPluginBreakpointHitResponse {
-  ///< Set to true if this berakpoint should be disabled.
-  bool disable_bp = false; 
-  /// Optional new breakpoints to set.
-  GPUActions actions;
-};
-
-bool fromJSON(const llvm::json::Value &value, 
-              GPUPluginBreakpointHitResponse &data,
-              llvm::json::Path path);
-
-llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data);
 
 struct GPUSectionInfo {
   /// Name of the section to load. If there are multiple sections, each section
@@ -227,6 +241,26 @@ bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
 llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data);
 
 
+
+///-----------------------------------------------------------------------------
+/// GPUPluginBreakpointHitResponse
+///
+/// A response structure from the GPU plugin from hitting a native breakpoint
+/// set by the GPU plugin.
+///-----------------------------------------------------------------------------
+struct GPUPluginBreakpointHitResponse {
+  ///< Set to true if this berakpoint should be disabled.
+  bool disable_bp = false; 
+  /// Optional new breakpoints to set.
+  GPUActions actions;
+};
+
+bool fromJSON(const llvm::json::Value &value, 
+              GPUPluginBreakpointHitResponse &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data);
+
 struct GPUDynamicLoaderArgs {
   /// Set to true to get all shared library information. Set to false to get
   /// only the libraries that were updated since the last call to 
diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
index 2057bc9f67946..f587f5a2f4cba 100644
--- a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.cpp
@@ -46,20 +46,19 @@ DynamicLoaderGDBRemoteGPU::DynamicLoaderGDBRemoteGPU(Process *process)
 ///
 /// Allow DynamicLoader plug-ins to execute some code after
 /// attaching to a process.
-void DynamicLoaderGDBRemoteGPU::DidAttach() { LoadModules(true); }
+void DynamicLoaderGDBRemoteGPU::DidAttach() { LoadModulesFromGDBServer(true); }
 
 /// Called after attaching a process.
 ///
 /// Allow DynamicLoader plug-ins to execute some code after
 /// attaching to a process.
-void DynamicLoaderGDBRemoteGPU::DidLaunch() { LoadModules(true); }
+void DynamicLoaderGDBRemoteGPU::DidLaunch() { LoadModulesFromGDBServer(true); }
 
 bool DynamicLoaderGDBRemoteGPU::HandleStopReasonDynammicLoader() { 
-  LoadModules(false);
-  return GetStopWhenImagesChange();
+  return LoadModulesFromGDBServer(false);
 }
 
-void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
+bool DynamicLoaderGDBRemoteGPU::LoadModulesFromGDBServer(bool full) {
   Log *log = GetLog(LLDBLog::DynamicLoader);
 
   ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(m_process);
@@ -71,7 +70,7 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
       gdb_process->GetGDBRemote().GetGPUDynamicLoaderLibraryInfos(args);
   if (!response) {
     LLDB_LOG(log, "Failed to get dynamic loading info from GDB server");
-    return;
+    return false;
   }
   for (const GPUDynamicLoaderLibraryInfo &info : response->library_infos) {
     std::shared_ptr<DataBufferHeap> data_sp;
@@ -156,6 +155,7 @@ void DynamicLoaderGDBRemoteGPU::LoadModules(bool full) {
     }
   }
   target.ModulesDidLoad(loaded_module_list);
+  return true; // Handled the request.
 }
 
 ThreadPlanSP
diff --git a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h
index 11e3e377b3c99..0284f98fdcaf3 100644
--- a/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h
+++ b/lldb/source/Plugins/DynamicLoader/GDBRemoteGPU/DynamicLoaderGDBRemoteGPU.h
@@ -61,11 +61,15 @@ class DynamicLoaderGDBRemoteGPU : public lldb_private::DynamicLoader {
   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
 
 private:
-  /// Load all modules by calling sending a packet via GDB remote.
+  /// Load all modules by sending a "jGPUPluginGetDynamicLoaderLibraryInfo"
+  /// packet to the GDB server.
   ///
   /// \param[in] full
   ///     If true, load all modules. If false, load or unload only new modules.
-  void LoadModules(bool full);
+  ///
+  /// \returns True if the GDB server supports the packet named 
+  ///     "jGPUPluginGetDynamicLoaderLibraryInfo", false otherwise.
+  bool LoadModulesFromGDBServer(bool full);
 };
 
 #endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_STATIC_DYNAMICLOADERGDBREMOTEGPU_H
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index d72c3c212c169..17c4177d62a77 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -875,15 +875,28 @@ bool ProcessGDBRemote::GPUBreakpointHit(void *baton,
 
 Status ProcessGDBRemote::HandleGPUActions(const GPUActions &gpu_action) {
   Status error;
-  if (gpu_action.breakpoints)
-    HandleGPUBreakpoints(gpu_action.plugin_name, *gpu_action.breakpoints);
+  if (!gpu_action.breakpoints.empty())
+    HandleGPUBreakpoints(gpu_action);
   if (gpu_action.connect_info)
-    error = HandleConnectionRequest(*gpu_action.connect_info);
+    error = HandleConnectionRequest(gpu_action);
+  if (gpu_action.load_libraries) {
+    lldb::TargetSP gpu_target_sp = 
+        GetTarget().GetGPUPluginTarget(gpu_action.plugin_name);
+    if (gpu_target_sp) {
+      lldb::ProcessSP gpu_process_sp = gpu_target_sp->GetProcessSP();
+      if (gpu_process_sp) {
+        llvm::Error error = gpu_process_sp->LoadModules();
+        if (error)
+          llvm::consumeError(std::move(error));
+      }
+    }
+  }
   return error;
 }
 
-Status ProcessGDBRemote::HandleConnectionRequest(
-    const GPUPluginConnectionInfo &connection_info) {
+Status ProcessGDBRemote::HandleConnectionRequest(const GPUActions &gpu_action) {
+  const GPUPluginConnectionInfo &connection_info = 
+    gpu_action.connect_info.value();
   Log *log = GetLog(GDBRLog::Plugin);
   LLDB_LOG(log, "ProcessGDBRemote::HandleConnectionRequest()"); 
   auto &debugger = GetTarget().GetDebugger();
@@ -918,21 +931,21 @@ Status ProcessGDBRemote::HandleConnectionRequest(
   if (!process_sp)
     return Status::FromErrorString("invalid process after conneting");
 
+  GetTarget().SetGPUPluginTarget(gpu_action.plugin_name, 
+                                 process_sp->GetTarget().shared_from_this());
   LLDB_LOG(log, "ProcessGDBRemote::HandleConnectionRequest(): successfully "
            "created process!!!");
   return Status();
 }
 
-void ProcessGDBRemote::HandleGPUBreakpoints(
-    const std::string plugin_name,
-    const std::vector<GPUBreakpointInfo> &breakpoints) {
+void ProcessGDBRemote::HandleGPUBreakpoints(const GPUActions &gpu_action) {
   Target &target = GetTarget();
-  for (const auto &bp: breakpoints) {
+  for (const auto &bp: gpu_action.breakpoints) {
     // Create data that will live with the breakpoint so when we hit the 
     // breakpoint and the GPUBreakpointHitCallback is called, we can use this
     // data.
     auto args_up = std::make_unique<GPUPluginBreakpointHitArgs>();
-    args_up->plugin_name = plugin_name;
+    args_up->plugin_name = gpu_action.plugin_name;
     args_up->breakpoint = bp;
     FileSpecList bp_modules;
     BreakpointSP bp_sp;
@@ -2092,10 +2105,16 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
               StopInfo::CreateStopReasonVForkDone(*thread_sp));
           handled = true;
         } else if (reason == "dyld") {
-          // Let the dynamic loader handle the dynamic loader stop reason.
-          if (m_dyld_up)
-            m_dyld_up->HandleStopReasonDynammicLoader();
-          // TODO: create dyld stop reason
+          // When plugins can't set a breakpoint, they might stop with a "dyld"
+          // reason to indicate that they need to load shared libraries.
+          auto error = LoadModules();
+          if (error) {
+            Log *log(GetLog(GDBRLog::Process));
+            LLDB_LOG_ERROR(log, std::move(error), "Failed to load modules: {0}");
+          }
+          // TODO: create dyld stop reason, or auto resume depending on value
+          // of setting that specifies if we should stop for shared library
+          // load events.
           thread_sp->SetStopInfo(StopInfo::CreateStopReasonToTrace(*thread_sp));
           handled = true;
         }
@@ -5359,6 +5378,29 @@ lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file,
 llvm::Error ProcessGDBRemote::LoadModules() {
   using lldb_private::process_gdb_remote::ProcessGDBRemote;
 
+  /// See if the dynamic loader knows how to load the modules when requested.
+  /// This can get triggered in multiple ways:
+  /// - If a breakpoint in the native process that was set by any GPUActions 
+  ///   gets hit, and the breakpoint hit response from the GPU plug-in via an 
+  ///   instance of GPUPluginBreakpointHitResponse has the "load_libraries" 
+  ///   bool member variable is set to true. This is the preferred method if
+  ///   it is possible for a breakpoint to be set in the native process as it
+  ///   allows the native process to be stopped while GPU shared libraries are
+  ///   loaded. The breakpoint will auto resume the process after the GPU
+  ///   shared libraries are loaded.
+  /// - Stop reason for a thread in GPU process is set to the 
+  ///   eStopReasonDynammicLoader stop reason. This is used when the GPU process
+  ///   doesn't require synchronization with the native process. If the GPU
+  ///   can't set a breakpoint in GPU code and the GPU driver gets a 
+  ///   notification that shared libraries are available. This should be used
+  ///   if we want to stop for shared library loading and LLDB should auto 
+  ///   continue the process. It doesn't do this yet, but it can and will in the
+  ///   future if we need this method of shared library load notification.
+  /// - The GPU process stop reply packet contains for a GPU thread has the
+  ///   "library;" key in the key value pairs.
+  if (m_dyld_up && m_dyld_up->HandleStopReasonDynammicLoader())
+    return llvm::ErrorSuccess();
+
   // request a list of loaded libraries from GDBServer
   llvm::Expected<LoadedModuleInfoList> module_list = GetLoadedModuleList();
   if (!module_list)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index d2f6ed57c31a8..a540b2b870069 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -447,10 +447,9 @@ class ProcessGDBRemote : public Process,
   bool GPUBreakpointHit(void *baton, StoppointCallbackContext *context, 
                         lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
                     
-  void HandleGPUBreakpoints(const std::string plugin_name,
-                            const std::vector<GPUBreakpointInfo> &breakpoints);
+  void HandleGPUBreakpoints(const GPUActions &gpu_action);
 
-  Status HandleConnectionRequest(const GPUPluginConnectionInfo &connection_info);
+  Status HandleConnectionRequest(const GPUActions &gpu_action);
 
   Status HandleGPUActions(const GPUActions &gpu_action);
 
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 9abd04fc68c70..f6ddbe17e77ed 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -131,6 +131,14 @@ json::Value toJSON(const GPUPluginBreakpointHitArgs &data) {
             });
 }
 
+std::optional<uint64_t> 
+GPUPluginBreakpointHitArgs::GetSymbolValue(llvm::StringRef symbol_name) {
+  for (const auto &symbol: symbol_values)
+    if (symbol_name == symbol.name)
+      return symbol.value;
+  return std::nullopt;
+}
+
 //------------------------------------------------------------------------------
 // GPUActions
 //------------------------------------------------------------------------------
@@ -139,8 +147,9 @@ bool fromJSON(const llvm::json::Value &value, GPUActions &data,
   ObjectMapper o(value, path);
   return o && 
          o.map("plugin_name", data.plugin_name) &&
-         o.mapOptional("breakpoints", data.breakpoints) &&
-         o.mapOptional("connect_info", data.connect_info);
+         o.map("breakpoints", data.breakpoints) &&
+         o.mapOptional("connect_info", data.connect_info) &&
+         o.map("load_libraries", data.load_libraries);
 }
 
 llvm::json::Value toJSON(const GPUActions &data) {
@@ -148,6 +157,7 @@ llvm::json::Value toJSON(const GPUActions &data) {
     Object{{"plugin_name", data.plugin_name},
            {"breakpoints", data.breakpoints},
            {"connect_info", data.connect_info},
+           {"load_libraries", data.load_libraries},
           });
 }
 
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index f320b0ef89a9d..03a50f4220870 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -175,9 +175,7 @@ std::optional<GPUActions> LLDBServerPluginMockGPU::NativeProcessIsStopping() {
     GPUBreakpointInfo bp;
     bp.identifier = "3rd stop breakpoint";
     bp.name_info = {"a.out", "gpu_third_stop"};
-    std::vector<GPUBreakpointInfo> breakpoints;
-    breakpoints.emplace_back(std::move(bp));
-    actions.breakpoints = std::move(breakpoints);
+    actions.breakpoints.emplace_back(std::move(bp));
     return actions;
   }
   return std::nullopt;
@@ -200,6 +198,23 @@ LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
     LLDB_LOGF(log, "LLDBServerPluginMockGPU::BreakpointWasHit(\"%s\") disabling breakpoint", 
               bp_identifier.c_str());
     response.actions.connect_info = CreateConnection();
+
+    // We asked for the symbol "gpu_shlib_load" to be delivered as a symbol
+    // value when the "gpu_initialize" breakpoint was set. So we will use this
+    // to set a breakpoint by address to test setting breakpoints by address.
+    std::optional<uint64_t> gpu_shlib_load_addr = 
+        args.GetSymbolValue("gpu_shlib_load");
+    if (gpu_shlib_load_addr) {
+      GPUBreakpointInfo bp;
+      bp.identifier = "gpu_shlib_load";
+      bp.addr_info = {*gpu_shlib_load_addr};
+      bp.symbol_names.push_back("g_shlib_list");
+      bp.symbol_names.push_back("invalid_symbol");
+      response.actions.breakpoints.emplace_back(std::move(bp));
+    }
+  } else if (bp_identifier == "gpu_shlib_load") {
+    // Tell the native process to tell the GPU process to load libraries.
+    response.actions.load_libraries = true;
   }
   return response;
 }
@@ -210,19 +225,8 @@ GPUActions LLDBServerPluginMockGPU::GetInitializeActions() {
   
   GPUBreakpointInfo bp1;
   bp1.identifier = "gpu_initialize";
-  bp1.name_info = {"a.out", "gpu_initialize"};
-  bp1.symbol_names.push_back("printf");
-  bp1.symbol_names.push_back("puts");
-
-  GPUBreakpointInfo bp2;
-  bp2.identifier = "gpu_shlib_load";
-  bp2.name_info = {"a.out", "gpu_shlib_load"};
-  bp2.symbol_names.push_back("g_shlib_list");
-  bp2.symbol_names.push_back("invalid_symbol");
-
-  std::vector<GPUBreakpointInfo> breakpoints;
-  breakpoints.emplace_back(std::move(bp1));
-  breakpoints.emplace_back(std::move(bp2));
-  init_actions.breakpoints = std::move(breakpoints);
+  bp1.name_info = {"a.out", "gpu_initialize"};  
+  bp1.symbol_names.push_back("gpu_shlib_load");
+  init_actions.breakpoints.emplace_back(std::move(bp1));
   return init_actions;
 }
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index 3dcee3b6f3bb7..f153579b1160d 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -32,12 +32,16 @@ int gpu_initialize() {
 int gpu_shlib_load() {
   return puts(__FUNCTION__);
 }
+int gpu_third_stop() {
+  return puts(__FUNCTION__);
+}
 int main(int argc, const char **argv) {
   gpu_initialize();
   gpu_shlib_load();
+  gpu_third_stop();
+  gpu_shlib_load();
   return 0; // Break here
 }
-
 $ clang++ -g -O0 -o a.out main.cpp
 $ ninja lldb lldb-server
 $ ./bin/lldb a.out -o 'b /Break here/ -o run
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
index 9b30ce4f0605c..82fafc1b68903 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
@@ -13,7 +13,7 @@ using namespace lldb_server;
 
 ThreadMockGPU::ThreadMockGPU(ProcessMockGPU &process, lldb::tid_t tid)
     : NativeThreadProtocol(process, tid), m_reg_context(*this) {
-  m_stop_info.reason = lldb::eStopReasonDynammicLoader;
+  m_stop_info.reason = lldb::eStopReasonTrace; //eStopReasonDynammicLoader;
 }
 
 // NativeThreadProtocol Interface

>From cbc63f7879eddd4221df7c21174691cf0828e4a3 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 6 May 2025 20:28:57 -0700
Subject: [PATCH 18/34] Add a stop reason class for the dynamic loader.

The idea behind this change is a GDB server can return a "reason:dyld;"
by having its NativeThreadProtocol class return a stop reason of
lldb::eStopReasonDynammicLoader. This allows the GPU to halt its process
and return this stop reason during a LLDBServerPlugin::BreakpointWasHit()
call. When the GPU reports this stop the shared libraries will be
requested and a stop StopReasonDyld() will be created in LLDB that will
auto resume the GPU process.
---
 lldb/include/lldb/Target/StopInfo.h           |  3 ++
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |  2 +-
 lldb/source/Target/StopInfo.cpp               | 31 +++++++++++++++++++
 .../Plugins/MockGPU/ThreadMockGPU.cpp         |  2 +-
 4 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h
index 9a13371708be5..a06efb5e2cfc1 100644
--- a/lldb/include/lldb/Target/StopInfo.h
+++ b/lldb/include/lldb/Target/StopInfo.h
@@ -161,6 +161,9 @@ class StopInfo : public std::enable_shared_from_this<StopInfo> {
   static lldb::StopInfoSP
   CreateStopReasonHistoryBoundary(Thread &thread, const char *description);
 
+  static lldb::StopInfoSP
+  CreateStopReasonDyld(Thread &thread, const char *description = nullptr);
+
   static lldb::StopInfoSP CreateStopReasonFork(Thread &thread,
                                                lldb::pid_t child_pid,
                                                lldb::tid_t child_tid);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 17c4177d62a77..ace1d52e7f40c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2115,7 +2115,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
           // TODO: create dyld stop reason, or auto resume depending on value
           // of setting that specifies if we should stop for shared library
           // load events.
-          thread_sp->SetStopInfo(StopInfo::CreateStopReasonToTrace(*thread_sp));
+          thread_sp->SetStopInfo(StopInfo::CreateStopReasonDyld(*thread_sp));
           handled = true;
         }
       }
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index e9e3603e55316..83fce2f781c46 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1462,6 +1462,31 @@ class StopInfoVForkDone : public StopInfo {
   bool m_performed_action = false;
 };
 
+/// A stop reason for causing shared libraries to load. This will create a stop
+/// info that will resume the process by returning the right value for 
+/// StopInfo::ShouldStopSynchronous(...). This allows a process to stop and 
+/// report it wants to load/unload shared libraries and auto continue after all 
+/// libraries have been loaded/unloaded.
+class StopInfoDyld : public StopInfo {
+public:
+  StopInfoDyld(Thread &thread, const char *description)
+      : StopInfo(thread, 0) {
+    SetDescription(description ? description : "dynamic loader");
+  }
+  ~StopInfoDyld() override = default;
+  StopReason GetStopReason() const override { 
+    return lldb::eStopReasonDynammicLoader; 
+  }
+  bool ShouldStopSynchronous(Event *event_ptr) override {
+    if (ThreadSP thread_sp = GetThread()) {
+      ProcessSP process_sp = thread_sp->GetProcess();
+      if (process_sp)
+        return process_sp->GetStopOnSharedLibraryEvents();
+    }
+    return false;
+  }
+};
+
 } // namespace lldb_private
 
 StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
@@ -1524,6 +1549,12 @@ StopInfoSP StopInfo::CreateStopReasonHistoryBoundary(Thread &thread,
   return StopInfoSP(new StopInfoHistoryBoundary(thread, description));
 }
 
+StopInfoSP StopInfo::CreateStopReasonDyld(Thread &thread, 
+                                          const char *description) {
+  return StopInfoSP(new StopInfoDyld(thread, description));
+}
+
+
 StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
   return StopInfoSP(new StopInfoExec(thread));
 }
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
index 82fafc1b68903..31024fc99fe58 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ThreadMockGPU.cpp
@@ -13,7 +13,7 @@ using namespace lldb_server;
 
 ThreadMockGPU::ThreadMockGPU(ProcessMockGPU &process, lldb::tid_t tid)
     : NativeThreadProtocol(process, tid), m_reg_context(*this) {
-  m_stop_info.reason = lldb::eStopReasonTrace; //eStopReasonDynammicLoader;
+  m_stop_info.reason = lldb::eStopReasonTrace; // lldb::eStopReasonDynammicLoader;
 }
 
 // NativeThreadProtocol Interface

>From adc4a16852824b4370acbe56d9477761cca06073 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 6 May 2025 22:59:31 -0700
Subject: [PATCH 19/34] Fix typo in comment.

---
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index ace1d52e7f40c..f92a2ab661ced 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -929,7 +929,7 @@ Status ProcessGDBRemote::HandleConnectionRequest(const GPUActions &gpu_action) {
   if (error.Fail())
     return error;
   if (!process_sp)
-    return Status::FromErrorString("invalid process after conneting");
+    return Status::FromErrorString("invalid process after connecting");
 
   GetTarget().SetGPUPluginTarget(gpu_action.plugin_name, 
                                  process_sp->GetTarget().shared_from_this());

>From c5fcff756a6cc1797a9e251ca0bc000453f5cc0b Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 6 May 2025 23:00:01 -0700
Subject: [PATCH 20/34] Add the GDBRemoteCommunication this pointer to log.

Adding the this pointer to each log line for sending and receiving
packets allows us to see the log for the native process and GPU plug-in
so we can tell which communication is being used.
---
 .../gdb-remote/GDBRemoteCommunication.cpp     | 38 +++++++++++--------
 1 file changed, 22 insertions(+), 16 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
index 77eadfc8c9f6c..7ed80b048c77e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -97,7 +97,8 @@ size_t GDBRemoteCommunication::SendAck() {
   ConnectionStatus status = eConnectionStatusSuccess;
   char ch = '+';
   const size_t bytes_written = WriteAll(&ch, 1, status, nullptr);
-  LLDB_LOGF(log, "<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
+  LLDB_LOGF(log, "%p <%4" PRIu64 "> send packet: %c", static_cast<void *>(this), 
+            (uint64_t)bytes_written, ch);
   m_history.AddPacket(ch, GDBRemotePacket::ePacketTypeSend, bytes_written);
   return bytes_written;
 }
@@ -107,7 +108,8 @@ size_t GDBRemoteCommunication::SendNack() {
   ConnectionStatus status = eConnectionStatusSuccess;
   char ch = '-';
   const size_t bytes_written = WriteAll(&ch, 1, status, nullptr);
-  LLDB_LOGF(log, "<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
+  LLDB_LOGF(log, "%p <%4" PRIu64 "> send packet: %c", static_cast<void *>(this),
+            (uint64_t)bytes_written, ch);
   m_history.AddPacket(ch, GDBRemotePacket::ePacketTypeSend, bytes_written);
   return bytes_written;
 }
@@ -178,7 +180,8 @@ GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
       if (binary_start_offset) {
         StreamString strm;
         // Print non binary data header
-        strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written,
+        strm.Printf("%p <%4" PRIu64 "> send packet: %.*s", 
+                    static_cast<void *>(this), (uint64_t)bytes_written,
                     (int)binary_start_offset, packet_data);
         const uint8_t *p;
         // Print binary data exactly as sent
@@ -189,8 +192,9 @@ GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
         strm.Printf("%*s", (int)3, p);
         log->PutString(strm.GetString());
       } else
-        LLDB_LOGF(log, "<%4" PRIu64 "> send packet: %.*s",
-                  (uint64_t)bytes_written, (int)packet_length, packet_data);
+        LLDB_LOGF(log, "%p <%4" PRIu64 "> send packet: %.*s", 
+                  static_cast<void *>(this), (uint64_t)bytes_written, 
+                  (int)packet_length, packet_data);
     }
 
     m_history.AddPacket(packet.str(), packet_length,
@@ -749,12 +753,13 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
           StreamString strm;
           // Packet header...
           if (CompressionIsEnabled())
-            strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c",
-                        (uint64_t)original_packet_size, (uint64_t)total_length,
-                        m_bytes[0]);
-          else
-            strm.Printf("<%4" PRIu64 "> read packet: %c",
+            strm.Printf("%p <%4" PRIu64 ":%" PRIu64 "> read packet: %c",
+                        static_cast<void *>(this), (uint64_t)original_packet_size, 
                         (uint64_t)total_length, m_bytes[0]);
+          else
+            strm.Printf("%p <%4" PRIu64 "> read packet: %c", 
+                        static_cast<void *>(this), (uint64_t)total_length, 
+                        m_bytes[0]);
           for (size_t i = content_start; i < content_end; ++i) {
             // Remove binary escaped bytes when displaying the packet...
             const char ch = m_bytes[i];
@@ -773,13 +778,14 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
           log->PutString(strm.GetString());
         } else {
           if (CompressionIsEnabled())
-            LLDB_LOGF(log, "<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s",
-                      (uint64_t)original_packet_size, (uint64_t)total_length,
-                      (int)(total_length), m_bytes.c_str());
-          else
-            LLDB_LOGF(log, "<%4" PRIu64 "> read packet: %.*s",
-                      (uint64_t)total_length, (int)(total_length),
+            LLDB_LOGF(log, "%p <%4" PRIu64 ":%" PRIu64 "> read packet: %.*s",
+                      static_cast<void *>(this), (uint64_t)original_packet_size, 
+                      (uint64_t)total_length, (int)(total_length), 
                       m_bytes.c_str());
+          else
+            LLDB_LOGF(log, "%p <%4" PRIu64 "> read packet: %.*s", 
+                      static_cast<void *>(this), (uint64_t)total_length, 
+                      (int)(total_length), m_bytes.c_str());
         }
       }
 

>From 7516ccd0b509c48a032e62041409ad0993069513 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 6 May 2025 23:10:06 -0700
Subject: [PATCH 21/34] Add gpu plug-ins as an extension.

Only allow the native process linux to claim it has GPU plug-ins. Prior
to this change the GPU GDB server connection was claiming to have GPU
plug-ins as well.
---
 lldb/include/lldb/Host/common/NativeProcessProtocol.h        | 3 ++-
 lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp     | 2 +-
 .../Process/gdb-remote/GDBRemoteCommunicationClient.cpp      | 2 +-
 .../Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp  | 5 +++--
 4 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index e0c78158ec81e..d8d5f8a27735a 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -273,7 +273,8 @@ class NativeProcessProtocol {
     memory_tagging = (1u << 6),
     savecore = (1u << 7),
     siginfo_read = (1u << 8),
-    json_dynamic_loader = (1u << 9),
+    gpu_plugins = (1u << 9),
+    json_dynamic_loader = (1u << 10),
 
     LLVM_MARK_AS_BITMASK_ENUM(json_dynamic_loader)
   };
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 7f2aba0e4eb2c..87827011429ef 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -330,7 +330,7 @@ NativeProcessLinux::Manager::GetSupportedExtensions() const {
   NativeProcessLinux::Extension supported =
       Extension::multiprocess | Extension::fork | Extension::vfork |
       Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 |
-      Extension::siginfo_read;
+      Extension::siginfo_read | Extension::gpu_plugins;
 
 #ifdef __aarch64__
   // At this point we do not have a process so read auxv directly.
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 26bb6785ff1df..4a6a5a7d65654 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -424,7 +424,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
         m_uses_native_signals = eLazyBoolYes;
       else if (x == "binary-upload+")
         m_x_packet_state = xPacketState::Prefixed;
-      else if (x == "gpu-plugins")
+      else if (x == "gpu-plugins+")
         m_supports_gpu_plugins = eLazyBoolYes;
       else if (x == "ReverseContinue+")
         m_supports_reverse_continue = eLazyBoolYes;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 51bafe391f243..e6c1ed8aed1df 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -4319,8 +4319,7 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
                             "QThreadSuffixSupported+",
                             "QListThreadsInStopReply+",
                             "qXfer:features:read+",
-                            "QNonStop+",
-                            "gpu-plugins"
+                            "QNonStop+"
                         });
 
   // report server-only features
@@ -4338,6 +4337,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
     ret.push_back("memory-tagging+");
   if (bool(plugin_features & Extension::savecore))
     ret.push_back("qSaveCore+");
+  if (bool(plugin_features & Extension::gpu_plugins))
+    ret.push_back("gpu-plugins+");
   if (bool(plugin_features & Extension::json_dynamic_loader))
     ret.push_back("jGPUPluginGetDynamicLoaderLibraryInfo+");
 

>From 33ca2a016d26e5df224397c620d8b8fd8aa7b18b Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Tue, 6 May 2025 23:28:50 -0700
Subject: [PATCH 22/34] Rename some features and enums for gpu plug-ins and
 dyld.

---
 lldb/include/lldb/Host/common/NativeProcessProtocol.h         | 4 ++--
 .../Process/gdb-remote/GDBRemoteCommunicationClient.cpp       | 4 ++--
 .../Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h | 4 ++--
 .../Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp   | 4 ++--
 lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp     | 2 +-
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index d8d5f8a27735a..4436d7ed07bd2 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -274,9 +274,9 @@ class NativeProcessProtocol {
     savecore = (1u << 7),
     siginfo_read = (1u << 8),
     gpu_plugins = (1u << 9),
-    json_dynamic_loader = (1u << 10),
+    gpu_dyld = (1u << 10),
 
-    LLVM_MARK_AS_BITMASK_ENUM(json_dynamic_loader)
+    LLVM_MARK_AS_BITMASK_ENUM(gpu_dyld)
   };
 
   class Manager {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 4a6a5a7d65654..f24817df688ac 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -418,14 +418,14 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
         m_supports_memory_tagging = eLazyBoolYes;
       else if (x == "qSaveCore+")
         m_supports_qSaveCore = eLazyBoolYes;
-      else if (x == "jGPUPluginGetDynamicLoaderLibraryInfo+")
-        m_supports_jGPUPluginGetDynamicLoaderLibraryInfo = eLazyBoolYes;
       else if (x == "native-signals+")
         m_uses_native_signals = eLazyBoolYes;
       else if (x == "binary-upload+")
         m_x_packet_state = xPacketState::Prefixed;
       else if (x == "gpu-plugins+")
         m_supports_gpu_plugins = eLazyBoolYes;
+      else if (x == "gpu-dyld+")
+        m_supports_gdb_remote_gpu_dyld = eLazyBoolYes;
       else if (x == "ReverseContinue+")
         m_supports_reverse_continue = eLazyBoolYes;
       else if (x == "ReverseStep+")
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index e029bae4f5fc7..e776376195234 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -547,7 +547,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   void SetFilePassingFD(int fd);
 
   bool SupportsGPUDynamicLoader() const {
-    return m_supports_jGPUPluginGetDynamicLoaderLibraryInfo == eLazyBoolYes;
+    return m_supports_gdb_remote_gpu_dyld == eLazyBoolYes;
   }
   
 protected:
@@ -590,7 +590,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   LazyBool m_supports_multiprocess = eLazyBoolCalculate;
   LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
   LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
-  LazyBool m_supports_jGPUPluginGetDynamicLoaderLibraryInfo = eLazyBoolCalculate;
+  LazyBool m_supports_gdb_remote_gpu_dyld = eLazyBoolCalculate;
   LazyBool m_uses_native_signals = eLazyBoolCalculate;
   std::optional<xPacketState> m_x_packet_state;
   LazyBool m_supports_reverse_continue = eLazyBoolCalculate;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index e6c1ed8aed1df..b0e0e14f2a4b2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -4339,8 +4339,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
     ret.push_back("qSaveCore+");
   if (bool(plugin_features & Extension::gpu_plugins))
     ret.push_back("gpu-plugins+");
-  if (bool(plugin_features & Extension::json_dynamic_loader))
-    ret.push_back("jGPUPluginGetDynamicLoaderLibraryInfo+");
+  if (bool(plugin_features & Extension::gpu_dyld))
+    ret.push_back("gpu-dyld+");
 
   // check for client features
   m_extensions_supported = {};
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
index 3421211947601..4518df6db8e22 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/ProcessMockGPU.cpp
@@ -210,5 +210,5 @@ ProcessMockGPU::Manager::Attach(
 
 ProcessMockGPU::Extension
 ProcessMockGPU::Manager::GetSupportedExtensions() const {
-  return Extension::json_dynamic_loader;
+  return Extension::gpu_dyld;
 }

>From cc4aaa53820bee3122c4747d67086aa622151601 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Thu, 8 May 2025 09:35:57 -0700
Subject: [PATCH 23/34] Allow GPUActions to auto resume the GPU process.

Anytime a GPUActions is returned, clients can now set
GPUActions.resume_gpu_process = true if they want to resume the GPU
process from the native process.
---
 lldb/include/lldb/Utility/GPUGDBRemotePackets.h             | 2 ++
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 2 ++
 lldb/source/Utility/GPUGDBRemotePackets.cpp                 | 4 +++-
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 1094c3addcf98..5554f07030652 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -169,6 +169,8 @@ struct GPUActions {
   /// process to be notified that it should query for the shared libraries on 
   /// the GPU connection.
   bool load_libraries = false;
+  /// Set this to true if the native plug-in resume the GPU process.
+  bool resume_gpu_process = false;
 };
 
 bool fromJSON(const llvm::json::Value &value, 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index f92a2ab661ced..23746ad0ddaa4 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -888,6 +888,8 @@ Status ProcessGDBRemote::HandleGPUActions(const GPUActions &gpu_action) {
         llvm::Error error = gpu_process_sp->LoadModules();
         if (error)
           llvm::consumeError(std::move(error));
+        if (gpu_action.resume_gpu_process)
+          gpu_process_sp->Resume();
       }
     }
   }
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index f6ddbe17e77ed..078eab7b77f7a 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -149,7 +149,8 @@ bool fromJSON(const llvm::json::Value &value, GPUActions &data,
          o.map("plugin_name", data.plugin_name) &&
          o.map("breakpoints", data.breakpoints) &&
          o.mapOptional("connect_info", data.connect_info) &&
-         o.map("load_libraries", data.load_libraries);
+         o.map("load_libraries", data.load_libraries) &&
+         o.map("resume_gpu_process", data.resume_gpu_process);
 }
 
 llvm::json::Value toJSON(const GPUActions &data) {
@@ -158,6 +159,7 @@ llvm::json::Value toJSON(const GPUActions &data) {
            {"breakpoints", data.breakpoints},
            {"connect_info", data.connect_info},
            {"load_libraries", data.load_libraries},
+           {"resume_gpu_process", data.resume_gpu_process},
           });
 }
 

>From 1782a4c2a643a66beb8b74d8c54707ce94b5b56d Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Thu, 8 May 2025 16:25:50 -0700
Subject: [PATCH 24/34] Make module loading work within a file.

This patch enables a ModuleSpec to specify the file offset and size
with:

void SBModuleSpec::SetObjectOffset(uint64_t offset);
void SBModuleSpec::SetObjectSize(uint64_t size);

And allows LLDB to load the right file for AMD.
---
 lldb/source/API/SBModuleSpec.cpp                     |  2 +-
 lldb/source/Core/Module.cpp                          | 10 ++++++----
 lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp |  8 ++------
 lldb/source/Utility/ArchSpec.cpp                     |  6 ++++--
 4 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/lldb/source/API/SBModuleSpec.cpp b/lldb/source/API/SBModuleSpec.cpp
index fbbcfeac20178..548b056fac277 100644
--- a/lldb/source/API/SBModuleSpec.cpp
+++ b/lldb/source/API/SBModuleSpec.cpp
@@ -170,7 +170,7 @@ uint64_t SBModuleSpec::GetObjectSize() {
 
 void SBModuleSpec::SetObjectSize(uint64_t object_size) {
   LLDB_INSTRUMENT_VA(this, object_size);
-
+  
   m_opaque_up->SetObjectSize(object_size);
 }
 
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index d70f292abaea4..796e45570bea5 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -151,15 +151,17 @@ Module::Module(const ModuleSpec &module_spec)
               module_spec.GetObjectName().IsEmpty() ? "" : ")");
 
   auto data_sp = module_spec.GetData();
-  lldb::offset_t file_size = 0;
-  if (data_sp)
-    file_size = data_sp->GetByteSize();
 
   // First extract all module specifications from the file using the local file
   // path. If there are no specifications, then don't fill anything in
   ModuleSpecList modules_specs;
+
   if (ObjectFile::GetModuleSpecifications(
-          module_spec.GetFileSpec(), 0, file_size, modules_specs, data_sp) == 0)
+          module_spec.GetFileSpec(), 
+          module_spec.GetObjectOffset(), 
+          module_spec.GetObjectSize(), 
+          modules_specs, 
+          data_sp) == 0)
     return;
 
   // Now make sure that one of the module specifications matches what we just
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 13e1198516f78..e94eba8e98685 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -603,8 +603,8 @@ size_t ObjectFileELF::GetModuleSpecifications(
           //
           // Validate it is ok to remove GetOsFromOSABI
           GetOsFromOSABI(header.e_ident[EI_OSABI], ostype);
-          assert(spec_ostype == ostype);
-          if (spec_ostype != llvm::Triple::OSType::UnknownOS) {
+          if (ostype != llvm::Triple::OSType::UnknownOS && 
+              spec_ostype != llvm::Triple::OSType::UnknownOS) {
             LLDB_LOGF(log,
                       "ObjectFileELF::%s file '%s' set ELF module OS type "
                       "from ELF header OSABI.",
@@ -1370,7 +1370,6 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
   // We'll refine this with note data as we parse the notes.
   if (arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS) {
     llvm::Triple::OSType ostype;
-    llvm::Triple::OSType spec_ostype;
     const uint32_t sub_type = subTypeFromElfHeader(header);
     arch_spec.SetArchitecture(eArchTypeELF, header.e_machine, sub_type,
                               header.e_ident[EI_OSABI]);
@@ -1382,9 +1381,6 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
     // notes at all and have EI_OSABI flag set to System V, as result the OS
     // will be set to UnknownOS.
     GetOsFromOSABI(header.e_ident[EI_OSABI], ostype);
-    spec_ostype = arch_spec.GetTriple().getOS();
-    assert(spec_ostype == ostype);
-    UNUSED_IF_ASSERT_DISABLED(spec_ostype);
   }
 
   if (arch_spec.GetMachine() == llvm::Triple::mips ||
diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp
index 3cdde2f1f822e..2451f9e611532 100644
--- a/lldb/source/Utility/ArchSpec.cpp
+++ b/lldb/source/Utility/ArchSpec.cpp
@@ -440,8 +440,10 @@ static const ArchDefinitionEntry g_elf_arch_entries[] = {
     {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH,
      ArchSpec::eLoongArchSubType_loongarch64, 0xFFFFFFFFu,
      0xFFFFFFFFu}, // loongarch64
-    {ArchSpec::eCore_amd_gpu_r600, llvm::ELF::EM_AMDGPU, LLDB_INVALID_CPUTYPE,
-     0xFFFFFFFFu, 0xFFFFFFFFu},
+    // TODO: add data that says what the address byte size is in there 
+    // structures so we can tell r600 from gcn
+    // {ArchSpec::eCore_amd_gpu_r600, llvm::ELF::EM_AMDGPU, LLDB_INVALID_CPUTYPE,
+    //  0xFFFFFFFFu, 0xFFFFFFFFu},
     {ArchSpec::eCore_amd_gpu_gcn, llvm::ELF::EM_AMDGPU, LLDB_INVALID_CPUTYPE,
      0xFFFFFFFFu, 0xFFFFFFFFu},
     {ArchSpec::eCore_nvidia_nvptx, llvm::ELF::EM_CUDA, LLDB_INVALID_CPUTYPE,

>From 095224c0df2782075d2ea587c61754ee6c277288 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Fri, 9 May 2025 14:03:22 -0700
Subject: [PATCH 25/34] Add the ability to halt the native process from the GPU
 plug-in.

If the GPU plug-ins need to halt the process so that the GPU plug-in
will receive a LLDBServerPlugin::NativeProcessIsStopping(...) call, then
it can call this function. This allows synchronization with the native
process and the GPU plug-in can return GPUActions to perform.
---
 .../lldb/Host/common/NativeProcessProtocol.h  |  4 +-
 .../Process/gdb-remote/LLDBServerPlugin.cpp   | 22 ++++++++++
 .../Process/gdb-remote/LLDBServerPlugin.h     | 41 ++++++++++++++-----
 3 files changed, 55 insertions(+), 12 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 4436d7ed07bd2..4d7b880ec61e0 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -191,7 +191,9 @@ class NativeProcessProtocol {
 
   bool IsStepping() const { return m_state == lldb::eStateStepping; }
 
-  bool CanResume() const { return m_state == lldb::eStateStopped; }
+  bool IsStopped() const { return m_state == lldb::eStateStopped; }
+
+  bool CanResume() const { return IsStopped(); }
 
   lldb::ByteOrder GetByteOrder() const {
     return GetArchitecture().GetByteOrder();
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
index 6db7120a245a2..a520309f4e916 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
@@ -8,6 +8,8 @@
 
 #include "LLDBServerPlugin.h"
 #include "GDBRemoteCommunicationServerLLGS.h"
+#include <chrono>
+#include <thread>
 
 using namespace lldb_private;
 using namespace lldb_server;
@@ -17,3 +19,23 @@ LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process) :
   m_native_process(native_process) {}
 
 LLDBServerPlugin::~LLDBServerPlugin() {}
+
+
+lldb::StateType 
+LLDBServerPlugin::HaltNativeProcessIfNeeded(bool &was_halted, 
+                                            uint32_t timeout_sec) {
+  using namespace std::chrono;
+  NativeProcessProtocol *process = m_native_process.GetCurrentProcess();
+  if (process->IsRunning()) {
+    was_halted = true;
+    process->Halt();
+ 
+    auto end_time = steady_clock::now() + seconds(timeout_sec);
+    while (std::chrono::steady_clock::now() < end_time) {
+      std::this_thread::sleep_for(milliseconds(250));
+      if (process->IsStopped())
+        break;
+    }
+  }
+  return process->GetState();
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index 6bafce998ea95..671529c08cbe6 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -26,7 +26,7 @@ namespace lldb_private {
 namespace process_gdb_remote {
   class GDBRemoteCommunicationServerLLGS;
 }
-  
+
 namespace lldb_server {
 
 class LLDBServerPlugin {
@@ -50,14 +50,33 @@ class LLDBServerPlugin {
 
   virtual llvm::StringRef GetPluginName() = 0;
 
+  /// Stop the native process if it is running.
+  ///
+  /// Some plug-ins might want to stop the native process if it is running so
+  /// that the plug-in can return some GPUActions from the call to the
+  /// NativeProcessIsStopping(). This function will trigger the native process
+  /// to stop only if it is running.
+  ///
+  /// \param[out] was_halted The \a was_halted parameter will be set to
+  ///   true if the process was running and was halted. It will be false if the
+  ///   process was already stopped.
+  ///
+  /// \param[out] timeout_sec The timeout in seconds to wait for the process to
+  ///   enter the stopped state.
+  ///
+  /// \return The actual state of the process in case the process was not able
+  /// to be stopped within the specified timeout.
+  lldb::StateType HaltNativeProcessIfNeeded(bool &was_halted,
+                                            uint32_t timeout_sec = 5);
+
   /// Get notified when the process is stopping.
   ///
   /// This function will get called each time native process stops as the stop
   /// reply packet is being created. If the plug-in is ready to be activated,
-  /// return a GPUPluginConnectionInfo with a value connection URL to use with 
-  /// "process connect" which can connect to this plug-in. Plug-ins should wait 
-  /// for a connection to be made before trying to do any blocking code. The 
-  /// plug-in should assume the users do not want to use any features unless a 
+  /// return a GPUPluginConnectionInfo with a value connection URL to use with
+  /// "process connect" which can connect to this plug-in. Plug-ins should wait
+  /// for a connection to be made before trying to do any blocking code. The
+  /// plug-in should assume the users do not want to use any features unless a
   /// connection is made.
   virtual std::optional<GPUActions> NativeProcessIsStopping() {
     return std::nullopt;
@@ -65,16 +84,16 @@ class LLDBServerPlugin {
 
   /// Get the GPU plug-in initialization actions.
   ///
-  /// Each GPU plugin can return a structure that describes the GPU plug-in and 
+  /// Each GPU plugin can return a structure that describes the GPU plug-in and
   /// any actions that should be performed right away .Actions include setting
   /// any breakpoints it requires in the native process. GPU plug-ins might want
-  /// to set breakpoints in the native process to know when the GPU has been 
+  /// to set breakpoints in the native process to know when the GPU has been
   /// initialized, or when the GPU has shared libraries that get loaded.
   /// They can do this by populating returning any actions needed when this
   /// function is called.
-  /// 
-  /// The contents of this structure will be converted to JSON and sent to the 
-  /// LLDB client. The structure allows plug-ins to set breakpoints by name 
+  ///
+  /// The contents of this structure will be converted to JSON and sent to the
+  /// LLDB client. The structure allows plug-ins to set breakpoints by name
   /// and to also request symbol values that should be sent when the breakpoint
   /// gets hit. When the breakpoint is hit, the BreakpointWasHit(...) method
   /// will get called with a structure that identifies the plugin,
@@ -106,7 +125,7 @@ class LLDBServerPlugin {
   /// calling m_process.SetBreakpoint(...) to help implement funcionality,
   /// such as dynamic library loading in GPUs or to synchronize in any other
   /// way with the native process.
-  virtual GPUPluginBreakpointHitResponse 
+  virtual GPUPluginBreakpointHitResponse
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) = 0;
 
   protected:

>From 2e050ba2e0ad6b7ddc5870c3048ed3244a962f47 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Thu, 22 May 2025 09:37:37 -0700
Subject: [PATCH 26/34] Added the ability to have the native process wait for
 the GPU process to resume.

GPUActions now has a new "bool wait_for_gpu_process_to_resume" member
variable. If this is set then when the native process gets GPUActions,
they will wait for the GPU process to resume.
---
 lldb/include/lldb/Target/Process.h            | 15 ++++++
 .../lldb/Utility/GPUGDBRemotePackets.h        |  3 ++
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 46 +++++++++++++------
 lldb/source/Target/Process.cpp                | 32 +++++++++++++
 lldb/source/Utility/GPUGDBRemotePackets.cpp   |  6 ++-
 5 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 2e827d4c5cb74..005e1b3b20de9 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -2253,6 +2253,21 @@ class Process : public std::enable_shared_from_this<Process>,
                        SelectMostRelevant select_most_relevant =
                            DoNoSelectMostRelevantFrame);
 
+  /// Wait for the process to resume.
+  ///
+  /// Given the current ProcessModID that identifies a current state, wait for
+  /// the process to resume. 
+  ///
+  /// \param[in] curr_resume_id
+  ///     The resume ID that identifies the resume ID from a stopped state.
+  ///
+  /// \param[in] timeout_sec
+  ///     The maximum time in seconds to wait for the process to transition to
+  ///     the eStateRunning state. If no timeout is supplied, block and wait
+  ///     indefinitely.
+  Status WaitForNextResume(const uint32_t curr_resume_id, 
+                           std::optional<uint32_t> timeout_sec);
+
   uint32_t GetIOHandlerID() const { return m_iohandler_sync.GetValue(); }
 
   /// Waits for the process state to be running within a given msec timeout.
diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 5554f07030652..2563b9f6a6d46 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -171,6 +171,9 @@ struct GPUActions {
   bool load_libraries = false;
   /// Set this to true if the native plug-in resume the GPU process.
   bool resume_gpu_process = false;
+  /// Set this to true if the native plug-in sync with the GPU process and wait
+  /// for it to return to a running state.
+  bool wait_for_gpu_process_to_resume = false;
 };
 
 bool fromJSON(const llvm::json::Value &value, 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 23746ad0ddaa4..1b8e676e041a1 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -877,23 +877,41 @@ Status ProcessGDBRemote::HandleGPUActions(const GPUActions &gpu_action) {
   Status error;
   if (!gpu_action.breakpoints.empty())
     HandleGPUBreakpoints(gpu_action);
-  if (gpu_action.connect_info)
+  if (gpu_action.connect_info) {
     error = HandleConnectionRequest(gpu_action);
+    if (error.Fail())
+      return error;
+  }
+
+  // Any commands below require a GPU process
+  if (!(gpu_action.load_libraries || gpu_action.resume_gpu_process || 
+      gpu_action.wait_for_gpu_process_to_resume))
+    return;
+  lldb::TargetSP gpu_target_sp = 
+      GetTarget().GetGPUPluginTarget(gpu_action.plugin_name);
+  if (!gpu_target_sp)
+    return;
+  lldb::ProcessSP gpu_process_sp = gpu_target_sp->GetProcessSP();
+  if (!gpu_process_sp)
+    return;
+  // Save the resume ID in case we need to wait for the process to resume below.
+  const uint32_t gpu_process_resume_id = gpu_process_sp->GetResumeID();
   if (gpu_action.load_libraries) {
-    lldb::TargetSP gpu_target_sp = 
-        GetTarget().GetGPUPluginTarget(gpu_action.plugin_name);
-    if (gpu_target_sp) {
-      lldb::ProcessSP gpu_process_sp = gpu_target_sp->GetProcessSP();
-      if (gpu_process_sp) {
-        llvm::Error error = gpu_process_sp->LoadModules();
-        if (error)
-          llvm::consumeError(std::move(error));
-        if (gpu_action.resume_gpu_process)
-          gpu_process_sp->Resume();
-      }
-    }
+    llvm::Error error = gpu_process_sp->LoadModules();
+    if (error)
+      return Status::FromError(std::move(error));
   }
-  return error;
+  if (gpu_action.resume_gpu_process) {
+    error = gpu_process_sp->Resume();
+    if (error.Fail())
+      return error;
+  }
+  if (gpu_action.wait_for_gpu_process_to_resume) {
+    error = gpu_process_sp->WaitForNextResume(gpu_process_resume_id, 5);
+    if (error.Fail())
+      return error;
+  }
+  return Status();
 }
 
 Status ProcessGDBRemote::HandleConnectionRequest(const GPUActions &gpu_action) {
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index c962af98542cf..a4366d3d774ec 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2578,6 +2578,38 @@ Status Process::DisableWatchpoint(WatchpointSP wp_sp, bool notify) {
   return error;
 }
 
+Status Process::WaitForNextResume(const uint32_t curr_resume_id, 
+                                std::optional<uint32_t> opt_timeout_seconds) {
+  ListenerSP listener_sp;
+  {
+    // Don't let the private state change on us while we do some checking by 
+    // locking the private state mutex. The resume ID can't be bumped while we
+    // hold this lock. We also need to start listening for state changed events
+    // prior to letting go of this mutex to ensure there is no race condition.
+    std::lock_guard<std::recursive_mutex> guard(m_private_state.GetMutex());
+    if (GetResumeID() != curr_resume_id)
+      return Status(); ///< Process already resumed.
+  
+    listener_sp = Listener::MakeListener("Process::WaitForNextResume");
+    listener_sp->StartListeningForEvents(this, eBroadcastBitStateChanged);
+  }
+
+  lldb::EventSP event_sp;
+  Timeout<std::micro> timeout(std::nullopt);
+  if (opt_timeout_seconds.has_value())
+    timeout = std::chrono::seconds(*opt_timeout_seconds);
+  listener_sp->GetEvent(event_sp, timeout);
+
+  if (!event_sp)
+    return Status::FromErrorString("timeout waiting for process to resume");
+  
+  // Don't let the private state change on us while we do some checking.
+  std::lock_guard<std::recursive_mutex> guard(m_private_state.GetMutex());
+  if (GetResumeID() != curr_resume_id)
+    return Status(); // Success
+  return Status::FromErrorString("process was not resumed");
+}
+
 StateType
 Process::WaitForProcessStopPrivate(EventSP &event_sp,
                                    const Timeout<std::micro> &timeout) {
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 078eab7b77f7a..651a56a2370f1 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -150,7 +150,9 @@ bool fromJSON(const llvm::json::Value &value, GPUActions &data,
          o.map("breakpoints", data.breakpoints) &&
          o.mapOptional("connect_info", data.connect_info) &&
          o.map("load_libraries", data.load_libraries) &&
-         o.map("resume_gpu_process", data.resume_gpu_process);
+         o.map("resume_gpu_process", data.resume_gpu_process) &&
+         o.map("wait_for_gpu_process_to_resume", 
+               data.wait_for_gpu_process_to_resume);
 }
 
 llvm::json::Value toJSON(const GPUActions &data) {
@@ -160,6 +162,8 @@ llvm::json::Value toJSON(const GPUActions &data) {
            {"connect_info", data.connect_info},
            {"load_libraries", data.load_libraries},
            {"resume_gpu_process", data.resume_gpu_process},
+           {"wait_for_gpu_process_to_resume", 
+            data.wait_for_gpu_process_to_resume},
           });
 }
 

>From e5d50715ca78a7070616ab2e8b099861b7b907b7 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 28 May 2025 11:24:03 -0700
Subject: [PATCH 27/34] Fix ProcessGDBRemote::HandleGPUActions() to return an
 error.

---
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 1b8e676e041a1..2a7704e73200d 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -886,14 +886,14 @@ Status ProcessGDBRemote::HandleGPUActions(const GPUActions &gpu_action) {
   // Any commands below require a GPU process
   if (!(gpu_action.load_libraries || gpu_action.resume_gpu_process || 
       gpu_action.wait_for_gpu_process_to_resume))
-    return;
+    return error;
   lldb::TargetSP gpu_target_sp = 
       GetTarget().GetGPUPluginTarget(gpu_action.plugin_name);
   if (!gpu_target_sp)
-    return;
+    return error;
   lldb::ProcessSP gpu_process_sp = gpu_target_sp->GetProcessSP();
   if (!gpu_process_sp)
-    return;
+    return error;
   // Save the resume ID in case we need to wait for the process to resume below.
   const uint32_t gpu_process_resume_id = gpu_process_sp->GetResumeID();
   if (gpu_action.load_libraries) {

>From 0b720b84d1dd87a5c2089636c51abd4e6b5d5d47 Mon Sep 17 00:00:00 2001
From: Walter Erquinigo <werquinigo at nvidia.com>
Date: Thu, 5 Jun 2025 16:59:49 +0000
Subject: [PATCH 28/34] [To upstream] Add proper error handling for GPU packets

---
 .../GDBRemoteCommunicationClient.cpp          | 89 ++++++++++++-------
 .../GDBRemoteCommunicationServerLLGS.cpp      |  7 +-
 .../Process/gdb-remote/LLDBServerPlugin.h     | 12 +--
 .../MockGPU/LLDBServerPluginMockGPU.cpp       |  2 +-
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h | 10 +--
 5 files changed, 76 insertions(+), 44 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index f24817df688ac..fd7154715e5d3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -15,6 +15,7 @@
 #include <optional>
 #include <sstream>
 
+#include "lldb/Core/Debugger.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Host/SafeMachO.h"
@@ -37,7 +38,6 @@
 #include "lldb/Utility/GPUGDBRemotePackets.h"
 #include "lldb/Utility/StringExtractorGDBRemote.h"
 
-
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB
@@ -367,7 +367,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
   m_x_packet_state.reset();
   m_supports_reverse_continue = eLazyBoolNo;
   m_supports_reverse_step = eLazyBoolNo;
-  m_supports_gpu_plugins =  eLazyBoolNo;
+  m_supports_gpu_plugins = eLazyBoolNo;
   m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
                                   // not, we assume no limit
 
@@ -430,7 +430,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
         m_supports_reverse_continue = eLazyBoolYes;
       else if (x == "ReverseStep+")
         m_supports_reverse_step = eLazyBoolYes;
-      
+
       // Look for a list of compressions in the features list e.g.
       // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
       // deflate,lzma
@@ -606,14 +606,14 @@ StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
   return object_sp;
 }
 
-std::optional<std::vector<GPUActions>> 
+std::optional<std::vector<GPUActions>>
 GDBRemoteCommunicationClient::GetGPUInitializeActions() {
   // Get JSON information containing any breakpoints and other information
   // required by any GPU plug-ins using the "jGPUPluginInitialize" packet.
   //
   // GPU plug-ins might require breakpoints to be set in the native program
   // that controls the GPUs. This packet allows the GPU plug-ins to set one or
-  // more breakpoints in the native program and get a callback when those 
+  // more breakpoints in the native program and get a callback when those
   // breakpoints get hit.
 
   if (m_supports_gpu_plugins == eLazyBoolYes) {
@@ -623,20 +623,31 @@ GDBRemoteCommunicationClient::GetGPUInitializeActions() {
         PacketResult::Success) {
       if (response.IsUnsupportedResponse()) {
         m_supports_gpu_plugins = eLazyBoolNo;
-      } else if (!response.Empty()) {
-        if (llvm::Expected<std::vector<GPUActions>> info = 
-                llvm::json::parse<std::vector<GPUActions>>(response.Peek(), 
-                                                            "GPUActions"))
-          return std::move(*info);
-        else
-          llvm::consumeError(info.takeError());
+        return std::nullopt;
+      }
+      if (response.IsErrorResponse()) {
+        Debugger::ReportError(response.GetStatus().AsCString());
+        return std::nullopt;
+      }
+      if (llvm::Expected<std::vector<GPUActions>> info =
+              llvm::json::parse<std::vector<GPUActions>>(response.Peek(),
+                                                         "GPUActions")) {
+        return std::move(*info);
+      } else {
+        // We don't show JSON parsing errors to the user because they won't
+        // make sense to them.
+        llvm::consumeError(info.takeError());
+        Debugger::ReportError(
+            llvm::formatv("malformed jGPUPluginInitialize response packet. {0}",
+                          response.GetStringRef()));
       }
     }
   }
+
   return std::nullopt;
 }
 
-std::optional<GPUPluginBreakpointHitResponse> 
+std::optional<GPUPluginBreakpointHitResponse>
 GDBRemoteCommunicationClient::GPUBreakpointHit(
     const GPUPluginBreakpointHitArgs &args) {
   StreamGDBRemote packet;
@@ -645,20 +656,28 @@ GDBRemoteCommunicationClient::GPUBreakpointHit(
   StringExtractorGDBRemote response;
   if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
       PacketResult::Success) {
-    if (!response.Empty()) {
-      if (llvm::Expected<GPUPluginBreakpointHitResponse> info = 
-          llvm::json::parse<GPUPluginBreakpointHitResponse>(response.Peek(), 
-              "GPUPluginBreakpointHitResponse")) {
-        return *info;
-      } else {
-        llvm::consumeError(info.takeError());
-      }
+    if (response.IsErrorResponse()) {
+      Debugger::ReportError(response.GetStatus().AsCString());
+      return std::nullopt;
+    }
+    if (llvm::Expected<GPUPluginBreakpointHitResponse> info =
+            llvm::json::parse<GPUPluginBreakpointHitResponse>(
+                response.Peek(), "GPUPluginBreakpointHitResponse")) {
+      return *info;
+    } else {
+      // We don't show JSON parsing errors to the user because they won't
+      // make sense to them.
+      llvm::consumeError(info.takeError());
+      Debugger::ReportError(llvm::formatv("malformed jGPUPluginBreakpointHit "
+                                          "response packet. {0}",
+                                          response.GetStringRef()));
     }
   }
+
   return std::nullopt;
 }
 
-std::optional<GPUDynamicLoaderResponse> 
+std::optional<GPUDynamicLoaderResponse>
 GDBRemoteCommunicationClient::GetGPUDynamicLoaderLibraryInfos(
     const GPUDynamicLoaderArgs &args) {
   StreamGDBRemote packet;
@@ -667,16 +686,26 @@ GDBRemoteCommunicationClient::GetGPUDynamicLoaderLibraryInfos(
   StringExtractorGDBRemote response;
   if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
       PacketResult::Success) {
-    if (!response.Empty()) {
-      if (llvm::Expected<GPUDynamicLoaderResponse> info = 
-          llvm::json::parse<GPUDynamicLoaderResponse>(response.Peek(), 
-              "GPUDynamicLoaderResponse")) {
-        return *info;
-      } else {
-        llvm::consumeError(info.takeError());
-      }
+    if (response.IsErrorResponse()) {
+      Debugger::ReportError(response.GetStatus().AsCString());
+      return std::nullopt;
+    }
+
+    if (llvm::Expected<GPUDynamicLoaderResponse> info =
+            llvm::json::parse<GPUDynamicLoaderResponse>(
+                response.Peek(), "GPUDynamicLoaderResponse")) {
+      return *info;
+    } else {
+      // We don't show JSON parsing errors to the user because they won't
+      // make sense to them.
+      llvm::consumeError(info.takeError());
+      Debugger::ReportError(
+          llvm::formatv("malformed jGPUPluginGetDynamicLoaderLibraryInfo "
+                        "response packet. {0}",
+                        response.GetStringRef()));
     }
   }
+
   return std::nullopt;
 }
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index b0e0e14f2a4b2..0d543175ce49d 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -3718,10 +3718,13 @@ GDBRemoteCommunicationServerLLGS::Handle_jGPUPluginBreakpointHit(
 
   for (auto &plugin_up: m_plugins) {
     if (plugin_up->GetPluginName() == args->plugin_name) {
-      GPUPluginBreakpointHitResponse bp_response = 
+      Expected<GPUPluginBreakpointHitResponse> bp_response = 
           plugin_up->BreakpointWasHit(*args);
+      if (!bp_response)
+        return SendErrorResponse(bp_response.takeError());
+        
       StreamGDBRemote response;
-      response.PutAsJSON(bp_response, /*hex_ascii=*/false);
+      response.PutAsJSON(*bp_response, /*hex_ascii=*/false);
       return SendPacketNoLock(response.GetString());
     }
   }
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index 671529c08cbe6..82f449c5c088c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -16,16 +16,16 @@
 #include "llvm/Support/JSON.h"
 
 #include <functional>
+#include <mutex>
 #include <optional>
 #include <stdint.h>
 #include <string>
-#include <mutex>
 
 namespace lldb_private {
 
 namespace process_gdb_remote {
-  class GDBRemoteCommunicationServerLLGS;
-}
+class GDBRemoteCommunicationServerLLGS;
+} // namespace process_gdb_remote
 
 namespace lldb_server {
 
@@ -125,11 +125,11 @@ class LLDBServerPlugin {
   /// calling m_process.SetBreakpoint(...) to help implement funcionality,
   /// such as dynamic library loading in GPUs or to synchronize in any other
   /// way with the native process.
-  virtual GPUPluginBreakpointHitResponse
+  virtual llvm::Expected<GPUPluginBreakpointHitResponse>
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) = 0;
 
-  protected:
-    std::mutex m_connect_mutex;
+protected:
+  std::mutex m_connect_mutex;
 };
 
 } // namespace lldb_server
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index 03a50f4220870..41a6ed5921ba8 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -181,7 +181,7 @@ std::optional<GPUActions> LLDBServerPluginMockGPU::NativeProcessIsStopping() {
   return std::nullopt;
 }
 
-GPUPluginBreakpointHitResponse 
+llvm::Expected<GPUPluginBreakpointHitResponse>
 LLDBServerPluginMockGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
   Log *log = GetLog(GDBRLog::Plugin);
   std::string json_string;
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index f153579b1160d..620fdcaf9e7c4 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -49,13 +49,13 @@ int main(int argc, const char **argv) {
 */
 // If the above code is run, you will be stopped at the breakpoint and the Mock
 // GPU target will be selected. Try doing a "reg read --all" to see the state
-// of the GPU registers. Then you can select the native process target with 
+// of the GPU registers. Then you can select the native process target with
 // "target select 0" and issue commands to the native process, and then select
 // the GPU target with "target select 1" and issue commands to the GPU target.
 
 namespace lldb_private {
-  
-  class TCPSocket;
+
+class TCPSocket;
 
 namespace lldb_server {
 
@@ -67,8 +67,8 @@ class LLDBServerPluginMockGPU : public LLDBServerPlugin {
   int GetEventFileDescriptorAtIndex(size_t idx) override;
   bool HandleEventFileDescriptorEvent(int fd) override;
   GPUActions GetInitializeActions() override;
-  std::optional<struct GPUActions> NativeProcessIsStopping() override;  
-  GPUPluginBreakpointHitResponse 
+  std::optional<struct GPUActions> NativeProcessIsStopping() override;
+  llvm::Expected<GPUPluginBreakpointHitResponse>
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
 
 private:

>From 88cd79747645fabb5df33785029407b5f058a4a3 Mon Sep 17 00:00:00 2001
From: Walter Erquinigo <werquinigo at nvidia.com>
Date: Thu, 5 Jun 2025 17:05:36 +0000
Subject: [PATCH 29/34] [To upstream] minor changes in the packets files

---
 .../lldb/Utility/GPUGDBRemotePackets.h        |  64 ++++----
 lldb/source/Utility/GPUGDBRemotePackets.cpp   | 150 ++++++++----------
 2 files changed, 99 insertions(+), 115 deletions(-)

diff --git a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
index 2563b9f6a6d46..736fc4d70a014 100644
--- a/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/GPUGDBRemotePackets.h
@@ -24,7 +24,7 @@ struct SymbolValue {
   /// If the optional doesn't have a value, then the symbol was not available.
   std::optional<uint64_t> value;
 };
-bool fromJSON(const llvm::json::Value &value, SymbolValue &data, 
+bool fromJSON(const llvm::json::Value &value, SymbolValue &data,
               llvm::json::Path path);
 
 llvm::json::Value toJSON(const SymbolValue &data);
@@ -71,7 +71,7 @@ llvm::json::Value toJSON(const GPUBreakpointByAddress &data);
 ///
 /// Clients should either fill in the \a name_info or the \a addr_info. If the
 /// breakpoint callback needs some symbols from the native process, they can
-/// fill in the array of symbol names with any symbol names that are needed. 
+/// fill in the array of symbol names with any symbol names that are needed.
 /// These symbol values will be delivered in the breakpoint callback to the GPU
 /// plug-in.
 ///-----------------------------------------------------------------------------
@@ -86,16 +86,20 @@ struct GPUBreakpointInfo {
 };
 
 bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
-  llvm::json::Path path);
+              llvm::json::Path path);
 
 llvm::json::Value toJSON(const GPUBreakpointInfo &data);
 
 struct GPUPluginBreakpointHitArgs {
+  GPUPluginBreakpointHitArgs() = default;
+  GPUPluginBreakpointHitArgs(llvm::StringRef plugin_name)
+      : plugin_name(plugin_name) {}
+
   std::string plugin_name;
   GPUBreakpointInfo breakpoint;
   std::vector<SymbolValue> symbol_values;
 
-  std::optional<uint64_t> GetSymbolValue(llvm::StringRef symbol_name);
+  std::optional<uint64_t> GetSymbolValue(llvm::StringRef symbol_name) const;
 };
 
 bool fromJSON(const llvm::json::Value &value, GPUPluginBreakpointHitArgs &data,
@@ -116,7 +120,7 @@ struct GPUPluginConnectionInfo {
   std::optional<std::string> platform_name;
   /// The target triple to use as the architecture when creating the target.
   std::optional<std::string> triple;
-  /// The connection URL to use with "process connect <url>". 
+  /// The connection URL to use with "process connect <url>".
   std::string connect_url;
 };
 
@@ -140,11 +144,11 @@ llvm::json::Value toJSON(const GPUPluginConnectionInfo &data);
 ///   native process in LLDB will call into the native process' GDB server and
 ///   have it call the GPU plug-in method:
 ///
-///     GPUPluginBreakpointHitResponse 
+///     GPUPluginBreakpointHitResponse
 ///     LLDBServerPlugin::BreakpointWasHit(GPUPluginBreakpointHitArgs &args);
 ///
 ///   The GPUPluginBreakpointHitResponse contains a GPUActions member that will
-///   be encoded and sent back to the ProcessGDBRemote for the native process. 
+///   be encoded and sent back to the ProcessGDBRemote for the native process.
 ///
 /// - Anytime the native process stops, the native process' GDB server will ask
 ///   each GPU plug-in if there are any actions it would like to report, the
@@ -157,6 +161,9 @@ llvm::json::Value toJSON(const GPUPluginConnectionInfo &data);
 ///   native process.
 ///-----------------------------------------------------------------------------
 struct GPUActions {
+  GPUActions() = default;
+  GPUActions(llvm::StringRef plugin_name) : plugin_name(plugin_name) {}
+
   /// The name of the plugin.
   std::string plugin_name;
   /// New breakpoints to set. Nothing to set if this is empty.
@@ -165,8 +172,8 @@ struct GPUActions {
   /// connect to the GPU GDB server as a separate process.
   std::optional<GPUPluginConnectionInfo> connect_info;
   /// Set this to true if the native plug-in should tell the ProcessGDBRemote
-  /// in LLDB for the GPU process to load libraries. This allows the native 
-  /// process to be notified that it should query for the shared libraries on 
+  /// in LLDB for the GPU process to load libraries. This allows the native
+  /// process to be notified that it should query for the shared libraries on
   /// the GPU connection.
   bool load_libraries = false;
   /// Set this to true if the native plug-in resume the GPU process.
@@ -176,23 +183,21 @@ struct GPUActions {
   bool wait_for_gpu_process_to_resume = false;
 };
 
-bool fromJSON(const llvm::json::Value &value, 
-  GPUActions &data,
-  llvm::json::Path path);
+bool fromJSON(const llvm::json::Value &value, GPUActions &data,
+              llvm::json::Path path);
 
 llvm::json::Value toJSON(const GPUActions &data);
 
-
 struct GPUSectionInfo {
   /// Name of the section to load. If there are multiple sections, each section
   /// will be looked up and then a child section within the previous section
   /// will be looked up. This allows plug-ins to specify a hiearchy of sections
-  /// in the case where section names are not unique. A valid example looks 
+  /// in the case where section names are not unique. A valid example looks
   /// like: ["PT_LOAD[0]", ".text"]. If there is only one section name, LLDB
   /// will find the first section that matches that name.
   std::vector<std::string> names;
   /// The load address of this section only. If this value is valid, then this
-  /// section is loaded at this address, else child sections can be loaded 
+  /// section is loaded at this address, else child sections can be loaded
   /// individually.
   lldb::addr_t load_address;
 };
@@ -212,30 +217,30 @@ struct GPUDynamicLoaderLibraryInfo {
   bool load;
   /// The address where the object file is loaded. If this member has a value
   /// the object file is loaded at an address and all sections should be slid to
-  /// match this base address. If this member doesn't have a value, then 
+  /// match this base address. If this member doesn't have a value, then
   /// individual section's load address must be specified individually if
   /// \a loaded_sections has a value. If this doesn't have a value and the
   /// \a loaded_Section doesn't have a value, this library will be unloaded.
   std::optional<lldb::addr_t> load_address;
 
-  /// If the object file specified by this structure has sections that get 
+  /// If the object file specified by this structure has sections that get
   /// loaded at different times then this will not be empty. If it is empty
   /// the \a load_address must be specified if \a load is true.
   std::vector<GPUSectionInfo> loaded_sections;
 
   /// If this library is only available as an in memory image of an object file
-  /// in the native process, then this address holds the address from which the 
+  /// in the native process, then this address holds the address from which the
   /// image can be read.
   std::optional<lldb::addr_t> native_memory_address;
   /// If this library is only available as an in memory image of an object file
   /// in the native process, then this size of the in memory image that starts
   /// at \a native_memory_address.
   std::optional<lldb::addr_t> native_memory_size;
-  /// If the library exists inside of a file at an offset, \a file_offset will 
-  /// have a value that is the offset in bytes from the start of the file 
+  /// If the library exists inside of a file at an offset, \a file_offset will
+  /// have a value that is the offset in bytes from the start of the file
   /// specified by \a pathname.
   std::optional<uint64_t> file_offset;
-  /// If the library exists inside of a file at an offset, \a file_size will 
+  /// If the library exists inside of a file at an offset, \a file_size will
   /// have a value that indicates the size in bytes of the object file.
   std::optional<uint64_t> file_size;
 };
@@ -245,8 +250,6 @@ bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
 
 llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data);
 
-
-
 ///-----------------------------------------------------------------------------
 /// GPUPluginBreakpointHitResponse
 ///
@@ -254,21 +257,24 @@ llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data);
 /// set by the GPU plugin.
 ///-----------------------------------------------------------------------------
 struct GPUPluginBreakpointHitResponse {
+  GPUPluginBreakpointHitResponse() = default;
+  GPUPluginBreakpointHitResponse(llvm::StringRef plugin_name)
+      : actions(plugin_name) {}
+
   ///< Set to true if this berakpoint should be disabled.
-  bool disable_bp = false; 
+  bool disable_bp = false;
   /// Optional new breakpoints to set.
   GPUActions actions;
 };
 
-bool fromJSON(const llvm::json::Value &value, 
-              GPUPluginBreakpointHitResponse &data,
-              llvm::json::Path path);
+bool fromJSON(const llvm::json::Value &value,
+              GPUPluginBreakpointHitResponse &data, llvm::json::Path path);
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data);
 
 struct GPUDynamicLoaderArgs {
   /// Set to true to get all shared library information. Set to false to get
-  /// only the libraries that were updated since the last call to 
+  /// only the libraries that were updated since the last call to
   /// the "jGPUPluginGetDynamicLoaderLibraryInfo" packet.
   bool full;
 };
@@ -280,7 +286,7 @@ llvm::json::Value toJSON(const GPUDynamicLoaderArgs &data);
 
 struct GPUDynamicLoaderResponse {
   /// Set to true to get all shared library information. Set to false to get
-  /// only the libraries that were updated since the last call to 
+  /// only the libraries that were updated since the last call to
   /// the "jGPUPluginGetDynamicLoaderLibraryInfo" packet.
   std::vector<GPUDynamicLoaderLibraryInfo> library_infos;
 };
diff --git a/lldb/source/Utility/GPUGDBRemotePackets.cpp b/lldb/source/Utility/GPUGDBRemotePackets.cpp
index 651a56a2370f1..a79f376bd33b6 100644
--- a/lldb/source/Utility/GPUGDBRemotePackets.cpp
+++ b/lldb/source/Utility/GPUGDBRemotePackets.cpp
@@ -36,16 +36,13 @@ json::Value toJSON(const SymbolValue &data) {
 bool fromJSON(const llvm::json::Value &value, GPUBreakpointByName &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-          o.mapOptional("shlib", data.shlib) &&
-          o.map("function_name", data.function_name);              
+  return o && o.mapOptional("shlib", data.shlib) &&
+         o.map("function_name", data.function_name);
 }
 
 llvm::json::Value toJSON(const GPUBreakpointByName &data) {
   return json::Value(
-    Object{{"shlib", data.shlib},
-           {"function_name", data.function_name}
-          });
+      Object{{"shlib", data.shlib}, {"function_name", data.function_name}});
 }
 
 ///-----------------------------------------------------------------------------
@@ -55,37 +52,34 @@ llvm::json::Value toJSON(const GPUBreakpointByName &data) {
 ///-----------------------------------------------------------------------------
 
 bool fromJSON(const llvm::json::Value &value, GPUBreakpointByAddress &data,
-              llvm::json::Path path){
+              llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && o.map("load_address", data.load_address);              
+  return o && o.map("load_address", data.load_address);
 }
 
 llvm::json::Value toJSON(const GPUBreakpointByAddress &data) {
-  return json::Value(
-    Object{{"load_address", data.load_address}});
+  return json::Value(Object{{"load_address", data.load_address}});
 }
 
-
 //------------------------------------------------------------------------------
 // GPUBreakpointInfo
 //------------------------------------------------------------------------------
 bool fromJSON(const llvm::json::Value &value, GPUBreakpointInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("identifier", data.identifier) &&
+  return o && o.map("identifier", data.identifier) &&
          o.mapOptional("name_info", data.name_info) &&
          o.mapOptional("addr_info", data.addr_info) &&
          o.map("symbol_names", data.symbol_names);
 }
 
 llvm::json::Value toJSON(const GPUBreakpointInfo &data) {
-  return json::Value(
-    Object{{"identifier", data.identifier}, 
-           {"name_info", data.name_info},
-           {"addr_info", data.addr_info},
-           {"symbol_names", data.symbol_names},
-          });
+  return json::Value(Object{
+      {"identifier", data.identifier},
+      {"name_info", data.name_info},
+      {"addr_info", data.addr_info},
+      {"symbol_names", data.symbol_names},
+  });
 }
 
 //------------------------------------------------------------------------------
@@ -94,46 +88,43 @@ llvm::json::Value toJSON(const GPUBreakpointInfo &data) {
 bool fromJSON(const llvm::json::Value &value, GPUPluginConnectionInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.mapOptional("exe_path", data.exe_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);
 }
 
 llvm::json::Value toJSON(const GPUPluginConnectionInfo &data) {
-  return json::Value(
-      Object{{"exe_path", data.exe_path}, 
-             {"platform_name", data.platform_name}, 
-             {"triple", data.triple},
-             {"connect_url", data.connect_url},
-            });
+  return json::Value(Object{
+      {"exe_path", data.exe_path},
+      {"platform_name", data.platform_name},
+      {"triple", data.triple},
+      {"connect_url", data.connect_url},
+  });
 }
 
-
 //------------------------------------------------------------------------------
 // GPUPluginBreakpointHitArgs
 //------------------------------------------------------------------------------
 bool fromJSON(const json::Value &value, GPUPluginBreakpointHitArgs &data,
               Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("plugin_name", data.plugin_name) &&
+  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 GPUPluginBreakpointHitArgs &data) {
-  return json::Value(
-      Object{{"plugin_name", data.plugin_name}, 
-             {"breakpoint", data.breakpoint},
-             {"symbol_values", data.symbol_values},
-            });
+  return json::Value(Object{
+      {"plugin_name", data.plugin_name},
+      {"breakpoint", data.breakpoint},
+      {"symbol_values", data.symbol_values},
+  });
 }
 
-std::optional<uint64_t> 
-GPUPluginBreakpointHitArgs::GetSymbolValue(llvm::StringRef symbol_name) {
-  for (const auto &symbol: symbol_values)
+std::optional<uint64_t>
+GPUPluginBreakpointHitArgs::GetSymbolValue(llvm::StringRef symbol_name) const {
+  for (const auto &symbol : symbol_values)
     if (symbol_name == symbol.name)
       return symbol.value;
   return std::nullopt;
@@ -145,47 +136,42 @@ GPUPluginBreakpointHitArgs::GetSymbolValue(llvm::StringRef symbol_name) {
 bool fromJSON(const llvm::json::Value &value, GPUActions &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("plugin_name", data.plugin_name) &&
+  return o && o.map("plugin_name", data.plugin_name) &&
          o.map("breakpoints", data.breakpoints) &&
          o.mapOptional("connect_info", data.connect_info) &&
          o.map("load_libraries", data.load_libraries) &&
          o.map("resume_gpu_process", data.resume_gpu_process) &&
-         o.map("wait_for_gpu_process_to_resume", 
+         o.map("wait_for_gpu_process_to_resume",
                data.wait_for_gpu_process_to_resume);
 }
 
 llvm::json::Value toJSON(const GPUActions &data) {
-  return json::Value(
-    Object{{"plugin_name", data.plugin_name},
-           {"breakpoints", data.breakpoints},
-           {"connect_info", data.connect_info},
-           {"load_libraries", data.load_libraries},
-           {"resume_gpu_process", data.resume_gpu_process},
-           {"wait_for_gpu_process_to_resume", 
-            data.wait_for_gpu_process_to_resume},
-          });
+  return json::Value(Object{
+      {"plugin_name", data.plugin_name},
+      {"breakpoints", data.breakpoints},
+      {"connect_info", data.connect_info},
+      {"load_libraries", data.load_libraries},
+      {"resume_gpu_process", data.resume_gpu_process},
+      {"wait_for_gpu_process_to_resume", data.wait_for_gpu_process_to_resume},
+  });
 }
 
 //------------------------------------------------------------------------------
 // GPUPluginBreakpointHitResponse
 //------------------------------------------------------------------------------
 
-
-bool fromJSON(const llvm::json::Value &value, 
-              GPUPluginBreakpointHitResponse &data,
-              llvm::json::Path path) {
+bool fromJSON(const llvm::json::Value &value,
+              GPUPluginBreakpointHitResponse &data, llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("disable_bp", data.disable_bp) &&
+  return o && o.map("disable_bp", data.disable_bp) &&
          o.map("actions", data.actions);
 }
 
 llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data) {
-  return json::Value(
-    Object{{"disable_bp", data.disable_bp}, 
-           {"actions", data.actions},
-          });
+  return json::Value(Object{
+      {"disable_bp", data.disable_bp},
+      {"actions", data.actions},
+  });
 }
 
 //------------------------------------------------------------------------------
@@ -195,16 +181,13 @@ llvm::json::Value toJSON(const GPUPluginBreakpointHitResponse &data) {
 bool fromJSON(const llvm::json::Value &value, GPUSectionInfo &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("names", data.names) &&
+  return o && o.map("names", data.names) &&
          o.map("load_address", data.load_address);
 }
 
 llvm::json::Value toJSON(const GPUSectionInfo &data) {
   return json::Value(
-    Object{{"names", data.names}, 
-           {"load_address", data.load_address}
-          });
+      Object{{"names", data.names}, {"load_address", data.load_address}});
 }
 
 //------------------------------------------------------------------------------
@@ -212,12 +195,10 @@ llvm::json::Value toJSON(const GPUSectionInfo &data) {
 //------------------------------------------------------------------------------
 
 bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
-  llvm::json::Path path) {
+              llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("pathname", data.pathname) &&
-         o.mapOptional("uuid", data.uuid_str) &&
-         o.map("load", data.load) &&
+  return o && o.map("pathname", data.pathname) &&
+         o.mapOptional("uuid", data.uuid_str) && o.map("load", data.load) &&
          o.mapOptional("load_address", data.load_address) &&
          o.mapOptional("native_memory_address", data.native_memory_address) &&
          o.mapOptional("native_memory_size", data.native_memory_size) &&
@@ -227,17 +208,16 @@ bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderLibraryInfo &data,
 }
 
 llvm::json::Value toJSON(const GPUDynamicLoaderLibraryInfo &data) {
-return json::Value(
-Object{{"pathname", data.pathname}, 
-       {"uuid", data.uuid_str},
-       {"load", data.load},
-       {"load_address", data.load_address},
-       {"native_memory_address", data.native_memory_address},
-       {"native_memory_size", data.native_memory_size},
-       {"file_offset", data.file_offset},
-       {"file_size", data.file_size},
-       {"loaded_sections", data.loaded_sections}
-      });
+  return json::Value(
+      Object{{"pathname", data.pathname},
+             {"uuid", data.uuid_str},
+             {"load", data.load},
+             {"load_address", data.load_address},
+             {"native_memory_address", data.native_memory_address},
+             {"native_memory_size", data.native_memory_size},
+             {"file_offset", data.file_offset},
+             {"file_size", data.file_size},
+             {"loaded_sections", data.loaded_sections}});
 }
 
 //------------------------------------------------------------------------------
@@ -245,10 +225,9 @@ Object{{"pathname", data.pathname},
 //------------------------------------------------------------------------------
 
 bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderArgs &data,
-    llvm::json::Path path) {
+              llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("full", data.full);
+  return o && o.map("full", data.full);
 }
 
 llvm::json::Value toJSON(const GPUDynamicLoaderArgs &data) {
@@ -261,8 +240,7 @@ llvm::json::Value toJSON(const GPUDynamicLoaderArgs &data) {
 bool fromJSON(const llvm::json::Value &value, GPUDynamicLoaderResponse &data,
               llvm::json::Path path) {
   ObjectMapper o(value, path);
-  return o && 
-         o.map("library_infos", data.library_infos);
+  return o && o.map("library_infos", data.library_infos);
 }
 
 llvm::json::Value toJSON(const GPUDynamicLoaderResponse &data) {

>From bc51f6533042df622689bb90a041f2df979798f6 Mon Sep 17 00:00:00 2001
From: Walter Erquinigo <werquinigo at nvidia.com>
Date: Mon, 9 Jun 2025 16:55:17 +0000
Subject: [PATCH 30/34] [To upstream] Add a name to server comm channels and
 include them in the logs

This allows differenciating GPU and CPU gdb-remote logs emitted by the
server.

E.g.
```
1749485814.359431028 [754988/755010] nvidia-gpu.server <  19> read packet: $QStartNoAckMode#b0
1749485814.359459400 [754988/755010] nvidia-gpu.server <   1> send packet: +

1749485813.658699989 [754988/754988] gdb-server <  29> read packet: $qXfer:auxv:read::0,131071#d7
1749485813.658788681 [754988/754988] gdb-server < 341> send packet: $l!
```
---
 .../gdb-remote/GDBRemoteClientBase.cpp        |  2 +-
 .../gdb-remote/GDBRemoteCommunication.cpp     | 38 +++++++++----------
 .../gdb-remote/GDBRemoteCommunication.h       |  6 ++-
 .../GDBRemoteCommunicationServer.cpp          |  4 +-
 .../gdb-remote/GDBRemoteCommunicationServer.h |  4 +-
 .../GDBRemoteCommunicationServerCommon.cpp    |  5 ++-
 .../GDBRemoteCommunicationServerCommon.h      |  4 +-
 .../GDBRemoteCommunicationServerLLGS.cpp      |  5 ++-
 .../GDBRemoteCommunicationServerLLGS.h        | 10 ++++-
 .../GDBRemoteCommunicationServerPlatform.cpp  |  4 +-
 .../MockGPU/LLDBServerPluginMockGPU.cpp       |  4 +-
 lldb/tools/lldb-server/lldb-gdbserver.cpp     |  2 +-
 12 files changed, 51 insertions(+), 37 deletions(-)

diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
index 394b62559da76..6268650242a90 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -32,7 +32,7 @@ static const seconds kWakeupInterval(5);
 GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;
 
 GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name)
-    : GDBRemoteCommunication(), Broadcaster(nullptr, comm_name),
+    : GDBRemoteCommunication(comm_name), Broadcaster(nullptr, comm_name),
       m_async_count(0), m_is_running(false), m_should_stop(false) {}
 
 StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
index 7ed80b048c77e..68d18d6741232 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -59,7 +59,7 @@ using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
 
 // GDBRemoteCommunication constructor
-GDBRemoteCommunication::GDBRemoteCommunication()
+GDBRemoteCommunication::GDBRemoteCommunication(llvm::StringRef name)
     : Communication(),
 #ifdef LLDB_CONFIGURATION_DEBUG
       m_packet_timeout(1000),
@@ -68,7 +68,7 @@ GDBRemoteCommunication::GDBRemoteCommunication()
 #endif
       m_echo_number(0), m_supports_qEcho(eLazyBoolCalculate), m_history(512),
       m_send_acks(true), m_is_platform(false),
-      m_compression_type(CompressionType::None), m_listen_url() {
+      m_compression_type(CompressionType::None), m_listen_url(), m_name(name) {
 }
 
 // Destructor
@@ -97,7 +97,7 @@ size_t GDBRemoteCommunication::SendAck() {
   ConnectionStatus status = eConnectionStatusSuccess;
   char ch = '+';
   const size_t bytes_written = WriteAll(&ch, 1, status, nullptr);
-  LLDB_LOGF(log, "%p <%4" PRIu64 "> send packet: %c", static_cast<void *>(this), 
+  LLDB_LOGF(log, "%s <%4" PRIu64 "> send packet: %c", m_name.c_str(),
             (uint64_t)bytes_written, ch);
   m_history.AddPacket(ch, GDBRemotePacket::ePacketTypeSend, bytes_written);
   return bytes_written;
@@ -108,7 +108,7 @@ size_t GDBRemoteCommunication::SendNack() {
   ConnectionStatus status = eConnectionStatusSuccess;
   char ch = '-';
   const size_t bytes_written = WriteAll(&ch, 1, status, nullptr);
-  LLDB_LOGF(log, "%p <%4" PRIu64 "> send packet: %c", static_cast<void *>(this),
+  LLDB_LOGF(log, "%s <%4" PRIu64 "> send packet: %c", m_name.c_str(),
             (uint64_t)bytes_written, ch);
   m_history.AddPacket(ch, GDBRemotePacket::ePacketTypeSend, bytes_written);
   return bytes_written;
@@ -180,9 +180,9 @@ GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
       if (binary_start_offset) {
         StreamString strm;
         // Print non binary data header
-        strm.Printf("%p <%4" PRIu64 "> send packet: %.*s", 
-                    static_cast<void *>(this), (uint64_t)bytes_written,
-                    (int)binary_start_offset, packet_data);
+        strm.Printf("%s <%4" PRIu64 "> send packet: %.*s", m_name.c_str(),
+                    (uint64_t)bytes_written, (int)binary_start_offset,
+                    packet_data);
         const uint8_t *p;
         // Print binary data exactly as sent
         for (p = (const uint8_t *)packet_data + binary_start_offset; *p != '#';
@@ -192,9 +192,8 @@ GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
         strm.Printf("%*s", (int)3, p);
         log->PutString(strm.GetString());
       } else
-        LLDB_LOGF(log, "%p <%4" PRIu64 "> send packet: %.*s", 
-                  static_cast<void *>(this), (uint64_t)bytes_written, 
-                  (int)packet_length, packet_data);
+        LLDB_LOGF(log, "%s <%4" PRIu64 "> send packet: %.*s", m_name.c_str(),
+                  (uint64_t)bytes_written, (int)packet_length, packet_data);
     }
 
     m_history.AddPacket(packet.str(), packet_length,
@@ -753,13 +752,12 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
           StreamString strm;
           // Packet header...
           if (CompressionIsEnabled())
-            strm.Printf("%p <%4" PRIu64 ":%" PRIu64 "> read packet: %c",
-                        static_cast<void *>(this), (uint64_t)original_packet_size, 
+            strm.Printf("%s <%4" PRIu64 ":%" PRIu64 "> read packet: %c",
+                        m_name.c_str(), (uint64_t)original_packet_size,
                         (uint64_t)total_length, m_bytes[0]);
           else
-            strm.Printf("%p <%4" PRIu64 "> read packet: %c", 
-                        static_cast<void *>(this), (uint64_t)total_length, 
-                        m_bytes[0]);
+            strm.Printf("%s <%4" PRIu64 "> read packet: %c", m_name.c_str(),
+                        (uint64_t)total_length, m_bytes[0]);
           for (size_t i = content_start; i < content_end; ++i) {
             // Remove binary escaped bytes when displaying the packet...
             const char ch = m_bytes[i];
@@ -778,13 +776,13 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
           log->PutString(strm.GetString());
         } else {
           if (CompressionIsEnabled())
-            LLDB_LOGF(log, "%p <%4" PRIu64 ":%" PRIu64 "> read packet: %.*s",
-                      static_cast<void *>(this), (uint64_t)original_packet_size, 
-                      (uint64_t)total_length, (int)(total_length), 
+            LLDB_LOGF(log, "%s <%4" PRIu64 ":%" PRIu64 "> read packet: %.*s",
+                      m_name.c_str(), (uint64_t)original_packet_size,
+                      (uint64_t)total_length, (int)(total_length),
                       m_bytes.c_str());
           else
-            LLDB_LOGF(log, "%p <%4" PRIu64 "> read packet: %.*s", 
-                      static_cast<void *>(this), (uint64_t)total_length, 
+            LLDB_LOGF(log, "%s <%4" PRIu64 "> read packet: %.*s",
+                      m_name.c_str(), (uint64_t)total_length,
                       (int)(total_length), m_bytes.c_str());
         }
       }
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
index 107c0896c4e61..834e9ea2002f3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -117,7 +117,9 @@ class GDBRemoteCommunication : public Communication {
     bool m_timeout_modified;
   };
 
-  GDBRemoteCommunication();
+  /// \param[in] name
+  ///     The name of the communication channel.
+  GDBRemoteCommunication(llvm::StringRef name);
 
   ~GDBRemoteCommunication() override;
 
@@ -227,6 +229,8 @@ class GDBRemoteCommunication : public Communication {
   HostThread m_listen_thread;
   std::string m_listen_url;
 
+  std::string m_name;
+
 #if defined(HAVE_LIBCOMPRESSION)
   CompressionType m_decompression_scratch_type = CompressionType::None;
   void *m_decompression_scratch = nullptr;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
index 5bd29ae40aa9e..4c0c1e9348b6d 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -24,8 +24,8 @@ using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
 using namespace llvm;
 
-GDBRemoteCommunicationServer::GDBRemoteCommunicationServer()
-    : GDBRemoteCommunication(), m_exit_now(false) {
+GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(llvm::StringRef name)
+    : GDBRemoteCommunication(name), m_exit_now(false) {
   RegisterPacketHandler(
       StringExtractorGDBRemote::eServerPacketType_QEnableErrorStrings,
       [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt,
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
index ccbcd0ac5bf58..b40800ed7c70e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -31,7 +31,9 @@ class GDBRemoteCommunicationServer : public GDBRemoteCommunication {
       std::function<PacketResult(StringExtractorGDBRemote &packet,
                                  Status &error, bool &interrupt, bool &quit)>;
 
-  GDBRemoteCommunicationServer();
+  /// \param[in] name
+  ///     The name of the communication channel.
+  GDBRemoteCommunicationServer(llvm::StringRef name);
 
   ~GDBRemoteCommunicationServer() override;
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
index 67ba42f33d1dd..0846ca5241c76 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -58,8 +58,9 @@ const static uint32_t g_default_packet_timeout_sec = 0; // not specified
 #endif
 
 // GDBRemoteCommunicationServerCommon constructor
-GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon()
-    : GDBRemoteCommunicationServer(), m_process_launch_info(),
+GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon(
+    llvm::StringRef name)
+    : GDBRemoteCommunicationServer(name), m_process_launch_info(),
       m_process_launch_error(), m_proc_infos(), m_proc_infos_index(0) {
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A,
                                 &GDBRemoteCommunicationServerCommon::Handle_A);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
index b4f1eb3e61c41..36a39efcaef60 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
@@ -25,7 +25,9 @@ class ProcessGDBRemote;
 
 class GDBRemoteCommunicationServerCommon : public GDBRemoteCommunicationServer {
 public:
-  GDBRemoteCommunicationServerCommon();
+  /// \param[in] name
+  ///     The name of the communication channel.
+  GDBRemoteCommunicationServerCommon(llvm::StringRef name);
 
   ~GDBRemoteCommunicationServerCommon() override;
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index b0e0e14f2a4b2..401ba691493f8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -72,8 +72,9 @@ enum GDBRemoteServerError {
 
 // GDBRemoteCommunicationServerLLGS constructor
 GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
-    MainLoop &mainloop, NativeProcessProtocol::Manager &process_manager)
-    : GDBRemoteCommunicationServerCommon(), m_mainloop(mainloop),
+    MainLoop &mainloop, NativeProcessProtocol::Manager &process_manager,
+    llvm::StringRef name)
+    : GDBRemoteCommunicationServerCommon(name), m_mainloop(mainloop),
       m_process_manager(process_manager), m_current_process(nullptr),
       m_continue_process(nullptr), m_stdio_communication() {
   RegisterPacketHandlers();
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index a2ac100ccdde0..4fe653e232ec5 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -37,9 +37,15 @@ class GDBRemoteCommunicationServerLLGS
     : public GDBRemoteCommunicationServerCommon,
       public NativeProcessProtocol::NativeDelegate {
 public:
-  // Constructors and Destructors
+  /// \param[in] mainloop
+  ///     The main loop.
+  /// \param[in] process_manager
+  ///     The process manager.
+  /// \param[in] name
+  ///     The name of the communication channel.
   GDBRemoteCommunicationServerLLGS(
-      MainLoop &mainloop, NativeProcessProtocol::Manager &process_manager);
+      MainLoop &mainloop, NativeProcessProtocol::Manager &process_manager,
+      llvm::StringRef name);
 
   void SetLaunchInfo(const ProcessLaunchInfo &info);
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
index 89fdfa74bc025..10b99d7695abb 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -47,8 +47,8 @@ using namespace lldb_private;
 // GDBRemoteCommunicationServerPlatform constructor
 GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(
     const Socket::SocketProtocol socket_protocol, uint16_t gdbserver_port)
-    : GDBRemoteCommunicationServerCommon(), m_socket_protocol(socket_protocol),
-      m_gdbserver_port(gdbserver_port) {
+    : GDBRemoteCommunicationServerCommon("gdb-server-platform"),
+      m_socket_protocol(socket_protocol), m_gdbserver_port(gdbserver_port) {
 
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_qC,
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index 03a50f4220870..15c91c0f67964 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -29,8 +29,8 @@ LLDBServerPluginMockGPU::LLDBServerPluginMockGPU(
   LLDBServerPlugin::GDBServer &native_process)
     : LLDBServerPlugin(native_process) {
   m_process_manager_up.reset(new ProcessMockGPU::Manager(m_main_loop));
-  m_gdb_server.reset(
-      new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up));
+  m_gdb_server.reset(new GDBRemoteCommunicationServerLLGS(
+      m_main_loop, *m_process_manager_up, "mock-gpu.server"));
 
   Log *log = GetLog(GDBRLog::Plugin);
   LLDB_LOGF(log, "LLDBServerPluginMockGPU::LLDBServerPluginMockGPU() faking launch...");
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index caebc98b43928..07a0a18818fa3 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -454,7 +454,7 @@ int main_gdbserver(int argc, char *argv[]) {
   }
 
   NativeProcessManager manager(mainloop);
-  GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
+  GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager, "gdb-server");
 
   // Install the mock GPU plugin.
   gdb_server.InstallPlugin(std::make_unique<LLDBServerPluginMockGPU>(gdb_server));

>From 7bbb34e11f966028aba58114063f2c2482272bc5 Mon Sep 17 00:00:00 2001
From: Jeffrey Tan <jeffreytan at fb.com>
Date: Tue, 10 Jun 2025 15:58:58 -0700
Subject: [PATCH 31/34] Check object offset in ModuleSpec matching

---
 lldb/source/Core/Module.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index 796e45570bea5..0cb7a737d0f29 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -1538,6 +1538,9 @@ bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) {
   if (!FileSpec::Match(platform_file_spec, GetPlatformFileSpec()))
     return false;
 
+  if (m_object_offset != module_ref.GetObjectOffset())
+    return false;
+
   const ArchSpec &arch = module_ref.GetArchitecture();
   if (arch.IsValid()) {
     if (!m_arch.IsCompatibleMatch(arch))

>From 3fda41c47437d10bd857fd2fe796913da994b6c6 Mon Sep 17 00:00:00 2001
From: Jeffrey Tan <jeffreytan at fb.com>
Date: Mon, 2 Jun 2025 12:04:50 -0700
Subject: [PATCH 32/34] AMD GPU plugin v0

---
 .../Process/gdb-remote/LLDBServerPlugin.cpp   |   4 +-
 .../Process/gdb-remote/LLDBServerPlugin.h     |   4 +-
 lldb/tools/lldb-server/CMakeLists.txt         |   9 +-
 .../lldb-server/Plugins/AMDGPU/CMakeLists.txt |  30 +
 .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp | 598 ++++++++++++++++++
 .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.h   | 147 +++++
 .../Plugins/AMDGPU/ProcessAMDGPU.cpp          | 557 ++++++++++++++++
 .../Plugins/AMDGPU/ProcessAMDGPU.h            | 146 +++++
 .../Plugins/AMDGPU/RegisterContextAMDGPU.cpp  | 517 +++++++++++++++
 .../Plugins/AMDGPU/RegisterContextAMDGPU.h    |  65 ++
 .../Plugins/AMDGPU/ThreadAMDGPU.cpp           |  63 ++
 .../lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h |  75 +++
 lldb/tools/lldb-server/Plugins/CMakeLists.txt |   6 +-
 .../MockGPU/LLDBServerPluginMockGPU.cpp       |   4 +-
 .../Plugins/MockGPU/LLDBServerPluginMockGPU.h |   2 +-
 lldb/tools/lldb-server/lldb-gdbserver.cpp     |  18 +-
 llvm/cmake/modules/CrossCompile.cmake         |   1 +
 17 files changed, 2232 insertions(+), 14 deletions(-)
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp
 create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h

diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
index a520309f4e916..439f6b6af249c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp
@@ -15,8 +15,8 @@ using namespace lldb_private;
 using namespace lldb_server;
 
 
-LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process) :
-  m_native_process(native_process) {}
+LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process, MainLoop &main_loop) :
+  m_native_process(native_process), m_main_loop(main_loop) {}
 
 LLDBServerPlugin::~LLDBServerPlugin() {}
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
index 82f449c5c088c..a5c98ce26c177 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h
@@ -35,14 +35,14 @@ class LLDBServerPlugin {
   using GDBServer = process_gdb_remote::GDBRemoteCommunicationServerLLGS;
   using Manager = NativeProcessProtocol::Manager;
   GDBServer &m_native_process;
-  MainLoop m_main_loop;
+  MainLoop &m_main_loop;
   std::unique_ptr<Manager> m_process_manager_up;
   std::unique_ptr<GDBServer> m_gdb_server;
   bool m_is_listening = false;
   bool m_is_connected = false;
 
 public:
-  LLDBServerPlugin(GDBServer &native_process);
+  LLDBServerPlugin(GDBServer &native_process, MainLoop &main_loop);
   virtual ~LLDBServerPlugin();
 
   /// Check if we are already connected.
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index b842e8fd8632e..4126c02829441 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -42,6 +42,14 @@ if(APPLE_EMBEDDED)
   endif()
 endif()
 
+if(DEFINED ROCM_PATH AND EXISTS ${ROCM_PATH})
+  add_definitions(-DLLDB_ENABLE_AMDGPU_PLUGIN=1)
+  list(APPEND LLDB_PLUGINS lldbServerPluginAMDGPU)
+else()
+  add_definitions(-DLLDB_ENABLE_MOCKGPU_PLUGIN=1)
+  list(APPEND LLDB_PLUGINS lldbServerPluginMockGPU)
+endif()
+
 add_lldb_tool(lldb-server
     lldb-gdbserver.cpp
     lldb-platform.cpp
@@ -60,7 +68,6 @@ add_lldb_tool(lldb-server
       lldbPluginInstructionMIPS64
       lldbPluginInstructionRISCV
       ${LLDB_SYSTEM_LIBS}
-      lldbServerPluginMockGPU
 
     LINK_COMPONENTS
       Option
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt
new file mode 100644
index 0000000000000..f19d4a72aca9b
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt
@@ -0,0 +1,30 @@
+
+# Set ROCM paths
+set(ROCM_PATH "" CACHE PATH "Path to ROCm installation")
+
+message(STATUS "ROCM_PATH is set to: '${ROCM_PATH}'")
+if(NOT ROCM_PATH OR ROCM_PATH STREQUAL "")
+    message(FATAL_ERROR "ROCM_PATH must be specified. Use -DROCM_PATH=/path/to/rocm")
+endif()
+
+if (NOT EXISTS ${ROCM_PATH})
+    message(FATAL_ERROR "ROCM_PATH does not exist: '${ROCM_PATH}'")
+endif()
+
+# Find AMD-dbgapi package using the provided CMake config
+set(amd-dbgapi_DIR "${ROCM_PATH}/lib/cmake/amd-dbgapi" CACHE PATH "Path to amd-dbgapi cmake config")
+find_package(amd-dbgapi REQUIRED CONFIG)
+
+include_directories(${ROCM_PATH}/include)
+
+add_lldb_library(lldbServerPluginAMDGPU
+  LLDBServerPluginAMDGPU.cpp
+  ProcessAMDGPU.cpp
+  RegisterContextAMDGPU.cpp
+  ThreadAMDGPU.cpp
+
+  LINK_LIBS
+    amd-dbgapi
+)
+set_target_properties(lldbServerPluginAMDGPU PROPERTIES LINK_FLAGS "-Wl,-rpath,${ROCM_PATH}/lib")
+target_include_directories(lldbServerPluginAMDGPU PRIVATE "${LLDB_SOURCE_DIR}/source" ${ROCM_PATH}/include "../..")
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
new file mode 100644
index 0000000000000..32473b397baba
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
@@ -0,0 +1,598 @@
+//===-- LLDBServerPluginAMDGPU.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBServerPluginAMDGPU.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "ProcessAMDGPU.h"
+#include "ThreadAMDGPU.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "llvm/Support/Error.h"
+
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <thread>
+#include <unistd.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+
+static amd_dbgapi_status_t amd_dbgapi_client_process_get_info_callback(
+    amd_dbgapi_client_process_id_t client_process_id,
+    amd_dbgapi_client_process_info_t query, size_t value_size, void *value) {
+  LLDBServerPluginAMDGPU *debugger =
+      reinterpret_cast<LLDBServerPluginAMDGPU *>(client_process_id);
+  lldb::pid_t pid = debugger->GetNativeProcess()->GetID();
+  LLDB_LOGF(GetLog(GDBRLog::Plugin),
+            "amd_dbgapi_client_process_get_info_callback callback, with query "
+            "%d, pid %d",
+            query, pid);
+  switch (query) {
+  case AMD_DBGAPI_CLIENT_PROCESS_INFO_OS_PID: {
+    if (value_size != sizeof(amd_dbgapi_os_process_id_t))
+      return AMD_DBGAPI_STATUS_ERROR_INVALID_ARGUMENT_COMPATIBILITY;
+    *static_cast<amd_dbgapi_os_process_id_t *>(value) = pid;
+    return AMD_DBGAPI_STATUS_SUCCESS;
+  }
+  case AMD_DBGAPI_CLIENT_PROCESS_INFO_CORE_STATE: {
+    return AMD_DBGAPI_STATUS_SUCCESS;
+  }
+  }
+  return AMD_DBGAPI_STATUS_SUCCESS;
+}
+
+static amd_dbgapi_status_t amd_dbgapi_insert_breakpoint_callback(
+    amd_dbgapi_client_process_id_t client_process_id,
+    amd_dbgapi_global_address_t address,
+    amd_dbgapi_breakpoint_id_t breakpoint_id) {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin),
+            "insert_breakpoint callback at address: 0x%llx", address);
+  LLDBServerPluginAMDGPU *debugger =
+      reinterpret_cast<LLDBServerPluginAMDGPU *>(client_process_id);
+  debugger->GetNativeProcess()->Halt();
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "insert_breakpoint callback success");
+  LLDBServerPluginAMDGPU::GPUInternalBreakpoinInfo bp_info;
+  bp_info.addr = address;
+  bp_info.breakpoind_id = breakpoint_id;
+  debugger->m_gpu_internal_bp.emplace(std::move(bp_info));
+  debugger->m_wait_for_gpu_internal_bp_stop = true;
+  return AMD_DBGAPI_STATUS_SUCCESS;
+}
+
+/* remove_breakpoint callback.  */
+
+static amd_dbgapi_status_t amd_dbgapi_remove_breakpoint_callback(
+    amd_dbgapi_client_process_id_t client_process_id,
+    amd_dbgapi_breakpoint_id_t breakpoint_id) {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "remove_breakpoint callback for %llu",
+            breakpoint_id.handle);
+  return AMD_DBGAPI_STATUS_SUCCESS;
+}
+
+/* xfer_global_memory callback.  */
+
+static amd_dbgapi_status_t amd_dbgapi_xfer_global_memory_callback(
+    amd_dbgapi_client_process_id_t client_process_id,
+    amd_dbgapi_global_address_t global_address, amd_dbgapi_size_t *value_size,
+    void *read_buffer, const void *write_buffer) {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "xfer_global_memory callback");
+
+  return AMD_DBGAPI_STATUS_SUCCESS;
+}
+
+static void amd_dbgapi_log_message_callback(amd_dbgapi_log_level_t level,
+                                            const char *message) {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "ROCdbgapi [%d]: %s", level, message);
+};
+
+static amd_dbgapi_callbacks_t s_dbgapi_callbacks = {
+    .allocate_memory = malloc,
+    .deallocate_memory = free,
+    .client_process_get_info = amd_dbgapi_client_process_get_info_callback,
+    .insert_breakpoint = amd_dbgapi_insert_breakpoint_callback,
+    .remove_breakpoint = amd_dbgapi_remove_breakpoint_callback,
+    .xfer_global_memory = amd_dbgapi_xfer_global_memory_callback,
+    .log_message = amd_dbgapi_log_message_callback,
+};
+
+LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU(
+    LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop)
+    : LLDBServerPlugin(native_process, main_loop) {
+  m_process_manager_up.reset(new ProcessManagerAMDGPU(main_loop));
+  m_gdb_server.reset(
+      new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up));
+}
+
+LLDBServerPluginAMDGPU::~LLDBServerPluginAMDGPU() { CloseFDs(); }
+
+llvm::StringRef LLDBServerPluginAMDGPU::GetPluginName() { return "amd-gpu"; }
+
+void LLDBServerPluginAMDGPU::CloseFDs() {
+  if (m_fds[0] != -1) {
+    close(m_fds[0]);
+    m_fds[0] = -1;
+  }
+  if (m_fds[1] != -1) {
+    close(m_fds[1]);
+    m_fds[1] = -1;
+  }
+}
+
+int LLDBServerPluginAMDGPU::GetEventFileDescriptorAtIndex(size_t idx) {
+  if (idx != 0)
+    return -1;
+  if (m_fds[0] == -1) {
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, m_fds) == -1) {
+      m_fds[0] = -1;
+      m_fds[1] = -1;
+    }
+  }
+  return m_fds[0];
+}
+
+bool LLDBServerPluginAMDGPU::initRocm() {
+  // Initialize AMD Debug API with callbacks
+  amd_dbgapi_status_t status = amd_dbgapi_initialize(&s_dbgapi_callbacks);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Failed to initialize AMD debug API");
+    exit(-1);
+  }
+
+  // Attach to the process with AMD Debug API
+  status = amd_dbgapi_process_attach(
+      reinterpret_cast<amd_dbgapi_client_process_id_t>(
+          this), // Use pid_ as client_process_id
+      &m_gpu_pid);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "Failed to attach to process with AMD debug API: %d", status);
+    amd_dbgapi_finalize();
+    return false;
+  }
+
+  // Get the process notifier
+  status =
+      amd_dbgapi_process_get_info(m_gpu_pid, AMD_DBGAPI_PROCESS_INFO_NOTIFIER,
+                                  sizeof(m_notifier_fd), &m_notifier_fd);
+
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Failed to get process notifier: %d",
+              status);
+    amd_dbgapi_process_detach(m_gpu_pid);
+    amd_dbgapi_finalize();
+    return false;
+  }
+
+  // event_handler_.addNotifierFd(m_notifier_fd);
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "Process notifier fd: %d", m_notifier_fd);
+
+  amd_dbgapi_event_id_t eventId;
+  amd_dbgapi_event_kind_t eventKind;
+  // Process all pending events
+  if (amd_dbgapi_process_next_pending_event(m_gpu_pid, &eventId, &eventKind) ==
+      AMD_DBGAPI_STATUS_SUCCESS) {
+    GetGPUProcess()->handleDebugEvent(eventId, eventKind);
+
+    // Mark event as processed
+    amd_dbgapi_event_processed(eventId);
+  }
+
+  amd_dbgapi_architecture_id_t architecture_id;
+  // TODO: do not hardcode the device id
+  status = amd_dbgapi_get_architecture(0x04C, &architecture_id);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    // Handle error
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_get_architecture failed");
+    return false;
+  }
+  m_architecture_id = architecture_id;
+
+  return true;
+}
+
+bool LLDBServerPluginAMDGPU::processGPUEvent() {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "processGPUEvent");
+  char buf[256];
+  ssize_t bytesRead = 0;
+  bool result = false;
+  do {
+    do {
+      bytesRead = read(m_notifier_fd, buf, sizeof(buf));
+    } while (bytesRead <= 0);
+
+    auto *process = GetGPUProcess();
+    process->m_wave_ids.clear();
+    amd_dbgapi_status_t status = amd_dbgapi_process_set_progress(
+        m_gpu_pid, AMD_DBGAPI_PROGRESS_NO_FORWARD);
+    assert(status == AMD_DBGAPI_STATUS_SUCCESS);
+    process_event_queue(AMD_DBGAPI_EVENT_KIND_NONE);
+    if (process->m_gpu_state == ProcessAMDGPU::State::GPUStopped) {
+      for (auto wave_id : process->m_wave_ids) {
+        process->AddThread(wave_id);
+      }
+      process->Halt();
+    }
+    status =
+        amd_dbgapi_process_set_progress(m_gpu_pid, AMD_DBGAPI_PROGRESS_NORMAL);
+    assert(status == AMD_DBGAPI_STATUS_SUCCESS);
+    break;
+  } while (true);
+  return result;
+}
+
+bool LLDBServerPluginAMDGPU::HandleEventFileDescriptorEvent(int fd) {
+  return processGPUEvent();
+}
+
+void LLDBServerPluginAMDGPU::AcceptAndMainLoopThread(
+    std::unique_ptr<TCPSocket> listen_socket_up) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "%s spawned", __PRETTY_FUNCTION__);
+  Socket *socket = nullptr;
+  Status error = listen_socket_up->Accept(std::chrono::seconds(30), socket);
+  // Scope for lock guard.
+  {
+    // Protect access to m_is_listening and m_is_connected.
+    std::lock_guard<std::mutex> guard(m_connect_mutex);
+    m_is_listening = false;
+    if (error.Fail()) {
+      LLDB_LOGF(log, "%s error returned from Accept(): %s", __PRETTY_FUNCTION__,
+                error.AsCString());
+      return;
+    }
+    m_is_connected = true;
+  }
+
+  LLDB_LOGF(log, "%s initializing connection", __PRETTY_FUNCTION__);
+  std::unique_ptr<Connection> connection_up(
+      new ConnectionFileDescriptor(socket));
+  m_gdb_server->InitializeConnection(std::move(connection_up));
+  LLDB_LOGF(log, "%s running main loop", __PRETTY_FUNCTION__);
+  m_main_loop_status = m_main_loop.Run();
+  LLDB_LOGF(log, "%s main loop exited!", __PRETTY_FUNCTION__);
+  if (m_main_loop_status.Fail()) {
+    LLDB_LOGF(log, "%s main loop exited with an error: %s", __PRETTY_FUNCTION__,
+              m_main_loop_status.AsCString());
+  }
+  // Protect access to m_is_connected.
+  std::lock_guard<std::mutex> guard(m_connect_mutex);
+  m_is_connected = false;
+}
+
+std::optional<GPUPluginConnectionInfo>
+LLDBServerPluginAMDGPU::CreateConnection() {
+  std::lock_guard<std::mutex> guard(m_connect_mutex);
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "%s called", __PRETTY_FUNCTION__);
+  if (m_is_connected) {
+    LLDB_LOGF(log, "%s already connected", __PRETTY_FUNCTION__);
+    return std::nullopt;
+  }
+  if (m_is_listening) {
+    LLDB_LOGF(log, "%s already listening", __PRETTY_FUNCTION__);
+    return std::nullopt;
+  }
+  m_is_listening = true;
+  LLDB_LOGF(log, "%s trying to listen on port 0", __PRETTY_FUNCTION__);
+  llvm::Expected<std::unique_ptr<TCPSocket>> sock =
+      Socket::TcpListen("localhost:0", 5);
+  if (sock) {
+    GPUPluginConnectionInfo connection_info;
+    const uint16_t listen_port = (*sock)->GetLocalPortNumber();
+    connection_info.connect_url =
+        llvm::formatv("connect://localhost:{}", listen_port);
+    LLDB_LOGF(log, "%s listening to %u", __PRETTY_FUNCTION__, listen_port);
+    // std::thread t(&LLDBServerPluginAMDGPU::AcceptAndMainLoopThread, this,
+    //               std::move(*sock));
+    // t.detach();
+
+    // Store the socket in the member variable to keep it alive
+    m_listen_socket = std::move(*sock);
+    auto extra_args =
+        llvm::formatv("gpu-url:connect://localhost:{};", listen_port);
+    m_is_connected = false;
+    llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> res =
+        m_listen_socket->Accept(
+            m_main_loop, [this](std::unique_ptr<Socket> socket) {
+              Log *log = GetLog(GDBRLog::Plugin);
+              LLDB_LOGF(log,
+                        "LLDBServerPluginAMDGPU::AcceptAndMainLoopThread() "
+                        "initializing connection");
+              std::unique_ptr<Connection> connection_up(
+                  new ConnectionFileDescriptor(socket.release()));
+              this->m_gdb_server->InitializeConnection(
+                  std::move(connection_up));
+              m_is_connected = true;
+            });
+    if (res) {
+      m_read_handles = std::move(*res);
+    } else {
+      LLDB_LOGF(
+          log,
+          "LLDBServerPluginAMDGPU::GetConnectionURL() failed to accept: %s",
+          llvm::toString(res.takeError()).c_str());
+    }
+
+    return connection_info;
+  } else {
+    std::string error = llvm::toString(sock.takeError());
+    LLDB_LOGF(log, "%s failed to listen to localhost:0: %s",
+              __PRETTY_FUNCTION__, error.c_str());
+  }
+  m_is_listening = false;
+  return std::nullopt;
+}
+
+std::optional<GPUActions> LLDBServerPluginAMDGPU::NativeProcessIsStopping() {
+  Log *log = GetLog(GDBRLog::Plugin);
+  if (!m_is_connected) {
+    initRocm();
+    ProcessManagerAMDGPU *manager =
+        (ProcessManagerAMDGPU *)m_process_manager_up.get();
+    manager->m_debugger = this;
+
+    GPUActions actions;
+    actions.plugin_name = GetPluginName();
+
+    Status error;
+    LLDBServerPluginAMDGPU *amdGPUPlugin = this;
+    m_gpu_event_io_obj_sp = std::make_shared<GPUIOObject>(m_notifier_fd);
+    m_gpu_event_read_up = m_main_loop.RegisterReadObject(
+        m_gpu_event_io_obj_sp,
+        [amdGPUPlugin](MainLoopBase &) {
+          amdGPUPlugin->HandleEventFileDescriptorEvent(
+              amdGPUPlugin->m_notifier_fd);
+        },
+        error);
+    if (error.Fail()) {
+      LLDB_LOGF(log, "LLDBServerPluginAMDGPU::NativeProcessIsStopping() failed "
+                     "to RegisterReadObject");
+      // TODO: how to report this error?
+    } else {
+      LLDB_LOGF(
+          log,
+          "LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU() faking launch...");
+      ProcessLaunchInfo info;
+      info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |
+                          eLaunchFlagDisableASLR);
+      Args args;
+      args.AppendArgument("/pretend/path/to/amdgpu");
+      args.AppendArgument("--option1");
+      args.AppendArgument("--option2");
+      args.AppendArgument("--option3");
+      info.SetArguments(args, true);
+      info.GetEnvironment() = Host::GetEnvironment();
+      info.SetProcessID(m_gpu_pid.handle);
+      m_gdb_server->SetLaunchInfo(info);
+      Status error = m_gdb_server->LaunchProcess();
+      if (error.Fail()) {
+        LLDB_LOGF(log,
+                  "LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU() failed to "
+                  "launch: %s",
+                  error.AsCString());
+      } else {
+        LLDB_LOGF(log, "LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU() "
+                       "launched successfully");
+      }
+      actions.connect_info = CreateConnection();
+    }
+    return actions;
+  } else {
+    if (m_wait_for_gpu_internal_bp_stop && m_gpu_internal_bp.has_value()) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Please set gpu breakpoint at 0x%p",
+                (void *)m_gpu_internal_bp->addr);
+      GPUActions actions;
+      actions.plugin_name = GetPluginName();
+
+      GPUBreakpointByAddress bp_addr;
+      bp_addr.load_address = m_gpu_internal_bp->addr;
+
+      GPUBreakpointInfo bp;
+      bp.identifier = "GPU loader breakpoint";
+      bp.addr_info.emplace(bp_addr);
+
+      std::vector<GPUBreakpointInfo> breakpoints;
+      breakpoints.emplace_back(std::move(bp));
+
+      actions.breakpoints = std::move(breakpoints);
+      m_wait_for_gpu_internal_bp_stop = false;
+      return actions;
+    }
+  }
+  return std::nullopt;
+}
+
+bool LLDBServerPluginAMDGPU::HandleGPUInternalBreakpointHit(
+    const GPUInternalBreakpoinInfo &bp, bool &has_new_libraries) {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin),
+            "Hit GPU loader breakpoint at address: 0x%llx", bp.addr);
+  has_new_libraries = false;
+  amd_dbgapi_breakpoint_id_t breakpoint_id{bp.breakpoind_id};
+  amd_dbgapi_breakpoint_action_t action;
+
+  auto status = amd_dbgapi_report_breakpoint_hit(
+      breakpoint_id, reinterpret_cast<amd_dbgapi_client_thread_id_t>(this),
+      &action);
+
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "amd_dbgapi_report_breakpoint_hit failed: %d", status);
+    return false;
+  }
+
+  if (action == AMD_DBGAPI_BREAKPOINT_ACTION_RESUME) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "AMD_DBGAPI_BREAKPOINT_ACTION_RESUME");
+    return true;
+  } else if (action == AMD_DBGAPI_BREAKPOINT_ACTION_HALT) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "AMD_DBGAPI_BREAKPOINT_ACTION_HALT");
+
+    amd_dbgapi_event_id_t resume_event_id =
+        process_event_queue(AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME);
+    amd_dbgapi_event_processed(resume_event_id);
+    if (!GetGPUProcess()->GetGPUModules().empty()) {
+      has_new_libraries = true;
+    }
+    return true;
+  } else {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown action: %d", action);
+    return false;
+  }
+  return true;
+}
+
+amd_dbgapi_event_id_t LLDBServerPluginAMDGPU::process_event_queue(
+    amd_dbgapi_event_kind_t until_event_kind) {
+  while (true) {
+    amd_dbgapi_event_id_t event_id;
+    amd_dbgapi_event_kind_t event_kind;
+    amd_dbgapi_status_t status = amd_dbgapi_process_next_pending_event(
+        m_gpu_pid, &event_id, &event_kind);
+
+    if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin),
+                "amd_dbgapi_process_next_pending_event failed: %d", status);
+      return AMD_DBGAPI_EVENT_NONE;
+    }
+
+    if (event_kind != AMD_DBGAPI_EVENT_KIND_NONE)
+      LLDB_LOGF(GetLog(GDBRLog::Plugin),
+                "event_kind != AMD_DBGAPI_EVENT_KIND_NONE: %d", event_kind);
+
+    if (event_id.handle == AMD_DBGAPI_EVENT_NONE.handle ||
+        event_kind == until_event_kind)
+      return event_id;
+
+    GetGPUProcess()->handleDebugEvent(event_id, event_kind);
+    amd_dbgapi_event_processed(event_id);
+  }
+  return AMD_DBGAPI_EVENT_NONE;
+}
+
+bool LLDBServerPluginAMDGPU::SetGPUBreakpoint(uint64_t addr,
+                                              const uint8_t *bp_instruction,
+                                              size_t size) {
+  struct BreakpointInfo {
+    uint64_t addr;
+    std::vector<uint8_t> original_bytes;
+    std::vector<uint8_t> breakpoint_instruction;
+    std::optional<amd_dbgapi_breakpoint_id_t> gpu_breakpoint_id;
+  };
+
+  BreakpointInfo bp;
+  bp.addr = addr;
+  bp.breakpoint_instruction.assign(bp_instruction, bp_instruction + size);
+  bp.original_bytes.resize(size);
+  bp.gpu_breakpoint_id =
+      std::nullopt; // No GPU breakpoint ID for ptrace version
+
+  auto pid = GetNativeProcess()->GetID();
+  // Read original bytes word by word
+  std::vector<long> original_words;
+  for (size_t i = 0; i < size; i += sizeof(long)) {
+    long word = ptrace(PTRACE_PEEKDATA, pid, addr + i, nullptr);
+    assert(word != -1 && errno == 0);
+
+    original_words.push_back(word);
+    // Copy bytes from the word into our original_bytes
+    size_t bytes_to_copy = std::min(sizeof(long), size - i);
+    memcpy(&bp.original_bytes[i], &word, bytes_to_copy);
+  }
+
+  // Write breakpoint instruction word by word
+  for (size_t i = 0; i < size; i += sizeof(long)) {
+    long word = original_words[i / sizeof(long)];
+    size_t bytes_to_copy = std::min(sizeof(long), size - i);
+    memcpy(&word, &bp_instruction[i], bytes_to_copy);
+
+    auto ret = ptrace(PTRACE_POKEDATA, pid, addr + i, word);
+    assert(ret != -1 && errno == 0);
+  }
+  return true;
+}
+
+bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) {
+  // First get the architecture ID for this process
+  amd_dbgapi_architecture_id_t arch_id;
+  amd_dbgapi_status_t status = amd_dbgapi_get_architecture(0x02C, &arch_id);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    // Handle error
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_get_architecture failed");
+    return false;
+  }
+
+  // Get breakpoint instruction
+  const uint8_t *bp_instruction;
+  status = amd_dbgapi_architecture_get_info(
+      arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION,
+      sizeof(bp_instruction), &bp_instruction);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION failed");
+    return false;
+  }
+
+  // Get breakpoint instruction size
+  size_t bp_size;
+  status = amd_dbgapi_architecture_get_info(
+      arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE,
+      sizeof(bp_size), &bp_size);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(
+        GetLog(GDBRLog::Plugin),
+        "AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE failed");
+    return false;
+  }
+
+  // Now call SetGPUBreakpoint with the retrieved instruction and size
+  return SetGPUBreakpoint(addr, bp_instruction, bp_size);
+}
+
+GPUPluginBreakpointHitResponse
+LLDBServerPluginAMDGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  std::string json_string;
+  std::string &bp_identifier = args.breakpoint.identifier;
+  llvm::raw_string_ostream os(json_string);
+  os << toJSON(args);
+  LLDB_LOGF(log, "LLDBServerPluginAMDGPU::BreakpointWasHit(\"%s\"):\nJSON:\n%s",
+            bp_identifier.c_str(), json_string.c_str());
+
+  GPUPluginBreakpointHitResponse response;
+  response.actions.plugin_name = GetPluginName();
+  if (bp_identifier == "GPU loader breakpoint") {
+    bool has_new_libraries = false;
+    bool success = HandleGPUInternalBreakpointHit(m_gpu_internal_bp.value(),
+                                                  has_new_libraries);
+    assert(success);
+    if (has_new_libraries) {
+      response.actions.wait_for_gpu_process_to_resume = true;
+      auto process = m_gdb_server->GetCurrentProcess();
+      ThreadAMDGPU *thread = (ThreadAMDGPU *)process->GetCurrentThread();
+      thread->SetStopReason(lldb::eStopReasonDynammicLoader);
+      process->Halt();
+    }
+  }
+  return response;
+}
+
+GPUActions LLDBServerPluginAMDGPU::GetInitializeActions() {
+  GPUActions init_actions;
+  init_actions.plugin_name = GetPluginName();
+
+  GPUBreakpointInfo bp1;
+  bp1.identifier = "gpu_initialize";
+  bp1.name_info = {"a.out", "gpu_initialize"};
+  bp1.symbol_names.push_back("gpu_shlib_load");
+  init_actions.breakpoints.emplace_back(std::move(bp1));
+  return init_actions;
+}
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
new file mode 100644
index 0000000000000..e1f057e7f8956
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
@@ -0,0 +1,147 @@
+//===-- LLDBServerPluginAMDGPU.h -------------------------------*- C++ -*-===//
+//
+// 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_LLDBSERVERPLUGINAMDGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINAMDGPU_H
+
+#include "Plugins/Process/gdb-remote/LLDBServerPlugin.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
+#include "ProcessAMDGPU.h"
+#include <amd-dbgapi/amd-dbgapi.h>
+
+// This is a mock GPU plugin that is used for testing the LLDBServerPlugin. It
+// should be run with the following code as the main binary:
+/*
+
+$ cat main.cpp
+#include <stdio.h>
+
+struct ShlibInfo {
+  const char *path = nullptr;
+  ShlibInfo *next = nullptr;
+};
+
+ShlibInfo g_shlib_list = { "/tmp/a.out", nullptr};
+
+int gpu_initialize() {
+  return puts(__FUNCTION__);
+}
+int gpu_shlib_load() {
+  return puts(__FUNCTION__);
+}
+int main(int argc, const char **argv) {
+  gpu_initialize();
+  gpu_shlib_load();
+  return 0; // Break here
+}
+
+$ clang++ -g -O0 -o a.out main.cpp
+$ ninja lldb lldb-server
+$ ./bin/lldb a.out -o 'b /Break here/ -o run
+
+*/
+// If the above code is run, you will be stopped at the breakpoint and the Mock
+// GPU target will be selected. Try doing a "reg read --all" to see the state
+// of the GPU registers. Then you can select the native process target with
+// "target select 0" and issue commands to the native process, and then select
+// the GPU target with "target select 1" and issue commands to the GPU target.
+
+namespace lldb_private {
+
+class TCPSocket;
+
+namespace lldb_server {
+
+class GPUIOObject : public IOObject {
+public:
+  GPUIOObject(int notifier_fd)
+      : lldb_private::IOObject(eFDTypeSocket), m_notifier_fd(notifier_fd) {}
+
+  Status Read(void *buf, size_t &num_bytes) override {
+    Status error;
+    return error;
+  }
+  Status Write(const void *buf, size_t &num_bytes) override {
+    Status error;
+    return error;
+  }
+  virtual bool IsValid() const override { return true; }
+  virtual Status Close() override {
+    Status error;
+    return error;
+  }
+
+  virtual WaitableHandle GetWaitableHandle() override { return m_notifier_fd; }
+
+private:
+  int m_notifier_fd = -1;
+};
+
+class LLDBServerPluginAMDGPU : public LLDBServerPlugin {
+public:
+  LLDBServerPluginAMDGPU(LLDBServerPlugin::GDBServer &native_process,
+                         MainLoop &main_loop);
+  ~LLDBServerPluginAMDGPU() override;
+  llvm::StringRef GetPluginName() override;
+  int GetEventFileDescriptorAtIndex(size_t idx) override;
+  bool HandleEventFileDescriptorEvent(int fd) override;
+  GPUActions GetInitializeActions() override;
+  std::optional<struct GPUActions> NativeProcessIsStopping() override;
+  GPUPluginBreakpointHitResponse
+  BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
+
+  NativeProcessProtocol *GetNativeProcess() {
+    return m_native_process.GetCurrentProcess();
+  }
+  ProcessAMDGPU *GetGPUProcess() {
+    return (ProcessAMDGPU *)m_gdb_server->GetCurrentProcess();
+  }
+
+  bool CreateGPUBreakpoint(uint64_t addr);
+
+  // TODO: make this private
+  struct GPUInternalBreakpoinInfo {
+    uint64_t addr;
+    amd_dbgapi_breakpoint_id_t breakpoind_id;
+  };
+  std::optional<GPUInternalBreakpoinInfo> m_gpu_internal_bp;
+  bool m_wait_for_gpu_internal_bp_stop = false;
+  amd_dbgapi_architecture_id_t m_architecture_id = AMD_DBGAPI_ARCHITECTURE_NONE;
+
+private:
+  std::optional<GPUPluginConnectionInfo> CreateConnection();
+  void CloseFDs();
+  void AcceptAndMainLoopThread(std::unique_ptr<TCPSocket> listen_socket_up);
+
+  bool initRocm();
+  bool HandleGPUInternalBreakpointHit(const GPUInternalBreakpoinInfo &bp,
+                                      bool &has_new_libraries);
+  amd_dbgapi_event_id_t
+  process_event_queue(amd_dbgapi_event_kind_t until_event_kind);
+  bool processGPUEvent();
+  bool SetGPUBreakpoint(uint64_t addr, const uint8_t *bp_instruction,
+                        size_t size);
+
+  // Used with a socketpair to get events on the native ptrace event queue.
+  int m_fds[2] = {-1, -1};
+  Status m_main_loop_status;
+  MainLoopBase::ReadHandleUP m_gpu_event_read_up;
+  std::vector<MainLoopBase::ReadHandleUP> m_read_handles;
+  std::unique_ptr<TCPSocket> m_listen_socket; // Keep socket alive for main_loop
+  std::shared_ptr<GPUIOObject> m_gpu_event_io_obj_sp;
+
+  amd_dbgapi_process_id_t m_gpu_pid = AMD_DBGAPI_PROCESS_NONE;
+  int m_notifier_fd = -1;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINAMDGPU_H
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
new file mode 100644
index 0000000000000..df6015aabb9ee
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
@@ -0,0 +1,557 @@
+//===-- ProcessAMDGPU.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessAMDGPU.h"
+#include "ThreadAMDGPU.h"
+
+#include "LLDBServerPluginAMDGPU.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UnimplementedError.h"
+#include "llvm/Support/Error.h"
+
+#include <iostream>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+
+ProcessAMDGPU::ProcessAMDGPU(lldb::pid_t pid, NativeDelegate &delegate,
+                             LLDBServerPluginAMDGPU *plugin)
+    : NativeProcessProtocol(pid, -1, delegate), m_debugger(plugin) {
+  m_state = eStateStopped;
+  UpdateThreads();
+}
+
+Status ProcessAMDGPU::Resume(const ResumeActionList &resume_actions) {
+  SetState(StateType::eStateRunning, true);
+  ThreadAMDGPU *thread = (ThreadAMDGPU *)GetCurrentThread();
+  thread->GetRegisterContext().InvalidateAllRegisters();
+  // if (!m_debugger->resume_process()) {
+  //   return Status::FromErrorString("resume_process failed");
+  // }
+  return Status();
+}
+
+Status ProcessAMDGPU::Halt() {
+  SetState(StateType::eStateStopped, true);
+  return Status();
+}
+
+Status ProcessAMDGPU::Detach() {
+  SetState(StateType::eStateDetached, true);
+  return Status();
+}
+
+/// Sends a process a UNIX signal \a signal.
+///
+/// \return
+///     Returns an error object.
+Status ProcessAMDGPU::Signal(int signo) {
+  return Status::FromErrorString("unimplemented");
+}
+
+/// Tells a process to interrupt all operations as if by a Ctrl-C.
+///
+/// The default implementation will send a local host's equivalent of
+/// a SIGSTOP to the process via the NativeProcessProtocol::Signal()
+/// operation.
+///
+/// \return
+///     Returns an error object.
+Status ProcessAMDGPU::Interrupt() { return Status(); }
+
+Status ProcessAMDGPU::Kill() { return Status(); }
+
+Status ProcessAMDGPU::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                                 size_t &bytes_read) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ProcessAMDGPU::WriteMemory(lldb::addr_t addr, const void *buf,
+                                  size_t size, size_t &bytes_written) {
+  return Status::FromErrorString("unimplemented");
+}
+
+lldb::addr_t ProcessAMDGPU::GetSharedLibraryInfoAddress() {
+  return LLDB_INVALID_ADDRESS;
+}
+
+size_t ProcessAMDGPU::UpdateThreads() {
+  if (m_threads.empty()) {
+    lldb::tid_t tid = 3456;
+    m_threads.push_back(std::make_unique<ThreadAMDGPU>(*this, 3456));
+    // ThreadAMDGPU &thread = static_cast<ThreadAMDGPU &>(*m_threads.back());
+    SetCurrentThreadID(tid);
+  }
+  return m_threads.size();
+}
+
+const ArchSpec &ProcessAMDGPU::GetArchitecture() const {
+  m_arch = ArchSpec("amdgpu");
+  return m_arch;
+}
+
+// Breakpoint functions
+Status ProcessAMDGPU::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                                    bool hardware) {
+  // TODO: fix the race condition of GPU module load, client lldb setting
+  // breakpoint then resume GPU connection.
+  bool success = m_debugger->CreateGPUBreakpoint(addr);
+  if (!success) {
+    return Status::FromErrorString("CreateGPUBreakpoint failed");
+  }
+  return Status();
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ProcessAMDGPU::GetAuxvData() const {
+  return nullptr; // TODO: try to return
+                  // llvm::make_error<UnimplementedError>();
+}
+
+Status ProcessAMDGPU::GetLoadedModuleFileSpec(const char *module_path,
+                                              FileSpec &file_spec) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ProcessAMDGPU::GetFileLoadAddress(const llvm::StringRef &file_name,
+                                         lldb::addr_t &load_addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+void ProcessAMDGPU::SetLaunchInfo(ProcessLaunchInfo &launch_info) {
+  static_cast<ProcessInfo &>(m_process_info) =
+      static_cast<ProcessInfo &>(launch_info);
+}
+
+bool ProcessAMDGPU::GetProcessInfo(ProcessInstanceInfo &proc_info) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "ProcessAMDGPU::%s() entered", __FUNCTION__);
+  m_process_info.SetProcessID(m_pid);
+  m_process_info.SetArchitecture(GetArchitecture());
+  proc_info = m_process_info;
+  return true;
+}
+
+static std::pair<std::string, std::pair<uint64_t, uint64_t>>
+ParsePathname(const std::string &pathname) {
+  std::string file_path;
+  uint64_t offset = 0;
+  uint64_t size = 0;
+
+  // Find the position of #offset=
+  size_t offset_pos = pathname.find("#offset=");
+  if (offset_pos != std::string::npos) {
+    // Extract the file path (remove file:// prefix if present)
+    std::string path = pathname.substr(0, offset_pos);
+    if (path.find("file://") == 0) {
+      file_path = path.substr(7); // Remove "file://"
+    } else {
+      file_path = path;
+    }
+
+    // Extract offset
+    size_t size_pos = pathname.find("&size=", offset_pos);
+    if (size_pos != std::string::npos) {
+      std::string offset_str =
+          pathname.substr(offset_pos + 8, size_pos - (offset_pos + 8));
+      std::string size_str = pathname.substr(size_pos + 6);
+
+      offset = std::stoull(offset_str);
+      size = std::stoull(size_str);
+    }
+  } else {
+    // No offset/size parameters, just return the path
+    if (pathname.find("file://") == 0) {
+      file_path = pathname.substr(7);
+    } else {
+      file_path = pathname;
+    }
+  }
+
+  return {file_path, {offset, size}};
+}
+
+std::optional<GPUDynamicLoaderResponse>
+ProcessAMDGPU::GetGPUDynamicLoaderLibraryInfos(
+    const GPUDynamicLoaderArgs &args) {
+  Log *log = GetLog(GDBRLog::Plugin);
+  LLDB_LOGF(log, "ProcessAMDGPU::%s() entered", __FUNCTION__);
+
+  GPUDynamicLoaderResponse response;
+
+  // Access the GPU modules using the GetGPUModules() method
+  const auto &gpu_modules = m_gpu_modules;
+
+  LLDB_LOGF(log, "ProcessAMDGPU::%s() found %zu GPU modules", __FUNCTION__,
+            gpu_modules.size());
+
+  // Convert each GPU module to an SVR4LibraryInfo object
+  for (const auto &[addr, module] : gpu_modules) {
+    if (module.is_loaded) {
+      auto file_components = ParsePathname(module.path);
+      std::string path;
+      for (char c : file_components.first) {
+        if (c == '#')
+          path += "%23";
+        else if (c == '$')
+          path += "%24";
+        else if (c == '}')
+          path += "%7D";
+        else if (c == '&')
+          path += "&";
+        else
+          path += c;
+      }
+
+      GPUDynamicLoaderLibraryInfo lib_info;
+      lib_info.pathname = path;
+      lib_info.load = true;
+      lib_info.load_address = module.base_address;
+      lib_info.file_offset = file_components.second.first;
+      lib_info.file_size = file_components.second.second;
+
+      LLDB_LOGF(log,
+                "ProcessAMDGPU::%s() adding library: path=%s, addr=0x%" PRIx64
+                ", offset=%" PRIu64 ", size=%" PRIu64,
+                __FUNCTION__, lib_info.pathname.c_str(),
+                lib_info.load_address.value(), lib_info.file_offset.value(),
+                lib_info.file_size.value());
+
+      response.library_infos.push_back(lib_info);
+    }
+  }
+
+  return response;
+}
+
+llvm::Expected<std::vector<SVR4LibraryInfo>>
+ProcessAMDGPU::GetLoadedSVR4Libraries() {
+  std::vector<SVR4LibraryInfo> libraries;
+
+  // Check if we have a valid debugger instance
+  if (!m_debugger) {
+    return libraries; // Return empty vector if no debugger
+  }
+
+  // Access the GPU modules using the GetGPUModules() method
+  const auto &gpu_modules = GetGPUModules();
+
+  // Convert each GPU module to an SVR4LibraryInfo object
+  for (const auto &[addr, module] : gpu_modules) {
+    if (module.is_loaded) {
+      SVR4LibraryInfo lib_info;
+      std::string path;
+      for (char c : module.path) {
+        if (c == '#')
+          path += "%23";
+        else if (c == '$')
+          path += "%24";
+        else if (c == '}')
+          path += "%7D";
+        else if (c == '&')
+          path += "&";
+        else
+          path += c;
+      }
+      lib_info.name = path;
+      lib_info.link_map = addr;
+      lib_info.base_addr = module.base_address;
+      lib_info.ld_addr =
+          module.size;   // Using size as ld_addr as in handleLibrariesSvr4Read
+      lib_info.next = 0; // No next link in our implementation
+
+      libraries.push_back(lib_info);
+    }
+  }
+
+  return libraries;
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ProcessManagerAMDGPU::Launch(
+    ProcessLaunchInfo &launch_info,
+    NativeProcessProtocol::NativeDelegate &native_delegate) {
+  lldb::pid_t pid = launch_info.GetProcessID();
+  auto proc_up =
+      std::make_unique<ProcessAMDGPU>(pid, native_delegate, m_debugger);
+  proc_up->SetLaunchInfo(launch_info);
+  return proc_up;
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ProcessManagerAMDGPU::Attach(
+    lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) {
+  return llvm::createStringError("Unimplemented function");
+}
+
+bool ProcessAMDGPU::handleWaveStop(amd_dbgapi_event_id_t eventId) {
+  amd_dbgapi_wave_id_t wave_id;
+  auto status = amd_dbgapi_event_get_info(eventId, AMD_DBGAPI_EVENT_INFO_WAVE,
+                                          sizeof(wave_id), &wave_id);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_event_get_info failed: %d",
+              status);
+    return false;
+  }
+  amd_dbgapi_wave_stop_reasons_t stop_reason;
+  status = amd_dbgapi_wave_get_info(wave_id, AMD_DBGAPI_WAVE_INFO_STOP_REASON,
+                                    sizeof(stop_reason), &stop_reason);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_wave_get_info failed: %d",
+              status);
+    return false;
+  }
+  if ((stop_reason & AMD_DBGAPI_WAVE_STOP_REASON_BREAKPOINT) != 0) {
+    // auto ip = getPC();
+    uint64_t pc;
+    status = amd_dbgapi_wave_get_info(wave_id, AMD_DBGAPI_WAVE_INFO_PC,
+                                      sizeof(pc), &pc);
+    if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_wave_get_info failed: %d",
+                status);
+      exit(-1);
+    }
+    pc -= 4;
+    amd_dbgapi_register_id_t pc_register_id;
+    status = amd_dbgapi_architecture_get_info(
+        m_debugger->m_architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_PC_REGISTER,
+        sizeof(pc_register_id), &pc_register_id);
+    if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin),
+                "amd_dbgapi_architecture_get_info failed: %d", status);
+      exit(-1);
+    }
+    status =
+        amd_dbgapi_write_register(wave_id, pc_register_id, 0, sizeof(pc), &pc);
+    if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_write_register failed: %d",
+                status);
+      exit(-1);
+    }
+    // RemoveGPUBreakpoint(pc);
+    // auto thread = std::make_unique<ThreadAMDGPU>(*this, wave_id.handle,
+    // wave_id); thread->SetStopReason(lldb::eStopReasonBreakpoint);
+    m_wave_ids.emplace_back(wave_id);
+
+    if (m_threads.size() == 1 && m_gpu_state == State::Initializing) {
+      m_threads.clear();
+      SetCurrentThreadID(wave_id.handle);
+    }
+
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "Wave stopped due to breakpoint at: 0x%llx with wave id: %llu "
+              "event id: %llu",
+              pc, wave_id.handle, eventId.handle);
+    return true;
+  } else {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Wave stopped due to unknown reason: %d",
+              stop_reason);
+  }
+  return false;
+}
+
+static const char *event_kind_str(amd_dbgapi_event_kind_t kind) {
+  switch (kind) {
+  case AMD_DBGAPI_EVENT_KIND_NONE:
+    return "NONE";
+
+  case AMD_DBGAPI_EVENT_KIND_WAVE_STOP:
+    return "WAVE_STOP";
+
+  case AMD_DBGAPI_EVENT_KIND_WAVE_COMMAND_TERMINATED:
+    return "WAVE_COMMAND_TERMINATED";
+
+  case AMD_DBGAPI_EVENT_KIND_CODE_OBJECT_LIST_UPDATED:
+    return "CODE_OBJECT_LIST_UPDATED";
+
+  case AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME:
+    return "BREAKPOINT_RESUME";
+
+  case AMD_DBGAPI_EVENT_KIND_RUNTIME:
+    return "RUNTIME";
+
+  case AMD_DBGAPI_EVENT_KIND_QUEUE_ERROR:
+    return "QUEUE_ERROR";
+  }
+  assert(!"unhandled amd_dbgapi_event_kind_t value");
+}
+
+bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId,
+                                     amd_dbgapi_event_kind_t eventKind) {
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "handleDebugEvent(%llu, %s)",
+            eventId.handle, event_kind_str(eventKind));
+  bool result;
+  if (eventKind == AMD_DBGAPI_EVENT_KIND_NONE)
+    return result;
+
+  amd_dbgapi_runtime_state_t runtimeState = AMD_DBGAPI_RUNTIME_STATE_UNLOADED;
+
+  // Get runtime state for the event
+  amd_dbgapi_status_t status =
+      amd_dbgapi_event_get_info(eventId, AMD_DBGAPI_EVENT_INFO_RUNTIME_STATE,
+                                sizeof(runtimeState), &runtimeState);
+
+  if (status == AMD_DBGAPI_STATUS_SUCCESS) {
+    // Handle different runtime states
+    switch (runtimeState) {
+    case AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS:
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime loaded successfully");
+      break;
+    case AMD_DBGAPI_RUNTIME_STATE_LOADED_ERROR_RESTRICTION:
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime load restricted");
+      break;
+    case AMD_DBGAPI_RUNTIME_STATE_UNLOADED:
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime unloaded");
+      break;
+    default:
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown runtime state: %d",
+                runtimeState);
+      break;
+    }
+  }
+
+  // Handle event kinds
+  switch (eventKind) {
+  case AMD_DBGAPI_EVENT_KIND_WAVE_STOP: {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Wave stop event received");
+
+    // Handle wave stop
+    result = handleWaveStop(eventId);
+    m_gpu_state = State::GPUStopped;
+    break;
+  }
+
+    // case AMD_DBGAPI_EVENT_KIND_BREAKPOINT: {
+    //   std::cout << "Breakpoint event received" << std::endl;
+
+    //   // Get breakpoint information for this event
+    //   amd_dbgapi_breakpoint_id_t breakpointId;
+    //   if (amd_dbgapi_event_get_info(eventId,
+    //   AMD_DBGAPI_EVENT_INFO_BREAKPOINT,
+    //                                 sizeof(breakpointId), &breakpointId)
+    //                                 ==
+    //       AMD_DBGAPI_STATUS_SUCCESS) {
+    //     std::cout << "Breakpoint ID: " << breakpointId << std::endl;
+    //   }
+    //   break;
+    // }
+
+  case AMD_DBGAPI_EVENT_KIND_RUNTIME: {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "Runtime event received, runtimeState: %d", runtimeState);
+
+    // Additional runtime-specific handling based on state
+    if (runtimeState == AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS) {
+      // Runtime is now loaded, we can set breakpoints or perform other
+      // initialization
+    }
+    break;
+  }
+
+  case AMD_DBGAPI_EVENT_KIND_CODE_OBJECT_LIST_UPDATED: {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Code object event received");
+
+    amd_dbgapi_code_object_id_t *code_object_list;
+    size_t count;
+
+    amd_dbgapi_process_id_t gpu_pid{GetID()};
+    amd_dbgapi_status_t status = amd_dbgapi_process_code_object_list(
+        gpu_pid, &count, &code_object_list, nullptr);
+    if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Failed to get code object list: %d",
+                status);
+      return result;
+    }
+
+    m_gpu_modules.clear();
+    for (size_t i = 0; i < count; ++i) {
+      uint64_t l_addr;
+      char *uri_bytes;
+
+      status = amd_dbgapi_code_object_get_info(
+          code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
+          sizeof(l_addr), &l_addr);
+      if (status != AMD_DBGAPI_STATUS_SUCCESS)
+        continue;
+
+      status = amd_dbgapi_code_object_get_info(
+          code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
+          sizeof(uri_bytes), &uri_bytes);
+      if (status != AMD_DBGAPI_STATUS_SUCCESS)
+        continue;
+
+      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Code object %zu: %s at address %llu",
+                i, uri_bytes, l_addr);
+
+      if (m_gpu_modules.find(l_addr) == m_gpu_modules.end()) {
+        GPUModule mod = parseCodeObjectUrl(uri_bytes, l_addr);
+        m_gpu_modules[l_addr] = mod;
+      }
+    }
+    break;
+  }
+
+  default:
+    LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown event kind: %d", eventKind);
+    break;
+  }
+  return result;
+}
+
+ProcessAMDGPU::GPUModule
+ProcessAMDGPU::parseCodeObjectUrl(const std::string &url,
+                                  uint64_t load_address) {
+  GPUModule info;
+  info.path = url;
+  info.base_address = load_address;
+  info.offset = 0;
+  info.size = 0;
+  info.is_loaded = true;
+
+  // Find offset parameter
+  size_t offset_pos = url.find("#offset=");
+  if (offset_pos != std::string::npos) {
+    offset_pos += 8; // Skip "#offset="
+    size_t amp_pos = url.find('&', offset_pos);
+    std::string offset_str;
+
+    if (amp_pos != std::string::npos) {
+      offset_str = url.substr(offset_pos, amp_pos - offset_pos);
+    } else {
+      offset_str = url.substr(offset_pos);
+    }
+
+    // Handle hex format (0x prefix)
+    if (offset_str.substr(0, 2) == "0x") {
+      info.offset = std::stoull(offset_str.substr(2), nullptr, 16);
+    } else {
+      info.offset = std::stoull(offset_str);
+    }
+  }
+
+  // Find size parameter
+  size_t size_pos = url.find("&size=");
+  if (size_pos != std::string::npos) {
+    size_pos += 6; // Skip "&size="
+    std::string size_str = url.substr(size_pos);
+    info.size = std::stoull(size_str);
+  }
+
+  return info;
+}
+
+void ProcessAMDGPU::AddThread(amd_dbgapi_wave_id_t wave_id) {
+  auto thread = std::make_unique<ThreadAMDGPU>(*this, wave_id.handle, wave_id);
+  thread->SetStopReason(lldb::eStopReasonBreakpoint);
+  m_threads.emplace_back(std::move(thread));
+}
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h
new file mode 100644
index 0000000000000..08e8dd8352420
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h
@@ -0,0 +1,146 @@
+//===-- ProcessAMDGPU.h ----------------------------------------*- C++ -*-===//
+//
+// 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_PROCESSAMDGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_PROCESSAMDGPU_H
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include <amd-dbgapi/amd-dbgapi.h>
+
+namespace lldb_private {
+namespace lldb_server {
+
+class LLDBServerPluginAMDGPU;
+/// \class ProcessAMDGPU
+/// Abstract class that extends \a NativeProcessProtocol for a mock GPU. This
+/// class is used to unit testing the GPU plugins in lldb-server.
+class ProcessAMDGPU : public NativeProcessProtocol {
+  // TODO: change NativeProcessProtocol::GetArchitecture() to return by value
+  mutable ArchSpec m_arch;
+  ProcessInstanceInfo m_process_info;
+
+public:
+  ProcessAMDGPU(lldb::pid_t pid, NativeDelegate &delegate, LLDBServerPluginAMDGPU *plugin);
+
+  Status Resume(const ResumeActionList &resume_actions) override;
+
+  Status Halt() override;
+
+  Status Detach() override;
+
+  /// Sends a process a UNIX signal \a signal.
+  ///
+  /// \return
+  ///     Returns an error object.
+  Status Signal(int signo) override;
+
+  /// Tells a process to interrupt all operations as if by a Ctrl-C.
+  ///
+  /// The default implementation will send a local host's equivalent of
+  /// a SIGSTOP to the process via the NativeProcessProtocol::Signal()
+  /// operation.
+  ///
+  /// \return
+  ///     Returns an error object.
+  Status Interrupt() 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;
+
+  // Breakpoint functions
+  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;
+
+  bool GetProcessInfo(ProcessInstanceInfo &info) override;
+
+  // Custom accessors
+  void SetLaunchInfo(ProcessLaunchInfo &launch_info);
+
+  llvm::Expected<std::vector<SVR4LibraryInfo>>
+  GetLoadedSVR4Libraries() override;
+
+  std::optional<GPUDynamicLoaderResponse> 
+  GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) override;
+
+  bool handleWaveStop(amd_dbgapi_event_id_t eventId);
+
+  bool handleDebugEvent(amd_dbgapi_event_id_t eventId,
+    amd_dbgapi_event_kind_t eventKind);
+  
+  struct GPUModule {
+    std::string path;
+    uint64_t base_address;
+    uint64_t offset;
+    uint64_t size;
+    bool is_loaded;
+  };
+  std::unordered_map<uintptr_t, GPUModule>& GetGPUModules() {
+    return m_gpu_modules;
+  }
+
+  GPUModule parseCodeObjectUrl(const std::string &url, uint64_t load_address);
+  void AddThread(amd_dbgapi_wave_id_t wave_id);
+  
+  LLDBServerPluginAMDGPU* m_debugger = nullptr;
+  std::unordered_map<uintptr_t, GPUModule> m_gpu_modules;
+
+  enum class State {
+    Initializing,
+    ModuleLoadStopped,
+    Running,
+    GPUStopped,
+  };
+  State m_gpu_state = State::Initializing;
+  std::vector<amd_dbgapi_wave_id_t> m_wave_ids;
+};
+
+class ProcessManagerAMDGPU : public NativeProcessProtocol::Manager {
+public:
+  ProcessManagerAMDGPU(MainLoop &mainloop)
+      : NativeProcessProtocol::Manager(mainloop) {}
+
+  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+  Launch(ProcessLaunchInfo &launch_info,
+         NativeProcessProtocol::NativeDelegate &native_delegate) override;
+
+  NativeProcessProtocol::Extension GetSupportedExtensions() const override {
+    return NativeProcessProtocol::Extension::gpu_dyld;
+  }
+
+  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+  Attach(lldb::pid_t pid,
+         NativeProcessProtocol::NativeDelegate &native_delegate) override;
+  
+  LLDBServerPluginAMDGPU* m_debugger = nullptr;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp
new file mode 100644
index 0000000000000..e34728b5ff85d
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp
@@ -0,0 +1,517 @@
+//===-- RegisterContextAMDGPU.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 "RegisterContextAMDGPU.h"
+
+#include "LLDBServerPluginAMDGPU.h"
+#include "ProcessAMDGPU.h"
+#include "ThreadAMDGPU.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include <amd-dbgapi/amd-dbgapi.h>
+#include <unordered_map>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+
+static size_t kNumRegs = 0;
+static std::vector<RegisterSet> g_reg_sets;
+/// Define all of the information about all registers. The register info structs
+/// are accessed by the LLDB register numbers, which are defined above.
+static std::vector<RegisterInfo> g_reg_infos;
+size_t g_register_buffer_size = 0;
+static std::unordered_map<uint32_t, amd_dbgapi_register_id_t>
+    g_lldb_num_to_amd_reg_id;
+
+bool RegisterContextAMDGPU::InitRegisterInfos() {
+  if (!g_reg_infos.empty())
+    return true;
+  amd_dbgapi_status_t status;
+  ThreadAMDGPU *thread = (ThreadAMDGPU *)&m_thread;
+  amd_dbgapi_architecture_id_t architecture_id =
+      thread->GetProcess().m_debugger->m_architecture_id;
+  // Define custom hash functions for register IDs
+  struct RegisterClassIdHash {
+    std::size_t operator()(const amd_dbgapi_register_class_id_t &id) const {
+      return std::hash<uint64_t>{}(id.handle);
+    }
+  };
+  struct RegisterClassIdEqual {
+    bool operator()(const amd_dbgapi_register_class_id_t &lhs,
+                    const amd_dbgapi_register_class_id_t &rhs) const {
+      return lhs.handle == rhs.handle;
+    }
+  };
+
+  struct RegisterIdHash {
+    std::size_t operator()(const amd_dbgapi_register_id_t &id) const {
+      return std::hash<uint64_t>{}(id.handle);
+    }
+  };
+  struct RegisterIdEqual {
+    bool operator()(const amd_dbgapi_register_id_t &lhs,
+                    const amd_dbgapi_register_id_t &rhs) const {
+      return lhs.handle == rhs.handle;
+    }
+  };
+
+  /* Get register class ids.  */
+  size_t register_class_count;
+  amd_dbgapi_register_class_id_t *register_class_ids;
+  status = amd_dbgapi_architecture_register_class_list(
+      architecture_id, &register_class_count, &register_class_ids);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "Failed to get register class list from amd-dbgapi");
+    return false;
+  }
+
+  // Get register class names.
+  std::unordered_map<amd_dbgapi_register_class_id_t, std::string,
+                     RegisterClassIdHash, RegisterClassIdEqual>
+      register_class_names;
+  for (size_t i = 0; i < register_class_count; ++i) {
+    char *bytes;
+    status = amd_dbgapi_architecture_register_class_get_info(
+        register_class_ids[i], AMD_DBGAPI_REGISTER_CLASS_INFO_NAME,
+        sizeof(bytes), &bytes);
+    if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin),
+                "Failed to get register class name from amd-dbgapi");
+      return false;
+    }
+
+    // gdb::unique_xmalloc_ptr<char> name(bytes);
+    register_class_names.emplace(register_class_ids[i], bytes);
+  }
+
+  /* Get all register count. */
+  size_t register_count;
+  amd_dbgapi_register_id_t *register_ids;
+  status = amd_dbgapi_architecture_register_list(
+      architecture_id, &register_count, &register_ids);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "Failed to get register list from amd-dbgapi");
+    return false;
+  }
+  kNumRegs = register_count;
+
+  std::unordered_map<amd_dbgapi_register_class_id_t,
+                     std::vector<amd_dbgapi_register_id_t>, RegisterClassIdHash,
+                     RegisterClassIdEqual>
+      register_class_to_register_ids;
+  for (size_t i = 0; i < register_class_count; ++i) {
+    for (size_t j = 0; j < register_count; ++j) {
+      amd_dbgapi_register_class_state_t register_class_state;
+      status = amd_dbgapi_register_is_in_register_class(
+          register_class_ids[i], register_ids[j], &register_class_state);
+      if (status == AMD_DBGAPI_STATUS_SUCCESS &&
+          register_class_state == AMD_DBGAPI_REGISTER_CLASS_STATE_MEMBER) {
+        register_class_to_register_ids[register_class_ids[i]].push_back(
+            register_ids[j]);
+        break; // TODO: can a register be in multiple classes?
+      }
+    }
+  }
+
+  std::vector<amd_dbgapi_register_properties_t> all_register_properties;
+  all_register_properties.resize(register_count);
+  for (size_t regnum = 0; regnum < register_count; ++regnum) {
+    auto &register_properties = all_register_properties[regnum];
+    if (amd_dbgapi_register_get_info(
+            register_ids[regnum], AMD_DBGAPI_REGISTER_INFO_PROPERTIES,
+            sizeof(register_properties),
+            &register_properties) != AMD_DBGAPI_STATUS_SUCCESS) {
+      LLDB_LOGF(GetLog(GDBRLog::Plugin),
+                "Failed to get register properties from amd-dbgapi");
+      return false;
+    }
+  }
+
+  std::vector<int> dwarf_regnum_to_gdb_regnum;
+  std::unordered_map<amd_dbgapi_register_id_t, std::string, RegisterIdHash,
+                     RegisterIdEqual>
+      register_names;
+  for (size_t i = 0; i < register_count; ++i) {
+    /* Get register name.  */
+    char *bytes;
+    status = amd_dbgapi_register_get_info(
+        register_ids[i], AMD_DBGAPI_REGISTER_INFO_NAME, sizeof(bytes), &bytes);
+    if (status == AMD_DBGAPI_STATUS_SUCCESS) {
+      register_names[register_ids[i]] = bytes;
+      free(bytes);
+    }
+
+    /* Get register DWARF number.  */
+    uint64_t dwarf_num;
+    status = amd_dbgapi_register_get_info(register_ids[i],
+                                          AMD_DBGAPI_REGISTER_INFO_DWARF,
+                                          sizeof(dwarf_num), &dwarf_num);
+    if (status == AMD_DBGAPI_STATUS_SUCCESS) {
+      if (dwarf_num >= dwarf_regnum_to_gdb_regnum.size())
+        dwarf_regnum_to_gdb_regnum.resize(dwarf_num + 1, -1);
+
+      dwarf_regnum_to_gdb_regnum[dwarf_num] = i;
+    }
+  }
+
+  amd_dbgapi_register_id_t pc_register_id;
+  status = amd_dbgapi_architecture_get_info(
+      architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_PC_REGISTER,
+      sizeof(pc_register_id), &pc_register_id);
+  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
+    LLDB_LOGF(GetLog(GDBRLog::Plugin),
+              "Failed to get PC register from amd-dbgapi");
+    return false;
+  }
+  // Initialize g_reg_infos with register information from AMD dbgapi
+  g_reg_infos.resize(register_count);
+
+  // Map from register class ID to register numbers for that class
+  std::unordered_map<amd_dbgapi_register_class_id_t, std::vector<uint32_t>,
+                     RegisterClassIdHash, RegisterClassIdEqual>
+      register_class_to_lldb_regnums;
+  // Populate g_reg_infos with register information from AMD dbgapi
+  for (size_t i = 0; i < register_count; ++i) {
+    amd_dbgapi_register_id_t reg_id = register_ids[i];
+    RegisterInfo &reg_info = g_reg_infos[i];
+
+    // Set register name from AMD dbgapi
+    auto name_it = register_names.find(reg_id);
+    if (name_it != register_names.end()) {
+      reg_info.name = strdup(name_it->second.c_str());
+      reg_info.alt_name = nullptr;
+    } else {
+      // Fallback name if not found
+      char name[16];
+      snprintf(name, sizeof(name), "reg%zu", i);
+      reg_info.name = strdup(name);
+      reg_info.alt_name = nullptr;
+    }
+
+    // Get register size from AMD dbgapi
+    uint64_t reg_size;
+    status = amd_dbgapi_register_get_info(reg_id, AMD_DBGAPI_REGISTER_INFO_SIZE,
+                                          sizeof(reg_size), &reg_size);
+    if (status == AMD_DBGAPI_STATUS_SUCCESS) {
+      reg_info.byte_size = reg_size;
+    } else {
+      reg_info.byte_size = 8; // Default to 64-bit registers
+    }
+    reg_info.byte_offset = g_register_buffer_size; // Simple offset calculation
+    g_register_buffer_size += reg_info.byte_size;
+
+    // Set encoding and format based on register name
+    std::string reg_name =
+        name_it != register_names.end() ? name_it->second : "";
+
+    // Check if register name contains indicators of its type
+    // TODO: is this the correct way to do this?
+    if (reg_name.find("float") != std::string::npos ||
+        reg_name.find("fp") != std::string::npos) {
+      reg_info.encoding = eEncodingIEEE754;
+      reg_info.format = eFormatFloat;
+    } else if (reg_name.find("vec") != std::string::npos ||
+               reg_name.find("simd") != std::string::npos) {
+      reg_info.encoding = eEncodingVector;
+      reg_info.format = eFormatVectorOfUInt8;
+    } else if (reg_info.byte_size > 8) {
+      // TODO: check AMD_DBGAPI_REGISTER_INFO_TYPE and assign encoding/format.
+      reg_info.encoding = eEncodingVector;
+      reg_info.format = eFormatVectorOfUInt8;
+    } else {
+      // Default for other types
+      reg_info.encoding = eEncodingUint;
+      reg_info.format = eFormatHex;
+    }
+
+    // Set register kinds
+    reg_info.kinds[eRegisterKindLLDB] = i; // LLDB register number is the index
+    g_lldb_num_to_amd_reg_id[i] =
+        reg_id; // Map from LLDB register number to AMD
+
+    // Set DWARF register number if available
+    uint64_t dwarf_num;
+    status = amd_dbgapi_register_get_info(
+        reg_id, AMD_DBGAPI_REGISTER_INFO_DWARF, sizeof(dwarf_num), &dwarf_num);
+    if (status == AMD_DBGAPI_STATUS_SUCCESS) {
+      reg_info.kinds[eRegisterKindDWARF] = dwarf_num;
+    } else {
+      reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+    }
+
+    // Set EH_FRAME register number (same as DWARF for now)
+    reg_info.kinds[eRegisterKindEHFrame] = reg_info.kinds[eRegisterKindDWARF];
+
+    // Set generic register kind
+    reg_info.kinds[eRegisterKindGeneric] = LLDB_INVALID_REGNUM;
+
+    // Check if this is the PC register
+    if (reg_id.handle == pc_register_id.handle) {
+      reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+    }
+
+    // Add this register indices belong to its register classes
+    for (size_t j = 0; j < register_class_count; ++j) {
+      amd_dbgapi_register_class_state_t register_class_state;
+      status = amd_dbgapi_register_is_in_register_class(
+          register_class_ids[j], reg_id, &register_class_state);
+      if (status == AMD_DBGAPI_STATUS_SUCCESS &&
+          register_class_state == AMD_DBGAPI_REGISTER_CLASS_STATE_MEMBER) {
+        register_class_to_lldb_regnums[register_class_ids[j]].push_back(i);
+      }
+    }
+  }
+
+  // Create register sets from register classes
+  g_reg_sets.clear();
+
+  for (size_t i = 0; i < register_class_count; ++i) {
+    auto class_id = register_class_ids[i];
+    auto name_it = register_class_names.find(class_id);
+    if (name_it == register_class_names.end()) {
+      continue; // Skip if no name found
+    }
+
+    auto regnums_it = register_class_to_lldb_regnums.find(class_id);
+    if (regnums_it == register_class_to_lldb_regnums.end() ||
+        regnums_it->second.empty()) {
+      continue; // Skip if no registers in this class
+    }
+
+    // Create a new register set for this class
+    RegisterSet reg_set;
+    reg_set.name = strdup(name_it->second.c_str());
+
+    // Create short name from the full name (use first word or first few chars)
+    std::string short_name = name_it->second;
+    size_t space_pos = short_name.find(' ');
+    if (space_pos != std::string::npos) {
+      short_name = short_name.substr(0, space_pos);
+    } else if (short_name.length() > 3) {
+      short_name = short_name.substr(0, 3);
+    }
+    std::transform(short_name.begin(), short_name.end(), short_name.begin(),
+                   ::tolower);
+    reg_set.short_name = strdup(short_name.c_str());
+
+    // Get register numbers for this class
+    const auto &regnums = regnums_it->second;
+
+    // Store register numbers in a static container to ensure they live
+    // for the duration of the program
+    static std::vector<std::vector<uint32_t>> all_reg_nums;
+    all_reg_nums.push_back(regnums);
+
+    // Point the RegisterSet's registers field to the data in our static vector
+    reg_set.registers = all_reg_nums.back().data();
+    reg_set.num_registers = all_reg_nums.back().size();
+    g_reg_sets.push_back(reg_set);
+  }
+  return true;
+}
+
+RegisterContextAMDGPU::RegisterContextAMDGPU(
+    NativeThreadProtocol &native_thread)
+    : NativeRegisterContext(native_thread) {
+  InitRegisterInfos();
+  InitRegisters();
+  // Only doing this for the Mock GPU class, don't do this in real GPU classes.
+  // ReadRegs();
+}
+
+void RegisterContextAMDGPU::InitRegisters() {
+  m_regs.data.resize(g_register_buffer_size);
+  m_regs_valid.resize(kNumRegs, false);
+}
+
+void RegisterContextAMDGPU::InvalidateAllRegisters() {
+  // Do what ever book keeping we need to do to indicate that all register
+  // values are now invalid.
+  for (uint32_t i = 0; i < kNumRegs; ++i)
+    m_regs_valid[i] = false;
+}
+
+Status RegisterContextAMDGPU::ReadReg(const RegisterInfo *reg_info) {
+  Status error;
+  const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB];
+  assert(lldb_reg_num < kNumRegs);
+  auto amd_reg_id = g_lldb_num_to_amd_reg_id[lldb_reg_num];
+  ThreadAMDGPU *thread = (ThreadAMDGPU *)&m_thread;
+  auto wave_id = thread->GetWaveId();
+  if (!wave_id) {
+    // Swallow the error because so that we are returning the dummy register
+    // vlaues.
+    return error;
+  }
+  amd_dbgapi_register_exists_t exists;
+  amd_dbgapi_status_t amd_status =
+      amd_dbgapi_wave_register_exists(wave_id.value(), amd_reg_id, &exists);
+  if (amd_status != AMD_DBGAPI_STATUS_SUCCESS) {
+    error.FromErrorStringWithFormat(
+        "Failed to check register %s existence  due to error %d",
+        reg_info->name, amd_status);
+    return error;
+  }
+  if (exists != AMD_DBGAPI_REGISTER_PRESENT) {
+    error = Status::FromErrorStringWithFormat(
+        "Failed to read register %s due to register not present",
+        reg_info->name);
+    return error;
+  }
+
+  amd_status = amd_dbgapi_prefetch_register(wave_id.value(), amd_reg_id,
+                                            m_regs.data.size() - lldb_reg_num);
+  if (amd_status != AMD_DBGAPI_STATUS_SUCCESS) {
+    error = Status::FromErrorStringWithFormat(
+        "Failed to prefetch register %s due to error %d", reg_info->name,
+        amd_status);
+    return error;
+  }
+
+  // Read the register value
+  amd_status = amd_dbgapi_read_register(
+      wave_id.value(), amd_reg_id, 0, reg_info->byte_size,
+      m_regs.data.data() + reg_info->byte_offset);
+  if (amd_status != AMD_DBGAPI_STATUS_SUCCESS) {
+    error = Status::FromErrorStringWithFormat(
+        "Failed to read register %s due to error %d", reg_info->name,
+        amd_status);
+    return error;
+  }
+  m_regs_valid[lldb_reg_num] = true;
+  return error;
+}
+
+Status RegisterContextAMDGPU::ReadRegs() {
+  ThreadAMDGPU *thread = (ThreadAMDGPU *)&m_thread;
+  if (thread != nullptr) {
+    auto wave_id = thread->GetWaveId();
+    bool wave_stopped = wave_id.has_value();
+    if (wave_stopped) {
+      for (uint32_t i = 0; i < g_reg_infos.size(); ++i) {
+        Status error = ReadReg(&g_reg_infos[i]);
+        if (error.Fail())
+          return error;
+      }
+    }
+  } else {
+    // Fill all registers with unique values.
+    for (uint32_t i = 0; i < g_reg_infos.size(); ++i) {
+      memcpy(m_regs.data.data() + g_reg_infos[i].byte_offset, &i, sizeof(i));
+    }
+  }
+  return Status();
+}
+
+uint32_t RegisterContextAMDGPU::GetRegisterSetCount() const {
+  return g_reg_sets.size();
+}
+
+uint32_t RegisterContextAMDGPU::GetRegisterCount() const { return kNumRegs; }
+
+uint32_t RegisterContextAMDGPU::GetUserRegisterCount() const {
+  return GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextAMDGPU::GetRegisterInfoAtIndex(uint32_t reg) const {
+  if (reg < kNumRegs)
+    return &g_reg_infos[reg];
+  return nullptr;
+}
+
+const RegisterSet *
+RegisterContextAMDGPU::GetRegisterSet(uint32_t set_index) const {
+  if (set_index < GetRegisterSetCount())
+    return &g_reg_sets[set_index];
+  return nullptr;
+}
+
+Status RegisterContextAMDGPU::ReadRegister(const RegisterInfo *reg_info,
+                                           RegisterValue &reg_value) {
+  Status error;
+  const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB];
+  if (!m_regs_valid[lldb_reg_num]) {
+    error = ReadReg(reg_info);
+  }
+  if (error.Fail())
+    return error;
+  reg_value.SetBytes(m_regs.data.data() + reg_info->byte_offset,
+                     reg_info->byte_size, lldb::eByteOrderLittle);
+  return Status();
+}
+
+Status RegisterContextAMDGPU::WriteRegister(const RegisterInfo *reg_info,
+                                            const RegisterValue &reg_value) {
+  const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB];
+  const void *new_value = reg_value.GetBytes();
+  memcpy(m_regs.data.data() + reg_info->byte_offset, new_value,
+         reg_info->byte_size);
+  m_regs_valid[lldb_reg_num] = true;
+  return Status();
+}
+
+Status RegisterContextAMDGPU::ReadAllRegisterValues(
+    lldb::WritableDataBufferSP &data_sp) {
+  ReadRegs(); // Read all registers first
+  const size_t regs_byte_size = m_regs.data.size();
+  data_sp.reset(new DataBufferHeap(regs_byte_size, 0));
+  uint8_t *dst = data_sp->GetBytes();
+  memcpy(dst, &m_regs.data[0], regs_byte_size);
+  return Status();
+}
+
+Status RegisterContextAMDGPU::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  const size_t regs_byte_size = m_regs.data.size();
+
+  if (!data_sp) {
+    return Status::FromErrorStringWithFormat(
+        "RegisterContextAMDGPU::%s invalid data_sp provided", __FUNCTION__);
+  }
+
+  if (data_sp->GetByteSize() != regs_byte_size) {
+    return Status::FromErrorStringWithFormat(
+        "RegisterContextAMDGPU::%s data_sp contained mismatched "
+        "data size, expected %" PRIu64 ", actual %" PRIu64,
+        __FUNCTION__, regs_byte_size, data_sp->GetByteSize());
+  }
+
+  const uint8_t *src = data_sp->GetBytes();
+  if (src == nullptr) {
+    return Status::FromErrorStringWithFormat(
+        "RegisterContextAMDGPU::%s "
+        "DataBuffer::GetBytes() returned a null "
+        "pointer",
+        __FUNCTION__);
+  }
+  memcpy(&m_regs.data[0], src, regs_byte_size);
+  return Status();
+}
+
+std::vector<uint32_t>
+RegisterContextAMDGPU::GetExpeditedRegisters(ExpeditedRegs expType) const {
+  static std::vector<uint32_t> g_expedited_regs;
+  if (g_expedited_regs.empty()) {
+    // TODO: is this the correct way to do this?
+    // g_expedited_regs.push_back(LLDB_REGNUM_GENERIC_PC);
+    // g_expedited_regs.push_back(LLDB_SP);
+    // g_expedited_regs.push_back(LLDB_FP);
+  }
+  return g_expedited_regs;
+}
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h
new file mode 100644
index 0000000000000..938d6d6bc7e0b
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h
@@ -0,0 +1,65 @@
+//===-- RegisterContextAMDGPU.h --------------------------------*- C++ -*-===//
+//
+// 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_REGISTERCONTEXTAMDGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTAMDGPU_H
+
+// #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/lldb-forward.h"
+
+namespace lldb_private {
+namespace lldb_server {
+
+class RegisterContextAMDGPU : public NativeRegisterContext {
+public:
+  RegisterContextAMDGPU(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;
+
+  std::vector<uint32_t>
+  GetExpeditedRegisters(ExpeditedRegs expType) const override;
+
+  void InvalidateAllRegisters();
+
+private:
+  bool InitRegisterInfos();
+  void InitRegisters();
+  
+  Status ReadRegs();
+  Status ReadReg(const RegisterInfo *reg_info);
+
+  // All AMD GPU registers are contained in this buffer.
+  struct {
+    std::vector<uint8_t> data;
+  } m_regs;
+  std::vector<bool> m_regs_valid;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTAMDGPU_H
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp
new file mode 100644
index 0000000000000..0c724afa95894
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp
@@ -0,0 +1,63 @@
+//===-- ThreadAMDGPU.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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "ThreadAMDGPU.h"
+#include "ProcessAMDGPU.h"
+
+using namespace lldb_private;
+using namespace lldb_server;
+
+ThreadAMDGPU::ThreadAMDGPU(ProcessAMDGPU &process, lldb::tid_t tid,
+                           std::optional<amd_dbgapi_wave_id_t> wave_id)
+    : NativeThreadProtocol(process, tid), m_reg_context(*this),
+      m_wave_id(wave_id) {
+  m_stop_info.reason = lldb::eStopReasonSignal;
+  m_stop_info.signo = SIGTRAP;
+}
+
+// NativeThreadProtocol Interface
+std::string ThreadAMDGPU::GetName() {
+  if (!m_wave_id)
+    return "AMD Native Shadow Thread";
+  else
+    return std::string("AMD GPU Thread ") +
+           std::to_string(m_wave_id.value().handle);
+}
+
+lldb::StateType ThreadAMDGPU::GetState() { return lldb::eStateStopped; }
+
+bool ThreadAMDGPU::GetStopReason(ThreadStopInfo &stop_info,
+                                 std::string &description) {
+  stop_info = m_stop_info;
+  description = m_description;
+  return true;
+}
+
+Status ThreadAMDGPU::SetWatchpoint(lldb::addr_t addr, size_t size,
+                                   uint32_t watch_flags, bool hardware) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadAMDGPU::RemoveWatchpoint(lldb::addr_t addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadAMDGPU::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) {
+  return Status::FromErrorString("unimplemented");
+}
+
+Status ThreadAMDGPU::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+  return Status::FromErrorString("unimplemented");
+}
+
+ProcessAMDGPU &ThreadAMDGPU::GetProcess() {
+  return static_cast<ProcessAMDGPU &>(m_process);
+}
+
+const ProcessAMDGPU &ThreadAMDGPU::GetProcess() const {
+  return static_cast<const ProcessAMDGPU &>(m_process);
+}
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h
new file mode 100644
index 0000000000000..02991a20bfdd9
--- /dev/null
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h
@@ -0,0 +1,75 @@
+//===-- ThreadAMDGPU.h --------------------------------------- -*- C++ -*-===//
+//
+// 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_THREADAMDGPU_H
+#define LLDB_TOOLS_LLDB_SERVER_THREADAMDGPU_H
+
+#include "RegisterContextAMDGPU.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/lldb-private-forward.h"
+#include <string>
+#include <amd-dbgapi/amd-dbgapi.h>
+
+namespace lldb_private {
+namespace lldb_server {
+class ProcessAMDGPU;
+
+class NativeProcessLinux;
+
+class ThreadAMDGPU : public NativeThreadProtocol {
+  friend class ProcessAMDGPU;
+
+public:
+  ThreadAMDGPU(ProcessAMDGPU &process, lldb::tid_t tid, std::optional<amd_dbgapi_wave_id_t> wave_id = std::nullopt);
+
+  // NativeThreadProtocol Interface
+  std::string GetName() override;
+
+  lldb::StateType GetState() override;
+
+  bool GetStopReason(ThreadStopInfo &stop_info,
+                     std::string &description) override;
+  
+  void SetStopReason(lldb::StopReason reason) {
+    m_stop_info.reason = reason;
+  }
+  
+  RegisterContextAMDGPU &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;
+
+  ProcessAMDGPU &GetProcess();
+
+  const ProcessAMDGPU &GetProcess() const;
+
+  std::optional<amd_dbgapi_wave_id_t> GetWaveId() const {
+    return m_wave_id;
+  }
+
+private:
+  // Member Variables
+  lldb::StateType m_state;
+  ThreadStopInfo m_stop_info;
+  std::string m_description = "";
+  RegisterContextAMDGPU m_reg_context;
+  std::string m_stop_description;
+  std::optional<amd_dbgapi_wave_id_t> m_wave_id;
+};
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_THREADAMDGPU_H
diff --git a/lldb/tools/lldb-server/Plugins/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
index 45f42a7afd1fa..0d8efaaff13c1 100644
--- a/lldb/tools/lldb-server/Plugins/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/CMakeLists.txt
@@ -1 +1,5 @@
-add_subdirectory(MockGPU)
+if(DEFINED ROCM_PATH AND EXISTS ${ROCM_PATH})
+  add_subdirectory(AMDGPU)
+else()
+  add_subdirectory(MockGPU)
+endif()
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
index f06d7fc4e5682..75d5f473f0cc4 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp
@@ -26,8 +26,8 @@ using namespace lldb_private::lldb_server;
 using namespace lldb_private::process_gdb_remote;
 
 LLDBServerPluginMockGPU::LLDBServerPluginMockGPU(
-  LLDBServerPlugin::GDBServer &native_process)
-    : LLDBServerPlugin(native_process) {
+  LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop)
+    : LLDBServerPlugin(native_process, main_loop) {
   m_process_manager_up.reset(new ProcessMockGPU::Manager(m_main_loop));
   m_gdb_server.reset(new GDBRemoteCommunicationServerLLGS(
       m_main_loop, *m_process_manager_up, "mock-gpu.server"));
diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
index 620fdcaf9e7c4..55c5307afa6c4 100644
--- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
+++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h
@@ -61,7 +61,7 @@ namespace lldb_server {
 
 class LLDBServerPluginMockGPU : public LLDBServerPlugin {
 public:
-  LLDBServerPluginMockGPU(LLDBServerPlugin::GDBServer &native_process);
+  LLDBServerPluginMockGPU(LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop);
   ~LLDBServerPluginMockGPU() override;
   llvm::StringRef GetPluginName() override;
   int GetEventFileDescriptorAtIndex(size_t idx) override;
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 07a0a18818fa3..11b17f05bb92c 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -18,7 +18,6 @@
 #endif
 
 #include "LLDBServerUtilities.h"
-#include "Plugins/MockGPU/LLDBServerPluginMockGPU.h"
 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
 #include "lldb/Host/Config.h"
@@ -48,7 +47,13 @@
 #include "Plugins/Process/Windows/Common/NativeProcessWindows.h"
 #endif
 
-#include "Plugins/MockGPU/ProcessMockGPU.h"
+#if defined(LLDB_ENABLE_AMDGPU_PLUGIN)
+#include "Plugins/AMDGPU/LLDBServerPluginAMDGPU.h"
+typedef lldb_private::lldb_server::LLDBServerPluginAMDGPU LLDBServerGPUPlugin;
+#elif defined(LLDB_ENABLE_MOCKGPU_PLUGIN)
+#include "Plugins/MockGPU/LLDBServerPluginMockGPU.h"
+typedef lldb_private::lldb_server::LLDBServerPluginMockGPU LLDBServerGPUPlugin;
+#endif
 
 #ifndef LLGS_PROGRAM_NAME
 #define LLGS_PROGRAM_NAME "lldb-server"
@@ -438,7 +443,7 @@ int main_gdbserver(int argc, char *argv[]) {
   if (!LLDBServerUtilities::SetupLogging(
           log_file, log_channels,
           LLDB_LOG_OPTION_PREPEND_TIMESTAMP |
-          LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
+              LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
     return -1;
 
   std::vector<llvm::StringRef> Inputs;
@@ -456,8 +461,11 @@ int main_gdbserver(int argc, char *argv[]) {
   NativeProcessManager manager(mainloop);
   GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager, "gdb-server");
 
-  // Install the mock GPU plugin.
-  gdb_server.InstallPlugin(std::make_unique<LLDBServerPluginMockGPU>(gdb_server));
+#if defined(LLDB_ENABLE_AMDGPU_PLUGIN) || defined(LLDB_ENABLE_MOCKGPU_PLUGIN)
+  // Install GPU plugin.
+  gdb_server.InstallPlugin(
+      std::make_unique<LLDBServerGPUPlugin>(gdb_server, mainloop));
+#endif
 
   llvm::StringRef host_and_port;
   if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {
diff --git a/llvm/cmake/modules/CrossCompile.cmake b/llvm/cmake/modules/CrossCompile.cmake
index 3b31d3e218a37..cb52a88ef81c7 100644
--- a/llvm/cmake/modules/CrossCompile.cmake
+++ b/llvm/cmake/modules/CrossCompile.cmake
@@ -100,6 +100,7 @@ function(llvm_create_cross_target project_name target_name toolchain buildtype)
         -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN="${LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN}"
         -DLLVM_INCLUDE_BENCHMARKS=OFF
         -DLLVM_INCLUDE_TESTS=OFF
+        -DROCM_PATH=${ROCM_PATH}
         ${build_type_flags} ${linker_flag} ${external_clang_dir} ${libc_flags}
         ${ARGN}
     WORKING_DIRECTORY ${${project_name}_${target_name}_BUILD}

>From ca0b83a0f58bf762fd490d03706c9a305983d537 Mon Sep 17 00:00:00 2001
From: Jeffrey Tan <jeffreytan at fb.com>
Date: Tue, 10 Jun 2025 15:49:36 -0700
Subject: [PATCH 33/34] Feedback from Greg

---
 .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp | 33 ++++++++++---------
 .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.h   |  2 +-
 .../Plugins/AMDGPU/ProcessAMDGPU.cpp          | 21 ++++++------
 .../Plugins/AMDGPU/RegisterContextAMDGPU.cpp  | 10 +++---
 lldb/tools/lldb-server/lldb-gdbserver.cpp     | 11 ++++++-
 5 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
index 32473b397baba..af8d230e7f5ce 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
@@ -15,6 +15,7 @@
 #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
 #include "llvm/Support/Error.h"
 
+#include <cinttypes>
 #include <sys/ptrace.h>
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -35,8 +36,8 @@ static amd_dbgapi_status_t amd_dbgapi_client_process_get_info_callback(
   lldb::pid_t pid = debugger->GetNativeProcess()->GetID();
   LLDB_LOGF(GetLog(GDBRLog::Plugin),
             "amd_dbgapi_client_process_get_info_callback callback, with query "
-            "%d, pid %d",
-            query, pid);
+            "%d, pid %lu",
+            query, (unsigned long)pid);
   switch (query) {
   case AMD_DBGAPI_CLIENT_PROCESS_INFO_OS_PID: {
     if (value_size != sizeof(amd_dbgapi_os_process_id_t))
@@ -56,7 +57,7 @@ static amd_dbgapi_status_t amd_dbgapi_insert_breakpoint_callback(
     amd_dbgapi_global_address_t address,
     amd_dbgapi_breakpoint_id_t breakpoint_id) {
   LLDB_LOGF(GetLog(GDBRLog::Plugin),
-            "insert_breakpoint callback at address: 0x%llx", address);
+            "insert_breakpoint callback at address: 0x%" PRIx64, address);
   LLDBServerPluginAMDGPU *debugger =
       reinterpret_cast<LLDBServerPluginAMDGPU *>(client_process_id);
   debugger->GetNativeProcess()->Halt();
@@ -74,7 +75,7 @@ static amd_dbgapi_status_t amd_dbgapi_insert_breakpoint_callback(
 static amd_dbgapi_status_t amd_dbgapi_remove_breakpoint_callback(
     amd_dbgapi_client_process_id_t client_process_id,
     amd_dbgapi_breakpoint_id_t breakpoint_id) {
-  LLDB_LOGF(GetLog(GDBRLog::Plugin), "remove_breakpoint callback for %llu",
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "remove_breakpoint callback for %" PRIu64,
             breakpoint_id.handle);
   return AMD_DBGAPI_STATUS_SUCCESS;
 }
@@ -93,24 +94,24 @@ static amd_dbgapi_status_t amd_dbgapi_xfer_global_memory_callback(
 static void amd_dbgapi_log_message_callback(amd_dbgapi_log_level_t level,
                                             const char *message) {
   LLDB_LOGF(GetLog(GDBRLog::Plugin), "ROCdbgapi [%d]: %s", level, message);
-};
+}
 
 static amd_dbgapi_callbacks_t s_dbgapi_callbacks = {
-    .allocate_memory = malloc,
-    .deallocate_memory = free,
-    .client_process_get_info = amd_dbgapi_client_process_get_info_callback,
-    .insert_breakpoint = amd_dbgapi_insert_breakpoint_callback,
-    .remove_breakpoint = amd_dbgapi_remove_breakpoint_callback,
-    .xfer_global_memory = amd_dbgapi_xfer_global_memory_callback,
-    .log_message = amd_dbgapi_log_message_callback,
+    malloc,
+    free,
+    amd_dbgapi_client_process_get_info_callback,
+    amd_dbgapi_insert_breakpoint_callback,
+    amd_dbgapi_remove_breakpoint_callback,
+    amd_dbgapi_xfer_global_memory_callback,
+    amd_dbgapi_log_message_callback,
 };
 
 LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU(
     LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop)
     : LLDBServerPlugin(native_process, main_loop) {
   m_process_manager_up.reset(new ProcessManagerAMDGPU(main_loop));
-  m_gdb_server.reset(
-      new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up));
+  m_gdb_server.reset(new GDBRemoteCommunicationServerLLGS(
+      m_main_loop, *m_process_manager_up, "amd-gpu.server"));
 }
 
 LLDBServerPluginAMDGPU::~LLDBServerPluginAMDGPU() { CloseFDs(); }
@@ -415,7 +416,7 @@ std::optional<GPUActions> LLDBServerPluginAMDGPU::NativeProcessIsStopping() {
 bool LLDBServerPluginAMDGPU::HandleGPUInternalBreakpointHit(
     const GPUInternalBreakpoinInfo &bp, bool &has_new_libraries) {
   LLDB_LOGF(GetLog(GDBRLog::Plugin),
-            "Hit GPU loader breakpoint at address: 0x%llx", bp.addr);
+            "Hit GPU loader breakpoint at address: 0x%" PRIx64, bp.addr);
   has_new_libraries = false;
   amd_dbgapi_breakpoint_id_t breakpoint_id{bp.breakpoind_id};
   amd_dbgapi_breakpoint_action_t action;
@@ -557,7 +558,7 @@ bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) {
   return SetGPUBreakpoint(addr, bp_instruction, bp_size);
 }
 
-GPUPluginBreakpointHitResponse
+llvm::Expected<GPUPluginBreakpointHitResponse>
 LLDBServerPluginAMDGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) {
   Log *log = GetLog(GDBRLog::Plugin);
   std::string json_string;
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
index e1f057e7f8956..79cb771319aca 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
@@ -94,7 +94,7 @@ class LLDBServerPluginAMDGPU : public LLDBServerPlugin {
   bool HandleEventFileDescriptorEvent(int fd) override;
   GPUActions GetInitializeActions() override;
   std::optional<struct GPUActions> NativeProcessIsStopping() override;
-  GPUPluginBreakpointHitResponse
+  llvm::Expected<GPUPluginBreakpointHitResponse>
   BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override;
 
   NativeProcessProtocol *GetNativeProcess() {
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
index df6015aabb9ee..af76f038e2117 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Utility/UnimplementedError.h"
 #include "llvm/Support/Error.h"
 
+#include <cinttypes>
 #include <iostream>
 
 using namespace lldb;
@@ -349,8 +350,9 @@ bool ProcessAMDGPU::handleWaveStop(amd_dbgapi_event_id_t eventId) {
     }
 
     LLDB_LOGF(GetLog(GDBRLog::Plugin),
-              "Wave stopped due to breakpoint at: 0x%llx with wave id: %llu "
-              "event id: %llu",
+              "Wave stopped due to breakpoint at: 0x%" PRIx64
+              " with wave id: %" PRIu64 " "
+              "event id: %" PRIu64,
               pc, wave_id.handle, eventId.handle);
     return true;
   } else {
@@ -383,14 +385,14 @@ static const char *event_kind_str(amd_dbgapi_event_kind_t kind) {
   case AMD_DBGAPI_EVENT_KIND_QUEUE_ERROR:
     return "QUEUE_ERROR";
   }
-  assert(!"unhandled amd_dbgapi_event_kind_t value");
+  assert(false && "unhandled amd_dbgapi_event_kind_t value");
 }
 
 bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId,
                                      amd_dbgapi_event_kind_t eventKind) {
-  LLDB_LOGF(GetLog(GDBRLog::Plugin), "handleDebugEvent(%llu, %s)",
+  LLDB_LOGF(GetLog(GDBRLog::Plugin), "handleDebugEvent(%" PRIu64 ", %s)",
             eventId.handle, event_kind_str(eventKind));
-  bool result;
+  bool result = false;
   if (eventKind == AMD_DBGAPI_EVENT_KIND_NONE)
     return result;
 
@@ -413,10 +415,6 @@ bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId,
     case AMD_DBGAPI_RUNTIME_STATE_UNLOADED:
       LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime unloaded");
       break;
-    default:
-      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown runtime state: %d",
-                runtimeState);
-      break;
     }
   }
 
@@ -490,8 +488,9 @@ bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId,
       if (status != AMD_DBGAPI_STATUS_SUCCESS)
         continue;
 
-      LLDB_LOGF(GetLog(GDBRLog::Plugin), "Code object %zu: %s at address %llu",
-                i, uri_bytes, l_addr);
+      LLDB_LOGF(GetLog(GDBRLog::Plugin),
+                "Code object %zu: %s at address %" PRIu64, i, uri_bytes,
+                l_addr);
 
       if (m_gpu_modules.find(l_addr) == m_gpu_modules.end()) {
         GPUModule mod = parseCodeObjectUrl(uri_bytes, l_addr);
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp
index e34728b5ff85d..670a0eae75df2 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp
@@ -33,6 +33,7 @@ static std::vector<RegisterSet> g_reg_sets;
 /// are accessed by the LLDB register numbers, which are defined above.
 static std::vector<RegisterInfo> g_reg_infos;
 size_t g_register_buffer_size = 0;
+static uint32_t s_gpu_pc_reg_num = 0;
 static std::unordered_map<uint32_t, amd_dbgapi_register_id_t>
     g_lldb_num_to_amd_reg_id;
 
@@ -263,6 +264,7 @@ bool RegisterContextAMDGPU::InitRegisterInfos() {
     // Check if this is the PC register
     if (reg_id.handle == pc_register_id.handle) {
       reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+      s_gpu_pc_reg_num = i;
     }
 
     // Add this register indices belong to its register classes
@@ -508,10 +510,10 @@ std::vector<uint32_t>
 RegisterContextAMDGPU::GetExpeditedRegisters(ExpeditedRegs expType) const {
   static std::vector<uint32_t> g_expedited_regs;
   if (g_expedited_regs.empty()) {
-    // TODO: is this the correct way to do this?
-    // g_expedited_regs.push_back(LLDB_REGNUM_GENERIC_PC);
-    // g_expedited_regs.push_back(LLDB_SP);
-    // g_expedited_regs.push_back(LLDB_FP);
+    // We can't expedite all registers because that would cause jThreadsInfo to
+    // fetch registers from all stopped waves eagarly which would be too slow
+    // and unnecessary. 
+    g_expedited_regs.push_back(s_gpu_pc_reg_num);
   }
   return g_expedited_regs;
 }
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 11b17f05bb92c..e03b89ae20727 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -462,9 +462,18 @@ int main_gdbserver(int argc, char *argv[]) {
   GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager, "gdb-server");
 
 #if defined(LLDB_ENABLE_AMDGPU_PLUGIN) || defined(LLDB_ENABLE_MOCKGPU_PLUGIN)
+#if defined(LLDB_ENABLE_AMDGPU_PLUGIN)
+  // AMD GPU plugin requires to use the same mainloop as the native process.
+  // This is because AMD debug API has to be called from the same thread as the
+  // ptrace() thread.
+  MainLoop &gpu_mainloop = mainloop;
+#else
+  // Any GPU plugins can use a separate mainloop.
+  MainLoop gpu_mainloop;
+#endif
   // Install GPU plugin.
   gdb_server.InstallPlugin(
-      std::make_unique<LLDBServerGPUPlugin>(gdb_server, mainloop));
+      std::make_unique<LLDBServerGPUPlugin>(gdb_server, gpu_mainloop));
 #endif
 
   llvm::StringRef host_and_port;

>From 8dc56535d043907b20a335dc43f8a644c389129a Mon Sep 17 00:00:00 2001
From: Jeffrey Tan <jeffreytan at fb.com>
Date: Thu, 12 Jun 2025 11:12:10 -0700
Subject: [PATCH 34/34] Some refactoring

---
 lldb/tools/lldb-server/CMakeLists.txt         |  2 +-
 .../lldb-server/Plugins/AMDGPU/CMakeLists.txt |  5 +-
 .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp | 20 +++-----
 .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.h   | 37 ---------------
 .../Plugins/AMDGPU/ProcessAMDGPU.cpp          | 46 -------------------
 .../Plugins/AMDGPU/ProcessAMDGPU.h            |  3 --
 6 files changed, 9 insertions(+), 104 deletions(-)

diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index 4126c02829441..ade88d8618539 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -42,7 +42,7 @@ if(APPLE_EMBEDDED)
   endif()
 endif()
 
-if(DEFINED ROCM_PATH AND EXISTS ${ROCM_PATH})
+if(DEFINED ROCM_PATH)
   add_definitions(-DLLDB_ENABLE_AMDGPU_PLUGIN=1)
   list(APPEND LLDB_PLUGINS lldbServerPluginAMDGPU)
 else()
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt
index f19d4a72aca9b..99493a3b03fb6 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt
@@ -1,9 +1,6 @@
 
-# Set ROCM paths
-set(ROCM_PATH "" CACHE PATH "Path to ROCm installation")
-
 message(STATUS "ROCM_PATH is set to: '${ROCM_PATH}'")
-if(NOT ROCM_PATH OR ROCM_PATH STREQUAL "")
+if(NOT ROCM_PATH)
     message(FATAL_ERROR "ROCM_PATH must be specified. Use -DROCM_PATH=/path/to/rocm")
 endif()
 
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
index af8d230e7f5ce..2bf34f73119b2 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp
@@ -496,6 +496,8 @@ bool LLDBServerPluginAMDGPU::SetGPUBreakpoint(uint64_t addr,
   bp.gpu_breakpoint_id =
       std::nullopt; // No GPU breakpoint ID for ptrace version
 
+  // TODO: use memory read/write API from native process instead of ptrace
+  // directly.
   auto pid = GetNativeProcess()->GetID();
   // Read original bytes word by word
   std::vector<long> original_words;
@@ -522,19 +524,10 @@ bool LLDBServerPluginAMDGPU::SetGPUBreakpoint(uint64_t addr,
 }
 
 bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) {
-  // First get the architecture ID for this process
-  amd_dbgapi_architecture_id_t arch_id;
-  amd_dbgapi_status_t status = amd_dbgapi_get_architecture(0x02C, &arch_id);
-  if (status != AMD_DBGAPI_STATUS_SUCCESS) {
-    // Handle error
-    LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_get_architecture failed");
-    return false;
-  }
-
   // Get breakpoint instruction
   const uint8_t *bp_instruction;
-  status = amd_dbgapi_architecture_get_info(
-      arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION,
+  amd_dbgapi_status_t status = amd_dbgapi_architecture_get_info(
+      m_architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION,
       sizeof(bp_instruction), &bp_instruction);
   if (status != AMD_DBGAPI_STATUS_SUCCESS) {
     LLDB_LOGF(GetLog(GDBRLog::Plugin),
@@ -545,8 +538,9 @@ bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) {
   // Get breakpoint instruction size
   size_t bp_size;
   status = amd_dbgapi_architecture_get_info(
-      arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE,
-      sizeof(bp_size), &bp_size);
+      m_architecture_id,
+      AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE, sizeof(bp_size),
+      &bp_size);
   if (status != AMD_DBGAPI_STATUS_SUCCESS) {
     LLDB_LOGF(
         GetLog(GDBRLog::Plugin),
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
index 79cb771319aca..7f657879b6ca4 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h
@@ -16,43 +16,6 @@
 #include "ProcessAMDGPU.h"
 #include <amd-dbgapi/amd-dbgapi.h>
 
-// This is a mock GPU plugin that is used for testing the LLDBServerPlugin. It
-// should be run with the following code as the main binary:
-/*
-
-$ cat main.cpp
-#include <stdio.h>
-
-struct ShlibInfo {
-  const char *path = nullptr;
-  ShlibInfo *next = nullptr;
-};
-
-ShlibInfo g_shlib_list = { "/tmp/a.out", nullptr};
-
-int gpu_initialize() {
-  return puts(__FUNCTION__);
-}
-int gpu_shlib_load() {
-  return puts(__FUNCTION__);
-}
-int main(int argc, const char **argv) {
-  gpu_initialize();
-  gpu_shlib_load();
-  return 0; // Break here
-}
-
-$ clang++ -g -O0 -o a.out main.cpp
-$ ninja lldb lldb-server
-$ ./bin/lldb a.out -o 'b /Break here/ -o run
-
-*/
-// If the above code is run, you will be stopped at the breakpoint and the Mock
-// GPU target will be selected. Try doing a "reg read --all" to see the state
-// of the GPU registers. Then you can select the native process target with
-// "target select 0" and issue commands to the native process, and then select
-// the GPU target with "target select 1" and issue commands to the GPU target.
-
 namespace lldb_private {
 
 class TCPSocket;
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
index af76f038e2117..22dae40ea2486 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp
@@ -104,8 +104,6 @@ const ArchSpec &ProcessAMDGPU::GetArchitecture() const {
 // Breakpoint functions
 Status ProcessAMDGPU::SetBreakpoint(lldb::addr_t addr, uint32_t size,
                                     bool hardware) {
-  // TODO: fix the race condition of GPU module load, client lldb setting
-  // breakpoint then resume GPU connection.
   bool success = m_debugger->CreateGPUBreakpoint(addr);
   if (!success) {
     return Status::FromErrorString("CreateGPUBreakpoint failed");
@@ -190,7 +188,6 @@ ProcessAMDGPU::GetGPUDynamicLoaderLibraryInfos(
 
   GPUDynamicLoaderResponse response;
 
-  // Access the GPU modules using the GetGPUModules() method
   const auto &gpu_modules = m_gpu_modules;
 
   LLDB_LOGF(log, "ProcessAMDGPU::%s() found %zu GPU modules", __FUNCTION__,
@@ -235,49 +232,6 @@ ProcessAMDGPU::GetGPUDynamicLoaderLibraryInfos(
   return response;
 }
 
-llvm::Expected<std::vector<SVR4LibraryInfo>>
-ProcessAMDGPU::GetLoadedSVR4Libraries() {
-  std::vector<SVR4LibraryInfo> libraries;
-
-  // Check if we have a valid debugger instance
-  if (!m_debugger) {
-    return libraries; // Return empty vector if no debugger
-  }
-
-  // Access the GPU modules using the GetGPUModules() method
-  const auto &gpu_modules = GetGPUModules();
-
-  // Convert each GPU module to an SVR4LibraryInfo object
-  for (const auto &[addr, module] : gpu_modules) {
-    if (module.is_loaded) {
-      SVR4LibraryInfo lib_info;
-      std::string path;
-      for (char c : module.path) {
-        if (c == '#')
-          path += "%23";
-        else if (c == '$')
-          path += "%24";
-        else if (c == '}')
-          path += "%7D";
-        else if (c == '&')
-          path += "&";
-        else
-          path += c;
-      }
-      lib_info.name = path;
-      lib_info.link_map = addr;
-      lib_info.base_addr = module.base_address;
-      lib_info.ld_addr =
-          module.size;   // Using size as ld_addr as in handleLibrariesSvr4Read
-      lib_info.next = 0; // No next link in our implementation
-
-      libraries.push_back(lib_info);
-    }
-  }
-
-  return libraries;
-}
-
 llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
 ProcessManagerAMDGPU::Launch(
     ProcessLaunchInfo &launch_info,
diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h
index 08e8dd8352420..7c6d3956c3982 100644
--- a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h
+++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h
@@ -82,9 +82,6 @@ class ProcessAMDGPU : public NativeProcessProtocol {
   // Custom accessors
   void SetLaunchInfo(ProcessLaunchInfo &launch_info);
 
-  llvm::Expected<std::vector<SVR4LibraryInfo>>
-  GetLoadedSVR4Libraries() override;
-
   std::optional<GPUDynamicLoaderResponse> 
   GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) override;
 



More information about the lldb-commits mailing list