[Lldb-commits] [lldb] r211381 - Added gdb-remote tests for Q{Save, Restore}RegisterState.

Todd Fiala todd.fiala at gmail.com
Fri Jun 20 10:39:25 PDT 2014


Author: tfiala
Date: Fri Jun 20 12:39:24 2014
New Revision: 211381

URL: http://llvm.org/viewvc/llvm-project?rev=211381&view=rev
Log:
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.

Added:
    lldb/trunk/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py
Modified:
    lldb/trunk/test/tools/lldb-gdbserver/TestLldbGdbServer.py
    lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py

Added: lldb/trunk/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py?rev=211381&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/TestGdbRemoteRegisterState.py Fri Jun 20 12:39:24 2014
@@ -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()

Modified: lldb/trunk/test/tools/lldb-gdbserver/TestLldbGdbServer.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/TestLldbGdbServer.py?rev=211381&r1=211380&r2=211381&view=diff
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/TestLldbGdbServer.py (original)
+++ lldb/trunk/test/tools/lldb-gdbserver/TestLldbGdbServer.py Fri Jun 20 12:39:24 2014
@@ -1670,99 +1670,6 @@ class LldbGdbServerTestCase(gdbremote_te
         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_te
 
         # 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)

Modified: lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py?rev=211381&r1=211380&r2=211381&view=diff
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py (original)
+++ lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py Fri Jun 20 12:39:24 2014
@@ -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





More information about the lldb-commits mailing list