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