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