541 lines · python
1"""2Test breakpoint serialization.3"""4 5import os6import json7import lldb8from lldbsuite.test.decorators import *9from lldbsuite.test.lldbtest import *10from lldbsuite.test import lldbutil11 12 13class BreakpointSerialization(TestBase):14 NO_DEBUG_INFO_TESTCASE = True15 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"], 42109 )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_bps167 )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_bps173 )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, and192 # read back in ditto. That is true right now, and I can't see any reason to do it differently193 # but if we do we can go to writing the breakpoints one by one, or sniffing the descriptions to194 # 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 breakpoint224 # 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_list234 )235 )236 source_bps.Append(237 self.orig_target.BreakpointCreateByName(238 "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list239 )240 )241 source_bps.Append(242 self.orig_target.BreakpointCreateBySourceRegex(243 "dont really care", blubby_file_spec244 )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 breakpoint266 # actually have locations.267 source_bps = lldb.SBBreakpointList(self.orig_target)268 bkpt = self.orig_target.BreakpointCreateByLocation(269 blubby_file_spec, 666, 0, module_list270 )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_list279 )280 source_bps.Append(bkpt)281 bkpt = self.orig_target.BreakpointCreateByName(282 "blubby", lldb.eFunctionNameTypeFull, module_list, cu_list283 )284 source_bps.Append(bkpt)285 bkpt = self.orig_target.BreakpointCreateBySourceRegex(286 "dont really care", blubby_file_spec287 )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 breakpoint303 # 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_list320 )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_list327 )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_list334 )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_spec346 )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 breakpoint366 # 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_bps383 )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_list390 )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_list398 )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, True409 )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_bps433 )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_bps442 )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_effect448 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_name455 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 = None475 copy_bps = lldb.SBBreakpointList(self.copy_target)476 error = self.copy_target.BreakpointsCreateFromFile(477 self.bkpts_file_spec, copy_bps478 )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_args510 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 = None517 518 copy_bps = lldb.SBBreakpointList(self.copy_target)519 error = self.copy_target.BreakpointsCreateFromFile(520 self.bkpts_file_spec, copy_bps521 )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_args527 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