| """ | |
| Test breakpoint serialization. | |
| """ | |
| import os | |
| import json | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| class BreakpointSerialization(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| @add_test_categories(["pyapi"]) | |
| def test_resolvers(self): | |
| """Use Python APIs to test that we serialize resolvers.""" | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| self.do_check_resolvers() | |
| def test_filters(self): | |
| """Use Python APIs to test that we serialize search filters correctly.""" | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| self.do_check_filters() | |
| def test_options(self): | |
| """Use Python APIs to test that we serialize breakpoint options correctly.""" | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| self.do_check_options() | |
| def test_appending(self): | |
| """Use Python APIs to test that we serialize breakpoint options correctly.""" | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| self.do_check_appending() | |
| def test_name_filters(self): | |
| """Use python APIs to test that reading in by name works correctly.""" | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| self.do_check_names() | |
| def test_scripted_extra_args(self): | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| self.do_check_extra_args() | |
| def test_resolver_serialization(self): | |
| """Test that breakpoint resolvers contain the expected information""" | |
| self.build() | |
| self.setup_targets_and_cleanup() | |
| exe_path = self.getBuildArtifact("a.out") | |
| exe_module = self.orig_target.module["a.out"] | |
| self.assertTrue( | |
| exe_module.IsValid(), "Failed to find the executable module in target" | |
| ) | |
| sym_ctx_list = exe_module.FindFunctions("main") | |
| self.assertEqual(sym_ctx_list.GetSize(), 1, "Unable to find function 'main'") | |
| sym_ctx = sym_ctx_list.GetContextAtIndex(0) | |
| self.assertTrue( | |
| sym_ctx.IsValid(), "SBSymbolContext representing function 'main' is invalid" | |
| ) | |
| main_func = sym_ctx.GetFunction() | |
| self.assertTrue( | |
| main_func.IsValid(), "SBFunction representing 'main' is invalid" | |
| ) | |
| main_addr = main_func.GetStartAddress() | |
| bkpt = self.orig_target.BreakpointCreateBySBAddress(main_addr) | |
| self.assertTrue( | |
| bkpt.IsValid(), "Could not place breakpoint on 'main' by address" | |
| ) | |
| stream = lldb.SBStream() | |
| sd = bkpt.SerializeToStructuredData() | |
| sd.GetAsJSON(stream) | |
| serialized_data = json.loads(stream.GetData()) | |
| self.assertIn( | |
| exe_path, | |
| serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["ModuleName"], | |
| ) | |
| def test_structured_data_serialization(self): | |
| target = self.dbg.GetDummyTarget() | |
| self.assertTrue(target.IsValid(), VALID_TARGET) | |
| interpreter = self.dbg.GetCommandInterpreter() | |
| result = lldb.SBCommandReturnObject() | |
| interpreter.HandleCommand("br set -f foo -l 42", result) | |
| result = lldb.SBCommandReturnObject() | |
| interpreter.HandleCommand("br set -c 'argc == 1' -n main", result) | |
| bkp1 = target.GetBreakpointAtIndex(0) | |
| self.assertTrue(bkp1.IsValid(), VALID_BREAKPOINT) | |
| stream = lldb.SBStream() | |
| sd = bkp1.SerializeToStructuredData() | |
| sd.GetAsJSON(stream) | |
| serialized_data = json.loads(stream.GetData()) | |
| self.assertEqual( | |
| serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["FileName"], "foo" | |
| ) | |
| self.assertEqual( | |
| serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["LineNumber"], 42 | |
| ) | |
| bkp2 = target.GetBreakpointAtIndex(1) | |
| self.assertTrue(bkp2.IsValid(), VALID_BREAKPOINT) | |
| stream = lldb.SBStream() | |
| sd = bkp2.SerializeToStructuredData() | |
| sd.GetAsJSON(stream) | |
| serialized_data = json.loads(stream.GetData()) | |
| self.assertIn( | |
| "main", | |
| serialized_data["Breakpoint"]["BKPTResolver"]["Options"]["SymbolNames"], | |
| ) | |
| self.assertEqual( | |
| serialized_data["Breakpoint"]["BKPTOptions"]["ConditionText"], "argc == 1" | |
| ) | |
| invalid_bkp = lldb.SBBreakpoint() | |
| self.assertFalse(invalid_bkp.IsValid(), "Breakpoint should not be valid.") | |
| stream = lldb.SBStream() | |
| sd = invalid_bkp.SerializeToStructuredData() | |
| sd.GetAsJSON(stream) | |
| self.assertFalse( | |
| stream.GetData(), "Invalid breakpoint should have an empty structured data" | |
| ) | |
| def setup_targets_and_cleanup(self): | |
| def cleanup(): | |
| self.RemoveTempFile(self.bkpts_file_path) | |
| if self.orig_target.IsValid(): | |
| self.dbg.DeleteTarget(self.orig_target) | |
| self.dbg.DeleteTarget(self.copy_target) | |
| self.addTearDownHook(cleanup) | |
| self.RemoveTempFile(self.bkpts_file_path) | |
| exe = self.getBuildArtifact("a.out") | |
| # Create the targets we are making breakpoints in and copying them to: | |
| self.orig_target = self.dbg.CreateTarget(exe) | |
| self.assertTrue(self.orig_target, VALID_TARGET) | |
| self.copy_target = self.dbg.CreateTarget(exe) | |
| self.assertTrue(self.copy_target, VALID_TARGET) | |
| def setUp(self): | |
| # Call super's setUp(). | |
| TestBase.setUp(self) | |
| self.bkpts_file_path = self.getBuildArtifact("breakpoints.json") | |
| self.bkpts_file_spec = lldb.SBFileSpec(self.bkpts_file_path) | |
| def check_equivalence(self, source_bps, do_write=True): | |
| error = lldb.SBError() | |
| if do_write: | |
| error = self.orig_target.BreakpointsWriteToFile( | |
| self.bkpts_file_spec, source_bps | |
| ) | |
| self.assertSuccess(error, "Failed writing breakpoints to file") | |
| copy_bps = lldb.SBBreakpointList(self.copy_target) | |
| error = self.copy_target.BreakpointsCreateFromFile( | |
| self.bkpts_file_spec, copy_bps | |
| ) | |
| self.assertSuccess(error, "Failed reading breakpoints from file") | |
| num_source_bps = source_bps.GetSize() | |
| num_copy_bps = copy_bps.GetSize() | |
| self.assertEqual( | |
| num_source_bps, | |
| num_copy_bps, | |
| "Didn't get same number of input and output breakpoints - orig: %d copy: %d" | |
| % (num_source_bps, num_copy_bps), | |
| ) | |
| for i in range(0, num_source_bps): | |
| source_bp = source_bps.GetBreakpointAtIndex(i) | |
| source_desc = lldb.SBStream() | |
| source_bp.GetDescription(source_desc, False) | |
| source_text = source_desc.GetData() | |
| # I am assuming here that the breakpoints will get written out in breakpoint ID order, and | |
| # read back in ditto. That is true right now, and I can't see any reason to do it differently | |
| # but if we do we can go to writing the breakpoints one by one, or sniffing the descriptions to | |
| # see which one is which. | |
| copy_id = source_bp.GetID() | |
| copy_bp = copy_bps.FindBreakpointByID(copy_id) | |
| self.assertTrue( | |
| copy_bp.IsValid(), "Could not find copy breakpoint %d." % (copy_id) | |
| ) | |
| copy_desc = lldb.SBStream() | |
| copy_bp.GetDescription(copy_desc, False) | |
| copy_text = copy_desc.GetData() | |
| # These two should be identical. | |
| self.trace("Source text for %d is %s." % (i, source_text)) | |
| self.assertEqual( | |
| source_text, | |
| copy_text, | |
| "Source and dest breakpoints are not identical: \nsource: %s\ndest: %s" | |
| % (source_text, copy_text), | |
| ) | |
| self.assertEqual( | |
| source_bp.GetNumLocations(), | |
| copy_bp.GetNumLocations(), | |
| "Source and dest num locations are not the same", | |
| ) | |
| def do_check_resolvers(self): | |
| """Use Python APIs to check serialization of breakpoint resolvers""" | |
| empty_module_list = lldb.SBFileSpecList() | |
| empty_cu_list = lldb.SBFileSpecList() | |
| blubby_file_spec = lldb.SBFileSpec( | |
| os.path.join(self.getSourceDir(), "blubby.c") | |
| ) | |
| # It isn't actually important for these purposes that these breakpoint | |
| # actually have locations. | |
| source_bps = lldb.SBBreakpointList(self.orig_target) | |
| source_bps.Append(self.orig_target.BreakpointCreateByLocation("blubby.c", 666)) | |
| # Make sure we do one breakpoint right: | |
| self.check_equivalence(source_bps) | |
| source_bps.Clear() | |
| source_bps.Append( | |
| self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list | |
| ) | |
| ) | |
| source_bps.Append( | |
| self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list | |
| ) | |
| ) | |
| source_bps.Append( | |
| self.orig_target.BreakpointCreateBySourceRegex( | |
| "dont really care", blubby_file_spec | |
| ) | |
| ) | |
| # And some number greater than one: | |
| self.check_equivalence(source_bps) | |
| def do_check_filters(self): | |
| """Use Python APIs to check serialization of breakpoint filters.""" | |
| module_list = lldb.SBFileSpecList() | |
| module_list.Append(lldb.SBFileSpec("SomeBinary")) | |
| module_list.Append(lldb.SBFileSpec("SomeOtherBinary")) | |
| cu_list = lldb.SBFileSpecList() | |
| cu_list.Append(lldb.SBFileSpec("SomeCU.c")) | |
| cu_list.Append(lldb.SBFileSpec("AnotherCU.c")) | |
| cu_list.Append(lldb.SBFileSpec("ThirdCU.c")) | |
| blubby_file_spec = lldb.SBFileSpec( | |
| os.path.join(self.getSourceDir(), "blubby.c") | |
| ) | |
| # It isn't actually important for these purposes that these breakpoint | |
| # actually have locations. | |
| source_bps = lldb.SBBreakpointList(self.orig_target) | |
| bkpt = self.orig_target.BreakpointCreateByLocation( | |
| blubby_file_spec, 666, 0, module_list | |
| ) | |
| source_bps.Append(bkpt) | |
| # Make sure we do one right: | |
| self.check_equivalence(source_bps) | |
| source_bps.Clear() | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeAuto, module_list, cu_list | |
| ) | |
| source_bps.Append(bkpt) | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeFull, module_list, cu_list | |
| ) | |
| source_bps.Append(bkpt) | |
| bkpt = self.orig_target.BreakpointCreateBySourceRegex( | |
| "dont really care", blubby_file_spec | |
| ) | |
| source_bps.Append(bkpt) | |
| # And some number greater than one: | |
| self.check_equivalence(source_bps) | |
| def do_check_options(self): | |
| """Use Python APIs to check serialization of breakpoint options.""" | |
| empty_module_list = lldb.SBFileSpecList() | |
| empty_cu_list = lldb.SBFileSpecList() | |
| blubby_file_spec = lldb.SBFileSpec( | |
| os.path.join(self.getSourceDir(), "blubby.c") | |
| ) | |
| # It isn't actually important for these purposes that these breakpoint | |
| # actually have locations. | |
| source_bps = lldb.SBBreakpointList(self.orig_target) | |
| bkpt = self.orig_target.BreakpointCreateByLocation( | |
| lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList() | |
| ) | |
| bkpt.SetEnabled(False) | |
| bkpt.SetOneShot(True) | |
| bkpt.SetThreadID(10) | |
| source_bps.Append(bkpt) | |
| # Make sure we get one right: | |
| self.check_equivalence(source_bps) | |
| source_bps.Clear() | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list | |
| ) | |
| bkpt.SetIgnoreCount(10) | |
| bkpt.SetThreadName("grubby") | |
| source_bps.Append(bkpt) | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list | |
| ) | |
| bkpt.SetCondition("gonna remove this") | |
| bkpt.SetCondition("") | |
| source_bps.Append(bkpt) | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list | |
| ) | |
| bkpt.SetCondition("something != something_else") | |
| bkpt.SetQueueName("grubby") | |
| bkpt.AddName("FirstName") | |
| bkpt.AddName("SecondName") | |
| bkpt.SetScriptCallbackBody( | |
| '\tprint("I am a function that prints.")\n\tprint("I don\'t do anything else")\n' | |
| ) | |
| source_bps.Append(bkpt) | |
| bkpt = self.orig_target.BreakpointCreateBySourceRegex( | |
| "dont really care", blubby_file_spec | |
| ) | |
| cmd_list = lldb.SBStringList() | |
| cmd_list.AppendString("frame var") | |
| cmd_list.AppendString("thread backtrace") | |
| bkpt.SetCommandLineCommands(cmd_list) | |
| source_bps.Append(bkpt) | |
| self.check_equivalence(source_bps) | |
| def do_check_appending(self): | |
| """Use Python APIs to check appending to already serialized options.""" | |
| empty_module_list = lldb.SBFileSpecList() | |
| empty_cu_list = lldb.SBFileSpecList() | |
| blubby_file_spec = lldb.SBFileSpec( | |
| os.path.join(self.getSourceDir(), "blubby.c") | |
| ) | |
| # It isn't actually important for these purposes that these breakpoint | |
| # actually have locations. | |
| all_bps = lldb.SBBreakpointList(self.orig_target) | |
| source_bps = lldb.SBBreakpointList(self.orig_target) | |
| bkpt = self.orig_target.BreakpointCreateByLocation( | |
| lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList() | |
| ) | |
| bkpt.SetEnabled(False) | |
| bkpt.SetOneShot(True) | |
| bkpt.SetThreadID(10) | |
| source_bps.Append(bkpt) | |
| all_bps.Append(bkpt) | |
| error = lldb.SBError() | |
| error = self.orig_target.BreakpointsWriteToFile( | |
| self.bkpts_file_spec, source_bps | |
| ) | |
| self.assertSuccess(error, "Failed writing breakpoints to file") | |
| source_bps.Clear() | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "main", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list | |
| ) | |
| bkpt.SetIgnoreCount(10) | |
| bkpt.SetThreadName("grubby") | |
| source_bps.Append(bkpt) | |
| all_bps.Append(bkpt) | |
| bkpt = self.orig_target.BreakpointCreateByName( | |
| "main", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list | |
| ) | |
| bkpt.SetCondition("something != something_else") | |
| bkpt.SetQueueName("grubby") | |
| bkpt.AddName("FirstName") | |
| bkpt.AddName("SecondName") | |
| source_bps.Append(bkpt) | |
| all_bps.Append(bkpt) | |
| error = self.orig_target.BreakpointsWriteToFile( | |
| self.bkpts_file_spec, source_bps, True | |
| ) | |
| self.assertSuccess(error, "Failed appending breakpoints to file") | |
| self.check_equivalence(all_bps) | |
| def do_check_names(self): | |
| bkpt = self.orig_target.BreakpointCreateByLocation( | |
| lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList() | |
| ) | |
| good_bkpt_name = "GoodBreakpoint" | |
| write_bps = lldb.SBBreakpointList(self.orig_target) | |
| bkpt.AddName(good_bkpt_name) | |
| write_bps.Append(bkpt) | |
| error = lldb.SBError() | |
| error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) | |
| self.assertSuccess(error, "Failed writing breakpoints to file") | |
| copy_bps = lldb.SBBreakpointList(self.copy_target) | |
| names_list = lldb.SBStringList() | |
| names_list.AppendString("NoSuchName") | |
| error = self.copy_target.BreakpointsCreateFromFile( | |
| self.bkpts_file_spec, names_list, copy_bps | |
| ) | |
| self.assertSuccess(error, "Failed reading breakpoints from file") | |
| self.assertEqual( | |
| copy_bps.GetSize(), 0, "Found breakpoints with a nonexistent name." | |
| ) | |
| names_list.AppendString(good_bkpt_name) | |
| error = self.copy_target.BreakpointsCreateFromFile( | |
| self.bkpts_file_spec, names_list, copy_bps | |
| ) | |
| self.assertSuccess(error, "Failed reading breakpoints from file") | |
| self.assertEqual(copy_bps.GetSize(), 1, "Found the matching breakpoint.") | |
| def do_check_extra_args(self): | |
| import side_effect | |
| interp = self.dbg.GetCommandInterpreter() | |
| error = lldb.SBError() | |
| script_name = os.path.join(self.getSourceDir(), "resolver.py") | |
| command = "command script import " + script_name | |
| result = lldb.SBCommandReturnObject() | |
| interp.HandleCommand(command, result) | |
| self.assertTrue( | |
| result.Succeeded(), "com scr imp failed: %s" % (result.GetError()) | |
| ) | |
| # First make sure a scripted breakpoint with no args works: | |
| bkpt = self.orig_target.BreakpointCreateFromScript( | |
| "resolver.Resolver", | |
| lldb.SBStructuredData(), | |
| lldb.SBFileSpecList(), | |
| lldb.SBFileSpecList(), | |
| ) | |
| self.assertTrue(bkpt.IsValid(), "Bkpt is valid") | |
| write_bps = lldb.SBBreakpointList(self.orig_target) | |
| error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) | |
| self.assertSuccess(error, "Failed writing breakpoints") | |
| side_effect.g_extra_args = None | |
| copy_bps = lldb.SBBreakpointList(self.copy_target) | |
| error = self.copy_target.BreakpointsCreateFromFile( | |
| self.bkpts_file_spec, copy_bps | |
| ) | |
| self.assertSuccess(error, "Failed reading breakpoints") | |
| self.assertEqual(copy_bps.GetSize(), 1, "Got one breakpoint from file.") | |
| no_keys = lldb.SBStringList() | |
| side_effect.g_extra_args.GetKeys(no_keys) | |
| self.assertEqual(no_keys.GetSize(), 0, "Should have no keys") | |
| self.orig_target.DeleteAllBreakpoints() | |
| self.copy_target.DeleteAllBreakpoints() | |
| # Now try one with extra args: | |
| extra_args = lldb.SBStructuredData() | |
| stream = lldb.SBStream() | |
| stream.Print('{"first_arg" : "first_value", "second_arg" : "second_value"}') | |
| extra_args.SetFromJSON(stream) | |
| self.assertTrue(extra_args.IsValid(), "SBStructuredData is valid.") | |
| bkpt = self.orig_target.BreakpointCreateFromScript( | |
| "resolver.Resolver", | |
| extra_args, | |
| lldb.SBFileSpecList(), | |
| lldb.SBFileSpecList(), | |
| ) | |
| self.assertTrue(bkpt.IsValid(), "Bkpt is valid") | |
| write_bps = lldb.SBBreakpointList(self.orig_target) | |
| error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) | |
| self.assertSuccess(error, "Failed writing breakpoints") | |
| orig_extra_args = side_effect.g_extra_args | |
| self.assertTrue(orig_extra_args.IsValid(), "Extra args originally valid") | |
| orig_keys = lldb.SBStringList() | |
| orig_extra_args.GetKeys(orig_keys) | |
| self.assertEqual(2, orig_keys.GetSize(), "Should have two keys") | |
| side_effect.g_extra_args = None | |
| copy_bps = lldb.SBBreakpointList(self.copy_target) | |
| error = self.copy_target.BreakpointsCreateFromFile( | |
| self.bkpts_file_spec, copy_bps | |
| ) | |
| self.assertSuccess(error, "Failed reading breakpoints") | |
| self.assertEqual(copy_bps.GetSize(), 1, "Got one breakpoint from file.") | |
| copy_extra_args = side_effect.g_extra_args | |
| copy_keys = lldb.SBStringList() | |
| copy_extra_args.GetKeys(copy_keys) | |
| self.assertEqual(2, copy_keys.GetSize(), "Copy should have two keys") | |
| for idx in range(0, orig_keys.GetSize()): | |
| key = orig_keys.GetStringAtIndex(idx) | |
| copy_value = copy_extra_args.GetValueForKey(key).GetStringValue(100) | |
| if key == "first_arg": | |
| self.assertEqual(copy_value, "first_value") | |
| elif key == "second_arg": | |
| self.assertEqual(copy_value, "second_value") | |
| else: | |
| self.Fail("Unknown key: %s" % (key)) |