blob: 1f4268bd26bdc268d08a8beef945c1da7f5d03df [file]
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test.gdbclientutils import *
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
class TestAddressSpaceMemoryRead(GDBRemoteTestBase):
"""
End-to-end test that the same numeric address read from two different
address spaces returns different bytes. The server advertises
"address-spaces+" in qSupported, reports the spaces via "jAddressSpacesInfo",
and reads memory with an optional "address_space:<hex-id>;" suffix on the
standard memory packet.
"""
def test(self):
address_spaces_json = (
'[{"name":"global","space_id":1,"is_thread_specific":false},'
'{"name":"local","space_id":26,"is_thread_specific":true}]'
)
class MyResponder(MockGDBServerResponder):
def qSupported(self, client_supported):
return "PacketSize=3fff;QStartNoAckMode+;address-spaces+"
def qHostInfo(self):
return "ptrsize:8;endian:little;"
def _bytes_for_space(self, space):
if space == 1:
return "aabbccdd"
if space == 26:
return "11223344"
return "E01"
def __init__(self):
super().__init__()
self.reads = []
def _respond_impl(self, packet):
# The base dispatcher can't parse the "address_space:<hex-id>;"
# suffix, so handle suffixed reads here.
if packet and packet[0] in ("m", "x") and "address_space:" in packet:
self.reads.append(packet)
space = 0
for field in packet[1:].split(";"):
key, _, value = field.partition(":")
if key == "address_space":
space = int(value, 16)
return self._bytes_for_space(space)
return super()._respond_impl(packet)
def x(self, addr, length):
# Force the client onto the hex "m" read path.
return ""
def other(self, packet):
if packet == "jAddressSpacesInfo":
return escape_binary(address_spaces_json)
return ""
self.server.responder = MyResponder()
target = self.dbg.CreateTarget("")
process = self.connect(target)
error = lldb.SBError()
# Same numeric address, two spaces (global == id 1, local == id 26).
global_bytes = process.ReadMemory(lldb.SBProcessAddress(0x1000, 1), 4, error)
self.assertSuccess(error)
self.assertEqual(global_bytes, b"\xaa\xbb\xcc\xdd")
# "local" is thread specific, so reading it needs a thread.
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid())
local_bytes = process.ReadMemory(
lldb.SBProcessAddress(0x1000, 26, thread), 4, error
)
self.assertSuccess(error)
self.assertEqual(local_bytes, b"\x11\x22\x33\x44")
# Same address, different address space, different bytes.
self.assertNotEqual(global_bytes, local_bytes)
# Ids go out in base 16, like the other numeric values in the packet.
self.assertEqual(len(self.server.responder.reads), 2)
self.assertIn("address_space:1;", self.server.responder.reads[0])
self.assertIn("address_space:1a;", self.server.responder.reads[1])
# Only the thread specific read carries a "thread:" field, and it names
# the thread that was asked for.
self.assertNotIn("thread:", self.server.responder.reads[0])
self.assertIn(
"thread:%x;" % thread.GetThreadID(), self.server.responder.reads[1]
)
# A thread specific space without a thread is an error, not a read.
process.ReadMemory(lldb.SBProcessAddress(0x1000, 26), 4, error)
self.assertTrue(error.Fail())
self.assertIn("thread specific", error.GetCString())
# Address spaces can be resolved by name, and reading through the
# resolved id gives the same bytes as reading through the id directly.
global_id = process.GetAddressSpaceID("global", error)
self.assertSuccess(error)
self.assertEqual(global_id, 1)
self.assertEqual(
process.ReadMemory(lldb.SBProcessAddress(0x1000, global_id), 4, error),
global_bytes,
)
self.assertSuccess(error)
self.assertEqual(process.GetAddressSpaceID("local", error), 26)
self.assertSuccess(error)
# An unknown name is an error rather than a silent default.
process.GetAddressSpaceID("nonexistent", error)
self.assertTrue(error.Fail())
# So is no name at all.
process.GetAddressSpaceID(None, error)
self.assertTrue(error.Fail())