| import lldb | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.gdbclientutils import * | |
| from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase | |
| # Test that lldb will cache when a register is unavailable, | |
| # and not request the value multiple times at a stop event. | |
| # | |
| # Also test that a stop reply packet which indicates that | |
| # the register cannot be read currently will keep lldb from | |
| # trying to read it again. | |
| class TestUnavailableRegisters(GDBRemoteTestBase): | |
| @skipIfXmlSupportMissing | |
| @skipIfLLVMTargetMissing("AArch64") | |
| def test(self): | |
| class MyResponder(MockGDBServerResponder): | |
| def __init__(self): | |
| super().__init__() | |
| self.initial_x2_read = True | |
| def haltReason(self): | |
| # Register 1 cannot be fetched. | |
| return ( | |
| "T02thread:1ff0d;threads:1ff0d;thread-pcs:000000010001bc00;00:0100000000000000;" | |
| "01:;" | |
| "03:0500000000000000;04:00bc010001000000;" | |
| ) | |
| def threadStopInfo(self, threadnum): | |
| return self.haltReason() | |
| def writeRegisters(self): | |
| return "E02" | |
| def qHostInfo(self): | |
| return "cputype:16777228;cpusubtype:2;endian:little;ptrsize:8;" | |
| def readRegisters(self): | |
| return "E01" | |
| def readRegister(self, regnum): | |
| # Register 1 was listed in the stop reply packet's expedited | |
| # registers as unavailable. lldb should not send a "p1" to try | |
| # to read it. Send back a value so we can detect if this happened. | |
| if regnum == 1: | |
| return "5555555555555555" | |
| # Register 2 was not included in the stop reply packet. Return an | |
| # error when we try to read it the first time, return a value the | |
| # second time we try to read it. We should never see the value. | |
| if regnum == 2: | |
| if self.initial_x2_read: | |
| return "E20" | |
| self.initial_x2_read = False | |
| else: | |
| return "8888888888888888" | |
| return "E03" | |
| def qXferRead(self, obj, annex, offset, length): | |
| if annex == "target.xml": | |
| return ( | |
| """<?xml version="1.0"?> | |
| <target version="1.0"> | |
| <architecture>aarch64</architecture> | |
| <feature name="org.gnu.gdb.aarch64.core"> | |
| <reg name="x0" regnum="0" bitsize="64"/> | |
| <reg name="x1" bitsize="64"/> | |
| <reg name="x2" bitsize="64"/> | |
| <reg name="x3" bitsize="64"/> | |
| <reg name="x4" bitsize="64"/> | |
| <reg name="pc" bitsize="64"/> | |
| </feature> | |
| </target>""", | |
| False, | |
| ) | |
| else: | |
| return None, False | |
| self.server.responder = MyResponder() | |
| target = self.dbg.CreateTarget("") | |
| if self.TraceOn(): | |
| self.runCmd("log enable gdb-remote packets") | |
| self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets")) | |
| process = self.connect(target) | |
| thread = process.GetThreadAtIndex(0) | |
| frame = thread.GetFrameAtIndex(0) | |
| self.assertEqual(frame.FindRegister("pc").GetValueAsUnsigned(), 0x10001BC00) | |
| # value 1 from the stop reply packet's expedited registers | |
| self.assertEqual(frame.FindRegister("x0").GetValueAsUnsigned(), 1) | |
| # Register is marked unavailable in the stop reply packet's | |
| # expedited register list. Should not get a value by sending | |
| # 'p1'. | |
| self.assertFailure(frame.FindRegister("x1").GetError()) | |
| # x2 is _not_ in the expedited registers, and will reply with | |
| # an error the first time it is requested. | |
| self.assertFailure(frame.FindRegister("x2").GetError()) | |
| # x2 will reply with a value the second time it is requested. | |
| # lldb should cache the error from the first attempt and not | |
| # try again. | |
| self.assertFailure(frame.FindRegister("x2").GetError()) |