| """ |
| 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", |
| ) |
| |
| # 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 |
| ) |
| 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() |