[Lldb-commits] [lldb] [lldb-server] Add dynamic loader support to accelerator plugin protocol (PR #214564)
satyanarayana reddy janga via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 19 09:57:51 PDT 2026
https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/214564
>From 92201e957f5af130f46ca1b0f5787727f688ce74 Mon Sep 17 00:00:00 2001
From: satya janga <satyajanga at fb.com>
Date: Wed, 3 Jun 2026 13:20:04 -0700
Subject: [PATCH] [lldb-server] Add dynamic loader support to accelerator
plugin protocol
Add jAcceleratorPluginGetDynamicLoaderLibraryInfo packet for querying
shared library information from accelerator plugins. This includes:
- AcceleratorSectionInfo for per-section load addresses
- AcceleratorDynamicLoaderLibraryInfo with pathname, UUID, load address,
native memory location, file offset/size, and loaded sections
- AcceleratorDynamicLoaderArgs and AcceleratorDynamicLoaderResponse
- GetDynamicLoaderLibraryInfos virtual method on the plugin interface
- LLGS packet handler with plugin name routing
- Mock plugin implementation returning a test library
- Unit tests for all new JSON serialization types
- API test verifying the packet round-trip
---
lldb/docs/resources/lldbgdbremote.md | 66 +++++++++++++++
.../Utility/AcceleratorGDBRemotePackets.h | 66 ++++++++++++++-
.../lldb/Utility/StringExtractorGDBRemote.h | 1 +
.../GDBRemoteCommunicationServerLLGS.cpp | 33 ++++++++
.../GDBRemoteCommunicationServerLLGS.h | 3 +
.../gdb-remote/LLDBServerAcceleratorPlugin.h | 5 ++
.../Utility/AcceleratorGDBRemotePackets.cpp | 80 +++++++++++++++---
.../Utility/StringExtractorGDBRemote.cpp | 2 +
.../mock/TestMockAcceleratorPackets.py | 55 +++++++++++++
.../Mock/LLDBServerMockAcceleratorPlugin.cpp | 12 +++
.../Mock/LLDBServerMockAcceleratorPlugin.h | 3 +
.../AcceleratorGDBRemotePacketsTest.cpp | 81 +++++++++++++++++++
12 files changed, 395 insertions(+), 12 deletions(-)
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 93090c19c5ec0..2b64b663c9461 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -2862,3 +2862,69 @@ STUB REPLIES: {"disable_bp":true,"auto_resume_native":false,"actions":{"plugin_
**Priority To Implement:** Required for hardware accelerator debugging
support. Not needed for non-hardware-accelerator debugging.
+
+### jAcceleratorPluginGetDynamicLoaderLibraryInfo
+
+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`.
+
+```
+LLDB SENDS: jAcceleratorPluginGetDynamicLoaderLibraryInfo:<json>
+STUB REPLIES: <json_response>
+```
+
+The request JSON has the following fields:
+
+| Key | Type | Description |
+|---------------|--------|-------------|
+| `plugin_name` | string | Name of the accelerator plugin to query. |
+| `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
+already up to date ask only for the delta instead of re-receiving the whole
+list on every stop. It is the plugin that decides what "changed since the last
+query" means, and the plugin that keeps that state.
+
+Because that state lives in the plugin and is not per-client, a client cannot
+assume it starts from a known point: an earlier client may have consumed the
+pending changes with `full=false`. The first query of a session must therefore
+use `full=true`, and a client should also use it whenever it discards its own
+module list and needs to rebuild it.
+
+The response JSON is an object with a single `library_infos` key, holding an
+array of library info objects:
+
+| Key | Type | Description |
+|-------------------------|--------|-------------|
+| `pathname` | string | Path to the object file. |
+| `load` | bool | True when the library is being loaded, false when it is being unloaded. |
+| `load_address` | int | (optional) Base address the whole object file is slid to. |
+| `loaded_sections` | array | (optional) Per-section load addresses, for object files whose sections load at independent addresses. Each entry has `names` (the section name, or a path of nested section names to descend) and `load_address`. |
+| `uuid` | string | (optional) UUID of the object file, if the plugin knows it. |
+| `native_memory_address` | int | (optional) Address **in the native (host) process** where the object file image can be read, for a library that only exists in memory. |
+| `native_memory_size` | int | (optional) Size of that in-memory image. |
+| `file_offset` | int | (optional) Byte offset of the object file within `pathname`, for a library embedded in a containing file. |
+| `file_size` | int | (optional) Size of the object file within that containing file. |
+
+`load_address`, `loaded_sections` and neither-of-the-two are three different
+requests, and are resolved in that order:
+
+* `load_address` present: slide the whole object file to that address.
+* otherwise `loaded_sections` non-empty: load only the named sections, each at
+ its own address.
+* otherwise: load at the file addresses with no slide.
+
+An absent `loaded_sections` and an empty `loaded_sections` therefore mean the
+same thing here: no per-section addresses were supplied. A plugin that wants
+sections loaded must send a non-empty array.
+
+Example:
+```
+LLDB SENDS: jAcceleratorPluginGetDynamicLoaderLibraryInfo:{"plugin_name":"mock","full":true}
+STUB REPLIES: {"library_infos":[{"pathname":"/path/to/lib.so","load":true,"load_address":2130706432,"loaded_sections":[]}]}
+```
+
+**Priority To Implement:** Required for hardware accelerator debugging
+support. Not needed for non-hardware-accelerator debugging.
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index faa7f5575f157..b804b216f51d0 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -9,6 +9,7 @@
#ifndef LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
#define LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
+#include "lldb/lldb-types.h"
#include "llvm/Support/JSON.h"
#include <cstdint>
#include <optional>
@@ -21,7 +22,7 @@ struct SymbolValue {
/// Symbol name as requested in AcceleratorBreakpointInfo::symbol_names.
std::string name;
/// Load address of the symbol in the native process, or nullopt if not found.
- std::optional<uint64_t> value;
+ std::optional<lldb::addr_t> value;
};
bool fromJSON(const llvm::json::Value &value, SymbolValue &data,
@@ -41,7 +42,7 @@ llvm::json::Value toJSON(const AcceleratorBreakpointByName &data);
struct AcceleratorBreakpointByAddress {
/// Load address in the native debug target.
- uint64_t load_address = 0;
+ lldb::addr_t load_address = 0;
};
bool fromJSON(const llvm::json::Value &value,
@@ -78,7 +79,7 @@ struct AcceleratorBreakpointHitArgs {
AcceleratorBreakpointInfo breakpoint;
std::vector<SymbolValue> symbol_values;
- std::optional<uint64_t> GetSymbolValue(llvm::StringRef symbol_name) const;
+ std::optional<lldb::addr_t> GetSymbolValue(llvm::StringRef symbol_name) const;
};
bool fromJSON(const llvm::json::Value &value,
@@ -166,6 +167,65 @@ bool fromJSON(const llvm::json::Value &value,
AcceleratorBreakpointHitResponse &data, llvm::json::Path path);
llvm::json::Value toJSON(const AcceleratorBreakpointHitResponse &data);
+struct AcceleratorSectionInfo {
+ /// Each name is looked up as a child of the previous one, e.g.
+ /// ["PT_LOAD[0]", ".text"].
+ std::vector<std::string> names;
+ lldb::addr_t load_address = 0;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorSectionInfo &data,
+ llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorSectionInfo &data);
+
+struct AcceleratorDynamicLoaderLibraryInfo {
+ std::string pathname;
+ std::optional<std::string> uuid_str;
+ /// False means unload.
+ bool load = true;
+ /// Slides the whole object file. If unset, use \a loaded_sections or the
+ /// file addresses.
+ std::optional<lldb::addr_t> load_address;
+ /// Used when sections load at independent addresses. Absent and empty mean
+ /// the same thing: no per-section addresses were supplied.
+ std::vector<AcceleratorSectionInfo> loaded_sections;
+ /// Where the image can be read in the native process, meaning the host
+ /// process driving the accelerator, not the accelerator itself. Set for a
+ /// library that only exists in memory.
+ std::optional<lldb::addr_t> native_memory_address;
+ std::optional<uint64_t> native_memory_size;
+ /// Slice of \a pathname holding the object file, when embedded in a
+ /// container.
+ std::optional<uint64_t> file_offset;
+ std::optional<uint64_t> file_size;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorDynamicLoaderLibraryInfo &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorDynamicLoaderLibraryInfo &data);
+
+/// Arguments for the jAcceleratorPluginGetDynamicLoaderLibraryInfo packet.
+struct AcceleratorDynamicLoaderArgs {
+ std::string plugin_name;
+ /// If false, return only what changed since the last query. That state lives
+ /// in the plugin rather than per-client, so the first query of a session
+ /// must use true.
+ bool full = true;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorDynamicLoaderArgs &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorDynamicLoaderArgs &data);
+
+/// Response from the jAcceleratorPluginGetDynamicLoaderLibraryInfo packet.
+struct AcceleratorDynamicLoaderResponse {
+ std::vector<AcceleratorDynamicLoaderLibraryInfo> library_infos;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ AcceleratorDynamicLoaderResponse &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorDynamicLoaderResponse &data);
+
} // namespace lldb_private
#endif // LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 624a2febe857e..0236891dfcde1 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -176,6 +176,7 @@ class StringExtractorGDBRemote : public StringExtractor {
eServerPacketType_jMultiBreakpoint,
eServerPacketType_jAcceleratorPluginInitialize,
eServerPacketType_jAcceleratorPluginBreakpointHit,
+ eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
eServerPacketType_qMemTags, // read memory tags
eServerPacketType_QMemTags, // write memory tags
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 2e824282079b4..791c02fd96b20 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -233,6 +233,11 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
eServerPacketType_jAcceleratorPluginBreakpointHit,
&GDBRemoteCommunicationServerLLGS::
Handle_jAcceleratorPluginBreakpointHit);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::
+ eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
+ &GDBRemoteCommunicationServerLLGS::
+ Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
&GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -4643,3 +4648,31 @@ GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginBreakpointHit(
return SendErrorResponse(
Status::FromErrorString("unknown accelerator plugin name"));
}
+
+GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::
+ Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo(
+ StringExtractorGDBRemote &packet) {
+ packet.ConsumeFront("jAcceleratorPluginGetDynamicLoaderLibraryInfo:");
+ llvm::Expected<AcceleratorDynamicLoaderArgs> args =
+ llvm::json::parse<AcceleratorDynamicLoaderArgs>(
+ packet.Peek(), "AcceleratorDynamicLoaderArgs");
+ if (!args)
+ return SendErrorResponse(args.takeError());
+
+ for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
+ m_accelerator_plugins) {
+ if (plugin_up->GetPluginName() == args->plugin_name) {
+ std::optional<AcceleratorDynamicLoaderResponse> response =
+ plugin_up->GetDynamicLoaderLibraryInfos(*args);
+ if (response) {
+ StreamGDBRemote stream;
+ stream.PutAsJSON(*response, /*hex_ascii=*/false);
+ return SendPacketNoLock(stream.GetString());
+ }
+ return SendErrorResponse(
+ Status::FromErrorString("no dynamic loader info available"));
+ }
+ }
+ return SendErrorResponse(
+ Status::FromErrorString("unknown accelerator plugin name"));
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index e5b4c9ec0bed0..3c8afe699bbaf 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -302,6 +302,9 @@ class GDBRemoteCommunicationServerLLGS
PacketResult
Handle_jAcceleratorPluginBreakpointHit(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo(
+ StringExtractorGDBRemote &packet);
+
void SetCurrentThreadID(lldb::tid_t tid);
lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
index f16ee82a39d95..2e585d9a64c6a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
@@ -47,6 +47,11 @@ class LLDBServerAcceleratorPlugin {
++m_accelerator_action_identifier);
}
+ virtual std::optional<AcceleratorDynamicLoaderResponse>
+ GetDynamicLoaderLibraryInfos(const AcceleratorDynamicLoaderArgs &args) {
+ return std::nullopt;
+ }
+
protected:
GDBServer &m_native_gdb_server;
MainLoop &m_native_main_loop;
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
index 3b9edada64c65..169e3ac8017b5 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -13,7 +13,7 @@ using namespace llvm::json;
namespace lldb_private {
-bool fromJSON(const Value &value, SymbolValue &data, Path path) {
+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 +22,7 @@ json::Value toJSON(const SymbolValue &data) {
return Object{{"name", data.name}, {"value", data.value}};
}
-bool fromJSON(const Value &value, AcceleratorBreakpointByName &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointByName &data,
Path path) {
ObjectMapper o(value, path);
return o && o.mapOptional("shlib", data.shlib) &&
@@ -33,7 +33,7 @@ json::Value toJSON(const AcceleratorBreakpointByName &data) {
return Object{{"shlib", data.shlib}, {"function_name", data.function_name}};
}
-bool fromJSON(const Value &value, AcceleratorBreakpointByAddress &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointByAddress &data,
Path path) {
ObjectMapper o(value, path);
return o && o.map("load_address", data.load_address);
@@ -43,7 +43,8 @@ json::Value toJSON(const AcceleratorBreakpointByAddress &data) {
return Object{{"load_address", static_cast<int64_t>(data.load_address)}};
}
-bool fromJSON(const Value &value, AcceleratorBreakpointInfo &data, Path path) {
+bool fromJSON(const json::Value &value, AcceleratorBreakpointInfo &data,
+ Path path) {
ObjectMapper o(value, path);
return o && o.map("identifier", data.identifier) &&
o.mapOptional("by_name", data.by_name) &&
@@ -60,7 +61,7 @@ json::Value toJSON(const AcceleratorBreakpointInfo &data) {
};
}
-bool fromJSON(const Value &value, AcceleratorBreakpointHitArgs &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointHitArgs &data,
Path path) {
ObjectMapper o(value, path);
return o && o.map("plugin_name", data.plugin_name) &&
@@ -76,7 +77,7 @@ json::Value toJSON(const AcceleratorBreakpointHitArgs &data) {
};
}
-std::optional<uint64_t>
+std::optional<lldb::addr_t>
AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef symbol_name) const {
auto it = llvm::find_if(symbol_values, [&](const SymbolValue &symbol) {
return symbol.name == symbol_name;
@@ -86,7 +87,8 @@ AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef symbol_name) const {
return std::nullopt;
}
-bool fromJSON(const Value &value, AcceleratorConnectionInfo &data, Path path) {
+bool fromJSON(const json::Value &value, AcceleratorConnectionInfo &data,
+ Path path) {
ObjectMapper o(value, path);
return o && o.map("connect_url", data.connect_url) &&
o.map("platform_name", data.platform_name) &&
@@ -103,7 +105,7 @@ json::Value toJSON(const AcceleratorConnectionInfo &data) {
};
}
-bool fromJSON(const Value &value, AcceleratorActions &data, Path path) {
+bool fromJSON(const json::Value &value, AcceleratorActions &data, Path path) {
ObjectMapper o(value, path);
return o && o.map("plugin_name", data.plugin_name) &&
o.map("session_name", data.session_name) &&
@@ -124,7 +126,7 @@ json::Value toJSON(const AcceleratorActions &data) {
return obj;
}
-bool fromJSON(const Value &value, AcceleratorBreakpointHitResponse &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointHitResponse &data,
Path path) {
ObjectMapper o(value, path);
return o && o.map("disable_bp", data.disable_bp) &&
@@ -142,4 +144,64 @@ json::Value toJSON(const AcceleratorBreakpointHitResponse &data) {
return obj;
}
+bool fromJSON(const json::Value &value, AcceleratorSectionInfo &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("names", data.names) &&
+ o.map("load_address", data.load_address);
+}
+
+json::Value toJSON(const AcceleratorSectionInfo &data) {
+ return Object{{"names", data.names},
+ {"load_address", static_cast<int64_t>(data.load_address)}};
+}
+
+bool fromJSON(const json::Value &value,
+ AcceleratorDynamicLoaderLibraryInfo &data, Path path) {
+ 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("loaded_sections", data.loaded_sections) &&
+ 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);
+}
+
+json::Value toJSON(const AcceleratorDynamicLoaderLibraryInfo &data) {
+ return Object{
+ {"pathname", data.pathname},
+ {"uuid", data.uuid_str},
+ {"load", data.load},
+ {"load_address", data.load_address},
+ {"loaded_sections", data.loaded_sections},
+ {"native_memory_address", data.native_memory_address},
+ {"native_memory_size", data.native_memory_size},
+ {"file_offset", data.file_offset},
+ {"file_size", data.file_size},
+ };
+}
+
+bool fromJSON(const json::Value &value, AcceleratorDynamicLoaderArgs &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("plugin_name", data.plugin_name) &&
+ o.map("full", data.full);
+}
+
+json::Value toJSON(const AcceleratorDynamicLoaderArgs &data) {
+ return Object{{"plugin_name", data.plugin_name}, {"full", data.full}};
+}
+
+bool fromJSON(const json::Value &value, AcceleratorDynamicLoaderResponse &data,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("library_infos", data.library_infos);
+}
+
+json::Value toJSON(const AcceleratorDynamicLoaderResponse &data) {
+ return Object{{"library_infos", data.library_infos}};
+}
+
} // namespace lldb_private
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 6fc3b63e02dd1..953a2cd3090d1 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -338,6 +338,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
return eServerPacketType_jAcceleratorPluginInitialize;
if (PACKET_STARTS_WITH("jAcceleratorPluginBreakpointHit:"))
return eServerPacketType_jAcceleratorPluginBreakpointHit;
+ if (PACKET_STARTS_WITH("jAcceleratorPluginGetDynamicLoaderLibraryInfo:"))
+ return eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo;
break;
case 'v':
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
index 941e0c97a8f2e..e40e6491e59a0 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
@@ -186,3 +186,58 @@ def test_jAcceleratorPluginBreakpointHit_returns_connect_info(self):
connect_info["connect_url"],
)
self.assertTrue(connect_info["synchronous"])
+
+ @add_test_categories(["llgs"])
+ def test_jAcceleratorPluginGetDynamicLoaderLibraryInfo(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ self.prep_debug_monitor_and_inferior()
+
+ self.add_qSupported_packets()
+ self.expect_gdbremote_sequence()
+
+ # Only "full" is exercised. The mock could answer full=False, but what
+ # a delta contains is entirely up to the plugin, so doing so would only
+ # test the mock's own bookkeeping. That belongs with a real plugin.
+ dyld_args = {"plugin_name": "mock", "full": True}
+ dyld_json = json.dumps(dyld_args, separators=(",", ":"))
+ escaped_json = escape_binary(dyld_json)
+ response = self.send_and_decode_json(
+ "jAcceleratorPluginGetDynamicLoaderLibraryInfo:" + escaped_json
+ )
+
+ self.assertIn("library_infos", response)
+ libs = response["library_infos"]
+ self.assertGreater(len(libs), 0)
+
+ lib = libs[0]
+ self.assertEqual(lib["pathname"], "/path/to/lib.so")
+ self.assertTrue(lib["load"])
+ self.assertEqual(lib["load_address"], 0x7F000000)
+
+ @add_test_categories(["llgs"])
+ def test_jAcceleratorPluginGetDynamicLoaderLibraryInfo_unknown_plugin(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ self.prep_debug_monitor_and_inferior()
+
+ self.add_qSupported_packets()
+ self.expect_gdbremote_sequence()
+
+ dyld_args = {"plugin_name": "no-such-plugin", "full": True}
+ dyld_json = json.dumps(dyld_args, separators=(",", ":"))
+ escaped_json = escape_binary(dyld_json)
+ self.test_sequence.add_log_lines(
+ [
+ "read packet: $jAcceleratorPluginGetDynamicLoaderLibraryInfo:%s#00"
+ % escaped_json,
+ {
+ "direction": "send",
+ "regex": r"^\$(E[0-9a-fA-F]{2}.*)#[0-9a-fA-F]{2}",
+ "capture": {1: "error"},
+ },
+ ],
+ True,
+ )
+ context = self.expect_gdbremote_sequence()
+ self.assertIsNotNone(context.get("error"))
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index 6c1d23fef8a71..0cce2955f6e0d 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -220,3 +220,15 @@ LLDBServerMockAcceleratorPlugin::CreateConnection() {
info.synchronous = true;
return info;
}
+
+std::optional<AcceleratorDynamicLoaderResponse>
+LLDBServerMockAcceleratorPlugin::GetDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args) {
+ AcceleratorDynamicLoaderResponse response;
+ AcceleratorDynamicLoaderLibraryInfo lib;
+ lib.pathname = "/path/to/lib.so";
+ lib.load = true;
+ lib.load_address = 0x7f000000;
+ response.library_infos.push_back(std::move(lib));
+ return response;
+}
diff --git a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
index b07266d0ef65f..36f6aa2500ba8 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -34,6 +34,9 @@ class LLDBServerMockAcceleratorPlugin : public LLDBServerAcceleratorPlugin {
llvm::Expected<AcceleratorBreakpointHitResponse>
BreakpointWasHit(AcceleratorBreakpointHitArgs &args) override;
+ std::optional<AcceleratorDynamicLoaderResponse> GetDynamicLoaderLibraryInfos(
+ const AcceleratorDynamicLoaderArgs &args) override;
+
private:
// Lazily bring up the mock accelerator GDB server and return its connection
// info. Called on the connection breakpoint hit, so inferiors that never
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
index 571dd2cab124a..8d64b4a8acf78 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -244,3 +244,84 @@ TEST(AcceleratorGDBRemotePacketsTest, AcceleratorActionsWithoutConnectInfo) {
ASSERT_THAT_EXPECTED(deserialized, Succeeded());
EXPECT_FALSE(deserialized->connect_info.has_value());
}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorSectionInfo) {
+ AcceleratorSectionInfo section;
+ section.names = {"PT_LOAD[0]", ".text"};
+ section.load_address = 0x400000;
+
+ Expected<AcceleratorSectionInfo> deserialized = roundtripJSON(section);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(section.names, deserialized->names);
+ EXPECT_EQ(section.load_address, deserialized->load_address);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorDynamicLoaderLibraryInfo) {
+ AcceleratorDynamicLoaderLibraryInfo lib;
+ lib.pathname = "/usr/lib/libgpu.so";
+ lib.uuid_str = "AABBCCDD";
+ lib.load = true;
+ lib.load_address = 0x7f000000;
+ lib.native_memory_address = 0x1000;
+ lib.native_memory_size = 0x2000;
+ lib.file_offset = 4096;
+ lib.file_size = 8192;
+
+ AcceleratorSectionInfo section;
+ section.names = {".text"};
+ section.load_address = 0x7f001000;
+ lib.loaded_sections.push_back(std::move(section));
+
+ Expected<AcceleratorDynamicLoaderLibraryInfo> deserialized =
+ roundtripJSON(lib);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(lib.pathname, deserialized->pathname);
+ EXPECT_EQ(lib.uuid_str, deserialized->uuid_str);
+ EXPECT_EQ(lib.load, deserialized->load);
+ EXPECT_EQ(lib.load_address, deserialized->load_address);
+ EXPECT_EQ(lib.native_memory_address, deserialized->native_memory_address);
+ EXPECT_EQ(lib.native_memory_size, deserialized->native_memory_size);
+ EXPECT_EQ(lib.file_offset, deserialized->file_offset);
+ EXPECT_EQ(lib.file_size, deserialized->file_size);
+ ASSERT_EQ(1u, deserialized->loaded_sections.size());
+ EXPECT_EQ(0x7f001000u, deserialized->loaded_sections[0].load_address);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorDynamicLoaderArgs) {
+ AcceleratorDynamicLoaderArgs args;
+ args.plugin_name = "mock";
+ args.full = true;
+
+ Expected<AcceleratorDynamicLoaderArgs> deserialized = roundtripJSON(args);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ(args.plugin_name, deserialized->plugin_name);
+ EXPECT_EQ(args.full, deserialized->full);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorDynamicLoaderResponse) {
+ AcceleratorDynamicLoaderResponse response;
+ AcceleratorDynamicLoaderLibraryInfo lib;
+ lib.pathname = "/path/to/lib.so";
+ lib.load = true;
+ response.library_infos.push_back(std::move(lib));
+
+ Expected<AcceleratorDynamicLoaderResponse> deserialized =
+ roundtripJSON(response);
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ ASSERT_EQ(1u, deserialized->library_infos.size());
+ EXPECT_EQ("/path/to/lib.so", deserialized->library_infos[0].pathname);
+ EXPECT_TRUE(deserialized->library_infos[0].load);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, DynamicLoaderLibraryInfoOptionalFields) {
+ // A server with no per-section addresses can omit "loaded_sections".
+ Expected<AcceleratorDynamicLoaderLibraryInfo> deserialized =
+ json::parse<AcceleratorDynamicLoaderLibraryInfo>(
+ R"({"pathname":"/path/to/lib.so","load":true})",
+ "AcceleratorDynamicLoaderLibraryInfo");
+ ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+ EXPECT_EQ("/path/to/lib.so", deserialized->pathname);
+ EXPECT_TRUE(deserialized->load);
+ EXPECT_TRUE(deserialized->loaded_sections.empty());
+ EXPECT_FALSE(deserialized->load_address.has_value());
+}
More information about the lldb-commits
mailing list