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