1""" 2Use lldb Python API to test dynamic values in ObjC 3""" 4 5 6 7import lldb 8from lldbsuite.test.decorators import * 9from lldbsuite.test.lldbtest import * 10from lldbsuite.test import lldbutil 11 12 13class ObjCDynamicValueTestCase(TestBase): 14 15 mydir = TestBase.compute_mydir(__file__) 16 17 def setUp(self): 18 # Call super's setUp(). 19 TestBase.setUp(self) 20 21 # Find the line number to break for main.c. 22 23 self.source_name = 'dynamic-value.m' 24 self.set_property_line = line_number( 25 self.source_name, 26 '// This is the line in setProperty, make sure we step to here.') 27 self.handle_SourceBase = line_number( 28 self.source_name, '// Break here to check dynamic values.') 29 self.main_before_setProperty_line = line_number( 30 self.source_name, '// Break here to see if we can step into real method.') 31 32 @add_test_categories(['pyapi']) 33 @expectedFailureDarwin("llvm.org/pr20271 rdar://18684107") 34 def test_get_objc_dynamic_vals(self): 35 """Test fetching ObjC dynamic values.""" 36 if self.getArchitecture() == 'i386': 37 # rdar://problem/9946499 38 self.skipTest("Dynamic types for ObjC V1 runtime not implemented") 39 40 self.build() 41 exe = self.getBuildArtifact("a.out") 42 43 # Create a target from the debugger. 44 45 target = self.dbg.CreateTarget(exe) 46 self.assertTrue(target, VALID_TARGET) 47 48 # Set up our breakpoints: 49 50 handle_SourceBase_bkpt = target.BreakpointCreateByLocation( 51 self.source_name, self.handle_SourceBase) 52 self.assertTrue(handle_SourceBase_bkpt and 53 handle_SourceBase_bkpt.GetNumLocations() == 1, 54 VALID_BREAKPOINT) 55 56 main_before_setProperty_bkpt = target.BreakpointCreateByLocation( 57 self.source_name, self.main_before_setProperty_line) 58 self.assertTrue(main_before_setProperty_bkpt and 59 main_before_setProperty_bkpt.GetNumLocations() == 1, 60 VALID_BREAKPOINT) 61 62 # Now launch the process, and do not stop at the entry point. 63 process = target.LaunchSimple( 64 None, None, self.get_process_working_directory()) 65 66 self.assertEquals(process.GetState(), lldb.eStateStopped, 67 PROCESS_STOPPED) 68 69 threads = lldbutil.get_threads_stopped_at_breakpoint( 70 process, main_before_setProperty_bkpt) 71 self.assertEquals(len(threads), 1) 72 thread = threads[0] 73 74 # 75 # At this point, myObserver has a Source pointer that is actually a KVO swizzled SourceDerived 76 # make sure we can get that properly: 77 78 frame = thread.GetFrameAtIndex(0) 79 myObserver = frame.FindVariable( 80 'myObserver', lldb.eDynamicCanRunTarget) 81 self.assertTrue(myObserver) 82 myObserver_source = myObserver.GetChildMemberWithName( 83 '_source', lldb.eDynamicCanRunTarget) 84 self.examine_SourceDerived_ptr(myObserver_source) 85 86 # 87 # Make sure a static value can be correctly turned into a dynamic 88 # value. 89 90 frame = thread.GetFrameAtIndex(0) 91 myObserver_static = frame.FindVariable( 92 'myObserver', lldb.eNoDynamicValues) 93 self.assertTrue(myObserver_static) 94 myObserver = myObserver_static.GetDynamicValue( 95 lldb.eDynamicCanRunTarget) 96 myObserver_source = myObserver.GetChildMemberWithName( 97 '_source', lldb.eDynamicCanRunTarget) 98 self.examine_SourceDerived_ptr(myObserver_source) 99 100 # The "frame var" code uses another path to get into children, so let's 101 # make sure that works as well: 102 103 result = lldb.SBCommandReturnObject() 104 105 self.expect( 106 'frame var -d run-target myObserver->_source', 107 'frame var finds its way into a child member', 108 patterns=['\(SourceDerived \*\)']) 109 110 # check that our ObjC GetISA() does a good job at hiding KVO swizzled 111 # classes 112 113 self.expect( 114 'frame var -d run-target myObserver->_source -T', 115 'the KVO-ed class is hidden', 116 substrs=['SourceDerived']) 117 118 self.expect( 119 'frame var -d run-target myObserver->_source -T', 120 'the KVO-ed class is hidden', 121 matching=False, 122 substrs=['NSKVONotify']) 123 124 # This test is not entirely related to the main thrust of this test case, but since we're here, 125 # try stepping into setProperty, and make sure we get into the version 126 # in Source: 127 128 thread.StepInto() 129 130 threads = lldbutil.get_stopped_threads( 131 process, lldb.eStopReasonPlanComplete) 132 self.assertEquals(len(threads), 1) 133 line_entry = threads[0].GetFrameAtIndex(0).GetLineEntry() 134 135 self.assertEqual(line_entry.GetLine(), self.set_property_line) 136 self.assertEqual( 137 line_entry.GetFileSpec().GetFilename(), 138 self.source_name) 139 140 # Okay, back to the main business. Continue to the handle_SourceBase 141 # and make sure we get the correct dynamic value. 142 143 threads = lldbutil.continue_to_breakpoint( 144 process, handle_SourceBase_bkpt) 145 self.assertEquals(len(threads), 1) 146 thread = threads[0] 147 148 frame = thread.GetFrameAtIndex(0) 149 150 # Get "object" using FindVariable: 151 152 noDynamic = lldb.eNoDynamicValues 153 useDynamic = lldb.eDynamicCanRunTarget 154 155 object_static = frame.FindVariable('object', noDynamic) 156 object_dynamic = frame.FindVariable('object', useDynamic) 157 158 # Delete this object to make sure that this doesn't cause havoc with 159 # the dynamic object that depends on it. 160 del (object_static) 161 162 self.examine_SourceDerived_ptr(object_dynamic) 163 164 # Get "this" using FindValue, make sure that works too: 165 object_static = frame.FindValue( 166 'object', lldb.eValueTypeVariableArgument, noDynamic) 167 object_dynamic = frame.FindValue( 168 'object', lldb.eValueTypeVariableArgument, useDynamic) 169 del (object_static) 170 self.examine_SourceDerived_ptr(object_dynamic) 171 172 # Get "this" using the EvaluateExpression: 173 object_static = frame.EvaluateExpression('object', noDynamic) 174 object_dynamic = frame.EvaluateExpression('object', useDynamic) 175 del (object_static) 176 self.examine_SourceDerived_ptr(object_dynamic) 177 178 # Continue again to the handle_SourceBase and make sure we get the correct dynamic value. 179 # This one looks exactly the same, but in fact this is an "un-KVO'ed" version of SourceBase, so 180 # its isa pointer points to SourceBase not NSKVOSourceBase or 181 # whatever... 182 183 threads = lldbutil.continue_to_breakpoint( 184 process, handle_SourceBase_bkpt) 185 self.assertEquals(len(threads), 1) 186 thread = threads[0] 187 188 frame = thread.GetFrameAtIndex(0) 189 190 # Get "object" using FindVariable: 191 192 object_static = frame.FindVariable('object', noDynamic) 193 object_dynamic = frame.FindVariable('object', useDynamic) 194 195 # Delete this object to make sure that this doesn't cause havoc with 196 # the dynamic object that depends on it. 197 del (object_static) 198 199 self.examine_SourceDerived_ptr(object_dynamic) 200 201 def examine_SourceDerived_ptr(self, object): 202 self.assertTrue(object) 203 self.assertNotEqual(object.GetTypeName().find('SourceDerived'), -1) 204 derivedValue = object.GetChildMemberWithName('_derivedValue') 205 self.assertTrue(derivedValue) 206 self.assertEquals(int(derivedValue.GetValue(), 0), 30) 207