| """Test stepping through ObjC method dispatch in various forms.""" |
| |
| import lldb |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| |
| |
| class TestObjCStepping(TestBase): |
| def setUp(self): |
| # Call super's setUp(). |
| TestBase.setUp(self) |
| # Find the line numbers that we will step to in main: |
| self.main_source = "stepping-tests.m" |
| self.source_randomMethod_line = line_number( |
| self.main_source, "// Source randomMethod start line." |
| ) |
| self.sourceBase_randomMethod_line = line_number( |
| self.main_source, "// SourceBase randomMethod start line." |
| ) |
| self.source_returnsStruct_start_line = line_number( |
| self.main_source, "// Source returnsStruct start line." |
| ) |
| self.sourceBase_returnsStruct_start_line = line_number( |
| self.main_source, "// SourceBase returnsStruct start line." |
| ) |
| self.stepped_past_nil_line = line_number( |
| self.main_source, "// Step over nil should stop here." |
| ) |
| |
| @add_test_categories(["pyapi", "basic_process"]) |
| def test_with_python_api(self): |
| """Test stepping through ObjC method dispatch in various forms.""" |
| self.build() |
| exe = self.getBuildArtifact("a.out") |
| |
| target = self.dbg.CreateTarget(exe) |
| self.assertTrue(target, VALID_TARGET) |
| |
| self.main_source_spec = lldb.SBFileSpec(self.main_source) |
| |
| breakpoints_to_disable = [] |
| |
| break1 = target.BreakpointCreateBySourceRegex( |
| "// Set first breakpoint here.", self.main_source_spec |
| ) |
| self.assertTrue(break1, VALID_BREAKPOINT) |
| breakpoints_to_disable.append(break1) |
| |
| break2 = target.BreakpointCreateBySourceRegex( |
| "// Set second breakpoint here.", self.main_source_spec |
| ) |
| self.assertTrue(break2, VALID_BREAKPOINT) |
| breakpoints_to_disable.append(break2) |
| |
| break3 = target.BreakpointCreateBySourceRegex( |
| "// Set third breakpoint here.", self.main_source_spec |
| ) |
| self.assertTrue(break3, VALID_BREAKPOINT) |
| breakpoints_to_disable.append(break3) |
| |
| break4 = target.BreakpointCreateBySourceRegex( |
| "// Set fourth breakpoint here.", self.main_source_spec |
| ) |
| self.assertTrue(break4, VALID_BREAKPOINT) |
| breakpoints_to_disable.append(break4) |
| |
| break5 = target.BreakpointCreateBySourceRegex( |
| "// Set fifth breakpoint here.", self.main_source_spec |
| ) |
| self.assertTrue(break5, VALID_BREAKPOINT) |
| breakpoints_to_disable.append(break5) |
| |
| break_returnStruct_call_super = target.BreakpointCreateBySourceRegex( |
| "// Source returnsStruct call line.", self.main_source_spec |
| ) |
| self.assertTrue(break_returnStruct_call_super, VALID_BREAKPOINT) |
| breakpoints_to_disable.append(break_returnStruct_call_super) |
| |
| break_step_nil = target.BreakpointCreateBySourceRegex( |
| "// Set nil step breakpoint here.", self.main_source_spec |
| ) |
| self.assertTrue(break_step_nil, VALID_BREAKPOINT) |
| |
| # Now launch the process, and do not stop at entry point. |
| process = target.LaunchSimple(None, None, self.get_process_working_directory()) |
| |
| self.assertTrue(process, PROCESS_IS_VALID) |
| |
| # The stop reason of the thread should be breakpoint. |
| threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1) |
| if len(threads) != 1: |
| self.fail("Failed to stop at breakpoint 1.") |
| |
| thread = threads[0] |
| |
| mySource = thread.GetFrameAtIndex(0).FindVariable("mySource") |
| self.assertTrue(mySource, "Found mySource local variable.") |
| mySource_isa = mySource.GetChildMemberWithName("isa") |
| self.assertTrue(mySource_isa, "Found mySource->isa local variable.") |
| className = mySource_isa.GetSummary() |
| |
| if self.TraceOn(): |
| print(mySource_isa) |
| |
| # Lets delete mySource so we can check that after stepping a child variable |
| # with no parent persists and is useful. |
| del mySource |
| |
| # Now step in, that should leave us in the Source randomMethod: |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.source_randomMethod_line, |
| "Stepped into Source randomMethod.", |
| ) |
| |
| # Now step in again, through the super call, and that should leave us |
| # in the SourceBase randomMethod: |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.sourceBase_randomMethod_line, |
| "Stepped through super into SourceBase randomMethod.", |
| ) |
| |
| threads = lldbutil.continue_to_breakpoint(process, break2) |
| self.assertEqual(len(threads), 1, "Continued to second breakpoint in main.") |
| |
| # Again, step in twice gets us to a stret method and a stret super |
| # call: |
| thread = threads[0] |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.source_returnsStruct_start_line, |
| "Stepped into Source returnsStruct.", |
| ) |
| |
| threads = lldbutil.continue_to_breakpoint( |
| process, break_returnStruct_call_super |
| ) |
| self.assertEqual( |
| len(threads), 1, "Stepped to the call super line in Source returnsStruct." |
| ) |
| thread = threads[0] |
| |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.sourceBase_returnsStruct_start_line, |
| "Stepped through super into SourceBase returnsStruct.", |
| ) |
| |
| # Cool now continue to get past the call that initializes the Observer, and then do our steps in again to see that |
| # we can find our way when we're stepping through a KVO swizzled |
| # object. |
| |
| threads = lldbutil.continue_to_breakpoint(process, break3) |
| self.assertEqual( |
| len(threads), |
| 1, |
| "Continued to third breakpoint in main, our object should now be swizzled.", |
| ) |
| |
| newClassName = mySource_isa.GetSummary() |
| |
| if self.TraceOn(): |
| print("className is %s, newClassName is %s" % (className, newClassName)) |
| print(mySource_isa) |
| |
| self.assertNotEqual( |
| newClassName, className, "The isa did indeed change, swizzled!" |
| ) |
| |
| # Now step in, that should leave us in the Source randomMethod: |
| thread = threads[0] |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.source_randomMethod_line, |
| "Stepped into Source randomMethod in swizzled object.", |
| ) |
| |
| # Now step in again, through the super call, and that should leave us |
| # in the SourceBase randomMethod: |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.sourceBase_randomMethod_line, |
| "Stepped through super into SourceBase randomMethod in swizzled object.", |
| ) |
| |
| threads = lldbutil.continue_to_breakpoint(process, break4) |
| self.assertEqual(len(threads), 1, "Continued to fourth breakpoint in main.") |
| thread = threads[0] |
| |
| # Again, step in twice gets us to a stret method and a stret super |
| # call: |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.source_returnsStruct_start_line, |
| "Stepped into Source returnsStruct in swizzled object.", |
| ) |
| |
| threads = lldbutil.continue_to_breakpoint( |
| process, break_returnStruct_call_super |
| ) |
| self.assertEqual( |
| len(threads), |
| 1, |
| "Stepped to the call super line in Source returnsStruct - second time.", |
| ) |
| thread = threads[0] |
| |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.sourceBase_returnsStruct_start_line, |
| "Stepped through super into SourceBase returnsStruct in swizzled object.", |
| ) |
| |
| for bkpt in breakpoints_to_disable: |
| bkpt.SetEnabled(False) |
| |
| threads = lldbutil.continue_to_breakpoint(process, break_step_nil) |
| self.assertEqual(len(threads), 1, "Continued to step nil breakpoint.") |
| |
| thread.StepInto() |
| line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() |
| self.assertEqual( |
| line_number, |
| self.stepped_past_nil_line, |
| "Step in over dispatch to nil stepped over.", |
| ) |