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