1# encoding: utf-8
2"""
3Test lldb's frame recognizers.
4"""
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11import recognizer
12
13class FrameRecognizerTestCase(TestBase):
14    NO_DEBUG_INFO_TESTCASE = True
15
16    @skipUnlessDarwin
17    def test_frame_recognizer_1(self):
18        self.build()
19        exe = self.getBuildArtifact("a.out")
20
21        # Clear internal & plugins recognizers that get initialized at launch
22        self.runCmd("frame recognizer clear")
23
24        self.runCmd("command script import " + os.path.join(self.getSourceDir(), "recognizer.py"))
25
26        self.expect("frame recognizer list",
27                    substrs=['no matching results found.'])
28
29        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo")
30
31        self.expect("frame recognizer list",
32                    substrs=['0: recognizer.MyFrameRecognizer, module a.out, symbol foo'])
33
34        self.runCmd("frame recognizer add -l recognizer.MyOtherFrameRecognizer -s a.out -n bar -x")
35
36        self.expect(
37            "frame recognizer list",
38            substrs=[
39                '1: recognizer.MyOtherFrameRecognizer, module a.out, symbol bar (regexp)',
40                '0: recognizer.MyFrameRecognizer, module a.out, symbol foo'
41            ])
42
43        self.runCmd("frame recognizer delete 0")
44
45        # Test that it deleted the recognizer with id 0.
46        self.expect("frame recognizer list",
47                    substrs=['1: recognizer.MyOtherFrameRecognizer, module a.out, symbol bar (regexp)'])
48        self.expect("frame recognizer list", matching=False,
49                    substrs=['MyFrameRecognizer'])
50
51        # Test that an invalid index and deleting the same index again
52        # is an error and doesn't do any changes.
53        self.expect("frame recognizer delete 2", error=True,
54                    substrs=["error: '2' is not a valid recognizer id."])
55        self.expect("frame recognizer delete 0", error=True,
56                    substrs=["error: '0' is not a valid recognizer id."])
57        # Recognizers should have the same state as above.
58        self.expect("frame recognizer list",
59                    substrs=['1: recognizer.MyOtherFrameRecognizer, module a.out, symbol bar (regexp)'])
60        self.expect("frame recognizer list", matching=False,
61                    substrs=['MyFrameRecognizer'])
62
63
64        self.runCmd("frame recognizer clear")
65
66        self.expect("frame recognizer list",
67                    substrs=['no matching results found.'])
68
69        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo")
70
71        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "foo",
72                                                                 exe_name = exe)
73        frame = thread.GetSelectedFrame()
74
75        self.expect("frame variable",
76                    substrs=['(int) a = 42', '(int) b = 56'])
77
78        # Recognized arguments don't show up by default...
79        variables = frame.GetVariables(lldb.SBVariablesOptions())
80        self.assertEqual(variables.GetSize(), 0)
81
82        # ...unless you set target.display-recognized-arguments to 1...
83        self.runCmd("settings set target.display-recognized-arguments 1")
84        variables = frame.GetVariables(lldb.SBVariablesOptions())
85        self.assertEqual(variables.GetSize(), 2)
86
87        # ...and you can reset it back to 0 to hide them again...
88        self.runCmd("settings set target.display-recognized-arguments 0")
89        variables = frame.GetVariables(lldb.SBVariablesOptions())
90        self.assertEqual(variables.GetSize(), 0)
91
92        # ... or explicitly ask for them with SetIncludeRecognizedArguments(True).
93        opts = lldb.SBVariablesOptions()
94        opts.SetIncludeRecognizedArguments(True)
95        variables = frame.GetVariables(opts)
96
97        self.assertEqual(variables.GetSize(), 2)
98        self.assertEqual(variables.GetValueAtIndex(0).name, "a")
99        self.assertEqual(variables.GetValueAtIndex(0).signed, 42)
100        self.assertEqual(variables.GetValueAtIndex(0).GetValueType(), lldb.eValueTypeVariableArgument)
101        self.assertEqual(variables.GetValueAtIndex(1).name, "b")
102        self.assertEqual(variables.GetValueAtIndex(1).signed, 56)
103        self.assertEqual(variables.GetValueAtIndex(1).GetValueType(), lldb.eValueTypeVariableArgument)
104
105        self.expect("frame recognizer info 0",
106                    substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
107
108        self.expect("frame recognizer info 999", error=True,
109                    substrs=['no frame with index 999'])
110
111        self.expect("frame recognizer info 1",
112                    substrs=['frame 1 not recognized by any recognizer'])
113
114        # FIXME: The following doesn't work yet, but should be fixed.
115        """
116        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "bar",
117                                                                 exe_name = exe)
118        frame = thread.GetSelectedFrame()
119
120        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
121                    substrs=['stopped', 'stop reason = breakpoint'])
122
123        self.expect("frame variable -t",
124                    substrs=['(int *) a = '])
125
126        self.expect("frame variable -t *a",
127                    substrs=['*a = 78'])
128        """
129
130    @skipUnlessDarwin
131    def test_frame_recognizer_multi_symbol(self):
132        self.build()
133        exe = self.getBuildArtifact("a.out")
134
135        # Clear internal & plugins recognizers that get initialized at launch
136        self.runCmd("frame recognizer clear")
137
138        self.runCmd("command script import " + os.path.join(self.getSourceDir(), "recognizer.py"))
139
140        self.expect("frame recognizer list",
141                    substrs=['no matching results found.'])
142
143        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar")
144
145        self.expect("frame recognizer list",
146                    substrs=['recognizer.MyFrameRecognizer, module a.out, symbol foo, symbol bar'])
147
148        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "foo",
149                                                                 exe_name = exe)
150        frame = thread.GetSelectedFrame()
151
152        self.expect("frame recognizer info 0",
153                    substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
154
155        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "bar",
156                                                                 exe_name = exe)
157        frame = thread.GetSelectedFrame()
158
159        self.expect("frame recognizer info 0",
160                    substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
161
162    @skipUnlessDarwin
163    def test_frame_recognizer_target_specific(self):
164        self.build()
165        exe = self.getBuildArtifact("a.out")
166
167        # Clear internal & plugins recognizers that get initialized at launch
168        self.runCmd("frame recognizer clear")
169
170        # Create a target.
171        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "foo",
172                                                                 exe_name = exe)
173
174        self.runCmd("command script import " + os.path.join(self.getSourceDir(), "recognizer.py"))
175
176        # Check that this doesn't contain our own FrameRecognizer somehow.
177        self.expect("frame recognizer list",
178                    matching=False, substrs=['MyFrameRecognizer'])
179
180        # Add a frame recognizer in that target.
181        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar")
182
183        self.expect("frame recognizer list",
184                    substrs=['recognizer.MyFrameRecognizer, module a.out, symbol foo, symbol bar'])
185
186        self.expect("frame recognizer info 0",
187                    substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
188
189        # Create a second target. That one shouldn't have the frame recognizer.
190        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "bar",
191                                                                 exe_name = exe)
192
193        self.expect("frame recognizer info 0",
194                    substrs=['frame 0 not recognized by any recognizer'])
195
196        # Add a frame recognizer to the new target.
197        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n bar")
198
199        self.expect("frame recognizer list",
200                    substrs=['recognizer.MyFrameRecognizer, module a.out, symbol bar'])
201
202        # Now the new target should also recognize the frame.
203        self.expect("frame recognizer info 0",
204                    substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
205
206    @skipUnlessDarwin
207    def test_frame_recognizer_not_only_first_instruction(self):
208        self.build()
209        exe = self.getBuildArtifact("a.out")
210
211        # Clear internal & plugins recognizers that get initialized at launch.
212        self.runCmd("frame recognizer clear")
213
214        self.runCmd("command script import " + os.path.join(self.getSourceDir(), "recognizer.py"))
215
216        self.expect("frame recognizer list",
217                    substrs=['no matching results found.'])
218
219        # Create a target.
220        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "foo",
221                                                                 exe_name = exe)
222
223        # Move the PC one instruction further.
224        self.runCmd("next")
225
226        # Add a frame recognizer in that target.
227        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar")
228
229        # It's not applied to foo(), because frame's PC is not at the first instruction of the function.
230        self.expect("frame recognizer info 0",
231                    substrs=['frame 0 not recognized by any recognizer'])
232
233        # Add a frame recognizer with --first-instruction-only=true.
234        self.runCmd("frame recognizer clear")
235
236        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar --first-instruction-only=true")
237
238        # It's not applied to foo(), because frame's PC is not at the first instruction of the function.
239        self.expect("frame recognizer info 0",
240                    substrs=['frame 0 not recognized by any recognizer'])
241
242        # Now add a frame recognizer with --first-instruction-only=false.
243        self.runCmd("frame recognizer clear")
244
245        self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar --first-instruction-only=false")
246
247        # This time it should recognize the frame.
248        self.expect("frame recognizer info 0",
249                    substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
250
251        opts = lldb.SBVariablesOptions()
252        opts.SetIncludeRecognizedArguments(True)
253        frame = thread.GetSelectedFrame()
254        variables = frame.GetVariables(opts)
255
256        self.assertEqual(variables.GetSize(), 2)
257        self.assertEqual(variables.GetValueAtIndex(0).name, "a")
258        self.assertEqual(variables.GetValueAtIndex(0).signed, 42)
259        self.assertEqual(variables.GetValueAtIndex(0).GetValueType(), lldb.eValueTypeVariableArgument)
260        self.assertEqual(variables.GetValueAtIndex(1).name, "b")
261        self.assertEqual(variables.GetValueAtIndex(1).signed, 56)
262        self.assertEqual(variables.GetValueAtIndex(1).GetValueType(), lldb.eValueTypeVariableArgument)
263
264    @no_debug_info_test
265    def test_frame_recognizer_delete_invalid_arg(self):
266        self.expect("frame recognizer delete a", error=True,
267                    substrs=["error: 'a' is not a valid recognizer id."])
268        self.expect("frame recognizer delete \"\"", error=True,
269                    substrs=["error: '' is not a valid recognizer id."])
270        self.expect("frame recognizer delete -1", error=True,
271                    substrs=["error: '-1' is not a valid recognizer id."])
272        self.expect("frame recognizer delete 4294967297", error=True,
273                    substrs=["error: '4294967297' is not a valid recognizer id."])
274
275    @no_debug_info_test
276    def test_frame_recognizer_info_invalid_arg(self):
277        self.expect("frame recognizer info a", error=True,
278                    substrs=["error: 'a' is not a valid frame index."])
279        self.expect("frame recognizer info \"\"", error=True,
280                    substrs=["error: '' is not a valid frame index."])
281        self.expect("frame recognizer info -1", error=True,
282                    substrs=["error: '-1' is not a valid frame index."])
283        self.expect("frame recognizer info 4294967297", error=True,
284                    substrs=["error: '4294967297' is not a valid frame index."])
285
286    @no_debug_info_test
287    def test_frame_recognizer_add_invalid_arg(self):
288        self.expect("frame recognizer add -f", error=True,
289                    substrs=["error: last option requires an argument"])
290        self.expect("frame recognizer add -f -1", error=True,
291                    substrs=["error: invalid boolean value '-1' passed for -f option"])
292        self.expect("frame recognizer add -f foo", error=True,
293                    substrs=["error: invalid boolean value 'foo' passed for -f option"])
294