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