| """ | |
| Test setting breakpoints using a scripted resolver | |
| """ | |
| import os | |
| import lldb | |
| import lldbsuite.test.lldbutil as lldbutil | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| class TestScriptedResolver(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") | |
| def test_scripted_resolver(self): | |
| """Use a scripted resolver to set a by symbol name breakpoint""" | |
| self.build() | |
| self.do_test() | |
| @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") | |
| def test_search_depths(self): | |
| """Make sure we are called at the right depths depending on what we return | |
| from __get_depth__""" | |
| self.build() | |
| self.do_test_depths() | |
| @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") | |
| def test_command_line(self): | |
| """Test setting a resolver breakpoint from the command line""" | |
| self.build() | |
| self.do_test_cli() | |
| def test_bad_command_lines(self): | |
| """Make sure we get appropriate errors when we give invalid key/value | |
| options""" | |
| self.build() | |
| self.do_test_bad_options() | |
| def test_copy_from_dummy_target(self): | |
| """Make sure we don't crash during scripted breakpoint copy from dummy target""" | |
| self.build() | |
| self.do_test_copy_from_dummy_target() | |
| def make_target_and_import(self): | |
| target = self.make_target() | |
| self.import_resolver_script() | |
| return target | |
| def make_target(self): | |
| return lldbutil.run_to_breakpoint_make_target(self) | |
| def import_resolver_script(self): | |
| interp = self.dbg.GetCommandInterpreter() | |
| error = lldb.SBError() | |
| script_name = os.path.join(self.getSourceDir(), "resolver.py") | |
| source_name = os.path.join(self.getSourceDir(), "main.c") | |
| command = "command script import " + script_name | |
| result = lldb.SBCommandReturnObject() | |
| interp.HandleCommand(command, result) | |
| self.assertTrue( | |
| result.Succeeded(), "com scr imp failed: %s" % (result.GetError()) | |
| ) | |
| def make_extra_args(self): | |
| json_string = '{"symbol":"break_on_me", "test1": "value1"}' | |
| json_stream = lldb.SBStream() | |
| json_stream.Print(json_string) | |
| extra_args = lldb.SBStructuredData() | |
| error = extra_args.SetFromJSON(json_stream) | |
| self.assertSuccess(error, "Error making SBStructuredData") | |
| return extra_args | |
| def do_test(self): | |
| """This reads in a python file and sets a breakpoint using it.""" | |
| target = self.make_target_and_import() | |
| extra_args = self.make_extra_args() | |
| file_list = lldb.SBFileSpecList() | |
| module_list = lldb.SBFileSpecList() | |
| # Make breakpoints with this resolver using different filters, first ones that will take: | |
| right = [] | |
| # one with no file or module spec - this one should fire: | |
| right.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # one with the right source file and no module - should also fire: | |
| file_list.Append(lldb.SBFileSpec("main.c")) | |
| right.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # Make sure the help text shows up in the "break list" output: | |
| self.expect( | |
| "break list", | |
| substrs=["I am a python breakpoint resolver"], | |
| msg="Help is listed in break list", | |
| ) | |
| # one with the right source file and right module - should also fire: | |
| module_list.Append(lldb.SBFileSpec("a.out")) | |
| right.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # And one with no source file but the right module: | |
| file_list.Clear() | |
| right.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # Make sure these all got locations: | |
| for i in range(0, len(right)): | |
| self.assertGreaterEqual( | |
| right[i].GetNumLocations(), 1, "Breakpoint %d has no locations." % (i) | |
| ) | |
| # Now some ones that won't take: | |
| module_list.Clear() | |
| file_list.Clear() | |
| wrong = [] | |
| # one with the wrong module - should not fire: | |
| module_list.Append(lldb.SBFileSpec("noSuchModule")) | |
| wrong.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # one with the wrong file - also should not fire: | |
| file_list.Clear() | |
| module_list.Clear() | |
| file_list.Append(lldb.SBFileSpec("noFileOfThisName.xxx")) | |
| wrong.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # Now make sure the CU level iteration obeys the file filters: | |
| file_list.Clear() | |
| module_list.Clear() | |
| file_list.Append(lldb.SBFileSpec("no_such_file.xxx")) | |
| wrong.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.ResolverCUDepth", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # And the Module filters: | |
| file_list.Clear() | |
| module_list.Clear() | |
| module_list.Append(lldb.SBFileSpec("NoSuchModule.dylib")) | |
| wrong.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.ResolverCUDepth", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # Now make sure the Function level iteration obeys the file filters: | |
| file_list.Clear() | |
| module_list.Clear() | |
| file_list.Append(lldb.SBFileSpec("no_such_file.xxx")) | |
| wrong.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.ResolverFuncDepth", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # And the Module filters: | |
| file_list.Clear() | |
| module_list.Clear() | |
| module_list.Append(lldb.SBFileSpec("NoSuchModule.dylib")) | |
| wrong.append( | |
| target.BreakpointCreateFromScript( | |
| "resolver.ResolverFuncDepth", extra_args, module_list, file_list | |
| ) | |
| ) | |
| # Make sure these didn't get locations: | |
| for i in range(0, len(wrong)): | |
| self.assertEqual( | |
| wrong[i].GetNumLocations(), 0, "Breakpoint %d has locations." % (i) | |
| ) | |
| # Now run to main and ensure we hit the breakpoints we should have: | |
| lldbutil.run_to_breakpoint_do_run(self, target, right[0]) | |
| # Test the hit counts: | |
| for i in range(0, len(right)): | |
| self.assertEqual( | |
| right[i].GetHitCount(), 1, "Breakpoint %d has the wrong hit count" % (i) | |
| ) | |
| for i in range(0, len(wrong)): | |
| self.assertEqual( | |
| wrong[i].GetHitCount(), 0, "Breakpoint %d has the wrong hit count" % (i) | |
| ) | |
| def do_test_depths(self): | |
| """This test uses a class variable in resolver.Resolver which gets set to 1 if we saw | |
| compile unit and 2 if we only saw modules. If the search depth is module, you get passed just | |
| the modules with no comp_unit. If the depth is comp_unit you get comp_units. So we can use | |
| this to test that our callback gets called at the right depth.""" | |
| target = self.make_target_and_import() | |
| extra_args = self.make_extra_args() | |
| file_list = lldb.SBFileSpecList() | |
| module_list = lldb.SBFileSpecList() | |
| module_list.Append(lldb.SBFileSpec("a.out")) | |
| # Make a breakpoint that has no __get_depth__, check that that is converted to eSearchDepthModule: | |
| bkpt = target.BreakpointCreateFromScript( | |
| "resolver.Resolver", extra_args, module_list, file_list | |
| ) | |
| self.assertGreater(bkpt.GetNumLocations(), 0, "Resolver got no locations.") | |
| self.expect( | |
| "script print(resolver.Resolver.got_files)", | |
| substrs=["2"], | |
| msg="Was only passed modules", | |
| ) | |
| print(f"Made first breakpoint: {bkpt}") | |
| bkpt = None | |
| # Make a breakpoint that asks for modules, check that we didn't get any files: | |
| bkpt = target.BreakpointCreateFromScript( | |
| "resolver.ResolverModuleDepth", extra_args, module_list, file_list | |
| ) | |
| print(f"Made Second breakpoint: {bkpt}") | |
| self.assertGreater( | |
| bkpt.GetNumLocations(), 0, "ResolverModuleDepth got no locations." | |
| ) | |
| self.expect( | |
| "script print(resolver.Resolver.got_files)", | |
| substrs=["2"], | |
| msg="Was only passed modules", | |
| ) | |
| # Make a breakpoint that asks for compile units, check that we didn't get any files: | |
| bkpt = target.BreakpointCreateFromScript( | |
| "resolver.ResolverCUDepth", extra_args, module_list, file_list | |
| ) | |
| self.assertGreater( | |
| bkpt.GetNumLocations(), 0, "ResolverCUDepth got no locations." | |
| ) | |
| self.expect( | |
| "script print(resolver.Resolver.got_files)", | |
| substrs=["1"], | |
| msg="Was passed compile units", | |
| ) | |
| # Make a breakpoint that returns a bad value - we should convert that to "modules" so check that: | |
| bkpt = target.BreakpointCreateFromScript( | |
| "resolver.ResolverBadDepth", extra_args, module_list, file_list | |
| ) | |
| self.assertGreater( | |
| bkpt.GetNumLocations(), 0, "ResolverBadDepth got no locations." | |
| ) | |
| self.expect( | |
| "script print(resolver.Resolver.got_files)", | |
| substrs=["2"], | |
| msg="Was only passed modules", | |
| ) | |
| # Make a breakpoint that searches at function depth: | |
| bkpt = target.BreakpointCreateFromScript( | |
| "resolver.ResolverFuncDepth", extra_args, module_list, file_list | |
| ) | |
| self.assertGreater( | |
| bkpt.GetNumLocations(), 0, "ResolverFuncDepth got no locations." | |
| ) | |
| self.expect( | |
| "script print(resolver.Resolver.got_files)", | |
| substrs=["3"], | |
| msg="Was only passed modules", | |
| ) | |
| self.expect( | |
| "script print(resolver.Resolver.func_list)", | |
| substrs=["test_func", "break_on_me", "main"], | |
| msg="Saw all the functions", | |
| ) | |
| def do_test_cli(self): | |
| target = self.make_target_and_import() | |
| lldbutil.run_break_set_by_script( | |
| self, "resolver.Resolver", extra_options="-k symbol -v break_on_me" | |
| ) | |
| # Make sure setting a resolver breakpoint doesn't pollute further breakpoint setting | |
| # by checking the description of a regular file & line breakpoint to make sure it | |
| # doesn't mention the Python Resolver function: | |
| bkpt_no = lldbutil.run_break_set_by_file_and_line(self, "main.c", 12) | |
| bkpt = target.FindBreakpointByID(bkpt_no) | |
| strm = lldb.SBStream() | |
| bkpt.GetDescription(strm, False) | |
| used_resolver = "I am a python breakpoint resolver" in strm.GetData() | |
| self.assertFalse( | |
| used_resolver, | |
| "Found the resolver description in the file & line breakpoint description.", | |
| ) | |
| # Also make sure the breakpoint was where we expected: | |
| bp_loc = bkpt.GetLocationAtIndex(0) | |
| bp_sc = bp_loc.GetAddress().GetSymbolContext(lldb.eSymbolContextEverything) | |
| bp_se = bp_sc.GetLineEntry() | |
| self.assertEqual(bp_se.GetLine(), 12, "Got the right line number") | |
| self.assertEqual( | |
| bp_se.GetFileSpec().GetFilename(), "main.c", "Got the right filename" | |
| ) | |
| def do_test_bad_options(self): | |
| target = self.make_target_and_import() | |
| self.expect( | |
| "break set -P resolver.Resolver -k a_key", | |
| error=True, | |
| msg="Missing value at end", | |
| substrs=['Key: "a_key" missing value'], | |
| ) | |
| self.expect( | |
| "break set -P resolver.Resolver -v a_value", | |
| error=True, | |
| msg="Missing key at end", | |
| substrs=['Value: "a_value" missing matching key'], | |
| ) | |
| self.expect( | |
| "break set -P resolver.Resolver -v a_value -k a_key -v another_value", | |
| error=True, | |
| msg="Missing key among args", | |
| substrs=['Value: "a_value" missing matching key'], | |
| ) | |
| self.expect( | |
| "break set -P resolver.Resolver -k a_key -k a_key -v another_value", | |
| error=True, | |
| msg="Missing value among args", | |
| substrs=['Key: "a_key" missing value'], | |
| ) | |
| def do_test_copy_from_dummy_target(self): | |
| # Import breakpoint scripted resolver. | |
| self.import_resolver_script() | |
| # Create a scripted breakpoint. | |
| self.runCmd( | |
| "breakpoint set -P resolver.Resolver -k symbol -v break_on_me", | |
| BREAKPOINT_CREATED, | |
| ) | |
| # This is the function to remove breakpoints from the dummy target | |
| # to get a clean state for the next test case. | |
| def cleanup(): | |
| self.runCmd("breakpoint delete -D -f", check=False) | |
| self.runCmd("breakpoint list", check=False) | |
| # Execute the cleanup function during test case tear down. | |
| self.addTearDownHook(cleanup) | |
| # Check that target creating doesn't crash. | |
| target = self.make_target() |