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