https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/214564
>From c686f2ebe828f8c4f26e45e43f93c73c169c2c4b Mon Sep 17 00:00:00 2001 From: satya janga <[email protected]> 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 | 34 ++++++++ .../Utility/AcceleratorGDBRemotePackets.h | 54 +++++++++++++ .../lldb/Utility/StringExtractorGDBRemote.h | 1 + .../GDBRemoteCommunicationServerLLGS.cpp | 33 ++++++++ .../GDBRemoteCommunicationServerLLGS.h | 3 + .../gdb-remote/LLDBServerAcceleratorPlugin.h | 5 ++ .../Utility/AcceleratorGDBRemotePackets.cpp | 59 ++++++++++++++ .../Utility/StringExtractorGDBRemote.cpp | 2 + .../mock/TestMockAcceleratorPackets.py | 25 ++++++ .../Mock/LLDBServerMockAcceleratorPlugin.cpp | 12 +++ .../Mock/LLDBServerMockAcceleratorPlugin.h | 3 + .../AcceleratorGDBRemotePacketsTest.cpp | 81 +++++++++++++++++++ 12 files changed, 312 insertions(+) diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md index 93090c19c5ec0..4966d6fd91f85 100644 --- a/lldb/docs/resources/lldbgdbremote.md +++ b/lldb/docs/resources/lldbgdbremote.md @@ -2862,3 +2862,37 @@ 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 all libraries. If false, return only updates since the last query. | + +The response JSON has the following fields: + +| Key | Type | Description | +|-----------------|-------|-------------| +| `library_infos` | array | Array of library info objects, each with `pathname`, `load` (bool), and optional `load_address`, `loaded_sections`, `uuid`, `native_memory_address`, `native_memory_size`, `file_offset`, `file_size`. | + +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..9964580c16469 100644 --- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h +++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h @@ -166,6 +166,60 @@ 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; + uint64_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<uint64_t> load_address; + /// Used when sections load at independent addresses. + std::vector<AcceleratorSectionInfo> loaded_sections; + /// Where the image can be read in the native process. + std::optional<uint64_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 updates since the last query. + 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..180b2121c6155 100644 --- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp +++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp @@ -142,4 +142,63 @@ json::Value toJSON(const AcceleratorBreakpointHitResponse &data) { return obj; } +bool fromJSON(const 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 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 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 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..865fd590e745f 100644 --- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py +++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py @@ -186,3 +186,28 @@ 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() + + 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) 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()); +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
