245 lines · python
1"""Test stepping through ObjC method dispatch in various forms."""2 3import lldb4from lldbsuite.test.decorators import *5from lldbsuite.test.lldbtest import *6from lldbsuite.test import lldbutil7 8 9class TestObjCStepping(TestBase):10 def setUp(self):11 # Call super's setUp().12 TestBase.setUp(self)13 # Find the line numbers that we will step to in main:14 self.main_source = "stepping-tests.m"15 self.source_randomMethod_line = line_number(16 self.main_source, "// Source randomMethod start line."17 )18 self.sourceBase_randomMethod_line = line_number(19 self.main_source, "// SourceBase randomMethod start line."20 )21 self.source_returnsStruct_start_line = line_number(22 self.main_source, "// Source returnsStruct start line."23 )24 self.sourceBase_returnsStruct_start_line = line_number(25 self.main_source, "// SourceBase returnsStruct start line."26 )27 self.stepped_past_nil_line = line_number(28 self.main_source, "// Step over nil should stop here."29 )30 31 @add_test_categories(["pyapi", "basic_process"])32 def test_with_python_api(self):33 """Test stepping through ObjC method dispatch in various forms."""34 self.build()35 exe = self.getBuildArtifact("a.out")36 37 target = self.dbg.CreateTarget(exe)38 self.assertTrue(target, VALID_TARGET)39 40 self.main_source_spec = lldb.SBFileSpec(self.main_source)41 42 breakpoints_to_disable = []43 44 break1 = target.BreakpointCreateBySourceRegex(45 "// Set first breakpoint here.", self.main_source_spec46 )47 self.assertTrue(break1, VALID_BREAKPOINT)48 breakpoints_to_disable.append(break1)49 50 break2 = target.BreakpointCreateBySourceRegex(51 "// Set second breakpoint here.", self.main_source_spec52 )53 self.assertTrue(break2, VALID_BREAKPOINT)54 breakpoints_to_disable.append(break2)55 56 break3 = target.BreakpointCreateBySourceRegex(57 "// Set third breakpoint here.", self.main_source_spec58 )59 self.assertTrue(break3, VALID_BREAKPOINT)60 breakpoints_to_disable.append(break3)61 62 break4 = target.BreakpointCreateBySourceRegex(63 "// Set fourth breakpoint here.", self.main_source_spec64 )65 self.assertTrue(break4, VALID_BREAKPOINT)66 breakpoints_to_disable.append(break4)67 68 break5 = target.BreakpointCreateBySourceRegex(69 "// Set fifth breakpoint here.", self.main_source_spec70 )71 self.assertTrue(break5, VALID_BREAKPOINT)72 breakpoints_to_disable.append(break5)73 74 break_returnStruct_call_super = target.BreakpointCreateBySourceRegex(75 "// Source returnsStruct call line.", self.main_source_spec76 )77 self.assertTrue(break_returnStruct_call_super, VALID_BREAKPOINT)78 breakpoints_to_disable.append(break_returnStruct_call_super)79 80 break_step_nil = target.BreakpointCreateBySourceRegex(81 "// Set nil step breakpoint here.", self.main_source_spec82 )83 self.assertTrue(break_step_nil, VALID_BREAKPOINT)84 85 # Now launch the process, and do not stop at entry point.86 process = target.LaunchSimple(None, None, self.get_process_working_directory())87 88 self.assertTrue(process, PROCESS_IS_VALID)89 90 # The stop reason of the thread should be breakpoint.91 threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1)92 if len(threads) != 1:93 self.fail("Failed to stop at breakpoint 1.")94 95 thread = threads[0]96 97 mySource = thread.GetFrameAtIndex(0).FindVariable("mySource")98 self.assertTrue(mySource, "Found mySource local variable.")99 mySource_isa = mySource.GetChildMemberWithName("isa")100 self.assertTrue(mySource_isa, "Found mySource->isa local variable.")101 className = mySource_isa.GetSummary()102 103 if self.TraceOn():104 print(mySource_isa)105 106 # Lets delete mySource so we can check that after stepping a child variable107 # with no parent persists and is useful.108 del mySource109 110 # Now step in, that should leave us in the Source randomMethod:111 thread.StepInto()112 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()113 self.assertEqual(114 line_number,115 self.source_randomMethod_line,116 "Stepped into Source randomMethod.",117 )118 119 # Now step in again, through the super call, and that should leave us120 # in the SourceBase randomMethod:121 thread.StepInto()122 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()123 self.assertEqual(124 line_number,125 self.sourceBase_randomMethod_line,126 "Stepped through super into SourceBase randomMethod.",127 )128 129 threads = lldbutil.continue_to_breakpoint(process, break2)130 self.assertEqual(len(threads), 1, "Continued to second breakpoint in main.")131 132 # Again, step in twice gets us to a stret method and a stret super133 # call:134 thread = threads[0]135 thread.StepInto()136 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()137 self.assertEqual(138 line_number,139 self.source_returnsStruct_start_line,140 "Stepped into Source returnsStruct.",141 )142 143 threads = lldbutil.continue_to_breakpoint(144 process, break_returnStruct_call_super145 )146 self.assertEqual(147 len(threads), 1, "Stepped to the call super line in Source returnsStruct."148 )149 thread = threads[0]150 151 thread.StepInto()152 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()153 self.assertEqual(154 line_number,155 self.sourceBase_returnsStruct_start_line,156 "Stepped through super into SourceBase returnsStruct.",157 )158 159 # Cool now continue to get past the call that initializes the Observer, and then do our steps in again to see that160 # we can find our way when we're stepping through a KVO swizzled161 # object.162 163 threads = lldbutil.continue_to_breakpoint(process, break3)164 self.assertEqual(165 len(threads),166 1,167 "Continued to third breakpoint in main, our object should now be swizzled.",168 )169 170 newClassName = mySource_isa.GetSummary()171 172 if self.TraceOn():173 print("className is %s, newClassName is %s" % (className, newClassName))174 print(mySource_isa)175 176 self.assertNotEqual(177 newClassName, className, "The isa did indeed change, swizzled!"178 )179 180 # Now step in, that should leave us in the Source randomMethod:181 thread = threads[0]182 thread.StepInto()183 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()184 self.assertEqual(185 line_number,186 self.source_randomMethod_line,187 "Stepped into Source randomMethod in swizzled object.",188 )189 190 # Now step in again, through the super call, and that should leave us191 # in the SourceBase randomMethod:192 thread.StepInto()193 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()194 self.assertEqual(195 line_number,196 self.sourceBase_randomMethod_line,197 "Stepped through super into SourceBase randomMethod in swizzled object.",198 )199 200 threads = lldbutil.continue_to_breakpoint(process, break4)201 self.assertEqual(len(threads), 1, "Continued to fourth breakpoint in main.")202 thread = threads[0]203 204 # Again, step in twice gets us to a stret method and a stret super205 # call:206 thread.StepInto()207 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()208 self.assertEqual(209 line_number,210 self.source_returnsStruct_start_line,211 "Stepped into Source returnsStruct in swizzled object.",212 )213 214 threads = lldbutil.continue_to_breakpoint(215 process, break_returnStruct_call_super216 )217 self.assertEqual(218 len(threads),219 1,220 "Stepped to the call super line in Source returnsStruct - second time.",221 )222 thread = threads[0]223 224 thread.StepInto()225 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()226 self.assertEqual(227 line_number,228 self.sourceBase_returnsStruct_start_line,229 "Stepped through super into SourceBase returnsStruct in swizzled object.",230 )231 232 for bkpt in breakpoints_to_disable:233 bkpt.SetEnabled(False)234 235 threads = lldbutil.continue_to_breakpoint(process, break_step_nil)236 self.assertEqual(len(threads), 1, "Continued to step nil breakpoint.")237 238 thread.StepInto()239 line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()240 self.assertEqual(241 line_number,242 self.stepped_past_nil_line,243 "Step in over dispatch to nil stepped over.",244 )245