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 &region_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

Reply via email to