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