[Lldb-commits] [lldb] [lldb] Add accelerator dynamic loader over gdb-remote (PR #217393)
satyanarayana reddy janga via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 30 12:46:01 PDT 2026
https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/217393
>From c6cf62eeb2fdb4cb32ef6a08dc50358ac0c4e6ce Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Fri, 30 Oct 2026 12:32:06 -0700
Subject: [PATCH 1/2] [lldb] Add server support for jLLDBSettings
---
.../lldb/Host/common/NativeProcessProtocol.h | 3 ++-
.../tools/lldb-server/gdbremote_testcase.py | 1 +
.../GDBRemoteCommunicationServerLLGS.cpp | 23 +++++++++++++++++++
.../GDBRemoteCommunicationServerLLGS.h | 13 +++++++++++
.../gdb-remote/LLDBServerAcceleratorPlugin.h | 13 +++++++++++
.../mock/TestMockAcceleratorActions.py | 15 ++++++++++++
.../mock/TestMockAcceleratorPackets.py | 1 +
.../Mock/LLDBServerMockAcceleratorPlugin.cpp | 1 +
.../Accelerator/Mock/ProcessMockAccelerator.h | 2 +-
9 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index da1fa26abefa9e..25c075077bb9c1 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -306,8 +306,9 @@ class NativeProcessProtocol {
libraries = (1u << 9),
accelerator_plugins = (1u << 10),
address_spaces = (1u << 11),
+ lldb_settings = (1u << 12),
- LLVM_MARK_AS_BITMASK_ENUM(address_spaces)
+ LLVM_MARK_AS_BITMASK_ENUM(lldb_settings)
};
class Manager {
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
index 41ba3d45d5e0bd..74ec0c9a4a436b 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -954,6 +954,7 @@ def add_qSupported_packets(self, client_features=[]):
"MultiMemRead",
"jMultiBreakpoint",
"accelerator-plugins",
+ "lldb-settings",
"ExpediteStack",
"ExpediteRecentReads",
]
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index db62be06d60ff9..5466817d844c59 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -210,6 +210,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
StringExtractorGDBRemote::eServerPacketType_QPassSignals,
&GDBRemoteCommunicationServerLLGS::Handle_QPassSignals);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jLLDBSettings,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBSettings);
RegisterMemberFunctionHandler(
StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupported,
&GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupported);
@@ -4568,6 +4571,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
}
if (!m_accelerator_plugins.empty())
ret.push_back("accelerator-plugins+");
+ if (bool(plugin_features & Extension::lldb_settings))
+ ret.push_back("lldb-settings+");
// check for client features
m_extensions_supported = {};
@@ -4667,6 +4672,24 @@ void GDBRemoteCommunicationServerLLGS::InstallPlugin(
m_accelerator_plugins.emplace_back(std::move(plugin_up));
}
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBSettings(
+ StringExtractorGDBRemote &) {
+ if (!m_connection_accelerator_plugin)
+ return SendErrorResponse(
+ Status::FromErrorString("invalid accelerator plugin"));
+
+ std::optional<LLDBSettings> settings =
+ m_connection_accelerator_plugin->GetLLDBSettings();
+ if (!settings)
+ return SendErrorResponse(
+ Status::FromErrorString("no LLDB settings available"));
+
+ StreamGDBRemote response;
+ response.PutAsJSON(*settings, /*hex_ascii=*/false);
+ return SendPacketNoLock(response.GetString());
+}
+
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize(
StringExtractorGDBRemote &) {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index ac24c69dee0fae..efb7c30b9464f7 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -77,6 +77,13 @@ class GDBRemoteCommunicationServerLLGS
void InstallPlugin(
std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> plugin_up);
+ /// Associate this server with the accelerator plugin for this connection.
+ /// The plugin must outlive this server.
+ void SetConnectionAcceleratorPlugin(
+ lldb_server::LLDBServerAcceleratorPlugin &plugin) {
+ m_connection_accelerator_plugin = &plugin;
+ }
+
// NativeProcessProtocol::NativeDelegate overrides
void InitializeDelegate(NativeProcessProtocol *process) override;
@@ -120,6 +127,10 @@ class GDBRemoteCommunicationServerLLGS
NativeProcessProtocol::Manager &m_process_manager;
std::vector<std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin>>
m_accelerator_plugins;
+ /// The plugin that owns this accelerator connection. This is null for the
+ /// native connection and does not own the plugin.
+ lldb_server::LLDBServerAcceleratorPlugin *m_connection_accelerator_plugin =
+ nullptr;
lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID;
lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID;
NativeProcessProtocol *m_current_process;
@@ -304,6 +315,8 @@ class GDBRemoteCommunicationServerLLGS
PacketResult Handle_jMultiBreakpoint(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBSettings(StringExtractorGDBRemote &packet);
+
PacketResult
Handle_jAcceleratorPluginInitialize(StringExtractorGDBRemote &packet);
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
index 2e585d9a64c6aa..af9cf944482ea0 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
@@ -35,6 +35,19 @@ class LLDBServerAcceleratorPlugin {
virtual llvm::StringRef GetPluginName() = 0;
+ /// Return the LLDB settings needed to debug this accelerator.
+ ///
+ /// The settings select the accelerator dynamic loader and identify this
+ /// server-side plugin. They also determine whether dynamic-loader packets
+ /// are sent over the accelerator or native connection.
+ virtual std::optional<LLDBSettings> GetLLDBSettings() {
+ LLDBSettings settings;
+ settings.dyld_plugin_name = "accelerator-gdb-remote";
+ settings.accelerator_plugin_name = GetPluginName();
+ settings.send_dyld_packet_to_accelerator = true;
+ return settings;
+ }
+
virtual std::optional<AcceleratorActions> GetInitializeActions() = 0;
virtual llvm::Expected<AcceleratorBreakpointHitResponse>
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorActions.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorActions.py
index 58c9f2e193ed9b..655e525da1201b 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorActions.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorActions.py
@@ -142,6 +142,21 @@ def test_accelerator_actions(self):
self.assertTrue(accelerator_process.IsValid())
self.assertState(accelerator_process.GetState(), lldb.eStateStopped)
+ # LLDB settings are advertised and served only on the accelerator
+ # connection.
+ self.dbg.SetSelectedTarget(accelerator_target)
+ self.expect("process plugin packet send qSupported", substrs=["lldb-settings+"])
+ self.expect(
+ "process plugin packet send jLLDBSettings",
+ substrs=[
+ '"dyld_plugin_name":"accelerator-gdb-remote"',
+ '"accelerator_plugin_name":"mock"',
+ '"send_dyld_packet_to_accelerator":true',
+ ],
+ ordered=False,
+ )
+ self.dbg.SetSelectedTarget(target)
+
# Validate the registers (each value == 0x1000 + register index).
accelerator_frame = accelerator_process.GetThreadAtIndex(0).GetFrameAtIndex(0)
expected_registers = {
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
index e40e6491e59a05..a9081e64e5567c 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
@@ -58,6 +58,7 @@ def test_qSupported_reports_accelerator_plugins(self):
self.assertIn("accelerator-plugins", features)
self.assertEqual(features["accelerator-plugins"], "+")
+ self.assertNotIn("lldb-settings", features)
@add_test_categories(["llgs"])
def test_jAcceleratorPluginInitialize_returns_breakpoints(self):
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index b525f8e3592a7b..069bee0e4ef52b 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -156,6 +156,7 @@ LLDBServerMockAcceleratorPlugin::CreateConnection() {
std::make_unique<ProcessMockAccelerator::Manager>(m_mock_main_loop);
m_accelerator_gdb_server = std::make_unique<GDBRemoteCommunicationServerLLGS>(
m_mock_main_loop, *m_process_manager_up);
+ m_accelerator_gdb_server->SetConnectionAcceleratorPlugin(*this);
// LLGS creates its current process from a launch; the manager ignores the
// path but requires a non-empty argument list.
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
index af1631ebc46084..7b88bd36f2ea53 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
@@ -32,7 +32,7 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override;
Extension GetSupportedExtensions() const override {
- return Extension::address_spaces;
+ return Extension::address_spaces | Extension::lldb_settings;
}
};
>From b384605f6df49b4f1b31c257705953c2fa6a9738 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Mon, 5 Oct 2026 11:22:20 -0700
Subject: [PATCH 2/2] [lldb] Add accelerator dynamic loader over gdb-remote
Add a DynamicLoader that gets accelerator library load and unload information over GDB-remote. Let the accelerator server select the LLDB loader through jLLDBSettings, and include its server-side plugin name in those settings.
Handle module loads and unloads with end-to-end coverage through the mock accelerator server.
---
lldb/docs/resources/lldbgdbremote.md | 8 +-
.../lldb/Host/common/NativeProcessProtocol.h | 11 +
.../Host/common/NativeProcessProtocol.cpp | 4 +
.../AcceleratorGDBRemote/CMakeLists.txt | 13 ++
.../DynamicLoaderAcceleratorGDBRemote.cpp | 217 ++++++++++++++++++
.../DynamicLoaderAcceleratorGDBRemote.h | 46 ++++
.../Plugins/DynamicLoader/CMakeLists.txt | 1 +
.../GDBRemoteCommunicationClient.cpp | 24 ++
.../gdb-remote/GDBRemoteCommunicationClient.h | 5 +
.../GDBRemoteCommunicationServerLLGS.cpp | 17 +-
.../Process/gdb-remote/ProcessGDBRemote.cpp | 8 +-
.../TestAcceleratorDynamicLoader.py | 157 +++++++++++++
.../dynamic_loader/accelerator.yaml | 23 ++
.../dynamic_loader/accelerator_lib.yaml | 23 ++
.../mock/TestMockAcceleratorDynamicLoader.py | 148 ++++++++++++
.../API/accelerator/mock/accelerator_lib.yaml | 23 ++
lldb/test/API/accelerator/mock/main.c | 2 +
.../Mock/LLDBServerMockAcceleratorPlugin.cpp | 43 +++-
.../Mock/LLDBServerMockAcceleratorPlugin.h | 6 +-
.../Mock/ProcessMockAccelerator.cpp | 41 +++-
.../Accelerator/Mock/ProcessMockAccelerator.h | 36 ++-
21 files changed, 832 insertions(+), 24 deletions(-)
create mode 100644 lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt
create mode 100644 lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
create mode 100644 lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
create mode 100644 lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
create mode 100644 lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
create mode 100644 lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
create mode 100644 lldb/test/API/accelerator/mock/TestMockAcceleratorDynamicLoader.py
create mode 100644 lldb/test/API/accelerator/mock/accelerator_lib.yaml
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 53a36646444bfe..d00bca80246278 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -312,6 +312,9 @@ The fields are:
through either the native or accelerator connection. If absent, this
defaults to true.
+An accelerator GDB server can use `dyld_plugin_name` to select either LLDB's
+generic `accelerator-gdb-remote` loader or a vendor-specific loader.
+
The settings object can be extended with more fields as needed.
Example:
@@ -3040,8 +3043,7 @@ support. Not needed for non-hardware-accelerator debugging.
Requests shared library information from an accelerator plugin. The client
sends this packet when it needs to load or update the accelerator's shared
-library list. This packet requires the `accelerator-plugins+` feature from
-`qSupported`.
+library list.
```
LLDB SENDS: jAcceleratorPluginGetDynamicLoaderLibraryInfo:<json>
@@ -3052,7 +3054,7 @@ The request JSON has the following fields:
| Key | Type | Description |
|---------------|--------|-------------|
-| `plugin_name` | string | Name of the accelerator plugin to query. |
+| `plugin_name` | string | Name of the accelerator plugin to query when using the native connection. It is empty on an accelerator connection, where the connection identifies the plugin. |
| `full` | bool | If true, return every library the plugin knows about. If false, return only what changed since the last query. |
A plugin may track thousands of code objects, so `full` lets a client that is
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 25c075077bb9c1..8eb5873c8de555 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -14,6 +14,7 @@
#include "NativeWatchpointList.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/AcceleratorGDBRemotePackets.h"
#include "lldb/Utility/AddressSpace.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Iterable.h"
@@ -170,6 +171,14 @@ class NativeProcessProtocol {
"Not implemented");
}
+ /// Answers "jAcceleratorPluginGetDynamicLoaderLibraryInfo" on an accelerator
+ /// connection. std::nullopt if this process does not provide libraries.
+ virtual std::optional<AcceleratorDynamicLoaderResponse>
+ GetAcceleratorDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args) {
+ return std::nullopt;
+ }
+
virtual bool HasPendingLibraryEvents() { return false; }
virtual bool IsAlive() const;
@@ -209,6 +218,8 @@ class NativeProcessProtocol {
// Accessors
lldb::pid_t GetID() const { return m_pid; }
+ 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 c94825828435ad..b4dd61495e03ba 100644
--- a/lldb/source/Host/common/NativeProcessProtocol.cpp
+++ b/lldb/source/Host/common/NativeProcessProtocol.cpp
@@ -851,4 +851,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/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt
new file mode 100644
index 00000000000000..b5cf71d1f8b31a
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_lldb_library(lldbPluginDynamicLoaderAcceleratorGDBRemote PLUGIN
+ DynamicLoaderAcceleratorGDBRemote.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbHost
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ lldbPluginProcessGDBRemote
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
new file mode 100644
index 00000000000000..a32f94cd0d2292
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
@@ -0,0 +1,217 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "DynamicLoaderAcceleratorGDBRemote.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetGroup.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+LLDB_PLUGIN_DEFINE(DynamicLoaderAcceleratorGDBRemote)
+
+DynamicLoader *
+DynamicLoaderAcceleratorGDBRemote::CreateInstance(Process *process,
+ bool force) {
+ // The library list comes from the accelerator's GDB server.
+ if (!force ||
+ process->GetPluginName() != ProcessGDBRemote::GetPluginNameStatic())
+ return nullptr;
+ return new DynamicLoaderAcceleratorGDBRemote(process);
+}
+
+DynamicLoaderAcceleratorGDBRemote::DynamicLoaderAcceleratorGDBRemote(
+ Process *process)
+ : DynamicLoader(process) {}
+
+void DynamicLoaderAcceleratorGDBRemote::DidAttach() {
+ LoadModulesFromGDBServer(/*full=*/true);
+}
+
+void DynamicLoaderAcceleratorGDBRemote::DidLaunch() {
+ LoadModulesFromGDBServer(/*full=*/true);
+}
+
+bool DynamicLoaderAcceleratorGDBRemote::LoadModulesFromGDBServer(bool full) {
+ Log *log = GetLog(LLDBLog::DynamicLoader);
+
+ // CreateInstance ensures that m_process is a ProcessGDBRemote. Send the
+ // request on its accelerator connection unless the server asks us to route
+ // it through the native connection instead.
+ ProcessGDBRemote *accelerator_process =
+ static_cast<ProcessGDBRemote *>(m_process);
+ ProcessGDBRemote *send_process = accelerator_process;
+ AcceleratorDynamicLoaderArgs args;
+ args.full = full;
+
+ Target &target = m_process->GetTarget();
+ std::shared_ptr<TargetGroupCPUAndAccelerators> group_sp =
+ target.GetFirstTargetGroupOfType<TargetGroupCPUAndAccelerators>();
+ TargetSP native_target_sp = group_sp ? group_sp->GetCPUTarget() : nullptr;
+ ProcessSP native_process_sp =
+ native_target_sp ? native_target_sp->GetProcessSP() : nullptr;
+
+ std::optional<LLDBSettings> settings =
+ accelerator_process->GetGDBRemote().GetLLDBSettings();
+ if (settings && !settings->send_dyld_packet_to_accelerator) {
+ if (settings->accelerator_plugin_name.empty()) {
+ LLDB_LOG(log, "cannot route dynamic-loader request through the native "
+ "connection without an accelerator plugin name");
+ return false;
+ }
+
+ if (!native_process_sp || native_process_sp->GetPluginName() !=
+ ProcessGDBRemote::GetPluginNameStatic()) {
+ LLDB_LOG(log, "cannot route dynamic-loader request through the native "
+ "connection: no native gdb-remote process");
+ return false;
+ }
+
+ send_process = static_cast<ProcessGDBRemote *>(native_process_sp.get());
+ args.plugin_name = settings->accelerator_plugin_name;
+ }
+
+ ModuleList loaded_module_list;
+ std::optional<AcceleratorDynamicLoaderResponse> response =
+ send_process->GetGDBRemote().GetAcceleratorDynamicLoaderLibraryInfos(
+ args);
+ if (!response) {
+ LLDB_LOG(log, "failed to get dynamic loader info from the GDB server");
+ return false;
+ }
+
+ for (const AcceleratorDynamicLoaderLibraryInfo &info :
+ response->library_infos) {
+ std::shared_ptr<DataBufferHeap> data_sp;
+ if (info.load && info.native_memory_address && info.native_memory_size) {
+ LLDB_LOG(log, "reading \"{0}\" from native memory at {1:x}",
+ info.pathname, *info.native_memory_address);
+ if (native_process_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(*info.native_memory_size, 0);
+ Status error;
+ const size_t bytes_read = native_process_sp->ReadMemory(
+ *info.native_memory_address, data_sp->GetBytes(),
+ data_sp->GetByteSize(), error);
+ if (bytes_read != *info.native_memory_size) {
+ LLDB_LOG(log,
+ "failed to read \"{0}\" from native memory at {1:x}: {2}",
+ info.pathname, *info.native_memory_address, error);
+ data_sp.reset();
+ }
+ } else {
+ LLDB_LOG(log, "no native process available to read \"{0}\" at {1:x}",
+ info.pathname, *info.native_memory_address);
+ }
+ }
+
+ UUID uuid;
+ if (info.uuid_str)
+ uuid.SetFromStringRef(*info.uuid_str);
+
+ // Either a whole file, or a slice of a containing file.
+ DataExtractorSP extractor_sp;
+ if (data_sp)
+ extractor_sp = std::make_shared<DataExtractor>(data_sp);
+ ModuleSpec module_spec(FileSpec(info.pathname), uuid, extractor_sp);
+ // Select the target device image when a loaded file contains an offload
+ // bundle. An unload identifies an existing module and does not need this
+ // architecture filter.
+ if (info.load)
+ module_spec.GetArchitecture() = target.GetArchitecture();
+ if (info.file_offset)
+ module_spec.SetObjectOffset(*info.file_offset);
+ if (info.file_size)
+ module_spec.SetObjectSize(*info.file_size);
+
+ if (!info.load) {
+ ModuleList matching_module_list;
+ target.GetImages().FindModules(module_spec, matching_module_list);
+ matching_module_list.ForEach(
+ [this](const ModuleSP &module_sp) -> IterationAction {
+ UnloadSections(module_sp);
+ return IterationAction::Continue;
+ });
+ target.ModulesDidUnload(matching_module_list,
+ /*delete_locations=*/false);
+ continue;
+ }
+
+ ModuleSP module_sp =
+ target.GetOrCreateModule(module_spec, /*notify=*/false);
+ if (!module_sp)
+ continue;
+
+ bool changed = false;
+ if (info.load_address) {
+ module_sp->SetLoadAddress(target, *info.load_address,
+ /*value_is_offset=*/true, changed);
+ } else if (!info.loaded_sections.empty()) {
+ for (const AcceleratorSectionInfo § : info.loaded_sections) {
+ if (sect.names.empty())
+ continue;
+ SectionSP section_sp;
+ for (const std::string &name : sect.names) {
+ ConstString section_name(name);
+ if (section_sp)
+ section_sp =
+ section_sp->GetChildren().FindSectionByName(section_name);
+ else
+ section_sp =
+ module_sp->GetSectionList()->FindSectionByName(section_name);
+ if (!section_sp)
+ break;
+ }
+ if (section_sp)
+ changed |= target.SetSectionLoadAddress(section_sp, sect.load_address,
+ /*warn_multiple=*/true);
+ }
+ } else {
+ // No slide: load at the file addresses.
+ module_sp->SetLoadAddress(target, 0, /*value_is_offset=*/true, changed);
+ }
+
+ if (changed)
+ loaded_module_list.AppendIfNeeded(module_sp);
+ }
+
+ target.ModulesDidLoad(loaded_module_list);
+ return true;
+}
+
+ThreadPlanSP DynamicLoaderAcceleratorGDBRemote::GetStepThroughTrampolinePlan(
+ Thread &thread, bool stop_others) {
+ return ThreadPlanSP();
+}
+
+Status DynamicLoaderAcceleratorGDBRemote::CanLoadImage() {
+ return Status::FromErrorString("can't load images on accelerator targets");
+}
+
+void DynamicLoaderAcceleratorGDBRemote::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderAcceleratorGDBRemote::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+llvm::StringRef
+DynamicLoaderAcceleratorGDBRemote::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that gets shared library loads/unloads from "
+ "an lldb-server accelerator plugin.";
+}
diff --git a/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
new file mode 100644
index 00000000000000..b5080322ed714d
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_ACCELERATORGDBREMOTE_DYNAMICLOADERACCELERATORGDBREMOTE_H
+#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_ACCELERATORGDBREMOTE_DYNAMICLOADERACCELERATORGDBREMOTE_H
+
+#include "lldb/Target/DynamicLoader.h"
+
+/// Dynamic loader for accelerator (e.g. GPU) targets.
+///
+/// Accelerators don't set a rendezvous breakpoint the way SVR4 loaders do;
+/// their runtime tells the lldb-server plugin when libraries load or unload.
+/// This loader asks the server for that list via
+/// "jAcceleratorPluginGetDynamicLoaderLibraryInfo".
+class DynamicLoaderAcceleratorGDBRemote : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderAcceleratorGDBRemote(lldb_private::Process *process);
+
+ static void Initialize();
+ static void Terminate();
+ static llvm::StringRef GetPluginNameStatic() {
+ return "accelerator-gdb-remote";
+ }
+ static llvm::StringRef GetPluginDescriptionStatic();
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ void DidAttach() override;
+ void DidLaunch() override;
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+ lldb_private::Status CanLoadImage() override;
+
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+private:
+ /// Returns true if the server answered the packet.
+ bool LoadModulesFromGDBServer(bool full);
+};
+
+#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_ACCELERATORGDBREMOTE_DYNAMICLOADERACCELERATORGDBREMOTE_H
diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
index 01aba34b941697..97604bb4824db4 100644
--- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
+++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
@@ -5,6 +5,7 @@ set_property(DIRECTORY PROPERTY LLDB_TOLERATED_PLUGIN_DEPENDENCIES
TypeSystem
)
+add_subdirectory(AcceleratorGDBRemote)
add_subdirectory(Darwin-Kernel)
add_subdirectory(FreeBSD-Kernel)
add_subdirectory(MacOSX-DYLD)
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 1a5b5a2c534d84..283418ca0590ca 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -341,6 +341,30 @@ GDBRemoteCommunicationClient::AcceleratorBreakpointHit(
response.GetStringRef(), llvm::toString(hit_response.takeError()));
}
+std::optional<AcceleratorDynamicLoaderResponse>
+GDBRemoteCommunicationClient::GetAcceleratorDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args) {
+ StreamGDBRemote packet;
+ packet.PutCString("jAcceleratorPluginGetDynamicLoaderLibraryInfo:");
+ packet.PutAsJSON(args, /*hex_ascii=*/false);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
+ PacketResult::Success ||
+ response.IsErrorResponse())
+ return std::nullopt;
+
+ llvm::Expected<AcceleratorDynamicLoaderResponse> parsed =
+ llvm::json::parse<AcceleratorDynamicLoaderResponse>(
+ response.Peek(), "AcceleratorDynamicLoaderResponse");
+ if (!parsed) {
+ llvm::consumeError(parsed.takeError());
+ return std::nullopt;
+ }
+ return *parsed;
+}
+
bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() {
if (m_supports_not_sending_acks == eLazyBoolCalculate) {
m_send_acks = true;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index e38eb03f7593e0..984d4534836d29 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -395,6 +395,11 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
llvm::Expected<AcceleratorBreakpointHitResponse>
AcceleratorBreakpointHit(const AcceleratorBreakpointHitArgs &args);
+ /// Returns std::nullopt if the packet failed or the response did not parse.
+ std::optional<AcceleratorDynamicLoaderResponse>
+ GetAcceleratorDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args);
+
LazyBool SupportsAllocDeallocMemory() // const
{
// Uncomment this to have lldb pretend the debug server doesn't respond to
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 5466817d844c59..717fb8a22b32a4 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -1543,7 +1543,7 @@ GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo(
return SendErrorResponse(1);
ProcessInstanceInfo proc_info;
- if (!Host::GetProcessInfo(pid, proc_info))
+ if (!m_current_process->GetProcessInfo(proc_info))
return SendErrorResponse(1);
StreamString response;
@@ -4741,6 +4741,21 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::
if (!args)
return SendErrorResponse(args.takeError());
+ // On the accelerator connection, ask the process directly.
+ if (m_connection_accelerator_plugin) {
+ if (!m_current_process)
+ return SendErrorResponse(Status::FromErrorString("no current process"));
+ std::optional<AcceleratorDynamicLoaderResponse> response =
+ m_current_process->GetAcceleratorDynamicLoaderLibraryInfos(*args);
+ if (!response)
+ return SendErrorResponse(
+ Status::FromErrorString("dynamic loader library info not supported"));
+ StreamGDBRemote stream;
+ stream.PutAsJSON(*response, /*hex_ascii=*/false);
+ return SendPacketNoLock(stream.GetString());
+ }
+
+ // On the native connection, forward to the named accelerator plugin.
for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
m_accelerator_plugins) {
if (plugin_up->GetPluginName() == args->plugin_name) {
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 540f620be4c957..4c59c698f782ad 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -4670,8 +4670,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) {
+ std::string plugin_name;
+ if (std::optional<LLDBSettings> settings = m_gdb_comm.GetLLDBSettings())
+ plugin_name = settings->dyld_plugin_name;
+ m_dyld_up.reset(DynamicLoader::FindPlugin(this, plugin_name));
+ }
return m_dyld_up.get();
}
diff --git a/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py b/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
new file mode 100644
index 00000000000000..152c76ee9d968f
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
@@ -0,0 +1,157 @@
+"""
+Test the accelerator dynamic loader against a mock GDB server.
+
+The server selects the loader by name through jLLDBSettings. The loader
+then asks the server for the loaded libraries via
+jAcceleratorPluginGetDynamicLoaderLibraryInfo and loads them into the target.
+"""
+
+import json
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+DYLD_PACKET = "jAcceleratorPluginGetDynamicLoaderLibraryInfo:"
+
+
+class AcceleratorResponder(MockGDBServerResponder):
+ """Serves a fixed set of library infos, and counts how often it is asked."""
+
+ def __init__(
+ self,
+ library_infos,
+ dyld_plugin_name="accelerator-gdb-remote",
+ ):
+ MockGDBServerResponder.__init__(self)
+ self.library_infos = library_infos
+ self.dyld_plugin_name = dyld_plugin_name
+ self.dyld_queries = 0
+ self.last_dyld_packet = None
+
+ def qSupported(self, client_supported):
+ features = super().qSupported(client_supported) + ";qXfer:features:read+"
+ if self.dyld_plugin_name is not None:
+ features += ";lldb-settings+"
+ return features
+
+ def qXferRead(self, obj, annex, offset, length):
+ # An accelerator architecture has no built-in register set in lldb.
+ if obj == "features" and annex == "target.xml":
+ return (
+ """<?xml version="1.0"?>
+ <target version="1.0">
+ <feature name="org.llvm.accelerator">
+ <reg name="pc" bitsize="64" regnum="0" type="code_ptr" group="general"/>
+ </feature>
+ </target>""",
+ False,
+ )
+ return None, False
+
+ def readRegisters(self):
+ return "00" * 8
+
+ def other(self, packet):
+ if packet == "jLLDBSettings":
+ response = {
+ "dyld_plugin_name": self.dyld_plugin_name,
+ "accelerator_plugin_name": "mock",
+ "send_dyld_packet_to_accelerator": True,
+ }
+ return escape_binary(json.dumps(response, separators=(",", ":")))
+ if packet.startswith(DYLD_PACKET):
+ self.dyld_queries += 1
+ self.last_dyld_packet = packet
+ # "}" is the gdb-remote escape character.
+ return escape_binary(
+ json.dumps({"library_infos": self.library_infos}, separators=(",", ":"))
+ )
+ return ""
+
+
+class TestAcceleratorDynamicLoader(GDBRemoteTestBase):
+ def find_module(self, target, path):
+ basename = os.path.basename(path)
+ for i in range(target.GetNumModules()):
+ module = target.GetModuleAtIndex(i)
+ if module.GetFileSpec().GetFilename() == basename:
+ return module
+ return None
+
+ def connect_accelerator(self, library_infos):
+ self.server.responder = AcceleratorResponder(library_infos)
+ target = self.createTarget("accelerator.yaml")
+ process = self.connect(target)
+ self.assertTrue(process.IsValid(), "Process is valid")
+ self.assertIsNotNone(self.server.responder.last_dyld_packet)
+ self.assertIn('"full":true', self.server.responder.last_dyld_packet)
+ return target
+
+ def assert_text_loaded_at(self, target, module, expected):
+ section = module.FindSection(".text")
+ self.assertTrue(section.IsValid(), "library should have a .text section")
+ self.assertEqual(section.GetLoadAddress(target), expected)
+
+ def test_whole_file_library(self):
+ """A library given as a whole file is loaded at the reported address."""
+ lib = self.getBuildArtifact("accelerator_lib.so")
+ self.yaml2obj("accelerator_lib.yaml", lib)
+ target = self.connect_accelerator(
+ [{"pathname": lib, "load": True, "load_address": 0x10000000}]
+ )
+
+ module = self.find_module(target, lib)
+ self.assertIsNotNone(module, "library should be loaded into the target")
+ # load_address slides the file, so .text (file address 0x1000) lands
+ # 0x1000 past the base.
+ self.assert_text_loaded_at(target, module, 0x10001000)
+
+ def test_library_unload(self):
+ """An unload clears section addresses and broadcasts the module."""
+ lib = self.getBuildArtifact("accelerator_lib.so")
+ self.yaml2obj("accelerator_lib.yaml", lib)
+ target = self.createTarget("accelerator.yaml")
+ module = target.AddModule(lib, None, None)
+ self.assertTrue(module.IsValid(), "library should be added to the target")
+
+ text = module.FindSection(".text")
+ self.assertTrue(text.IsValid(), "library should have a .text section")
+ error = target.SetModuleLoadAddress(module, 0x10000000)
+ self.assertSuccess(error)
+ self.assert_text_loaded_at(target, module, 0x10001000)
+
+ listener = lldb.SBListener("accelerator-module-unload")
+ listened = target.GetBroadcaster().AddListener(
+ listener, lldb.SBTarget.eBroadcastBitModulesUnloaded
+ )
+ self.assertEqual(listened, lldb.SBTarget.eBroadcastBitModulesUnloaded)
+
+ self.server.responder = AcceleratorResponder([{"pathname": lib, "load": False}])
+ process = self.connect(target)
+ self.assertTrue(process.IsValid(), "Process is valid")
+ self.assertEqual(self.server.responder.dyld_queries, 1)
+ self.assertEqual(text.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS)
+
+ event = lldb.SBEvent()
+ self.assertTrue(listener.WaitForEvent(1, event), "got module unload event")
+ self.assertEqual(event.GetType(), lldb.SBTarget.eBroadcastBitModulesUnloaded)
+ self.assertEqual(lldb.SBTarget.GetNumModulesFromEvent(event), 1)
+ unloaded = lldb.SBTarget.GetModuleAtIndexFromEvent(0, event)
+ self.assertEqual(unloaded.GetFileSpec().GetFilename(), os.path.basename(lib))
+
+ def test_accelerator_lldb_settings_not_supported(self):
+ """The loader is not used unless the server selects it by name."""
+ self.server.responder = AcceleratorResponder([], dyld_plugin_name=None)
+ target = self.createTarget("accelerator.yaml")
+ process = self.connect(target)
+ self.assertTrue(process.IsValid(), "Process is valid")
+
+ self.assertEqual(
+ self.server.responder.dyld_queries,
+ 0,
+ "the target architecture must not select the accelerator loader",
+ )
+ self.assertIsNone(self.server.responder.last_dyld_packet)
diff --git a/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml b/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
new file mode 100644
index 00000000000000..2c4fe0fcd752c0
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
@@ -0,0 +1,23 @@
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AMDGPU
+ OSABI: ELFOSABI_AMDGPU_HSA
+ ABIVersion: 0x1
+ Flags: [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ AddressAlign: 0x1000
+ Content: "00000000"
+ProgramHeaders:
+ - Type: PT_LOAD
+ Flags: [ PF_X, PF_R ]
+ VAddr: 0x1000
+ Align: 0x1000
+ FirstSec: .text
+ LastSec: .text
diff --git a/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml b/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
new file mode 100644
index 00000000000000..ccdd8b34aa929e
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
@@ -0,0 +1,23 @@
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_AMDGPU
+ OSABI: ELFOSABI_AMDGPU_HSA
+ ABIVersion: 0x1
+ Flags: [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ AddressAlign: 0x1000
+ Content: "00000000"
+ProgramHeaders:
+ - Type: PT_LOAD
+ Flags: [ PF_X, PF_R ]
+ VAddr: 0x1000
+ Align: 0x1000
+ FirstSec: .text
+ LastSec: .text
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorDynamicLoader.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorDynamicLoader.py
new file mode 100644
index 00000000000000..dc325c737fd36b
--- /dev/null
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorDynamicLoader.py
@@ -0,0 +1,148 @@
+"""End-to-end test for the mock accelerator's dynamic loader."""
+
+import os
+
+import lldb
+from lldbsuite.test import configuration
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class MockAcceleratorDynamicLoaderTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ super().setUp()
+ if "mock-accelerator" not in configuration.enabled_plugins:
+ self.skipTest("mock-accelerator plugin is not enabled")
+
+ def set_mock_env(self, name, value):
+ """Set an environment variable inherited by lldb-server."""
+ previous = os.environ.get(name)
+ os.environ[name] = value
+
+ def restore():
+ if previous is None:
+ os.environ.pop(name, None)
+ else:
+ os.environ[name] = previous
+
+ self.addTearDownHook(restore)
+
+ def accelerator_target(self, native_target):
+ for candidate in self.dbg:
+ if candidate != native_target:
+ return candidate
+ return None
+
+ def run_library_test(self, dyld_connection, use_native_memory=False):
+ self.build()
+
+ library_path = self.getBuildArtifact("accelerator_lib.so")
+ self.yaml2obj("accelerator_lib.yaml", library_path)
+ with open(library_path, "rb") as library_file:
+ library_data = library_file.read()
+
+ # No installed platform claims AMDGPU, so retain the selected host
+ # platform while creating a target with the requested architecture.
+ self.set_mock_env("LLDB_MOCK_ACCELERATOR_PLATFORM", "")
+ self.set_mock_env("LLDB_MOCK_ACCELERATOR_TRIPLE", "amdgpu-amd-amdhsa--gfx942")
+ self.set_mock_env("LLDB_MOCK_ACCELERATOR_DYLD_CONNECTION", dyld_connection)
+ reported_path = library_path
+ if dyld_connection == "accelerator":
+ if use_native_memory:
+ reported_path = self.getBuildArtifact("missing-accelerator-lib.so")
+ self.assertFalse(os.path.exists(reported_path))
+ self.assertLessEqual(len(library_data), 8192)
+ self.set_mock_env(
+ "LLDB_MOCK_ACCELERATOR_NATIVE_MEMORY_SIZE",
+ str(len(library_data)),
+ )
+ self.set_mock_env("LLDB_MOCK_ACCELERATOR_LIBRARY_PATH", reported_path)
+ else:
+ self.set_mock_env("LLDB_MOCK_ACCELERATOR_NATIVE_LIBRARY_PATH", library_path)
+
+ # This test does not exercise instruction decoding. Avoid asking the
+ # generic AMDGPU target to disassemble its synthetic stop PC.
+ self.runCmd("settings set stop-disassembly-display never")
+
+ native_target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(native_target, VALID_TARGET)
+
+ # The first stop initializes the plugin. Continuing to its connection
+ # hook synchronously creates the accelerator target and queries for its
+ # loaded libraries using the selected connection.
+ native_process = native_target.LaunchSimple(
+ None, None, self.get_process_working_directory()
+ )
+ self.assertTrue(native_process, PROCESS_IS_VALID)
+ self.assertState(native_process.GetState(), lldb.eStateStopped)
+ if use_native_memory:
+ symbols = native_target.FindSymbols("mock_gpu_accelerator_image")
+ self.assertEqual(symbols.GetSize(), 1)
+ symbol = symbols.GetContextAtIndex(0).GetSymbol()
+ image_address = symbol.GetStartAddress().GetLoadAddress(native_target)
+ self.assertNotEqual(image_address, lldb.LLDB_INVALID_ADDRESS)
+ error = lldb.SBError()
+ bytes_written = native_process.WriteMemory(
+ image_address, library_data, error
+ )
+ self.assertSuccess(error)
+ self.assertEqual(bytes_written, len(library_data))
+ native_process.Continue()
+ self.assertState(native_process.GetState(), lldb.eStateStopped)
+
+ self.assertEqual(self.dbg.GetNumTargets(), 2)
+ accelerator_target = self.accelerator_target(native_target)
+ self.assertIsNotNone(accelerator_target)
+ self.assertTrue(accelerator_target.IsValid())
+ accelerator_process = accelerator_target.GetProcess()
+ self.assertTrue(accelerator_process.IsValid())
+ self.assertState(accelerator_process.GetState(), lldb.eStateStopped)
+
+ self.dbg.SetSelectedTarget(accelerator_target)
+ self.expect(
+ "process plugin packet send jLLDBSettings",
+ substrs=[
+ '"accelerator_plugin_name":"mock"',
+ '"dyld_plugin_name":"accelerator-gdb-remote"',
+ '"send_dyld_packet_to_accelerator":'
+ + ("true" if dyld_connection == "accelerator" else "false"),
+ ],
+ )
+
+ module = None
+ library_basename = os.path.basename(reported_path)
+ for candidate in accelerator_target.module_iter():
+ if candidate.GetFileSpec().GetFilename() == library_basename:
+ module = candidate
+ break
+ self.assertIsNotNone(module, "accelerator library should be loaded")
+
+ text = module.FindSection(".text")
+ self.assertTrue(text.IsValid())
+ expected_load_address = (
+ 0x10001000 if dyld_connection == "accelerator" else 0x7F001000
+ )
+ self.assertEqual(text.GetLoadAddress(accelerator_target), expected_load_address)
+
+ @skipIfRemote
+ @skipIfDarwin # qProcessInfo cannot encode an AMDGPU architecture on Darwin.
+ @add_test_categories(["llgs"])
+ def test_library_from_accelerator_process(self):
+ """The accelerator loader queries the second LLGS process directly."""
+ self.run_library_test("accelerator")
+
+ @skipIfRemote
+ @skipIfDarwin # qProcessInfo cannot encode an AMDGPU architecture on Darwin.
+ @add_test_categories(["llgs"])
+ def test_library_from_native_plugin(self):
+ """The accelerator loader routes through the native LLGS plugin."""
+ self.run_library_test("native")
+
+ @skipIfRemote
+ @skipIfDarwin # qProcessInfo cannot encode an AMDGPU architecture on Darwin.
+ @add_test_categories(["llgs"])
+ def test_library_from_native_memory(self):
+ """An image in native memory is loaded over the accelerator connection."""
+ self.run_library_test("accelerator", use_native_memory=True)
diff --git a/lldb/test/API/accelerator/mock/accelerator_lib.yaml b/lldb/test/API/accelerator/mock/accelerator_lib.yaml
new file mode 100644
index 00000000000000..ccdd8b34aa929e
--- /dev/null
+++ b/lldb/test/API/accelerator/mock/accelerator_lib.yaml
@@ -0,0 +1,23 @@
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_AMDGPU
+ OSABI: ELFOSABI_AMDGPU_HSA
+ ABIVersion: 0x1
+ Flags: [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ AddressAlign: 0x1000
+ Content: "00000000"
+ProgramHeaders:
+ - Type: PT_LOAD
+ Flags: [ PF_X, PF_R ]
+ VAddr: 0x1000
+ Align: 0x1000
+ FirstSec: .text
+ LastSec: .text
diff --git a/lldb/test/API/accelerator/mock/main.c b/lldb/test/API/accelerator/mock/main.c
index 18aed944b8d8fb..5bafe26ba231e4 100644
--- a/lldb/test/API/accelerator/mock/main.c
+++ b/lldb/test/API/accelerator/mock/main.c
@@ -1,5 +1,7 @@
// The mock accelerator plugin sets its breakpoints on these dedicated, uniquely
// named functions.
+unsigned char mock_gpu_accelerator_image[8192];
+
void mock_gpu_accelerator_initialize(void) {}
int mock_gpu_accelerator_compute(int x) { return x * 2; }
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index 069bee0e4ef52b..3d0fb3ec747f30 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -31,7 +31,7 @@ using namespace lldb_private::lldb_server;
using namespace lldb_private::process_gdb_remote;
// Read a mock-accelerator setting from an environment variable so tests can
-// configure the connection the mock advertises; falls back to default_value.
+// configure the mock; falls back to default_value.
static std::string GetMockEnvSetting(const char *env_var,
std::string default_value) {
if (const char *value = ::getenv(env_var))
@@ -60,6 +60,16 @@ llvm::StringRef LLDBServerMockAcceleratorPlugin::GetPluginName() {
return "mock";
}
+std::optional<LLDBSettings> LLDBServerMockAcceleratorPlugin::GetLLDBSettings() {
+ LLDBSettings settings;
+ settings.dyld_plugin_name = "accelerator-gdb-remote";
+ settings.accelerator_plugin_name = GetPluginName();
+ settings.send_dyld_packet_to_accelerator =
+ GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_DYLD_CONNECTION",
+ "accelerator") != "native";
+ return settings;
+}
+
std::optional<AcceleratorActions>
LLDBServerMockAcceleratorPlugin::GetInitializeActions() {
AcceleratorActions actions = GetNewAcceleratorAction();
@@ -67,6 +77,7 @@ LLDBServerMockAcceleratorPlugin::GetInitializeActions() {
// Set a breakpoint by function name (no shared library scope) on the
// dedicated "mock_gpu_accelerator_initialize" hook and ask for the load
// address of "mock_gpu_accelerator_compute" to be delivered when it is hit.
+ // The native-memory test also requests its image buffer's load address.
// Using a dedicated, uniquely named function (rather than "main") keeps this
// mock from affecting other inferiors that lldb-server launches when the
// plugin is compiled in.
@@ -75,6 +86,9 @@ LLDBServerMockAcceleratorPlugin::GetInitializeActions() {
bp.by_name = AcceleratorBreakpointByName{std::nullopt,
"mock_gpu_accelerator_initialize"};
bp.symbol_names.push_back("mock_gpu_accelerator_compute");
+ if (!GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_NATIVE_MEMORY_SIZE", "")
+ .empty())
+ bp.symbol_names.push_back("mock_gpu_accelerator_image");
actions.breakpoints.push_back(std::move(bp));
return actions;
@@ -87,6 +101,8 @@ LLDBServerMockAcceleratorPlugin::BreakpointWasHit(
switch (args.breakpoint.identifier) {
case kBreakpointIDInitialize: {
+ m_native_memory_address = args.GetSymbolValue("mock_gpu_accelerator_image");
+
// The initialize breakpoint was hit. Arm the remaining test breakpoints:
// two more breakpoint types, plus the connection hook now that the
// accelerator has initialized.
@@ -150,10 +166,24 @@ std::optional<AcceleratorConnectionInfo>
LLDBServerMockAcceleratorPlugin::CreateConnection() {
Log *log = GetLog(GDBRLog::Plugin);
+ std::string triple =
+ GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_TRIPLE",
+ HostInfo::GetArchitecture().GetTriple().str());
+ std::string dynamic_loader_library_path = GetMockEnvSetting(
+ "LLDB_MOCK_ACCELERATOR_LIBRARY_PATH", "/path/to/lib.so");
+ std::optional<uint64_t> native_memory_size;
+ std::string native_memory_size_str =
+ GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_NATIVE_MEMORY_SIZE", "");
+ uint64_t size;
+ if (!native_memory_size_str.empty() &&
+ !llvm::StringRef(native_memory_size_str).getAsInteger(0, size))
+ native_memory_size = size;
+
// An in-process gdb-remote server backed by a synthetic
// ProcessMockAccelerator; no real process is launched.
- m_process_manager_up =
- std::make_unique<ProcessMockAccelerator::Manager>(m_mock_main_loop);
+ m_process_manager_up = std::make_unique<ProcessMockAccelerator::Manager>(
+ m_mock_main_loop, ArchSpec(triple), dynamic_loader_library_path,
+ m_native_memory_address, native_memory_size);
m_accelerator_gdb_server = std::make_unique<GDBRemoteCommunicationServerLLGS>(
m_mock_main_loop, *m_process_manager_up);
m_accelerator_gdb_server->SetConnectionAcceleratorPlugin(*this);
@@ -218,9 +248,7 @@ LLDBServerMockAcceleratorPlugin::CreateConnection() {
// invalid-platform and incompatible-triple failures.
info.platform_name =
GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_PLATFORM", "host");
- info.triple =
- GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_TRIPLE",
- HostInfo::GetArchitecture().GetTriple().str());
+ info.triple = std::move(triple);
info.synchronous = true;
return info;
}
@@ -230,7 +258,8 @@ LLDBServerMockAcceleratorPlugin::GetDynamicLoaderLibraryInfos(
const AcceleratorDynamicLoaderArgs &args) {
AcceleratorDynamicLoaderResponse response;
AcceleratorDynamicLoaderLibraryInfo lib;
- lib.pathname = "/path/to/lib.so";
+ lib.pathname = GetMockEnvSetting("LLDB_MOCK_ACCELERATOR_NATIVE_LIBRARY_PATH",
+ "/path/to/lib.so");
lib.load = true;
lib.load_address = 0x7f000000;
response.library_infos.push_back(std::move(lib));
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
index 36f6aa2500ba80..a76197352aa25a 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -15,6 +15,7 @@
#include "lldb/Host/MainLoopBase.h"
#include <memory>
+#include <optional>
#include <vector>
namespace lldb_private {
@@ -30,6 +31,7 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
~LLDBServerMockAcceleratorPlugin() override;
llvm::StringRef GetPluginName() override;
+ std::optional<LLDBSettings> GetLLDBSettings() override;
std::optional<AcceleratorActions> GetInitializeActions() override;
llvm::Expected<AcceleratorBreakpointHitResponse>
BreakpointWasHit(AcceleratorBreakpointHitArgs &args) override;
@@ -44,7 +46,8 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
std::optional<AcceleratorConnectionInfo> CreateConnection();
// Breakpoint set during initialization, by function name with no shared
- // library. Requests the "compute" symbol value when hit.
+ // library. Requests the "compute" symbol value and, when configured, the
+ // native-memory image buffer's value when hit.
static constexpr int64_t kBreakpointIDInitialize = 1;
// Breakpoint set by address, using the "compute" symbol value delivered when
// the initialize breakpoint was hit.
@@ -62,6 +65,7 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
HostThread m_mock_main_loop_thread;
std::unique_ptr<TCPSocket> m_listen_socket;
std::vector<MainLoopBase::ReadHandleUP> m_read_handles;
+ std::optional<uint64_t> m_native_memory_address;
};
} // namespace lldb_server
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
index 6ae74d290d7150..ec270ba1832a17 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -9,7 +9,6 @@
#include "ProcessMockAccelerator.h"
#include "ThreadMockAccelerator.h"
-#include "lldb/Host/HostInfo.h"
#include "lldb/Host/ProcessLaunchInfo.h"
#include "llvm/Support/Error.h"
@@ -26,7 +25,9 @@ static constexpr lldb::tid_t kMockTid = 3456;
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
ProcessMockAccelerator::Manager::Launch(ProcessLaunchInfo &launch_info,
NativeDelegate &native_delegate) {
- return std::make_unique<ProcessMockAccelerator>(kMockPid, native_delegate);
+ return std::make_unique<ProcessMockAccelerator>(
+ kMockPid, m_arch, m_dynamic_loader_library_path, m_native_memory_address,
+ m_native_memory_size, native_delegate);
}
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
@@ -36,9 +37,15 @@ ProcessMockAccelerator::Manager::Attach(lldb::pid_t pid,
"accelerator process");
}
-ProcessMockAccelerator::ProcessMockAccelerator(lldb::pid_t pid,
- NativeDelegate &delegate)
- : NativeProcessProtocol(pid, /*terminal_fd=*/-1, delegate) {
+ProcessMockAccelerator::ProcessMockAccelerator(
+ lldb::pid_t pid, ArchSpec arch, std::string dynamic_loader_library_path,
+ std::optional<uint64_t> native_memory_address,
+ std::optional<uint64_t> native_memory_size, NativeDelegate &delegate)
+ : NativeProcessProtocol(pid, /*terminal_fd=*/-1, delegate),
+ m_arch(std::move(arch)),
+ m_dynamic_loader_library_path(std::move(dynamic_loader_library_path)),
+ m_native_memory_address(native_memory_address),
+ m_native_memory_size(native_memory_size) {
m_state = eStateStopped;
UpdateThreads();
}
@@ -99,8 +106,6 @@ size_t ProcessMockAccelerator::UpdateThreads() {
}
const ArchSpec &ProcessMockAccelerator::GetArchitecture() const {
- if (!m_arch.IsValid())
- m_arch = HostInfo::GetArchitecture();
return m_arch;
}
@@ -125,7 +130,29 @@ ProcessMockAccelerator::GetFileLoadAddress(const llvm::StringRef &file_name,
return Status::FromErrorString("unimplemented");
}
+bool ProcessMockAccelerator::GetProcessInfo(ProcessInstanceInfo &proc_info) {
+ proc_info.SetProcessID(m_pid);
+ proc_info.SetArchitecture(GetArchitecture());
+ return true;
+}
+
std::vector<AddressSpaceInfo> ProcessMockAccelerator::GetAddressSpaces() {
return {{"global", 1, /*is_thread_specific=*/false},
{"local", 2, /*is_thread_specific=*/true}};
}
+
+std::optional<AcceleratorDynamicLoaderResponse>
+ProcessMockAccelerator::GetAcceleratorDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args) {
+ AcceleratorDynamicLoaderResponse response;
+ AcceleratorDynamicLoaderLibraryInfo info;
+ info.pathname = m_dynamic_loader_library_path;
+ if (m_native_memory_address && m_native_memory_size) {
+ info.native_memory_address = m_native_memory_address;
+ info.native_memory_size = m_native_memory_size;
+ }
+ info.load = true;
+ info.load_address = 0x10000000;
+ response.library_infos.push_back(std::move(info));
+ return response;
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
index 7b88bd36f2ea53..6e15b701770af4 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
@@ -12,6 +12,10 @@
#include "lldb/Host/common/NativeProcessProtocol.h"
#include "lldb/Utility/ArchSpec.h"
+#include <optional>
+#include <string>
+#include <utility>
+
namespace lldb_private {
namespace lldb_server {
@@ -22,7 +26,14 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
public:
class Manager : public NativeProcessProtocol::Manager {
public:
- using NativeProcessProtocol::Manager::Manager;
+ Manager(MainLoop &mainloop, ArchSpec arch,
+ std::string dynamic_loader_library_path,
+ std::optional<uint64_t> native_memory_address,
+ std::optional<uint64_t> native_memory_size)
+ : NativeProcessProtocol::Manager(mainloop), m_arch(std::move(arch)),
+ m_dynamic_loader_library_path(std::move(dynamic_loader_library_path)),
+ m_native_memory_address(native_memory_address),
+ m_native_memory_size(native_memory_size) {}
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
Launch(ProcessLaunchInfo &launch_info,
@@ -34,9 +45,19 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
Extension GetSupportedExtensions() const override {
return Extension::address_spaces | Extension::lldb_settings;
}
+
+ private:
+ ArchSpec m_arch;
+ std::string m_dynamic_loader_library_path;
+ std::optional<uint64_t> m_native_memory_address;
+ std::optional<uint64_t> m_native_memory_size;
};
- ProcessMockAccelerator(lldb::pid_t pid, NativeDelegate &delegate);
+ ProcessMockAccelerator(lldb::pid_t pid, ArchSpec arch,
+ std::string dynamic_loader_library_path,
+ std::optional<uint64_t> native_memory_address,
+ std::optional<uint64_t> native_memory_size,
+ NativeDelegate &delegate);
Status Resume(const ResumeActionList &resume_actions) override;
Status Halt() override;
@@ -64,10 +85,19 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
Status GetFileLoadAddress(const llvm::StringRef &file_name,
lldb::addr_t &load_addr) override;
+ bool GetProcessInfo(ProcessInstanceInfo &proc_info) override;
+
std::vector<AddressSpaceInfo> GetAddressSpaces() override;
+ std::optional<AcceleratorDynamicLoaderResponse>
+ GetAcceleratorDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args) override;
+
private:
- mutable ArchSpec m_arch;
+ ArchSpec m_arch;
+ std::string m_dynamic_loader_library_path;
+ std::optional<uint64_t> m_native_memory_address;
+ std::optional<uint64_t> m_native_memory_size;
};
} // namespace lldb_server
More information about the lldb-commits
mailing list