| """Check that compiler-generated register values work correctly""" |
| |
| from __future__ import print_function |
| |
| import os |
| import time |
| 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") |