From: Todd Fiala Date: Fri, 20 Jun 2014 17:39:24 +0000 (+0000) Subject: Added gdb-remote tests for Q{Save,Restore}RegisterState. X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9846d45d97f1d87ff8e40064aabd29804bb6415e;p=platform%2Fupstream%2Fllvm.git Added gdb-remote tests for Q{Save,Restore}RegisterState. Tests for both thread suffix and no thread suffix execution. Moved some bit-flipping helper methods from TestLldbGdbServer into the base GdbRemoteTestCaseBase class. llvm-svn: 211381 --- diff --git a/lldb/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py b/lldb/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py new file mode 100644 index 0000000..0d1934a --- /dev/null +++ b/lldb/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py @@ -0,0 +1,132 @@ +import unittest2 + +import gdbremote_testcase +from lldbtest import * + +class TestGdbRemoteRegisterState(gdbremote_testcase.GdbRemoteTestCaseBase): + """Test QSaveRegisterState/QRestoreRegisterState support.""" + + def grp_register_save_restore_works(self, with_suffix): + # Start up the process, use thread suffix, grab main thread id. + inferior_args = ["message:main entered", "sleep:5"] + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + + self.add_process_info_collection_packets() + self.add_register_info_collection_packets() + if with_suffix: + self.add_thread_suffix_request_packets() + self.add_threadinfo_collection_packets() + self.test_sequence.add_log_lines([ + # Start the inferior... + "read packet: $c#00", + # ... match output.... + { "type":"output_match", "regex":r"^message:main entered\r\n$" }, + ], True) + # ... then interrupt. + self.add_interrupt_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info. + process_info = self.parse_process_info_response(context) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + # Gather register info. + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.add_lldb_register_index(reg_infos) + + # Pull out the register infos that we think we can bit flip successfully. + gpr_reg_infos = [reg_info for reg_info in reg_infos if self.is_bit_flippable_register(reg_info)] + self.assertTrue(len(gpr_reg_infos) > 0) + + # Gather thread info. + if with_suffix: + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + thread_id = threads[0] + self.assertIsNotNone(thread_id) + # print "Running on thread: 0x{:x}".format(thread_id) + else: + thread_id = None + + # Save register state. + self.reset_test_sequence() + self.add_QSaveRegisterState_packets(thread_id) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + (success, state_id) = self.parse_QSaveRegisterState_response(context) + self.assertTrue(success) + self.assertIsNotNone(state_id) + # print "saved register state id: {}".format(state_id) + + # Remember initial register values. + initial_reg_values = self.read_register_values(gpr_reg_infos, endian, thread_id=thread_id) + # print "initial_reg_values: {}".format(initial_reg_values) + + # Flip gpr register values. + (successful_writes, failed_writes) = self.flip_all_bits_in_each_register_value(gpr_reg_infos, endian, thread_id=thread_id) + # print "successful writes: {}, failed writes: {}".format(successful_writes, failed_writes) + self.assertTrue(successful_writes > 0) + + flipped_reg_values = self.read_register_values(gpr_reg_infos, endian, thread_id=thread_id) + # print "flipped_reg_values: {}".format(flipped_reg_values) + + # Restore register values. + self.reset_test_sequence() + self.add_QRestoreRegisterState_packets(state_id, thread_id) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify registers match initial register values. + final_reg_values = self.read_register_values(gpr_reg_infos, endian, thread_id=thread_id) + # print "final_reg_values: {}".format(final_reg_values) + self.assertIsNotNone(final_reg_values) + self.assertEquals(final_reg_values, initial_reg_values) + + @debugserver_test + @dsym_test + def test_grp_register_save_restore_works_with_suffix_debugserver_dsym(self): + USE_THREAD_SUFFIX = True + self.init_debugserver_test() + self.buildDsym() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + @llgs_test + @dwarf_test + @unittest2.expectedFailure() + def test_grp_register_save_restore_works_with_suffix_llgs_dwarf(self): + USE_THREAD_SUFFIX = True + self.init_llgs_test() + self.buildDwarf() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + @debugserver_test + @dsym_test + def test_grp_register_save_restore_works_no_suffix_debugserver_dsym(self): + USE_THREAD_SUFFIX = False + self.init_debugserver_test() + self.buildDsym() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + @llgs_test + @dwarf_test + @unittest2.expectedFailure() + def test_grp_register_save_restore_works_no_suffix_llgs_dwarf(self): + USE_THREAD_SUFFIX = False + self.init_llgs_test() + self.buildDwarf() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + +if __name__ == '__main__': + unittest2.main() diff --git a/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py b/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py index 4451616..6aa1fe2 100644 --- a/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py +++ b/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py @@ -1670,99 +1670,6 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): self.set_inferior_startup_launch() self.written_M_content_reads_back_correctly() - def flip_all_bits_in_each_register_value(self, reg_infos, endian): - 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) - - reg_byte_size = int(reg_info["bitsize"])/8 - self.assertTrue(reg_byte_size > 0) - - # 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) - initial_reg_value = lldbgdbserverutils.unpack_register_hex_unsigned(endian, p_response) - - # Flip the value by xoring with all 1s - all_one_bits_raw = "ff" * (int(reg_info["bitsize"]) / 8) - flipped_bits_int = initial_reg_value ^ int(all_one_bits_raw, 16) - # print "reg (index={}, name={}): val={}, flipped bits (int={}, hex={:x})".format(reg_index, reg_info["name"], initial_reg_value, 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}#00".format(reg_index, lldbgdbserverutils.pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size)), - { "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, or partially fail - # (e.g. flags, segment selectors, etc.) due to register value restrictions. Don't worry about them - # all flipping perfectly. - 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_raw = context.get("p_response") - self.assertIsNotNone(verify_p_response_raw) - verify_bits = lldbgdbserverutils.unpack_register_hex_unsigned(endian, verify_p_response_raw) - - if verify_bits != 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 {:x}".format(reg_index, reg_info["name"], flipped_bits_int, verify_bits) - successful_writes -= 1 - failed_writes +=1 - - return (successful_writes, failed_writes) - - def is_bit_flippable_register(self, reg_info): - if not reg_info: - return False - if not "set" in reg_info: - return False - if reg_info["set"] != "General Purpose Registers": - return False - if ("container-regs" in reg_info) and (len(reg_info["container-regs"]) > 0): - # Don't try to bit flip registers contained in another register. - return False - if re.match("^.s$", reg_info["name"]): - # This is a 2-letter register name that ends in "s", like a segment register. - # Don't try to bit flip these. - return False - # Okay, this looks fine-enough. - return True - 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"]) @@ -1784,7 +1691,7 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): # Pull out the register infos that we think we can bit flip successfully,. gpr_reg_infos = [reg_info for reg_info in reg_infos if self.is_bit_flippable_register(reg_info)] - self.assertIsNotNone(len(gpr_reg_infos) > 0) + self.assertTrue(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, endian) diff --git a/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py b/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py index 653ba0c..de9b481 100644 --- a/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py +++ b/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py @@ -676,4 +676,179 @@ class GdbRemoteTestCaseBase(TestBase): def parse_interrupt_packets(self, context): self.assertIsNotNone(context.get("stop_signo")) self.assertIsNotNone(context.get("stop_key_val_text")) - return (int(context["stop_signo"], 16), self.parse_key_val_dict(context["stop_key_val_text"])) \ No newline at end of file + return (int(context["stop_signo"], 16), self.parse_key_val_dict(context["stop_key_val_text"])) + + def add_QSaveRegisterState_packets(self, thread_id): + if thread_id: + # Use the thread suffix form. + request = "read packet: $QSaveRegisterState;thread:{:x}#00".format(thread_id) + else: + request = "read packet: $QSaveRegisterState#00" + + self.test_sequence.add_log_lines([ + request, + {"direction":"send", "regex":r"^\$(E?.*)#[0-9a-fA-F]{2}$", "capture":{1:"save_response" } }, + ], True) + + def parse_QSaveRegisterState_response(self, context): + self.assertIsNotNone(context) + + save_response = context.get("save_response") + self.assertIsNotNone(save_response) + + if len(save_response) < 1 or save_response[0] == "E": + # error received + return (False, None) + else: + return (True, int(save_response)) + + def add_QRestoreRegisterState_packets(self, save_id, thread_id=None): + if thread_id: + # Use the thread suffix form. + request = "read packet: $QRestoreRegisterState:{};thread:{:x}#00".format(save_id, thread_id) + else: + request = "read packet: $QRestoreRegisterState:{}#00".format(save_id) + + self.test_sequence.add_log_lines([ + request, + "send packet: $OK#00" + ], True) + + def flip_all_bits_in_each_register_value(self, reg_infos, endian, thread_id=None): + 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) + + reg_byte_size = int(reg_info["bitsize"])/8 + self.assertTrue(reg_byte_size > 0) + + # Handle thread suffix. + if thread_id: + p_request = "read packet: $p{:x};thread:{:x}#00".format(reg_index, thread_id) + else: + p_request = "read packet: $p{:x}#00".format(reg_index) + + # Read the existing value. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + p_request, + { "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) + initial_reg_value = unpack_register_hex_unsigned(endian, p_response) + + # Flip the value by xoring with all 1s + all_one_bits_raw = "ff" * (int(reg_info["bitsize"]) / 8) + flipped_bits_int = initial_reg_value ^ int(all_one_bits_raw, 16) + # print "reg (index={}, name={}): val={}, flipped bits (int={}, hex={:x})".format(reg_index, reg_info["name"], initial_reg_value, flipped_bits_int, flipped_bits_int) + + # Handle thread suffix for P. + if thread_id: + P_request = "read packet: $P{:x}={};thread:{:x}#00".format(reg_index, pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size), thread_id) + else: + P_request = "read packet: $P{:x}={}#00".format(reg_index, pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size)) + + # Write the flipped value to the register. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + P_request, + { "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, or partially fail + # (e.g. flags, segment selectors, etc.) due to register value restrictions. Don't worry about them + # all flipping perfectly. + 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([ + p_request, + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + verify_p_response_raw = context.get("p_response") + self.assertIsNotNone(verify_p_response_raw) + verify_bits = unpack_register_hex_unsigned(endian, verify_p_response_raw) + + if verify_bits != 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 {:x}".format(reg_index, reg_info["name"], flipped_bits_int, verify_bits) + successful_writes -= 1 + failed_writes +=1 + + return (successful_writes, failed_writes) + + def is_bit_flippable_register(self, reg_info): + if not reg_info: + return False + if not "set" in reg_info: + return False + if reg_info["set"] != "General Purpose Registers": + return False + if ("container-regs" in reg_info) and (len(reg_info["container-regs"]) > 0): + # Don't try to bit flip registers contained in another register. + return False + if re.match("^.s$", reg_info["name"]): + # This is a 2-letter register name that ends in "s", like a segment register. + # Don't try to bit flip these. + return False + # Okay, this looks fine-enough. + return True + + def read_register_values(self, reg_infos, endian, thread_id=None): + self.assertIsNotNone(reg_infos) + values = {} + + for reg_info in reg_infos: + # We append a register index when load reg infos so we can work with subsets. + reg_index = reg_info.get("lldb_register_index") + self.assertIsNotNone(reg_index) + + # Handle thread suffix. + if thread_id: + p_request = "read packet: $p{:x};thread:{:x}#00".format(reg_index, thread_id) + else: + p_request = "read packet: $p{:x}#00".format(reg_index) + + # Read it with p. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + p_request, + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Convert value from target endian to integral. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + self.assertTrue(len(p_response) > 0) + self.assertFalse(p_response[0] == "E") + + values[reg_index] = unpack_register_hex_unsigned(endian, p_response) + + return values \ No newline at end of file