1 """ 2 Abstract base class of basic types provides a generic type tester method. 3 """ 4 5 import os, time 6 import re 7 import lldb 8 from lldbtest import * 9 import lldbutil 10 11 def Msg(var, val, using_frame_variable): 12 return "'%s %s' matches the output (from compiled code): %s" % ( 13 'frame variable --show-types' if using_frame_variable else 'expression' ,var, val) 14 15 class GenericTester(TestBase): 16 17 # This is the pattern by design to match the " var = 'value'" output from 18 # printf() stmts (see basic_type.cpp). 19 pattern = re.compile(" (\*?a[^=]*) = '([^=]*)'$") 20 21 # Assert message. 22 DATA_TYPE_GROKKED = "Data type from expr parser output is parsed correctly" 23 24 def setUp(self): 25 # Call super's setUp(). 26 TestBase.setUp(self) 27 # We'll use the test method name as the exe_name. 28 # There are a bunch of test cases under test/types and we don't want the 29 # module cacheing subsystem to be confused with executable name "a.out" 30 # used for all the test cases. 31 self.exe_name = self.testMethodName 32 33 def tearDown(self): 34 """Cleanup the test byproducts.""" 35 TestBase.tearDown(self) 36 #print "Removing golden-output.txt..." 37 os.remove("golden-output.txt") 38 39 #==========================================================================# 40 # Functions build_and_run() and build_and_run_expr() are generic functions # 41 # which are called from the Test*Types*.py test cases. The API client is # 42 # responsible for supplying two mandatory arguments: the source file, e.g.,# 43 # 'int.cpp', and the atoms, e.g., set(['unsigned', 'long long']) to the # 44 # functions. There are also three optional keyword arguments of interest, # 45 # as follows: # 46 # # 47 # dsym -> build for dSYM (defaulted to True) # 48 # True: build dSYM file # 49 # False: build DWARF map # 50 # bc -> blockCaptured (defaulted to False) # 51 # True: testing vars of various basic types from isnide a block # 52 # False: testing vars of various basic types from a function # 53 # qd -> quotedDisplay (defaulted to False) # 54 # True: the output from 'frame var' or 'expr var' contains a pair # 55 # of single quotes around the value # 56 # False: no single quotes are to be found around the value of # 57 # variable # 58 #==========================================================================# 59 60 def build_and_run(self, source, atoms, dsym=True, bc=False, qd=False): 61 self.build_and_run_with_source_atoms_expr(source, atoms, expr=False, dsym=dsym, bc=bc, qd=qd) 62 63 def build_and_run_expr(self, source, atoms, dsym=True, bc=False, qd=False): 64 self.build_and_run_with_source_atoms_expr(source, atoms, expr=True, dsym=dsym, bc=bc, qd=qd) 65 66 def build_and_run_with_source_atoms_expr(self, source, atoms, expr, dsym=True, bc=False, qd=False): 67 # See also Makefile and basic_type.cpp:177. 68 if bc: 69 d = {'CXX_SOURCES': source, 'EXE': self.exe_name, 'CFLAGS_EXTRAS': '-DTEST_BLOCK_CAPTURED_VARS'} 70 else: 71 d = {'CXX_SOURCES': source, 'EXE': self.exe_name} 72 if dsym: 73 self.buildDsym(dictionary=d) 74 else: 75 self.buildDwarf(dictionary=d) 76 self.setTearDownCleanup(dictionary=d) 77 if expr: 78 self.generic_type_expr_tester(self.exe_name, atoms, blockCaptured=bc, quotedDisplay=qd) 79 else: 80 self.generic_type_tester(self.exe_name, atoms, blockCaptured=bc, quotedDisplay=qd) 81 82 def generic_type_tester(self, exe_name, atoms, quotedDisplay=False, blockCaptured=False): 83 """Test that variables with basic types are displayed correctly.""" 84 85 self.runCmd("file %s" % exe_name, CURRENT_EXECUTABLE_SET) 86 87 # First, capture the golden output emitted by the oracle, i.e., the 88 # series of printf statements. 89 self.runCmd("process launch -o golden-output.txt") 90 with open("golden-output.txt") as f: 91 go = f.read() 92 93 # This golden list contains a list of (variable, value) pairs extracted 94 # from the golden output. 95 gl = [] 96 97 # Scan the golden output line by line, looking for the pattern: 98 # 99 # variable = 'value' 100 # 101 for line in go.split(os.linesep): 102 # We'll ignore variables of array types from inside a block. 103 if blockCaptured and '[' in line: 104 continue 105 match = self.pattern.search(line) 106 if match: 107 var, val = match.group(1), match.group(2) 108 gl.append((var, val)) 109 #print "golden list:", gl 110 111 # This test uses a #include of a the "basic_type.cpp" so we need to enable 112 # always setting inlined breakpoints. 113 self.runCmd('settings set target.inline-breakpoint-strategy always') 114 # And add hooks to restore the settings during tearDown(). 115 self.addTearDownHook( 116 lambda: self.runCmd("settings set target.inline-breakpoint-strategy headers")) 117 118 # Bring the program to the point where we can issue a series of 119 # 'frame variable --show-types' command. 120 if blockCaptured: 121 break_line = line_number ("basic_type.cpp", "// Break here to test block captured variables.") 122 else: 123 break_line = line_number ("basic_type.cpp", "// Here is the line we will break on to check variables.") 124 lldbutil.run_break_set_by_file_and_line (self, "basic_type.cpp", break_line, num_expected_locations=1, loc_exact=True) 125 126 self.runCmd("run", RUN_SUCCEEDED) 127 self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, 128 substrs = [" at basic_type.cpp:%d" % break_line, 129 "stop reason = breakpoint"]) 130 131 #self.runCmd("frame variable --show-types") 132 133 # Now iterate through the golden list, comparing against the output from 134 # 'frame variable --show-types var'. 135 for var, val in gl: 136 self.runCmd("frame variable --show-types %s" % var) 137 output = self.res.GetOutput() 138 139 # The input type is in a canonical form as a set of named atoms. 140 # The display type string must conatin each and every element. 141 # 142 # Example: 143 # runCmd: frame variable --show-types a_array_bounded[0] 144 # output: (char) a_array_bounded[0] = 'a' 145 # 146 try: 147 dt = re.match("^\((.*)\)", output).group(1) 148 except: 149 self.fail(self.DATA_TYPE_GROKKED) 150 151 # Expect the display type string to contain each and every atoms. 152 self.expect(dt, 153 "Display type: '%s' must contain the type atoms: '%s'" % 154 (dt, atoms), 155 exe=False, 156 substrs = list(atoms)) 157 158 # The (var, val) pair must match, too. 159 nv = ("%s = '%s'" if quotedDisplay else "%s = %s") % (var, val) 160 self.expect(output, Msg(var, val, True), exe=False, 161 substrs = [nv]) 162 163 def generic_type_expr_tester(self, exe_name, atoms, quotedDisplay=False, blockCaptured=False): 164 """Test that variable expressions with basic types are evaluated correctly.""" 165 166 self.runCmd("file %s" % exe_name, CURRENT_EXECUTABLE_SET) 167 168 # First, capture the golden output emitted by the oracle, i.e., the 169 # series of printf statements. 170 self.runCmd("process launch -o golden-output.txt") 171 with open("golden-output.txt") as f: 172 go = f.read() 173 174 # This golden list contains a list of (variable, value) pairs extracted 175 # from the golden output. 176 gl = [] 177 178 # Scan the golden output line by line, looking for the pattern: 179 # 180 # variable = 'value' 181 # 182 for line in go.split(os.linesep): 183 # We'll ignore variables of array types from inside a block. 184 if blockCaptured and '[' in line: 185 continue 186 match = self.pattern.search(line) 187 if match: 188 var, val = match.group(1), match.group(2) 189 gl.append((var, val)) 190 #print "golden list:", gl 191 192 # This test uses a #include of a the "basic_type.cpp" so we need to enable 193 # always setting inlined breakpoints. 194 self.runCmd('settings set target.inline-breakpoint-strategy always') 195 # And add hooks to restore the settings during tearDown(). 196 self.addTearDownHook( 197 lambda: self.runCmd("settings set target.inline-breakpoint-strategy headers")) 198 199 # Bring the program to the point where we can issue a series of 200 # 'expr' command. 201 if blockCaptured: 202 break_line = line_number ("basic_type.cpp", "// Break here to test block captured variables.") 203 else: 204 break_line = line_number ("basic_type.cpp", "// Here is the line we will break on to check variables.") 205 lldbutil.run_break_set_by_file_and_line (self, "basic_type.cpp", break_line, num_expected_locations=1, loc_exact=True) 206 207 self.runCmd("run", RUN_SUCCEEDED) 208 self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, 209 substrs = [" at basic_type.cpp:%d" % break_line, 210 "stop reason = breakpoint"]) 211 212 #self.runCmd("frame variable --show-types") 213 214 # Now iterate through the golden list, comparing against the output from 215 # 'expr var'. 216 for var, val in gl: 217 # Don't overwhelm the expression mechanism. 218 # This slows down the test suite quite a bit, to enable it, define 219 # the environment variable LLDB_TYPES_EXPR_TIME_WAIT. For example: 220 # 221 # export LLDB_TYPES_EXPR_TIME_WAIT=0.5 222 # 223 # causes a 0.5 second delay between 'expression' commands. 224 if "LLDB_TYPES_EXPR_TIME_WAIT" in os.environ: 225 time.sleep(float(os.environ["LLDB_TYPES_EXPR_TIME_WAIT"])) 226 227 self.runCmd("expression %s" % var) 228 output = self.res.GetOutput() 229 230 # The input type is in a canonical form as a set of named atoms. 231 # The display type string must conatin each and every element. 232 # 233 # Example: 234 # runCmd: expr a 235 # output: (double) $0 = 1100.12 236 # 237 try: 238 dt = re.match("^\((.*)\) \$[0-9]+ = ", output).group(1) 239 except: 240 self.fail(self.DATA_TYPE_GROKKED) 241 242 # Expect the display type string to contain each and every atoms. 243 self.expect(dt, 244 "Display type: '%s' must contain the type atoms: '%s'" % 245 (dt, atoms), 246 exe=False, 247 substrs = list(atoms)) 248 249 # The val part must match, too. 250 valPart = ("'%s'" if quotedDisplay else "%s") % val 251 self.expect(output, Msg(var, val, False), exe=False, 252 substrs = [valPart]) 253