forked from OSchip/llvm-project
Add gdb-remote P register write test.
Added test that attempts to write a value to each general purpose register that is bit-flipped from the initial read value. It then reads it back to see if it takes. Right now I just assert that at least one register bit flip write succeeds. I added a note that on the MacOSX x86_64 debugserver case, the only writes that succeed from the GPR set are rax thru rdx, rdi, rsi and rbp. The failures are E32 failure-at-write-attempt issues on the debugserver end. I'll revisit this after implementing in the llgs linux-x86_64 branch. The packet log looks good but I may have a subtle mistake in the code. llvm-svn: 210681
This commit is contained in:
parent
caa0ec2851
commit
5cc2417532
|
@ -49,7 +49,7 @@ class LldbGdbServerTestCase(TestBase):
|
|||
self.set_inferior_startup_launch()
|
||||
|
||||
# Uncomment this code to force only a single test to run (by name).
|
||||
# if not re.search(r"written_M", self._testMethodName):
|
||||
# if not re.search(r"P_writes", self._testMethodName):
|
||||
# self.skipTest("focusing on one test")
|
||||
|
||||
def reset_test_sequence(self):
|
||||
|
@ -295,6 +295,19 @@ class LldbGdbServerTestCase(TestBase):
|
|||
|
||||
return (None, None)
|
||||
|
||||
def add_lldb_register_index(self, reg_infos):
|
||||
"""Add a "lldb_register_index" key containing the 0-baed index of each reg_infos entry.
|
||||
|
||||
We'll use this when we want to call packets like P/p with a register index but do so
|
||||
on only a subset of the full register info set.
|
||||
"""
|
||||
self.assertIsNotNone(reg_infos)
|
||||
|
||||
reg_index = 0
|
||||
for reg_info in reg_infos:
|
||||
reg_info["lldb_register_index"] = reg_index
|
||||
reg_index += 1
|
||||
|
||||
def add_query_memory_region_packets(self, address):
|
||||
self.test_sequence.add_log_lines(
|
||||
["read packet: $qMemoryRegionInfo:{0:x}#00".format(address),
|
||||
|
@ -2019,6 +2032,121 @@ class LldbGdbServerTestCase(TestBase):
|
|||
self.set_inferior_startup_launch()
|
||||
self.written_M_content_reads_back_correctly()
|
||||
|
||||
def flip_all_bits_in_each_register_value(self, reg_infos):
|
||||
self.assertIsNotNone(reg_infos)
|
||||
|
||||
successful_writes = 0
|
||||
failed_writes = 0
|
||||
|
||||
for reg_info in reg_infos:
|
||||
# Use the lldb register index added to the reg info. We're not necessarily
|
||||
# working off a full set of register infos, so an inferred register index could be wrong.
|
||||
reg_index = reg_info["lldb_register_index"]
|
||||
self.assertIsNotNone(reg_index)
|
||||
|
||||
# Read the existing value.
|
||||
self.reset_test_sequence()
|
||||
self.test_sequence.add_log_lines(
|
||||
["read packet: $p{0:x}#00".format(reg_index),
|
||||
{ "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }],
|
||||
True)
|
||||
context = self.expect_gdbremote_sequence()
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
# Verify the response length.
|
||||
p_response = context.get("p_response")
|
||||
self.assertIsNotNone(p_response)
|
||||
self.assertEquals(len(p_response), 2 * int(reg_info["bitsize"]) / 8)
|
||||
|
||||
# Flip the value by xoring with all 1s
|
||||
all_one_bits_raw = "ff" * (int(reg_info["bitsize"]) / 8)
|
||||
flipped_bits_int = int(p_response, 16) ^ int(all_one_bits_raw, 16)
|
||||
# print "reg (index={}, name={}): val={}, flipped bits (int={}, hex={:x})".format(reg_index, reg_info["name"], p_response, flipped_bits_int, flipped_bits_int)
|
||||
|
||||
# Write the flipped value to the register.
|
||||
self.reset_test_sequence()
|
||||
self.test_sequence.add_log_lines(
|
||||
["read packet: $P{0:x}={1:x}#00".format(reg_index, flipped_bits_int),
|
||||
{ "direction":"send", "regex":r"^\$(OK|E[0-9a-fA-F]+)#[0-9a-fA-F]{2}", "capture":{1:"P_response"} },
|
||||
], True)
|
||||
context = self.expect_gdbremote_sequence()
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
# Determine if the write succeeded. There are a handful of registers that can fail.
|
||||
P_response = context.get("P_response")
|
||||
self.assertIsNotNone(P_response)
|
||||
if P_response == "OK":
|
||||
successful_writes += 1
|
||||
else:
|
||||
failed_writes += 1
|
||||
# print "reg (index={}, name={}) write FAILED (error: {})".format(reg_index, reg_info["name"], P_response)
|
||||
|
||||
# Read back the register value, ensure it matches the flipped value.
|
||||
if P_response == "OK":
|
||||
self.reset_test_sequence()
|
||||
self.test_sequence.add_log_lines(
|
||||
["read packet: $p{0:x}#00".format(reg_index),
|
||||
{ "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }],
|
||||
True)
|
||||
context = self.expect_gdbremote_sequence()
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
verify_p_response = context.get("p_response")
|
||||
self.assertIsNotNone(verify_p_response)
|
||||
if verify_p_response != "{:x}".format(flipped_bits_int):
|
||||
# Some registers, like mxcsrmask and others, will permute what's written. Adjust succeed/fail counts.
|
||||
# print "reg (index={}, name={}): read verify FAILED: wrote {:x}, verify read back {}".format(reg_index, reg_info["name"], flipped_bits_int, verify_p_response)
|
||||
successful_writes -= 1
|
||||
failed_writes +=1
|
||||
|
||||
return (successful_writes, failed_writes)
|
||||
|
||||
def P_writes_all_gpr_registers(self):
|
||||
# Start inferior debug session, grab all register info.
|
||||
procs = self.prep_debug_monitor_and_inferior(inferior_args=["sleep:2"])
|
||||
self.add_register_info_collection_packets()
|
||||
self.test_sequence.add_log_lines(
|
||||
[# Start running for a bit.
|
||||
"read packet: $c#00",
|
||||
"read packet: {}".format(chr(03)),
|
||||
{"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }],
|
||||
True)
|
||||
|
||||
context = self.expect_gdbremote_sequence()
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
reg_infos = self.parse_register_info_packets(context)
|
||||
self.assertIsNotNone(reg_infos)
|
||||
self.add_lldb_register_index(reg_infos)
|
||||
|
||||
# Pull out the general purpose register infos
|
||||
gpr_reg_infos = [reg_info for reg_info in reg_infos if reg_info["set"] == "General Purpose Registers"]
|
||||
self.assertIsNotNone(len(gpr_reg_infos) > 0)
|
||||
|
||||
# Write flipped bit pattern of existing value to each register.
|
||||
(successful_writes, failed_writes) = self.flip_all_bits_in_each_register_value(gpr_reg_infos)
|
||||
# print "successful writes: {}, failed writes: {}".format(successful_writes, failed_writes)
|
||||
self.assertTrue(successful_writes > 0)
|
||||
|
||||
# Note: as of this moment, a hefty number of the GPR writes are failing with E32 (everything except rax-rdx, rdi, rsi, rbp).
|
||||
# Come back to this. I have the test rigged to verify that at least some of the bit-flip writes work.
|
||||
@debugserver_test
|
||||
@dsym_test
|
||||
def test_P_writes_all_gpr_registers_debugserver_dsym(self):
|
||||
self.init_debugserver_test()
|
||||
self.buildDsym()
|
||||
self.set_inferior_startup_launch()
|
||||
self.P_writes_all_gpr_registers()
|
||||
|
||||
@llgs_test
|
||||
@dwarf_test
|
||||
@unittest2.expectedFailure()
|
||||
def test_P_writes_all_gpr_registers_llgs_dwarf(self):
|
||||
self.init_llgs_test()
|
||||
self.buildDwarf()
|
||||
self.set_inferior_startup_launch()
|
||||
self.P_writes_all_gpr_registers()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
|
|
Loading…
Reference in New Issue