1""" 2Test lldb data formatter subsystem. 3""" 4 5import lldb 6from lldbsuite.test.decorators import * 7from lldbsuite.test.lldbtest import * 8from lldbsuite.test import lldbutil 9 10 11class PythonSynthDataFormatterTestCase(TestBase): 12 13 def test_with_run_command(self): 14 """Test data formatter commands.""" 15 self.build() 16 self.data_formatter_commands() 17 18 def test_rdar10960550_with_run_command(self): 19 """Test data formatter commands.""" 20 self.build() 21 self.rdar10960550_formatter_commands() 22 23 def setUp(self): 24 # Call super's setUp(). 25 TestBase.setUp(self) 26 # Find the line number to break at. 27 self.line = line_number('main.cpp', '// Set break point at this line.') 28 self.line2 = line_number('main.cpp', 29 '// Set cast break point at this line.') 30 self.line3 = line_number( 31 'main.cpp', '// Set second cast break point at this line.') 32 33 def data_formatter_commands(self): 34 """Test using Python synthetic children provider.""" 35 36 _, process, thread, _ = lldbutil.run_to_line_breakpoint( 37 self, lldb.SBFileSpec("main.cpp"), self.line) 38 39 # This is the function to remove the custom formats in order to have a 40 # clean slate for the next test case. 41 def cleanup(): 42 self.runCmd('type format clear', check=False) 43 self.runCmd('type summary clear', check=False) 44 self.runCmd('type filter clear', check=False) 45 self.runCmd('type synth clear', check=False) 46 47 # Execute the cleanup function during test case tear down. 48 self.addTearDownHook(cleanup) 49 50 # print the f00_1 variable without a synth 51 self.expect("frame variable f00_1", 52 substrs=['a = 1', 53 'b = 2', 54 'r = 34']) 55 56 # now set up the synth 57 self.runCmd("script from fooSynthProvider import *") 58 self.runCmd("type synth add -l fooSynthProvider foo") 59 self.runCmd("type synth add -l wrapfooSynthProvider wrapfoo") 60 self.expect("type synthetic list foo", substrs=['fooSynthProvider']) 61 62 # note that the value of fake_a depends on target byte order 63 if process.GetByteOrder() == lldb.eByteOrderLittle: 64 fake_a_val = 0x02000000 65 else: 66 fake_a_val = 0x00000100 67 68 # check that we get the two real vars and the fake_a variables 69 self.expect( 70 "frame variable f00_1", 71 substrs=[ 72 'a = 1', 73 'fake_a = %d' % fake_a_val, 74 'r = 34', 75 ]) 76 77 # check that we do not get the extra vars 78 self.expect("frame variable f00_1", matching=False, 79 substrs=['b = 2']) 80 81 # check access to members by name 82 self.expect('frame variable f00_1.fake_a', 83 substrs=['%d' % fake_a_val]) 84 85 # check access to members by index 86 self.expect('frame variable f00_1[1]', 87 substrs=['%d' % fake_a_val]) 88 89 # put synthetic children in summary in several combinations 90 self.runCmd( 91 "type summary add --summary-string \"fake_a=${svar.fake_a}\" foo") 92 self.expect('frame variable f00_1', 93 substrs=['fake_a=%d' % fake_a_val]) 94 self.runCmd( 95 "type summary add --summary-string \"fake_a=${svar[1]}\" foo") 96 self.expect('frame variable f00_1', 97 substrs=['fake_a=%d' % fake_a_val]) 98 99 # clear the summary 100 self.runCmd("type summary delete foo") 101 102 # check that the caching does not span beyond the stopoint 103 self.runCmd("n") 104 105 if process.GetByteOrder() == lldb.eByteOrderLittle: 106 fake_a_val = 0x02000000 107 else: 108 fake_a_val = 0x00000200 109 110 self.expect( 111 "frame variable f00_1", 112 substrs=[ 113 'a = 2', 114 'fake_a = %d' % fake_a_val, 115 'r = 34', 116 ]) 117 118 # check that altering the object also alters fake_a 119 self.runCmd("expr f00_1.a = 280") 120 121 if process.GetByteOrder() == lldb.eByteOrderLittle: 122 fake_a_val = 0x02000001 123 else: 124 fake_a_val = 0x00011800 125 126 self.expect( 127 "frame variable f00_1", 128 substrs=[ 129 'a = 280', 130 'fake_a = %d' % fake_a_val, 131 'r = 34', 132 ]) 133 134 # check that expanding a pointer does the right thing 135 if process.GetByteOrder() == lldb.eByteOrderLittle: 136 fake_a_val = 0x0d000000 137 else: 138 fake_a_val = 0x00000c00 139 140 self.expect( 141 "frame variable --ptr-depth 1 f00_ptr", 142 substrs=[ 143 'a = 12', 144 'fake_a = %d' % fake_a_val, 145 'r = 45', 146 ]) 147 self.expect( 148 "frame variable --ptr-depth 1 wrapper", 149 substrs=[ 150 'a = 12', 151 'fake_a = %d' % fake_a_val, 152 'r = 45', 153 ]) 154 155 # now add a filter.. it should fail 156 self.expect("type filter add foo --child b --child j", error=True, 157 substrs=['cannot add']) 158 159 # we get the synth again.. 160 self.expect('frame variable f00_1', matching=False, 161 substrs=['b = 1', 162 'j = 17']) 163 self.expect( 164 "frame variable --ptr-depth 1 f00_ptr", 165 substrs=[ 166 'a = 12', 167 'fake_a = %d' % fake_a_val, 168 'r = 45', 169 ]) 170 self.expect( 171 "frame variable --ptr-depth 1 wrapper", 172 substrs=[ 173 'a = 12', 174 'fake_a = %d' % fake_a_val, 175 'r = 45', 176 ]) 177 178 # Test that the custom dereference operator for `wrapfoo` works through 179 # the Python API. The synthetic children provider gets queried at 180 # slightly different times in this case. 181 wrapper_var = thread.GetSelectedFrame().FindVariable('wrapper') 182 foo_var = wrapper_var.Dereference() 183 self.assertEqual(foo_var.GetNumChildren(), 3) 184 self.assertEqual(foo_var.GetChildAtIndex(0).GetName(), 'a') 185 self.assertEqual(foo_var.GetChildAtIndex(1).GetName(), 'fake_a') 186 self.assertEqual(foo_var.GetChildAtIndex(2).GetName(), 'r') 187 188 # now delete the synth and add the filter 189 self.runCmd("type synth delete foo") 190 self.runCmd("type synth delete wrapfoo") 191 self.runCmd("type filter add foo --child b --child j") 192 193 self.expect('frame variable f00_1', 194 substrs=['b = 2', 195 'j = 18']) 196 self.expect("frame variable --ptr-depth 1 f00_ptr", matching=False, 197 substrs=['r = 45', 198 'fake_a = %d' % fake_a_val, 199 'a = 12']) 200 self.expect("frame variable --ptr-depth 1 wrapper", matching=False, 201 substrs=['r = 45', 202 'fake_a = %d' % fake_a_val, 203 'a = 12']) 204 205 # now add the synth and it should fail 206 self.expect("type synth add -l fooSynthProvider foo", error=True, 207 substrs=['cannot add']) 208 209 # check the listing 210 self.expect('type synth list', matching=False, 211 substrs=['foo', 212 'Python class fooSynthProvider']) 213 self.expect('type filter list', 214 substrs=['foo', 215 '.b', 216 '.j']) 217 218 # delete the filter, add the synth 219 self.runCmd("type filter delete foo") 220 self.runCmd("type synth add -l fooSynthProvider foo") 221 222 self.expect('frame variable f00_1', matching=False, 223 substrs=['b = 2', 224 'j = 18']) 225 self.expect( 226 "frame variable --ptr-depth 1 f00_ptr", 227 substrs=[ 228 'a = 12', 229 'fake_a = %d' % fake_a_val, 230 'r = 45', 231 ]) 232 self.expect( 233 "frame variable --ptr-depth 1 wrapper", 234 substrs=[ 235 'a = 12', 236 'fake_a = %d' % fake_a_val, 237 'r = 45', 238 ]) 239 240 # check the listing 241 self.expect('type synth list', 242 substrs=['foo', 243 'Python class fooSynthProvider']) 244 self.expect('type filter list', matching=False, 245 substrs=['foo', 246 '.b', 247 '.j']) 248 249 # delete the synth and check that we get good output 250 self.runCmd("type synth delete foo") 251 252 self.expect("frame variable f00_1", 253 substrs=['a = 280', 254 'b = 2', 255 'j = 18']) 256 257 self.expect("frame variable f00_1", matching=False, 258 substrs=['fake_a = ']) 259 260 # check that we don't feed a regex into another regex when checking for 261 # existing conflicting synth/filters. The two following expressions 262 # accept different types: one will accept types that look like an array 263 # of MyType, the other will accept types that contain "MyType1" or 264 # "MyType2". But the second regex looks like an array of MyType, so 265 # lldb used to incorrectly reject it. 266 self.runCmd(r'type synth add -l fooSynthProvider -x "^MyType\[[0-9]+]$"') 267 self.runCmd(r'type filter add --child a -x "MyType[12]"') 268 269 # Same, but adding the filter first to verify the check when doing 270 # `type synth add`. We need to delete the synth from the previous test 271 # first. 272 self.runCmd(r'type synth delete "^MyType\[[0-9]+]$"') 273 self.runCmd(r'type filter add --child a -x "^MyType\[[0-9]+]$"') 274 self.runCmd(r'type synth add -l fooSynthProvider -x "MyType[12]"') 275 276 def rdar10960550_formatter_commands(self): 277 """Test that synthetic children persist stoppoints.""" 278 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 279 280 # The second breakpoint is on a multi-line expression, so the comment 281 # can't be on the right line... 282 lldbutil.run_break_set_by_file_and_line( 283 self, "main.cpp", self.line2, num_expected_locations=1, loc_exact=False) 284 lldbutil.run_break_set_by_file_and_line( 285 self, "main.cpp", self.line3, num_expected_locations=1, loc_exact=True) 286 287 self.runCmd("run", RUN_SUCCEEDED) 288 289 # The stop reason of the thread should be breakpoint. 290 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 291 substrs=['stopped', 292 'stop reason = breakpoint']) 293 294 # This is the function to remove the custom formats in order to have a 295 # clean slate for the next test case. 296 def cleanup(): 297 self.runCmd('type format clear', check=False) 298 self.runCmd('type summary clear', check=False) 299 self.runCmd('type filter clear', check=False) 300 self.runCmd('type synth clear', check=False) 301 302 # Execute the cleanup function during test case tear down. 303 self.addTearDownHook(cleanup) 304 305 self.runCmd("command script import ./ftsp.py --allow-reload") 306 self.runCmd("type synth add -l ftsp.ftsp wrapint") 307 308 # we need to check that the VO is properly updated so that the same synthetic children are reused 309 # but their values change correctly across stop-points - in order to do this, self.runCmd("next") 310 # does not work because it forces a wipe of the stack frame - this is why we are using this more contrived 311 # mechanism to achieve our goal of preserving test_cast as a VO 312 test_cast = self.dbg.GetSelectedTarget().GetProcess( 313 ).GetSelectedThread().GetSelectedFrame().FindVariable('test_cast') 314 315 str_cast = str(test_cast) 316 317 if self.TraceOn(): 318 print(str_cast) 319 320 self.assertTrue(str_cast.find('A') != -1, 'could not find A in output') 321 self.assertTrue(str_cast.find('B') != -1, 'could not find B in output') 322 self.assertTrue(str_cast.find('C') != -1, 'could not find C in output') 323 self.assertTrue(str_cast.find('D') != -1, 'could not find D in output') 324 self.assertTrue( 325 str_cast.find("4 = '\\0'") != -1, 326 'could not find item 4 == 0') 327 328 self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().StepOver() 329 330 str_cast = str(test_cast) 331 332 if self.TraceOn(): 333 print(str_cast) 334 335 # we detect that all the values of the child objects have changed - but the counter-generated item 336 # is still fixed at 0 because it is cached - this would fail if update(self): in ftsp returned False 337 # or if synthetic children were not being preserved 338 self.assertTrue(str_cast.find('Q') != -1, 'could not find Q in output') 339 self.assertTrue(str_cast.find('X') != -1, 'could not find X in output') 340 self.assertTrue(str_cast.find('T') != -1, 'could not find T in output') 341 self.assertTrue(str_cast.find('F') != -1, 'could not find F in output') 342 self.assertTrue( 343 str_cast.find("4 = '\\0'") != -1, 344 'could not find item 4 == 0') 345