1"""
2Test lldb data formatter subsystem.
3"""
4
5import os, time
6import unittest2
7import lldb
8from lldbtest import *
9import lldbutil
10
11class PythonSynthDataFormatterTestCase(TestBase):
12
13    mydir = os.path.join("functionalities", "data-formatter", "data-formatter-python-synth")
14
15    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
16    @dsym_test
17    def test_with_dsym_and_run_command(self):
18        """Test data formatter commands."""
19        self.buildDsym()
20        self.data_formatter_commands()
21
22    @dwarf_test
23    def test_with_dwarf_and_run_command(self):
24        """Test data formatter commands."""
25        self.buildDwarf()
26        self.data_formatter_commands()
27
28    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
29    @dsym_test
30    def test_rdar10960550_with_dsym_and_run_command(self):
31        """Test data formatter commands."""
32        self.buildDsym()
33        self.rdar10960550_formatter_commands()
34
35    @dwarf_test
36    def test_rdar10960550_with_dwarf_and_run_command(self):
37        """Test data formatter commands."""
38        self.buildDwarf()
39        self.rdar10960550_formatter_commands()
40
41
42    def setUp(self):
43        # Call super's setUp().
44        TestBase.setUp(self)
45        # Find the line number to break at.
46        self.line = line_number('main.cpp', '// Set break point at this line.')
47        self.line2 = line_number('main.cpp', '// Set cast break point at this line.')
48        self.line3 = line_number('main.cpp', '// Set second cast break point at this line.')
49
50    def data_formatter_commands(self):
51        """Test using Python synthetic children provider."""
52        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
53
54        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True)
55
56        self.runCmd("run", RUN_SUCCEEDED)
57
58        # The stop reason of the thread should be breakpoint.
59        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
60            substrs = ['stopped',
61                       'stop reason = breakpoint'])
62
63        # This is the function to remove the custom formats in order to have a
64        # clean slate for the next test case.
65        def cleanup():
66            self.runCmd('type format clear', check=False)
67            self.runCmd('type summary clear', check=False)
68            self.runCmd('type filter clear', check=False)
69            self.runCmd('type synth clear', check=False)
70
71        # Execute the cleanup function during test case tear down.
72        self.addTearDownHook(cleanup)
73
74        # print the f00_1 variable without a synth
75        self.expect("frame variable f00_1",
76            substrs = ['a = 0',
77                       'b = 1',
78                       'r = 33']);
79
80        # now set up the synth
81        self.runCmd("script from fooSynthProvider import *")
82        self.runCmd("type synth add -l fooSynthProvider foo")
83
84        # check that we get the two real vars and the fake_a variables
85        self.expect("frame variable f00_1",
86                    substrs = ['r = 33',
87                               'fake_a = 16777216',
88                               'a = 0']);
89
90        # check that we do not get the extra vars
91        self.expect("frame variable f00_1", matching=False,
92                    substrs = ['b = 1']);
93
94        # check access to members by name
95        self.expect('frame variable f00_1.fake_a',
96                substrs = ['16777216'])
97
98        # check access to members by index
99        self.expect('frame variable f00_1[1]',
100                    substrs = ['16777216'])
101
102        # put synthetic children in summary in several combinations
103        self.runCmd("type summary add --summary-string \"fake_a=${svar.fake_a}\" foo")
104        self.expect('frame variable f00_1',
105                    substrs = ['fake_a=16777216'])
106        self.runCmd("type summary add --summary-string \"fake_a=${svar[1]}\" foo")
107        self.expect('frame variable f00_1',
108            substrs = ['fake_a=16777216'])
109
110        # clear the summary
111        self.runCmd("type summary delete foo")
112
113        # check that the caching does not span beyond the stopoint
114        self.runCmd("n")
115
116        self.expect("frame variable f00_1",
117                    substrs = ['r = 33',
118                               'fake_a = 16777216',
119                               'a = 1']);
120
121        # check that altering the object also alters fake_a
122        self.runCmd("expr f00_1.a = 280")
123        self.expect("frame variable f00_1",
124                    substrs = ['r = 33',
125                               'fake_a = 16777217',
126                               'a = 280']);
127
128        # check that expanding a pointer does the right thing
129        self.expect("frame variable --ptr-depth 1 f00_ptr",
130            substrs = ['r = 45',
131                       'fake_a = 218103808',
132                       'a = 12'])
133
134        # now add a filter.. it should fail
135        self.expect("type filter add foo --child b --child j", error=True,
136                substrs = ['cannot add'])
137
138        # we get the synth again..
139        self.expect('frame variable f00_1', matching=False,
140            substrs = ['b = 1',
141                       'j = 17'])
142        self.expect("frame variable --ptr-depth 1 f00_ptr",
143                    substrs = ['r = 45',
144                               'fake_a = 218103808',
145                               'a = 12'])
146
147        # now delete the synth and add the filter
148        self.runCmd("type synth delete foo")
149        self.runCmd("type filter add foo --child b --child j")
150
151        self.expect('frame variable f00_1',
152                        substrs = ['b = 1',
153                                   'j = 17'])
154        self.expect("frame variable --ptr-depth 1 f00_ptr", matching=False,
155                    substrs = ['r = 45',
156                               'fake_a = 218103808',
157                               'a = 12'])
158
159        # now add the synth and it should fail
160        self.expect("type synth add -l fooSynthProvider foo", error=True,
161                    substrs = ['cannot add'])
162
163        # check the listing
164        self.expect('type synth list', matching=False,
165                    substrs = ['foo',
166                               'Python class fooSynthProvider'])
167        self.expect('type filter list',
168                    substrs = ['foo',
169                               '.b',
170                               '.j'])
171
172        # delete the filter, add the synth
173        self.runCmd("type filter delete foo")
174        self.runCmd("type synth add -l fooSynthProvider foo")
175
176        self.expect('frame variable f00_1', matching=False,
177                    substrs = ['b = 1',
178                               'j = 17'])
179        self.expect("frame variable --ptr-depth 1 f00_ptr",
180                    substrs = ['r = 45',
181                               'fake_a = 218103808',
182                               'a = 12'])
183
184        # check the listing
185        self.expect('type synth list',
186                    substrs = ['foo',
187                               'Python class fooSynthProvider'])
188        self.expect('type filter list', matching=False,
189                    substrs = ['foo',
190                               '.b',
191                               '.j'])
192
193        # delete the synth and check that we get good output
194        self.runCmd("type synth delete foo")
195
196        self.expect("frame variable f00_1",
197                    substrs = ['a = 280',
198                               'b = 1',
199                               'j = 17']);
200
201        self.expect("frame variable f00_1", matching=False,
202                substrs = ['fake_a = '])
203
204    def rdar10960550_formatter_commands(self):
205        """Test that synthetic children persist stoppoints."""
206        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
207
208        # The second breakpoint is on a multi-line expression, so the comment can't be on the right line...
209        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line2, num_expected_locations=1, loc_exact=False)
210        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line3, num_expected_locations=1, loc_exact=True)
211
212        self.runCmd("run", RUN_SUCCEEDED)
213
214        # The stop reason of the thread should be breakpoint.
215        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
216            substrs = ['stopped',
217                       'stop reason = breakpoint'])
218
219        # This is the function to remove the custom formats in order to have a
220        # clean slate for the next test case.
221        def cleanup():
222            self.runCmd('type format clear', check=False)
223            self.runCmd('type summary clear', check=False)
224            self.runCmd('type filter clear', check=False)
225            self.runCmd('type synth clear', check=False)
226
227        # Execute the cleanup function during test case tear down.
228        self.addTearDownHook(cleanup)
229
230        self.runCmd("command script import ./ftsp.py --allow-reload")
231        self.runCmd("type synth add -l ftsp.ftsp wrapint")
232
233        # we need to check that the VO is properly updated so that the same synthetic children are reused
234        # but their values change correctly across stop-points - in order to do this, self.runCmd("next")
235        # does not work because it forces a wipe of the stack frame - this is why we are using this more contrived
236        # mechanism to achieve our goal of preserving test_cast as a VO
237        test_cast = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('test_cast')
238
239        str_cast = str(test_cast)
240
241        if self.TraceOn():
242             print str_cast
243
244        self.assertTrue(str_cast.find('A') != -1, 'could not find A in output')
245        self.assertTrue(str_cast.find('B') != -1, 'could not find B in output')
246        self.assertTrue(str_cast.find('C') != -1, 'could not find C in output')
247        self.assertTrue(str_cast.find('D') != -1, 'could not find D in output')
248        self.assertTrue(str_cast.find("4 = '\\0'") != -1, 'could not find item 4 == 0')
249
250        self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().StepOver()
251
252        str_cast = str(test_cast)
253
254        if self.TraceOn():
255             print str_cast
256
257        # we detect that all the values of the child objects have changed - but the counter-generated item
258        # is still fixed at 0 because it is cached - this would fail if update(self): in ftsp returned False
259        # or if synthetic children were not being preserved
260        self.assertTrue(str_cast.find('Q') != -1, 'could not find Q in output')
261        self.assertTrue(str_cast.find('X') != -1, 'could not find X in output')
262        self.assertTrue(str_cast.find('T') != -1, 'could not find T in output')
263        self.assertTrue(str_cast.find('F') != -1, 'could not find F in output')
264        self.assertTrue(str_cast.find("4 = '\\0'") != -1, 'could not find item 4 == 0')
265
266
267if __name__ == '__main__':
268    import atexit
269    lldb.SBDebugger.Initialize()
270    atexit.register(lambda: lldb.SBDebugger.Terminate())
271    unittest2.main()
272