From 58c4dd7a71300a0e542efc2de0ce6a605627913a Mon Sep 17 00:00:00 2001 From: Todd Fiala Date: Wed, 6 Apr 2016 01:14:37 +0000 Subject: [PATCH] make TestRegisterVariables slightly more resilient This test sets the compiler optimization level to -O1 and makes some assumptions about how local frame vars will be stored (i.e. in registers). These assumptions are not always true. I did a first-pass set of improvements that: (1) no longer assumes that every one of the target locations has every variable in a register. Sometimes the compiler is even smarter and skips the register entirely. (2) simply expects one of the 5 or so variables it checks to be in a register. This test probably passes on a whole lot more systems than it used to now. This is certainly true on OS X. llvm-svn: 265498 --- .../test/lang/c/register_variables/Makefile | 8 +- .../c/register_variables/TestRegisterVariables.py | 131 ++++++++++++++++++--- 2 files changed, 123 insertions(+), 16 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile b/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile index 12e5561..0b2d3a2 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile +++ b/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile @@ -2,6 +2,12 @@ LEVEL = ../../../make C_SOURCES := test.c -CFLAGS ?= -g -O1 +CFLAGS := -g -O1 + +$(info OS: $(OS)) +HOST_OS = $(shell uname -s) +ifeq "$(HOST_OS)" "Darwin" + FRAMEWORK_INCLUDES = -isysroot $(shell xcrun -show-sdk-path) +endif include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py b/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py index 3a95e53..73db3d5 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py +++ b/lldb/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py @@ -2,23 +2,107 @@ from __future__ import print_function - - import os, 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): + # 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)) + + var_loc = var.GetLocation() + # 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(oslist=['macosx'], compiler='clang', compiler_version=['<', '7.0.0'], debug_info="dsym") - @expectedFailureAll(compiler="clang") - @expectedFailureAll(compiler="gcc", compiler_version=['>=', '4.8.2']) + + @expectedFailureAll(compiler="clang", compiler_version=['<', '3.5']) + @expectedFailureAll(compiler="gcc", compiler_version=['=', '4.8.2']) 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 = os.path.join(os.getcwd(), "a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) @@ -41,11 +125,16 @@ class RegisterVariableTestCase(TestBase): substrs = [' resolved, hit count = 1']) # Try some variables that should be visible - self.expect("expr a", VARIABLES_DISPLAYED_CORRECTLY, - substrs = ['(int) $0 = 2']) + 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)]) - self.expect("expr b->m1", VARIABLES_DISPLAYED_CORRECTLY, - substrs = ['(int) $1 = 3']) + if is_struct_pointer_in_register(frame, 'b'): + register_variables_count += 1 + self.expect("expr b->m1", VARIABLES_DISPLAYED_CORRECTLY, + patterns = [re_expr_equals('int', 3)]) ##################### # Second breakpoint @@ -62,11 +151,16 @@ class RegisterVariableTestCase(TestBase): substrs = [' resolved, hit count = 1']) # Try some variables that should be visible - self.expect("expr b->m2", VARIABLES_DISPLAYED_CORRECTLY, - substrs = ['(int) $2 = 5']) + frame = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + if is_struct_pointer_in_register(frame, 'b'): + register_variables_count += 1 + self.expect("expr b->m2", VARIABLES_DISPLAYED_CORRECTLY, + patterns = [re_expr_equals('int', 5)]) - self.expect("expr c", VARIABLES_DISPLAYED_CORRECTLY, - substrs = ['(int) $3 = 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 @@ -83,7 +177,14 @@ class RegisterVariableTestCase(TestBase): substrs = [' resolved, hit count = 1']) # Try some variables that should be visible - self.expect("expr f", VARIABLES_DISPLAYED_CORRECTLY, - substrs = ['(float) $4 = 3.1']) + 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") -- 2.7.4