"""Check that compiler-generated register values work correctly""" import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil def re_expr_equals(val_type, val): # Match ({val_type}) ${sum_digits} = {val} return re.compile(r"\(" + val_type + r"\) \$\d+ = " + str(val)) class RegisterVariableTestCase(TestBase): @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. lldbutil.check_breakpoint( self, bpno=1, location_id=1, expected_location_hit_count=1 ) # Try some variables that should be visible frame = ( self.dbg.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() ) if self.is_variable_in_register(frame, "a"): register_variables_count += 1 self.expect( "expr a", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals("int", 2)], ) if self.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. lldbutil.check_breakpoint( self, bpno=1, location_id=2, expected_location_hit_count=1 ) # Try some variables that should be visible frame = ( self.dbg.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() ) if self.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 self.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. lldbutil.check_breakpoint( self, bpno=1, location_id=3, expected_location_hit_count=1 ) # Try some variables that should be visible frame = ( self.dbg.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() ) if self.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.assertGreater( register_variables_count, 0, "expected to verify at least one variable in a register", ) self.trace( "executed {} expressions with values in registers".format( register_variables_count ) ) self.runCmd("kill") def is_variable_in_register(self, frame, var_name): # Ensure we can lookup the variable. var = frame.FindVariable(var_name) self.trace("\nchecking {}...".format(var_name)) if var is None or not var.IsValid(): self.trace("{} 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() self.trace("checking value...") if value is None: self.trace("value is invalid") return False else: self.trace("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() self.trace("checking address...") if var_addr.IsValid(): # We have an address, it must not be in a register. self.trace( "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. self.trace( "var {} is in a register (we don't have an address for it)".format( var_name ) ) return True def is_struct_pointer_in_register(self, 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(): self.trace("{} 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() self.trace("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. self.trace("frame var {} is not in a register".format(var_name)) return False else: self.trace("frame var {} is in a register".format(var_name)) return True