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