| """ | |
| Use lldb Python API to test dynamic values in ObjC | |
| """ | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| class ObjCDynamicValueTestCase(TestBase): | |
| def setUp(self): | |
| # Call super's setUp(). | |
| TestBase.setUp(self) | |
| # Find the line number to break for main.c. | |
| self.source_name = "dynamic-value.m" | |
| self.set_property_line = line_number( | |
| self.source_name, | |
| "// This is the line in setProperty, make sure we step to here.", | |
| ) | |
| self.handle_SourceBase = line_number( | |
| self.source_name, "// Break here to check dynamic values." | |
| ) | |
| self.main_before_setProperty_line = line_number( | |
| self.source_name, "// Break here to see if we can step into real method." | |
| ) | |
| @add_test_categories(["pyapi"]) | |
| @expectedFailureDarwin("llvm.org/pr20271 rdar://18684107") | |
| def test_get_objc_dynamic_vals(self): | |
| """Test fetching ObjC dynamic values.""" | |
| if self.getArchitecture() == "i386": | |
| # rdar://problem/9946499 | |
| self.skipTest("Dynamic types for ObjC V1 runtime not implemented") | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| # Create a target from the debugger. | |
| target = self.dbg.CreateTarget(exe) | |
| self.assertTrue(target, VALID_TARGET) | |
| # Set up our breakpoints: | |
| handle_SourceBase_bkpt = target.BreakpointCreateByLocation( | |
| self.source_name, self.handle_SourceBase | |
| ) | |
| self.assertTrue( | |
| handle_SourceBase_bkpt and handle_SourceBase_bkpt.GetNumLocations() == 1, | |
| VALID_BREAKPOINT, | |
| ) | |
| main_before_setProperty_bkpt = target.BreakpointCreateByLocation( | |
| self.source_name, self.main_before_setProperty_line | |
| ) | |
| self.assertTrue( | |
| main_before_setProperty_bkpt | |
| and main_before_setProperty_bkpt.GetNumLocations() == 1, | |
| VALID_BREAKPOINT, | |
| ) | |
| # Now launch the process, and do not stop at the entry point. | |
| process = target.LaunchSimple(None, None, self.get_process_working_directory()) | |
| self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) | |
| threads = lldbutil.get_threads_stopped_at_breakpoint( | |
| process, main_before_setProperty_bkpt | |
| ) | |
| self.assertEqual(len(threads), 1) | |
| thread = threads[0] | |
| # | |
| # At this point, myObserver has a Source pointer that is actually a KVO swizzled SourceDerived | |
| # make sure we can get that properly: | |
| frame = thread.GetFrameAtIndex(0) | |
| myObserver = frame.FindVariable("myObserver", lldb.eDynamicCanRunTarget) | |
| self.assertTrue(myObserver) | |
| myObserver_source = myObserver.GetChildMemberWithName( | |
| "_source", lldb.eDynamicCanRunTarget | |
| ) | |
| self.examine_SourceDerived_ptr(myObserver_source) | |
| # | |
| # Make sure a static value can be correctly turned into a dynamic | |
| # value. | |
| frame = thread.GetFrameAtIndex(0) | |
| myObserver_static = frame.FindVariable("myObserver", lldb.eNoDynamicValues) | |
| self.assertTrue(myObserver_static) | |
| myObserver = myObserver_static.GetDynamicValue(lldb.eDynamicCanRunTarget) | |
| myObserver_source = myObserver.GetChildMemberWithName( | |
| "_source", lldb.eDynamicCanRunTarget | |
| ) | |
| self.examine_SourceDerived_ptr(myObserver_source) | |
| # The "frame var" code uses another path to get into children, so let's | |
| # make sure that works as well: | |
| result = lldb.SBCommandReturnObject() | |
| self.expect( | |
| "frame var -d run-target myObserver->_source", | |
| "frame var finds its way into a child member", | |
| patterns=[r"\(SourceDerived \*\)"], | |
| ) | |
| # check that our ObjC GetISA() does a good job at hiding KVO swizzled | |
| # classes | |
| self.expect( | |
| "frame var -d run-target myObserver->_source -T", | |
| "the KVO-ed class is hidden", | |
| substrs=["SourceDerived"], | |
| ) | |
| self.expect( | |
| "frame var -d run-target myObserver->_source -T", | |
| "the KVO-ed class is hidden", | |
| matching=False, | |
| substrs=["NSKVONotify"], | |
| ) | |
| # This test is not entirely related to the main thrust of this test case, but since we're here, | |
| # try stepping into setProperty, and make sure we get into the version | |
| # in Source: | |
| thread.StepInto() | |
| threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete) | |
| self.assertEqual(len(threads), 1) | |
| line_entry = threads[0].GetFrameAtIndex(0).GetLineEntry() | |
| self.assertEqual(line_entry.GetLine(), self.set_property_line) | |
| self.assertEqual(line_entry.GetFileSpec().GetFilename(), self.source_name) | |
| # Okay, back to the main business. Continue to the handle_SourceBase | |
| # and make sure we get the correct dynamic value. | |
| threads = lldbutil.continue_to_breakpoint(process, handle_SourceBase_bkpt) | |
| self.assertEqual(len(threads), 1) | |
| thread = threads[0] | |
| frame = thread.GetFrameAtIndex(0) | |
| # Get "object" using FindVariable: | |
| noDynamic = lldb.eNoDynamicValues | |
| useDynamic = lldb.eDynamicCanRunTarget | |
| object_static = frame.FindVariable("object", noDynamic) | |
| object_dynamic = frame.FindVariable("object", useDynamic) | |
| # Delete this object to make sure that this doesn't cause havoc with | |
| # the dynamic object that depends on it. | |
| del object_static | |
| self.examine_SourceDerived_ptr(object_dynamic) | |
| # Get "this" using FindValue, make sure that works too: | |
| object_static = frame.FindValue( | |
| "object", lldb.eValueTypeVariableArgument, noDynamic | |
| ) | |
| object_dynamic = frame.FindValue( | |
| "object", lldb.eValueTypeVariableArgument, useDynamic | |
| ) | |
| del object_static | |
| self.examine_SourceDerived_ptr(object_dynamic) | |
| # Get "this" using the EvaluateExpression: | |
| object_static = frame.EvaluateExpression("object", noDynamic) | |
| object_dynamic = frame.EvaluateExpression("object", useDynamic) | |
| del object_static | |
| self.examine_SourceDerived_ptr(object_dynamic) | |
| # Continue again to the handle_SourceBase and make sure we get the correct dynamic value. | |
| # This one looks exactly the same, but in fact this is an "un-KVO'ed" version of SourceBase, so | |
| # its isa pointer points to SourceBase not NSKVOSourceBase or | |
| # whatever... | |
| threads = lldbutil.continue_to_breakpoint(process, handle_SourceBase_bkpt) | |
| self.assertEqual(len(threads), 1) | |
| thread = threads[0] | |
| frame = thread.GetFrameAtIndex(0) | |
| # Get "object" using FindVariable: | |
| object_static = frame.FindVariable("object", noDynamic) | |
| object_dynamic = frame.FindVariable("object", useDynamic) | |
| # Delete this object to make sure that this doesn't cause havoc with | |
| # the dynamic object that depends on it. | |
| del object_static | |
| self.examine_SourceDerived_ptr(object_dynamic) | |
| def examine_SourceDerived_ptr(self, object): | |
| self.assertTrue(object) | |
| self.assertNotEqual(object.GetTypeName().find("SourceDerived"), -1) | |
| derivedValue = object.GetChildMemberWithName("_derivedValue") | |
| self.assertTrue(derivedValue) | |
| self.assertEqual(int(derivedValue.GetValue(), 0), 30) |