| """ | |
| Test the OverridesResolver feature of scripted breakpoints | |
| """ | |
| import os | |
| import lldb | |
| import lldbsuite.test.lldbutil as lldbutil | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| class TestOverridesResolver(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") | |
| def test_overrides_resolver_resolver_python(self): | |
| """Use facade breakpoints to emulate hitting some locations""" | |
| self.build() | |
| self.do_test(True) | |
| @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") | |
| def test_overrides_resolver_resolver_cmd(self): | |
| """Use facade breakpoints to emulate hitting some locations""" | |
| self.build() | |
| self.do_test(False) | |
| def test_bad_values(self): | |
| self.build() | |
| self.do_test_bad_values() | |
| def make_target_and_import(self): | |
| self.target = lldbutil.run_to_breakpoint_make_target(self) | |
| self.import_resolver_script() | |
| return self.target | |
| def import_resolver_script(self): | |
| interp = self.dbg.GetCommandInterpreter() | |
| error = lldb.SBError() | |
| script_name = os.path.join(self.getSourceDir(), "bkpt_resolver.py") | |
| command = "command script import " + script_name | |
| self.runCmd(command) | |
| def calculate_mask_desc(self, mask): | |
| mask_str = "" | |
| if mask & lldb.eResolverFileAndLine: | |
| mask_str += "F" | |
| if mask & lldb.eResolverName: | |
| mask_str += "N" | |
| return mask_str | |
| def add_override_python( | |
| self, | |
| help_text, | |
| class_name, | |
| mask, | |
| key, | |
| value, | |
| expect_error=False, | |
| error_string="", | |
| ): | |
| extra_args = lldb.SBStructuredData() | |
| json_str = '{"' + str(key) + '":"' + str(value) + '"}' | |
| extra_args.SetFromJSON(json_str) | |
| error = lldb.SBError() | |
| override_id = self.target.AddBreakpointOverride( | |
| class_name, help_text, mask, extra_args, error | |
| ) | |
| if not expect_error: | |
| self.assertSuccess(error, "Made the override successfully") | |
| else: | |
| self.assertFailure(error, error_string) | |
| return 0 | |
| # Check the override listing, make sure our new entry is present: | |
| mask_str = self.calculate_mask_desc(mask) | |
| self.expect( | |
| "breakpoint override list", substrs=[str(override_id), mask_str, help_text] | |
| ) | |
| return override_id | |
| def add_override_cmd( | |
| self, | |
| help_text, | |
| class_name, | |
| mask_args, | |
| mask_desc, | |
| key, | |
| value, | |
| expect_error=False, | |
| error_string="", | |
| ): | |
| result = lldb.SBCommandReturnObject() | |
| self.ci.HandleCommand( | |
| f"breakpoint override add -P {class_name} -k {key} -v {value} -d '{help_text}' {mask_args}", | |
| result, | |
| ) | |
| if not expect_error: | |
| self.assertCommandReturn(result, "breakpoint override worked") | |
| override_id = int(result.GetOutput()) | |
| else: | |
| self.assertFalse(result.Succeeded(), "We expected this to fail") | |
| return 0 | |
| # Check the override listing, make sure our new entry is present: | |
| self.expect( | |
| "breakpoint override list", substrs=[str(override_id), mask_desc, help_text] | |
| ) | |
| return override_id | |
| def do_test(self, use_cmd): | |
| """This reads in a python file and sets a breakpoint using it.""" | |
| alternate_location = "stop_here_instead" | |
| self.make_target_and_import() | |
| # Add our trivial one first so we test more than one list element: | |
| trivial_help = "Trivial help text" | |
| trivial_id = 0 | |
| useful_id = 0 | |
| if use_cmd: | |
| trivial_id = self.add_override_cmd( | |
| trivial_help, | |
| "bkpt_resolver.TrivialExample", | |
| "-m name", | |
| "N", | |
| "test_key", | |
| "test_value", | |
| ) | |
| useful_help = "SOME HELP TEXT" | |
| useful_id = self.add_override_cmd( | |
| useful_help, | |
| "bkpt_resolver.OverrideExample", | |
| "-m file_and_line", | |
| "F", | |
| "symbol", | |
| "stop_here_instead", | |
| ) | |
| else: | |
| trivial_id = self.add_override_python( | |
| trivial_help, | |
| "bkpt_resolver.TrivialExample", | |
| lldb.eResolverName, | |
| "test_key", | |
| "test_value", | |
| ) | |
| useful_help = "SOME HELP TEXT" | |
| useful_id = self.add_override_python( | |
| useful_help, | |
| "bkpt_resolver.OverrideExample", | |
| lldb.eResolverFileAndLine, | |
| "symbol", | |
| "stop_here_instead", | |
| ) | |
| # Now exercise the list command by id: | |
| self.expect( | |
| f"breakpoint override list {trivial_id}", | |
| substrs=[str(useful_id), useful_help], | |
| matching=False, | |
| ) | |
| self.expect( | |
| f"breakpoint override list {trivial_id}", | |
| substrs=[str(trivial_id), trivial_help], | |
| ) | |
| self.expect( | |
| f"breakpoint override list {useful_id}", | |
| substrs=[str(trivial_id), trivial_help], | |
| matching=False, | |
| ) | |
| self.expect( | |
| f"breakpoint override list {useful_id}", | |
| substrs=[str(useful_id), useful_help], | |
| ) | |
| # Now make a breakpoint by file and line: | |
| bkpt = self.target.BreakpointCreateByLocation( | |
| "main.c", line_number("main.c", "I am in the stop symbol") | |
| ) | |
| self.assertEqual(bkpt.GetNumLocations(), 1, "We make one location") | |
| # Make sure that the override was called but trivial was not: | |
| self.expect("checker override", startstr="1") | |
| self.expect("checker trivial", startstr="0") | |
| # Now continue and we'll hit this breakpoint but not in the | |
| # right place: | |
| (_, process, thread, bkpt) = lldbutil.run_to_breakpoint_do_run( | |
| self, self.target, bkpt | |
| ) | |
| # This location should be bkpt_no.1: | |
| self.assertEqual( | |
| thread.stop_reason_data[0], bkpt.GetID(), "Hit the right breakpoint" | |
| ) | |
| self.assertEqual(thread.stop_reason_data[1], 1, "First location hit is 1") | |
| func_name = thread.frames[0].name | |
| self.assertEqual( | |
| func_name, alternate_location, "Stopped at overridden location" | |
| ) | |
| # Now set a symbol name breakpoint, that should not get overridden, and | |
| # when we continue we should hit it: | |
| name_bkpt = self.target.BreakpointCreateByName("change_him") | |
| self.assertGreater(name_bkpt.GetNumLocations(), 0, "Found locations") | |
| # Now we've made one by name and one file and line breakpoint so both | |
| # override functions should have been called. | |
| self.expect("checker trivial", startstr="1") | |
| self.expect("checker override", startstr="1") | |
| threads = lldbutil.continue_to_breakpoint(process, name_bkpt) | |
| self.assertEqual(len(threads), 1, "Hit our name breakpoint") | |
| func_name = threads[0].frames[0].name | |
| self.assertEqual(func_name, "change_him", "Stopped in the right place") | |
| # Now delete the override and make sure we hit newly set | |
| # source breakpoints: | |
| if use_cmd: | |
| self.runCmd(f"breakpoint override delete {useful_id}") | |
| else: | |
| self.assertTrue( | |
| self.target.RemoveBreakpointOverride(useful_id), "Delete the right one" | |
| ) | |
| # Make sure it's gone from the listings: | |
| self.expect( | |
| "breakpoint override list", | |
| substrs=[str(useful_id), useful_help], | |
| matching=False, | |
| ) | |
| # And that listing it is an error: | |
| self.expect(f"breakpoint override list {useful_id}", error=True) | |
| new_bkpt = self.target.BreakpointCreateByLocation( | |
| "main.c", line_number("main.c", "return 0") | |
| ) | |
| # Neither override should have been called for this breakpoint | |
| # so the counts should still be at 1 each: | |
| self.expect("checker trivial", startstr="1") | |
| self.expect("checker override", startstr="1") | |
| self.assertEqual(new_bkpt.num_locations, 1, "Made breakpoint") | |
| threads = lldbutil.continue_to_breakpoint(process, new_bkpt) | |
| self.assertEqual(len(threads), 1, "Hit our new breakpoint") | |
| func_name = threads[0].frames[0].name | |
| self.assertEqual(func_name, "main", "Stopped in unchanged location") | |
| # Finally, make sure neither of the overrides was called with a | |
| # type that was not part of the mask. | |
| self.expect("checker trivial_not_name", startstr="0") | |
| self.expect("checker override_not_file", startstr="0") | |
| def do_test_bad_values(self): | |
| self.make_target_and_import() | |
| # Add our trivial one with a bad mask: | |
| trivial_help = "Trivial help text" | |
| bad_type_mask = lldb.BreakpointResolverAllResolversMask + 1 | |
| trivial_id = self.add_override_python( | |
| trivial_help, | |
| "bkpt_resolver.TrivialExample", | |
| bad_type_mask, | |
| "test_key", | |
| "test_value", | |
| expect_error=True, | |
| error_string="invalid breakpoint type mask: 64, should be composed of the elements of the BreakpointResolverType enum.", | |
| ) | |
| # Now try with an empty class name: | |
| trivial_id = self.add_override_python( | |
| trivial_help, | |
| "", | |
| lldb.eResolverName, | |
| "test_key", | |
| "test_value", | |
| expect_error=True, | |
| error_string="empty class name", | |
| ) | |
| # FIXME: We aren't currently returning a correct | |
| # error when the class name doesn't exist. | |
| # And with a class name that doesn't exist: | |
| # trivial_id = self.add_override_python( | |
| # trivial_help, | |
| # "NoModuleOfThisName.NoClassOfThisName", | |
| # lldb.eResolverName, | |
| # "test_key", | |
| # "test_value", | |
| # expect_error=True, | |
| # error_string="" | |
| # ) | |
| trivial_id = self.add_override_cmd( | |
| trivial_help, | |
| "bkpt_resolver.TrivialExample", | |
| "-m no_such_resolver", | |
| "", | |
| "test_key", | |
| "test_value", | |
| expect_error=True, | |
| error_string="", | |
| ) |