https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/206370
>From 1e040f57f62c85d59e702f101ac40fe21d83258a Mon Sep 17 00:00:00 2001 From: satya janga <[email protected]> Date: Sun, 28 Jun 2026 12:59:16 -0700 Subject: [PATCH] [lldb] Add generic address space query support (jAddressSpacesInfo) Add a generic mechanism for a process to report the address spaces it exposes. Most processes have a single flat address space, but some (such as GPUs) have multiple address spaces where the same numeric address refers to different storage depending on the address space (for example global, local, private or generic memory). This is process-agnostic and lives entirely in the generic layers (no plugin-specific code): - Utility/AddressSpace.{h,cpp}: AddressSpaceInfo {name, value, is_thread_specific} with JSON serialization. - New "jAddressSpacesInfo" gdb-remote packet: NativeProcessProtocol gains a virtual GetAddressSpaces() (default empty); LLGS serves it via Handle_jAddressSpacesInfo; the client queries it via GetAddressSpaces() and caches "unsupported" so processes without address spaces pay nothing. - Process caches the result (Process::GetAddressSpaces()); ProcessGDBRemote populates it on connect. - Documented in docs/resources/lldbgdbremote.md. Tests: AddressSpaceInfo JSON round-trip unit tests, and a GDBRemoteCommunicationClient round-trip test (supported + unsupported) exercising the full packet encode/escape/parse path. --- lldb/docs/resources/lldbgdbremote.md | 31 +++++++++++ .../lldb/Host/common/NativeProcessProtocol.h | 7 +++ lldb/include/lldb/Target/Process.h | 11 ++++ lldb/include/lldb/Utility/AddressSpace.h | 37 +++++++++++++ .../lldb/Utility/StringExtractorGDBRemote.h | 1 + .../GDBRemoteCommunicationClient.cpp | 34 ++++++++++++ .../gdb-remote/GDBRemoteCommunicationClient.h | 7 +++ .../GDBRemoteCommunicationServerLLGS.cpp | 28 ++++++++++ .../GDBRemoteCommunicationServerLLGS.h | 2 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 3 + lldb/source/Utility/AddressSpace.cpp | 28 ++++++++++ lldb/source/Utility/CMakeLists.txt | 1 + .../Utility/StringExtractorGDBRemote.cpp | 2 + .../GDBRemoteCommunicationClientTest.cpp | 45 +++++++++++++++ lldb/unittests/Utility/AddressSpaceTest.cpp | 55 +++++++++++++++++++ lldb/unittests/Utility/CMakeLists.txt | 1 + 16 files changed, 293 insertions(+) create mode 100644 lldb/include/lldb/Utility/AddressSpace.h create mode 100644 lldb/source/Utility/AddressSpace.cpp create mode 100644 lldb/unittests/Utility/AddressSpaceTest.cpp diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md index 577a5ad31674f..f43f1958aeca0 100644 --- a/lldb/docs/resources/lldbgdbremote.md +++ b/lldb/docs/resources/lldbgdbremote.md @@ -825,6 +825,37 @@ This is a performance optimization, which speeds up debugging by avoiding multiple round-trips for retrieving thread information. The information from this packet can be retrieved using a combination of `qThreadStopInfo` and `m` packets. +## jAddressSpacesInfo + +Ask the server for the address spaces the process exposes. + +Most processes have a single, flat address space, but some (such as GPUs) have +multiple address spaces where the same numeric address refers to different +storage depending on the address space (for example global, local, private or +generic memory). This packet lets the client discover those address spaces. + +The response is a JSON array of dictionaries, one per address space: +``` + [ + {"name":"global","value":1,"is_thread_specific":false}, + {"name":"local","value":2,"is_thread_specific":true} + ] +``` + +Each dictionary has the following keys: + +* `name`: the human readable name of the address space. +* `value`: the integer identifier of the address space. +* `is_thread_specific`: `true` if the same address-space value resolves to + different storage for different threads. + +The server replies with an unsupported (empty) response when the process has no +address spaces, so the default flat-address-space case costs nothing. + +**Priority To Implement:** Low + +Only needed for targets that expose more than one address space. + ### MultiMemRead Read memory from multiple memory ranges. diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h index 435185a38f3f9..52534b7d84ca3 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/AddressSpace.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Iterable.h" #include "lldb/Utility/Status.h" @@ -96,6 +97,12 @@ class NativeProcessProtocol { virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); + /// Return the address spaces this process exposes. The default + /// implementation returns an empty list; processes that have multiple + /// address spaces (such as GPUs) override this. Served over the + /// "jAddressSpacesInfo" packet. + virtual std::vector<AddressSpaceInfo> GetAddressSpaces() { return {}; } + virtual Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) = 0; diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 8432c326d3281..ff186d0b65cb7 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -45,6 +45,7 @@ #include "lldb/Target/ThreadList.h" #include "lldb/Target/ThreadPlanStack.h" #include "lldb/Target/Trace.h" +#include "lldb/Utility/AddressSpace.h" #include "lldb/Utility/AddressableBits.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Args.h" @@ -2045,6 +2046,13 @@ class Process : public std::enable_shared_from_this<Process>, virtual Status GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list); + /// Get the address spaces this process exposes (for example a GPU's global, + /// local, private or generic memory). Empty for processes with a single + /// address space. Populated from the process plugin when it connects. + const std::vector<AddressSpaceInfo> &GetAddressSpaces() const { + return m_address_spaces; + } + /// Get the number of watchpoints supported by this target. /// /// We may be able to determine the number of watchpoints available @@ -3498,6 +3506,9 @@ void PruneThreadPlans(); ThreadList m_extended_thread_list; ///< Constituent for extended threads that may be /// generated, cleared on natural stops + std::vector<AddressSpaceInfo> + m_address_spaces; ///< Address spaces reported by the process plugin, + /// empty for single-address-space processes. lldb::RunDirection m_base_direction; ///< ThreadPlanBase run direction uint32_t m_extended_thread_stop_id; ///< The natural stop id when ///extended_thread_list was last updated diff --git a/lldb/include/lldb/Utility/AddressSpace.h b/lldb/include/lldb/Utility/AddressSpace.h new file mode 100644 index 0000000000000..adee7aef3e8db --- /dev/null +++ b/lldb/include/lldb/Utility/AddressSpace.h @@ -0,0 +1,37 @@ +//===-- AddressSpace.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_UTILITY_ADDRESSSPACE_H +#define LLDB_UTILITY_ADDRESSSPACE_H + +#include "lldb/lldb-types.h" +#include "llvm/Support/JSON.h" +#include <string> +#include <vector> + +namespace lldb_private { + +/// Describes a single address space reported by a process. Processes such as +/// GPUs can have multiple address spaces (for example global, local, private or +/// generic memory) where the same numeric address refers to different storage +/// depending on the address space. See the "jAddressSpacesInfo" packet in +/// docs/resources/lldbgdbremote.md for the wire format. +struct AddressSpaceInfo { + std::string name; ///< The name of the address space. + uint64_t value; ///< The integer identifier of the address space. + bool is_thread_specific; ///< True if the address space is thread specific. +}; + +bool fromJSON(const llvm::json::Value &value, AddressSpaceInfo &data, + llvm::json::Path path); + +llvm::json::Value toJSON(const AddressSpaceInfo &data); + +} // namespace lldb_private + +#endif // LLDB_UTILITY_ADDRESSSPACE_H diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h index 624a2febe857e..a16dfcfe1601a 100644 --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -108,6 +108,7 @@ class StringExtractorGDBRemote : public StringExtractor { eServerPacketType_QThreadSuffixSupported, eServerPacketType_jThreadsInfo, + eServerPacketType_jAddressSpacesInfo, eServerPacketType_qsThreadInfo, eServerPacketType_qfThreadInfo, eServerPacketType_qGetPid, diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 698bf5f7049cf..0c6bdfb636399 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -384,6 +384,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { m_attach_or_wait_reply = eLazyBoolCalculate; m_avoid_g_packets = eLazyBoolCalculate; m_supports_multiprocess = eLazyBoolCalculate; + m_supports_address_spaces = eLazyBoolCalculate; m_supports_qSaveCore = eLazyBoolCalculate; m_supports_qXfer_auxv_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate; @@ -1158,6 +1159,39 @@ GDBRemoteCommunicationClient::GetProcessStandaloneBinaries() { return m_binary_addresses; } +std::vector<AddressSpaceInfo> GDBRemoteCommunicationClient::GetAddressSpaces() { + // The server replies with the unsupported response when it has no address + // spaces, so cache that to avoid re-sending the packet on every call. + if (m_supports_address_spaces == eLazyBoolNo) + return {}; + + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (SendPacketAndWaitForResponse("jAddressSpacesInfo", response) != + PacketResult::Success) + return {}; + + if (response.IsUnsupportedResponse() || response.IsErrorResponse()) { + m_supports_address_spaces = eLazyBoolNo; + return {}; + } + m_supports_address_spaces = eLazyBoolYes; + + llvm::Expected<std::vector<AddressSpaceInfo>> info = + llvm::json::parse<std::vector<AddressSpaceInfo>>(response.Peek(), + "AddressSpaceInfo"); + if (info) + return std::move(*info); + + // A bare JSON parse error is meaningless on its own, so log both the full + // response and the parse error rather than surfacing it to the user. + Log *log = GetLog(GDBRLog::Process); + LLDB_LOG_ERROR(log, info.takeError(), + "malformed jAddressSpacesInfo response '{1}': {0}", + response.GetStringRef()); + return {}; +} + bool GDBRemoteCommunicationClient::GetGDBServerVersion() { if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { m_gdb_server_name.clear(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 3a0a34f840c21..5fd208ba2a0d5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -20,6 +20,7 @@ #include "lldb/Host/File.h" #include "lldb/Utility/AcceleratorGDBRemotePackets.h" +#include "lldb/Utility/AddressSpace.h" #include "lldb/Utility/AddressableBits.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/GDBRemote.h" @@ -223,6 +224,11 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { std::vector<lldb::addr_t> GetProcessStandaloneBinaries(); + /// Query the process for the address spaces it exposes via the + /// "jAddressSpacesInfo" packet. Returns an empty list if the process does + /// not support address spaces. + std::vector<AddressSpaceInfo> GetAddressSpaces(); + void GetRemoteQSupported(); bool GetVContSupported(llvm::StringRef flavor); @@ -606,6 +612,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { LazyBool m_supports_error_string_reply = eLazyBoolCalculate; LazyBool m_supports_multiprocess = eLazyBoolCalculate; LazyBool m_supports_memory_tagging = eLazyBoolCalculate; + LazyBool m_supports_address_spaces = eLazyBoolCalculate; LazyBool m_supports_qSaveCore = eLazyBoolCalculate; LazyBool m_uses_native_signals = eLazyBoolCalculate; std::optional<xPacketState> m_x_packet_state; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index f265fcb71be4e..787c305e73dcc 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -162,6 +162,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jThreadsInfo, &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jAddressSpacesInfo, + &GDBRemoteCommunicationServerLLGS::Handle_jAddressSpacesInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); @@ -3909,6 +3912,31 @@ GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo( return SendPacketNoLock(escaped_response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jAddressSpacesInfo( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Ensure we have a process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process " + "available", + __FUNCTION__); + return SendErrorResponse(Status::FromErrorString("invalid process")); + } + + std::vector<AddressSpaceInfo> address_spaces = + m_current_process->GetAddressSpaces(); + if (address_spaces.empty()) + return SendUnimplementedResponse(packet.GetStringRef().data()); + + StreamGDBRemote response; + response.PutAsJSONArray(address_spaces, /*hex_ascii=*/false); + return SendPacketNoLock(response.GetString()); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo( StringExtractorGDBRemote &packet) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h index e5b4c9ec0bed0..eaef6100445a8 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -268,6 +268,8 @@ class GDBRemoteCommunicationServerLLGS PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet); + PacketResult Handle_jAddressSpacesInfo(StringExtractorGDBRemote &packet); + PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index a986d0350bf57..fecf23d7eb4f2 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1192,6 +1192,9 @@ void ProcessGDBRemote::LoadStubBinaries() { allow_memory_image_last_resort); } } + + // Cache the address spaces the remote exposes (empty for most processes). + m_address_spaces = m_gdb_comm.GetAddressSpaces(); } void ProcessGDBRemote::MaybeLoadExecutableModule() { diff --git a/lldb/source/Utility/AddressSpace.cpp b/lldb/source/Utility/AddressSpace.cpp new file mode 100644 index 0000000000000..c118259706510 --- /dev/null +++ b/lldb/source/Utility/AddressSpace.cpp @@ -0,0 +1,28 @@ +//===-- AddressSpace.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/AddressSpace.h" + +using namespace llvm; +using namespace llvm::json; + +namespace lldb_private { + +bool fromJSON(const json::Value &value, AddressSpaceInfo &data, Path path) { + ObjectMapper o(value, path); + return o && o.map("name", data.name) && o.map("value", data.value) && + o.map("is_thread_specific", data.is_thread_specific); +} + +json::Value toJSON(const AddressSpaceInfo &data) { + return json::Value(Object{{"name", data.name}, + {"value", data.value}, + {"is_thread_specific", data.is_thread_specific}}); +} + +} // namespace lldb_private diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index e6d918c1ce9c7..f6000b9c330fb 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -25,6 +25,7 @@ endif() add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES AddressableBits.cpp + AddressSpace.cpp ArchSpec.cpp Args.cpp Baton.cpp diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index 6fc3b63e02dd1..20237aec70c50 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -321,6 +321,8 @@ StringExtractorGDBRemote::GetServerPacketType() const { return eServerPacketType_jSignalsInfo; if (PACKET_MATCHES("jThreadsInfo")) return eServerPacketType_jThreadsInfo; + if (PACKET_MATCHES("jAddressSpacesInfo")) + return eServerPacketType_jAddressSpacesInfo; if (PACKET_MATCHES("jLLDBTraceSupported")) return eServerPacketType_jLLDBTraceSupported; diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp index 3ec212b1c205d..8f96242d6bc61 100644 --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -11,7 +11,9 @@ #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/XML.h" #include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/AddressSpace.h" #include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/GDBRemote.h" #include "lldb/Utility/StructuredData.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/ArrayRef.h" @@ -191,6 +193,49 @@ TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers)); } +TEST_F(GDBRemoteCommunicationClientTest, GetAddressSpaces) { + std::future<std::vector<AddressSpaceInfo>> result = + std::async(std::launch::async, [&] { return client.GetAddressSpaces(); }); + // JSON responses are sent with the gdb-remote escaping applied (the same way + // the server's StreamGDBRemote::PutAsJSONArray does), so '{' and '}' survive + // the round trip. + StreamGDBRemote escaped; + llvm::StringRef json = + R"([{"name":"global","value":1,"is_thread_specific":false},)" + R"({"name":"local","value":2,"is_thread_specific":true}])"; + escaped.PutEscapedBytes(json.data(), json.size()); + HandlePacket(server, "jAddressSpacesInfo", escaped.GetString()); + + std::vector<AddressSpaceInfo> spaces = result.get(); + ASSERT_EQ(spaces.size(), 2u); + EXPECT_EQ(spaces[0].name, "global"); + EXPECT_EQ(spaces[0].value, 1u); + EXPECT_FALSE(spaces[0].is_thread_specific); + EXPECT_EQ(spaces[1].name, "local"); + EXPECT_EQ(spaces[1].value, 2u); + EXPECT_TRUE(spaces[1].is_thread_specific); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetAddressSpacesUnsupported) { + std::future<std::vector<AddressSpaceInfo>> result = + std::async(std::launch::async, [&] { return client.GetAddressSpaces(); }); + // An empty response is the "unsupported" reply. + HandlePacket(server, "jAddressSpacesInfo", ""); + EXPECT_TRUE(result.get().empty()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetAddressSpacesMalformed) { + std::future<std::vector<AddressSpaceInfo>> result = + std::async(std::launch::async, [&] { return client.GetAddressSpaces(); }); + // Valid-looking JSON array, but the objects are missing required fields, so + // parsing fails and the client returns an empty list rather than crashing. + StreamGDBRemote escaped; + llvm::StringRef bad = R"([{"name":"global"}])"; + escaped.PutEscapedBytes(bad.data(), bad.size()); + HandlePacket(server, "jAddressSpacesInfo", escaped.GetString()); + EXPECT_TRUE(result.get().empty()); +} + TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { const lldb::tid_t tid = 0x47; uint32_t save_id; diff --git a/lldb/unittests/Utility/AddressSpaceTest.cpp b/lldb/unittests/Utility/AddressSpaceTest.cpp new file mode 100644 index 0000000000000..7a4cf04cc8d2b --- /dev/null +++ b/lldb/unittests/Utility/AddressSpaceTest.cpp @@ -0,0 +1,55 @@ +//===-- AddressSpaceTest.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/AddressSpace.h" +#include "llvm/Support/JSON.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace lldb_private; + +static std::string ToString(const llvm::json::Value &value) { + return llvm::formatv("{0}", value).str(); +} + +TEST(AddressSpaceTest, RoundTrip) { + AddressSpaceInfo info{"global", 1, /*is_thread_specific=*/false}; + llvm::Expected<AddressSpaceInfo> parsed = llvm::json::parse<AddressSpaceInfo>( + ToString(toJSON(info)), "AddressSpaceInfo"); + ASSERT_THAT_EXPECTED(parsed, llvm::Succeeded()); + EXPECT_EQ(parsed->name, "global"); + EXPECT_EQ(parsed->value, 1u); + EXPECT_FALSE(parsed->is_thread_specific); +} + +TEST(AddressSpaceTest, ArrayRoundTrip) { + std::vector<AddressSpaceInfo> spaces = { + {"global", 1, false}, + {"local", 2, true}, + {"private", 3, true}, + }; + llvm::json::Array array; + for (const AddressSpaceInfo &space : spaces) + array.push_back(toJSON(space)); + + llvm::Expected<std::vector<AddressSpaceInfo>> parsed = + llvm::json::parse<std::vector<AddressSpaceInfo>>( + ToString(llvm::json::Value(std::move(array))), "AddressSpaceInfo"); + ASSERT_THAT_EXPECTED(parsed, llvm::Succeeded()); + ASSERT_EQ(parsed->size(), 3u); + EXPECT_EQ((*parsed)[1].name, "local"); + EXPECT_EQ((*parsed)[1].value, 2u); + EXPECT_TRUE((*parsed)[1].is_thread_specific); +} + +TEST(AddressSpaceTest, MissingFieldFails) { + // "is_thread_specific" is required. + llvm::Expected<AddressSpaceInfo> parsed = llvm::json::parse<AddressSpaceInfo>( + R"({"name":"global","value":1})", "AddressSpaceInfo"); + EXPECT_THAT_EXPECTED(parsed, llvm::Failed()); +} diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt index ed159748838b5..e46a1774f020d 100644 --- a/lldb/unittests/Utility/CMakeLists.txt +++ b/lldb/unittests/Utility/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_unittest(UtilityTests AcceleratorGDBRemotePacketsTest.cpp + AddressSpaceTest.cpp AnsiTerminalTest.cpp ArgsTest.cpp OptionsWithRawTest.cpp _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
