"""Check that compiler-generated register values work correctly""" from __future__ import print_function import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil # This method attempts to figure out if a given variable # is in a register. # # Return: # True if the value has a readable value and is in a register # False otherwise def is_variable_in_register(frame, var_name): # Ensure we can lookup the variable. var = frame.FindVariable(var_name) # print("\nchecking {}...".format(var_name)) if var is None or not var.IsValid(): # print("{} cannot be found".format(var_name)) return False # Check that we can get its value. If not, this # may be a variable that is just out of scope at this point. value = var.GetValue() # print("checking value...") if value is None: # print("value is invalid") return False # else: # print("value is {}".format(value)) # We have a variable and we can get its value. The variable is in # a register if we cannot get an address for it, assuming it is # not a struct pointer. (This is an approximation - compilers can # do other things with spitting up a value into multiple parts of # multiple registers, but what we're verifying here is much more # than it was doing before). var_addr = var.GetAddress() # print("checking address...") if var_addr.IsValid(): # We have an address, it must not be in a register. # print("var {} is not in a register: has a valid address {}".format(var_name, var_addr)) return False else: # We don't have an address but we can read the value. # It is likely stored in a register. # print("var {} is in a register (we don't have an address for it)".format(var_name)) return True def is_struct_pointer_in_register(frame, var_name, trace): # Ensure we can lookup the variable. var = frame.FindVariable(var_name) if trace: print("\nchecking {}...".format(var_name)) if var is None or not var.IsValid(): # print("{} cannot be found".format(var_name)) return False # Check that we can get its value. If not, this # may be a variable that is just out of scope at this point. value = var.GetValue() # print("checking value...") if value is None: if trace: print("value is invalid") return False else: if trace: print("value is {}".format(value)) var_loc = var.GetLocation() if trace: print("checking location: {}".format(var_loc)) if var_loc is None or var_loc.startswith("0x"): # The frame var is not in a register but rather a memory location. # print("frame var {} is not in a register".format(var_name)) return False else: # print("frame var {} is in a register".format(var_name)) return True def re_expr_equals(val_type, val): # Match ({val_type}) ${sum_digits} = {val} return re.compile(r'\(' + val_type + '\) \$\d+ = ' + str(val)) class RegisterVariableTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(compiler="clang", compiler_version=['<', '3.5']) @expectedFailureAll(compiler="gcc", compiler_version=[ '>=', '4.8.2'], archs=["i386"]) @expectedFailureAll(compiler="gcc", compiler_version=[ '<', '4.9'], archs=["x86_64"]) def test_and_run_command(self): """Test expressions on register values.""" # This test now ensures that each probable # register variable location is actually a register, and # if so, whether we can print out the variable there. # It only requires one of them to be handled in a non-error # way. register_variables_count = 0 self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Break inside the main. lldbutil.run_break_set_by_source_regexp( self, "break", num_expected_locations=3) #################### # First breakpoint self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Try some variables that should be visible frame = self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() if is_variable_in_register(frame, 'a'): register_variables_count += 1 self.expect("expr a", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 2)]) if is_struct_pointer_in_register(frame, 'b', self.TraceOn()): register_variables_count += 1 self.expect("expr b->m1", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 3)]) ##################### # Second breakpoint self.runCmd("continue") # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Try some variables that should be visible frame = self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() if is_struct_pointer_in_register(frame, 'b', self.TraceOn()): register_variables_count += 1 self.expect("expr b->m2", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 5)]) if is_variable_in_register(frame, 'c'): register_variables_count += 1 self.expect("expr c", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 5)]) ##################### # Third breakpoint self.runCmd("continue") # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Try some variables that should be visible frame = self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() if is_variable_in_register(frame, 'f'): register_variables_count += 1 self.expect("expr f", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('float', '3.1')]) # Validate that we verified at least one register variable self.assertTrue( register_variables_count > 0, "expected to verify at least one variable in a register") # print("executed {} expressions with values in registers".format(register_variables_count)) self.runCmd("kill")