| """ |
| 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.assertEquals(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=["\(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.assertEquals(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.assertEquals(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.assertEquals(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.assertEquals(int(derivedValue.GetValue(), 0), 30) |