1""" 2Test breakpoint serialization. 3""" 4 5import os 6import json 7import lldb 8from lldbsuite.test.decorators import * 9from lldbsuite.test.lldbtest import * 10from lldbsuite.test import lldbutil 11 12 13class BreakpointSerialization(TestBase): 14 NO_DEBUG_INFO_TESTCASE = True 15 16 @add_test_categories(["pyapi"]) 17 def test_resolvers(self): 18 """Use Python APIs to test that we serialize resolvers.""" 19 self.build() 20 self.setup_targets_and_cleanup() 21 self.do_check_resolvers() 22 23 def test_filters(self): 24 """Use Python APIs to test that we serialize search filters correctly.""" 25 self.build() 26 self.setup_targets_and_cleanup() 27 self.do_check_filters() 28 29 def test_options(self): 30 """Use Python APIs to test that we serialize breakpoint options correctly.""" 31 self.build() 32 self.setup_targets_and_cleanup() 33 self.do_check_options() 34 35 def test_appending(self): 36 """Use Python APIs to test that we serialize breakpoint options correctly.""" 37 self.build() 38 self.setup_targets_and_cleanup() 39 self.do_check_appending() 40 41 def test_name_filters(self): 42 """Use python APIs to test that reading in by name works correctly.""" 43 self.build() 44 self.setup_targets_and_cleanup() 45 self.do_check_names() 46 47 def test_scripted_extra_args(self): 48 self.build() 49 self.setup_targets_and_cleanup() 50 self.do_check_extra_args() 51 52 def test_resolver_serialization(self): 53 """Test that breakpoint resolvers contain the expected information""" 54 self.build() 55 self.setup_targets_and_cleanup() 56 57 exe_path = self.getBuildArtifact("a.out") 58 exe_module = self.orig_target.module["a.out"] 59 self.assertTrue( 60 exe_module.IsValid(), "Failed to find the executable module in target" 61 ) 62 sym_ctx_list = exe_module.FindFunctions("main") 63 self.assertEqual(sym_ctx_list.GetSize(), 1, "Unable to find function 'main'") 64 sym_ctx = sym_ctx_list.GetContextAtIndex(0) 65 self.assertTrue( 66 sym_ctx.IsValid(), "SBSymbolContext representing function 'main' is invalid" 67 ) 68 main_func = sym_ctx.GetFunction() 69 self.assertTrue( 70 main_func.IsValid(), "SBFunction representing 'main' is invalid" 71 ) 72 main_addr = main_func.GetStartAddress() 73 74 bkpt = self.orig_target.BreakpointCreateBySBAddress(main_addr) 75 self.assertTrue( 76 bkpt.IsValid(), "Could not place breakpoint on 'main' by address" 77 ) 78 stream = lldb.SBStream() 79 sd = bkpt.SerializeToStructuredData() 80 sd.GetAsJSON(stream) 81 serialized_data = json.loads(stream.GetData()) 82 83 self.assertIn( 84 exe_path, 85 serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["ModuleName"], 86 ) 87 88 def test_structured_data_serialization(self): 89 target = self.dbg.GetDummyTarget() 90 self.assertTrue(target.IsValid(), VALID_TARGET) 91 92 interpreter = self.dbg.GetCommandInterpreter() 93 result = lldb.SBCommandReturnObject() 94 interpreter.HandleCommand("br set -f foo -l 42", result) 95 result = lldb.SBCommandReturnObject() 96 interpreter.HandleCommand("br set -c 'argc == 1' -n main", result) 97 98 bkp1 = target.GetBreakpointAtIndex(0) 99 self.assertTrue(bkp1.IsValid(), VALID_BREAKPOINT) 100 stream = lldb.SBStream() 101 sd = bkp1.SerializeToStructuredData() 102 sd.GetAsJSON(stream) 103 serialized_data = json.loads(stream.GetData()) 104 self.assertEqual( 105 serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["FileName"], "foo" 106 ) 107 self.assertEqual( 108 serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["LineNumber"], 42 109 ) 110 111 bkp2 = target.GetBreakpointAtIndex(1) 112 self.assertTrue(bkp2.IsValid(), VALID_BREAKPOINT) 113 stream = lldb.SBStream() 114 sd = bkp2.SerializeToStructuredData() 115 sd.GetAsJSON(stream) 116 serialized_data = json.loads(stream.GetData()) 117 self.assertIn( 118 "main", 119 serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["SymbolNames"], 120 ) 121 self.assertEqual( 122 serialized_data["Breakpoint"]["BKPTOptions"]["ConditionText"], "argc == 1" 123 ) 124 125 invalid_bkp = lldb.SBBreakpoint() 126 self.assertFalse(invalid_bkp.IsValid(), "Breakpoint should not be valid.") 127 stream = lldb.SBStream() 128 sd = invalid_bkp.SerializeToStructuredData() 129 sd.GetAsJSON(stream) 130 self.assertFalse( 131 stream.GetData(), "Invalid breakpoint should have an empty structured data" 132 ) 133 134 def setup_targets_and_cleanup(self): 135 def cleanup(): 136 self.RemoveTempFile(self.bkpts_file_path) 137 138 if self.orig_target.IsValid(): 139 self.dbg.DeleteTarget(self.orig_target) 140 self.dbg.DeleteTarget(self.copy_target) 141 142 self.addTearDownHook(cleanup) 143 self.RemoveTempFile(self.bkpts_file_path) 144 145 exe = self.getBuildArtifact("a.out") 146 147 # Create the targets we are making breakpoints in and copying them to: 148 self.orig_target = self.dbg.CreateTarget(exe) 149 self.assertTrue(self.orig_target, VALID_TARGET) 150 151 self.copy_target = self.dbg.CreateTarget(exe) 152 self.assertTrue(self.copy_target, VALID_TARGET) 153 154 def setUp(self): 155 # Call super's setUp(). 156 TestBase.setUp(self) 157 158 self.bkpts_file_path = self.getBuildArtifact("breakpoints.json") 159 self.bkpts_file_spec = lldb.SBFileSpec(self.bkpts_file_path) 160 161 def check_equivalence(self, source_bps, do_write=True): 162 error = lldb.SBError() 163 164 if do_write: 165 error = self.orig_target.BreakpointsWriteToFile( 166 self.bkpts_file_spec, source_bps 167 ) 168 self.assertSuccess(error, "Failed writing breakpoints to file") 169 170 copy_bps = lldb.SBBreakpointList(self.copy_target) 171 error = self.copy_target.BreakpointsCreateFromFile( 172 self.bkpts_file_spec, copy_bps 173 ) 174 self.assertSuccess(error, "Failed reading breakpoints from file") 175 176 num_source_bps = source_bps.GetSize() 177 num_copy_bps = copy_bps.GetSize() 178 self.assertEqual( 179 num_source_bps, 180 num_copy_bps, 181 "Didn't get same number of input and output breakpoints - orig: %d copy: %d" 182 % (num_source_bps, num_copy_bps), 183 ) 184 185 for i in range(0, num_source_bps): 186 source_bp = source_bps.GetBreakpointAtIndex(i) 187 source_desc = lldb.SBStream() 188 source_bp.GetDescription(source_desc, False) 189 source_text = source_desc.GetData() 190 191 # I am assuming here that the breakpoints will get written out in breakpoint ID order, and 192 # read back in ditto. That is true right now, and I can't see any reason to do it differently 193 # but if we do we can go to writing the breakpoints one by one, or sniffing the descriptions to 194 # see which one is which. 195 copy_id = source_bp.GetID() 196 copy_bp = copy_bps.FindBreakpointByID(copy_id) 197 self.assertTrue( 198 copy_bp.IsValid(), "Could not find copy breakpoint %d." % (copy_id) 199 ) 200 201 copy_desc = lldb.SBStream() 202 copy_bp.GetDescription(copy_desc, False) 203 copy_text = copy_desc.GetData() 204 205 # These two should be identical. 206 self.trace("Source text for %d is %s." % (i, source_text)) 207 self.assertEqual( 208 source_text, 209 copy_text, 210 "Source and dest breakpoints are not identical: \nsource: %s\ndest: %s" 211 % (source_text, copy_text), 212 ) 213 214 def do_check_resolvers(self): 215 """Use Python APIs to check serialization of breakpoint resolvers""" 216 217 empty_module_list = lldb.SBFileSpecList() 218 empty_cu_list = lldb.SBFileSpecList() 219 blubby_file_spec = lldb.SBFileSpec( 220 os.path.join(self.getSourceDir(), "blubby.c") 221 ) 222 223 # It isn't actually important for these purposes that these breakpoint 224 # actually have locations. 225 source_bps = lldb.SBBreakpointList(self.orig_target) 226 source_bps.Append(self.orig_target.BreakpointCreateByLocation("blubby.c", 666)) 227 # Make sure we do one breakpoint right: 228 self.check_equivalence(source_bps) 229 source_bps.Clear() 230 231 source_bps.Append( 232 self.orig_target.BreakpointCreateByName( 233 "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list 234 ) 235 ) 236 source_bps.Append( 237 self.orig_target.BreakpointCreateByName( 238 "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list 239 ) 240 ) 241 source_bps.Append( 242 self.orig_target.BreakpointCreateBySourceRegex( 243 "dont really care", blubby_file_spec 244 ) 245 ) 246 247 # And some number greater than one: 248 self.check_equivalence(source_bps) 249 250 def do_check_filters(self): 251 """Use Python APIs to check serialization of breakpoint filters.""" 252 module_list = lldb.SBFileSpecList() 253 module_list.Append(lldb.SBFileSpec("SomeBinary")) 254 module_list.Append(lldb.SBFileSpec("SomeOtherBinary")) 255 256 cu_list = lldb.SBFileSpecList() 257 cu_list.Append(lldb.SBFileSpec("SomeCU.c")) 258 cu_list.Append(lldb.SBFileSpec("AnotherCU.c")) 259 cu_list.Append(lldb.SBFileSpec("ThirdCU.c")) 260 261 blubby_file_spec = lldb.SBFileSpec( 262 os.path.join(self.getSourceDir(), "blubby.c") 263 ) 264 265 # It isn't actually important for these purposes that these breakpoint 266 # actually have locations. 267 source_bps = lldb.SBBreakpointList(self.orig_target) 268 bkpt = self.orig_target.BreakpointCreateByLocation( 269 blubby_file_spec, 666, 0, module_list 270 ) 271 source_bps.Append(bkpt) 272 273 # Make sure we do one right: 274 self.check_equivalence(source_bps) 275 source_bps.Clear() 276 277 bkpt = self.orig_target.BreakpointCreateByName( 278 "blubby", lldb.eFunctionNameTypeAuto, module_list, cu_list 279 ) 280 source_bps.Append(bkpt) 281 bkpt = self.orig_target.BreakpointCreateByName( 282 "blubby", lldb.eFunctionNameTypeFull, module_list, cu_list 283 ) 284 source_bps.Append(bkpt) 285 bkpt = self.orig_target.BreakpointCreateBySourceRegex( 286 "dont really care", blubby_file_spec 287 ) 288 source_bps.Append(bkpt) 289 290 # And some number greater than one: 291 self.check_equivalence(source_bps) 292 293 def do_check_options(self): 294 """Use Python APIs to check serialization of breakpoint options.""" 295 296 empty_module_list = lldb.SBFileSpecList() 297 empty_cu_list = lldb.SBFileSpecList() 298 blubby_file_spec = lldb.SBFileSpec( 299 os.path.join(self.getSourceDir(), "blubby.c") 300 ) 301 302 # It isn't actually important for these purposes that these breakpoint 303 # actually have locations. 304 source_bps = lldb.SBBreakpointList(self.orig_target) 305 306 bkpt = self.orig_target.BreakpointCreateByLocation( 307 lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList() 308 ) 309 bkpt.SetEnabled(False) 310 bkpt.SetOneShot(True) 311 bkpt.SetThreadID(10) 312 source_bps.Append(bkpt) 313 314 # Make sure we get one right: 315 self.check_equivalence(source_bps) 316 source_bps.Clear() 317 318 bkpt = self.orig_target.BreakpointCreateByName( 319 "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list 320 ) 321 bkpt.SetIgnoreCount(10) 322 bkpt.SetThreadName("grubby") 323 source_bps.Append(bkpt) 324 325 bkpt = self.orig_target.BreakpointCreateByName( 326 "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list 327 ) 328 bkpt.SetCondition("gonna remove this") 329 bkpt.SetCondition("") 330 source_bps.Append(bkpt) 331 332 bkpt = self.orig_target.BreakpointCreateByName( 333 "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list 334 ) 335 bkpt.SetCondition("something != something_else") 336 bkpt.SetQueueName("grubby") 337 bkpt.AddName("FirstName") 338 bkpt.AddName("SecondName") 339 bkpt.SetScriptCallbackBody( 340 '\tprint("I am a function that prints.")\n\tprint("I don\'t do anything else")\n' 341 ) 342 source_bps.Append(bkpt) 343 344 bkpt = self.orig_target.BreakpointCreateBySourceRegex( 345 "dont really care", blubby_file_spec 346 ) 347 cmd_list = lldb.SBStringList() 348 cmd_list.AppendString("frame var") 349 cmd_list.AppendString("thread backtrace") 350 351 bkpt.SetCommandLineCommands(cmd_list) 352 source_bps.Append(bkpt) 353 354 self.check_equivalence(source_bps) 355 356 def do_check_appending(self): 357 """Use Python APIs to check appending to already serialized options.""" 358 359 empty_module_list = lldb.SBFileSpecList() 360 empty_cu_list = lldb.SBFileSpecList() 361 blubby_file_spec = lldb.SBFileSpec( 362 os.path.join(self.getSourceDir(), "blubby.c") 363 ) 364 365 # It isn't actually important for these purposes that these breakpoint 366 # actually have locations. 367 368 all_bps = lldb.SBBreakpointList(self.orig_target) 369 source_bps = lldb.SBBreakpointList(self.orig_target) 370 371 bkpt = self.orig_target.BreakpointCreateByLocation( 372 lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList() 373 ) 374 bkpt.SetEnabled(False) 375 bkpt.SetOneShot(True) 376 bkpt.SetThreadID(10) 377 source_bps.Append(bkpt) 378 all_bps.Append(bkpt) 379 380 error = lldb.SBError() 381 error = self.orig_target.BreakpointsWriteToFile( 382 self.bkpts_file_spec, source_bps 383 ) 384 self.assertSuccess(error, "Failed writing breakpoints to file") 385 386 source_bps.Clear() 387 388 bkpt = self.orig_target.BreakpointCreateByName( 389 "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list 390 ) 391 bkpt.SetIgnoreCount(10) 392 bkpt.SetThreadName("grubby") 393 source_bps.Append(bkpt) 394 all_bps.Append(bkpt) 395 396 bkpt = self.orig_target.BreakpointCreateByName( 397 "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list 398 ) 399 bkpt.SetCondition("something != something_else") 400 bkpt.SetQueueName("grubby") 401 bkpt.AddName("FirstName") 402 bkpt.AddName("SecondName") 403 404 source_bps.Append(bkpt) 405 all_bps.Append(bkpt) 406 407 error = self.orig_target.BreakpointsWriteToFile( 408 self.bkpts_file_spec, source_bps, True 409 ) 410 self.assertSuccess(error, "Failed appending breakpoints to file") 411 412 self.check_equivalence(all_bps) 413 414 def do_check_names(self): 415 bkpt = self.orig_target.BreakpointCreateByLocation( 416 lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList() 417 ) 418 good_bkpt_name = "GoodBreakpoint" 419 write_bps = lldb.SBBreakpointList(self.orig_target) 420 bkpt.AddName(good_bkpt_name) 421 write_bps.Append(bkpt) 422 423 error = lldb.SBError() 424 error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) 425 self.assertSuccess(error, "Failed writing breakpoints to file") 426 427 copy_bps = lldb.SBBreakpointList(self.copy_target) 428 names_list = lldb.SBStringList() 429 names_list.AppendString("NoSuchName") 430 431 error = self.copy_target.BreakpointsCreateFromFile( 432 self.bkpts_file_spec, names_list, copy_bps 433 ) 434 self.assertSuccess(error, "Failed reading breakpoints from file") 435 self.assertEqual( 436 copy_bps.GetSize(), 0, "Found breakpoints with a nonexistent name." 437 ) 438 439 names_list.AppendString(good_bkpt_name) 440 error = self.copy_target.BreakpointsCreateFromFile( 441 self.bkpts_file_spec, names_list, copy_bps 442 ) 443 self.assertSuccess(error, "Failed reading breakpoints from file") 444 self.assertEqual(copy_bps.GetSize(), 1, "Found the matching breakpoint.") 445 446 def do_check_extra_args(self): 447 import side_effect 448 449 interp = self.dbg.GetCommandInterpreter() 450 error = lldb.SBError() 451 452 script_name = os.path.join(self.getSourceDir(), "resolver.py") 453 454 command = "command script import " + script_name 455 result = lldb.SBCommandReturnObject() 456 interp.HandleCommand(command, result) 457 self.assertTrue( 458 result.Succeeded(), "com scr imp failed: %s" % (result.GetError()) 459 ) 460 461 # First make sure a scripted breakpoint with no args works: 462 bkpt = self.orig_target.BreakpointCreateFromScript( 463 "resolver.Resolver", 464 lldb.SBStructuredData(), 465 lldb.SBFileSpecList(), 466 lldb.SBFileSpecList(), 467 ) 468 self.assertTrue(bkpt.IsValid(), "Bkpt is valid") 469 write_bps = lldb.SBBreakpointList(self.orig_target) 470 471 error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) 472 self.assertSuccess(error, "Failed writing breakpoints") 473 474 side_effect.g_extra_args = None 475 copy_bps = lldb.SBBreakpointList(self.copy_target) 476 error = self.copy_target.BreakpointsCreateFromFile( 477 self.bkpts_file_spec, copy_bps 478 ) 479 self.assertSuccess(error, "Failed reading breakpoints") 480 481 self.assertEqual(copy_bps.GetSize(), 1, "Got one breakpoint from file.") 482 no_keys = lldb.SBStringList() 483 side_effect.g_extra_args.GetKeys(no_keys) 484 self.assertEqual(no_keys.GetSize(), 0, "Should have no keys") 485 486 self.orig_target.DeleteAllBreakpoints() 487 self.copy_target.DeleteAllBreakpoints() 488 489 # Now try one with extra args: 490 491 extra_args = lldb.SBStructuredData() 492 stream = lldb.SBStream() 493 stream.Print('{"first_arg" : "first_value", "second_arg" : "second_value"}') 494 extra_args.SetFromJSON(stream) 495 self.assertTrue(extra_args.IsValid(), "SBStructuredData is valid.") 496 497 bkpt = self.orig_target.BreakpointCreateFromScript( 498 "resolver.Resolver", 499 extra_args, 500 lldb.SBFileSpecList(), 501 lldb.SBFileSpecList(), 502 ) 503 self.assertTrue(bkpt.IsValid(), "Bkpt is valid") 504 write_bps = lldb.SBBreakpointList(self.orig_target) 505 506 error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) 507 self.assertSuccess(error, "Failed writing breakpoints") 508 509 orig_extra_args = side_effect.g_extra_args 510 self.assertTrue(orig_extra_args.IsValid(), "Extra args originally valid") 511 512 orig_keys = lldb.SBStringList() 513 orig_extra_args.GetKeys(orig_keys) 514 self.assertEqual(2, orig_keys.GetSize(), "Should have two keys") 515 516 side_effect.g_extra_args = None 517 518 copy_bps = lldb.SBBreakpointList(self.copy_target) 519 error = self.copy_target.BreakpointsCreateFromFile( 520 self.bkpts_file_spec, copy_bps 521 ) 522 self.assertSuccess(error, "Failed reading breakpoints") 523 524 self.assertEqual(copy_bps.GetSize(), 1, "Got one breakpoint from file.") 525 526 copy_extra_args = side_effect.g_extra_args 527 copy_keys = lldb.SBStringList() 528 copy_extra_args.GetKeys(copy_keys) 529 self.assertEqual(2, copy_keys.GetSize(), "Copy should have two keys") 530 531 for idx in range(0, orig_keys.GetSize()): 532 key = orig_keys.GetStringAtIndex(idx) 533 copy_value = copy_extra_args.GetValueForKey(key).GetStringValue(100) 534 535 if key == "first_arg": 536 self.assertEqual(copy_value, "first_value") 537 elif key == "second_arg": 538 self.assertEqual(copy_value, "second_value") 539 else: 540 self.Fail("Unknown key: %s" % (key)) 541