xref: /llvm-project/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py (revision 7293455cf292cfaa263ea04fc1bc2aee4ceab6a6)
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
13
14class FrameRecognizerTestCase(TestBase):
15    NO_DEBUG_INFO_TESTCASE = True
16
17    def test_frame_recognizer_1(self):
18        self.build()
19        exe = self.getBuildArtifact("a.out")
20        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
21            self, "foo", exe_name=exe
22        )
23        frame = thread.selected_frame
24
25        # Clear internal & plugins recognizers that get initialized at launch
26        self.runCmd("frame recognizer clear")
27
28        self.runCmd(
29            "command script import "
30            + os.path.join(self.getSourceDir(), "recognizer.py")
31        )
32
33        self.expect("frame recognizer list", substrs=["no matching results found."])
34
35        self.runCmd(
36            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo"
37        )
38
39        self.expect(
40            "frame recognizer list",
41            substrs=[
42                "0: recognizer.MyFrameRecognizer, module a.out, demangled symbol foo"
43            ],
44        )
45
46        self.runCmd(
47            "frame recognizer add -l recognizer.MyOtherFrameRecognizer -s a.out -n bar -x"
48        )
49
50        self.expect(
51            "frame recognizer list",
52            substrs=[
53                "1: recognizer.MyOtherFrameRecognizer, module a.out, demangled symbol regex bar",
54                "0: recognizer.MyFrameRecognizer, module a.out, demangled symbol foo",
55            ],
56        )
57
58        self.runCmd("frame recognizer delete 0")
59
60        # Test that it deleted the recognizer with id 0.
61        self.expect(
62            "frame recognizer list",
63            substrs=[
64                "1: recognizer.MyOtherFrameRecognizer, module a.out, demangled symbol regex bar"
65            ],
66        )
67        self.expect(
68            "frame recognizer list", matching=False, substrs=["MyFrameRecognizer"]
69        )
70
71        # Test that an invalid index and deleting the same index again
72        # is an error and doesn't do any changes.
73        self.expect(
74            "frame recognizer delete 2",
75            error=True,
76            substrs=["error: '2' is not a valid recognizer id."],
77        )
78        self.expect(
79            "frame recognizer delete 0",
80            error=True,
81            substrs=["error: '0' is not a valid recognizer id."],
82        )
83        # Recognizers should have the same state as above.
84        self.expect(
85            "frame recognizer list",
86            substrs=[
87                "1: recognizer.MyOtherFrameRecognizer, module a.out, demangled symbol regex bar"
88            ],
89        )
90        self.expect(
91            "frame recognizer list", matching=False, substrs=["MyFrameRecognizer"]
92        )
93
94        self.runCmd("frame recognizer clear")
95
96        self.expect("frame recognizer list", substrs=["no matching results found."])
97
98        self.runCmd(
99            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo"
100        )
101
102        self.expect("frame variable", substrs=["(int) a = 42", "(int) b = 56"])
103
104        # Recognized arguments don't show up by default...
105        variables = frame.GetVariables(lldb.SBVariablesOptions())
106        self.assertEqual(variables.GetSize(), 0)
107
108        # ...unless you set target.display-recognized-arguments to 1...
109        self.runCmd("settings set target.display-recognized-arguments 1")
110        variables = frame.GetVariables(lldb.SBVariablesOptions())
111        self.assertEqual(variables.GetSize(), 2)
112
113        # ...and you can reset it back to 0 to hide them again...
114        self.runCmd("settings set target.display-recognized-arguments 0")
115        variables = frame.GetVariables(lldb.SBVariablesOptions())
116        self.assertEqual(variables.GetSize(), 0)
117
118        # ... or explicitly ask for them with SetIncludeRecognizedArguments(True).
119        opts = lldb.SBVariablesOptions()
120        opts.SetIncludeRecognizedArguments(True)
121        variables = frame.GetVariables(opts)
122
123        self.assertEqual(variables.GetSize(), 2)
124        self.assertEqual(variables.GetValueAtIndex(0).name, "a")
125        self.assertEqual(variables.GetValueAtIndex(0).signed, 42)
126        self.assertEqual(
127            variables.GetValueAtIndex(0).GetValueType(), lldb.eValueTypeVariableArgument
128        )
129        self.assertEqual(variables.GetValueAtIndex(1).name, "b")
130        self.assertEqual(variables.GetValueAtIndex(1).signed, 56)
131        self.assertEqual(
132            variables.GetValueAtIndex(1).GetValueType(), lldb.eValueTypeVariableArgument
133        )
134
135        self.expect(
136            "frame recognizer info 0",
137            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
138        )
139
140        self.expect(
141            "frame recognizer info 999", error=True, substrs=["no frame with index 999"]
142        )
143
144        self.expect(
145            "frame recognizer info 1",
146            substrs=["frame 1 not recognized by any recognizer"],
147        )
148
149        # FIXME: The following doesn't work yet, but should be fixed.
150        """
151        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "bar",
152                                                                 exe_name = exe)
153        frame = thread.GetSelectedFrame()
154
155        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
156                    substrs=['stopped', 'stop reason = breakpoint'])
157
158        self.expect("frame variable -t",
159                    substrs=['(int *) a = '])
160
161        self.expect("frame variable -t *a",
162                    substrs=['*a = 78'])
163        """
164
165    def test_frame_recognizer_hiding(self):
166        self.build()
167
168        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, "nested")
169        frame = thread.selected_frame
170
171        # Sanity check.
172        self.expect(
173            "thread backtrace", patterns=["frame.*nested", "frame.*baz", "frame.*main"]
174        )
175
176        self.expect("frame recognizer clear")
177        self.expect(
178            "command script import "
179            + os.path.join(self.getSourceDir(), "recognizer.py")
180        )
181
182        self.expect(
183            "frame recognizer add -l recognizer.BazFrameRecognizer -f false -s a.out -n baz"
184        )
185
186        self.expect(
187            "frame recognizer list",
188            substrs=["0: recognizer.BazFrameRecognizer"],
189        )
190
191        # Now main should be hidden.
192        self.expect("thread backtrace", matching=False, patterns=["frame.*baz"])
193        self.assertFalse(frame.IsHidden())
194        frame = thread.SetSelectedFrame(1)
195        self.assertIn("baz", frame.name)
196        self.assertTrue(frame.IsHidden())
197
198        # Test StepOut.
199        frame = thread.SetSelectedFrame(0)
200        thread.StepOut()
201        frame = thread.GetSelectedFrame()
202        self.assertIn("main", frame.name)
203
204    def test_frame_recognizer_multi_symbol(self):
205        self.build()
206        exe = self.getBuildArtifact("a.out")
207
208        # Clear internal & plugins recognizers that get initialized at launch
209        self.runCmd("frame recognizer clear")
210
211        self.runCmd(
212            "command script import "
213            + os.path.join(self.getSourceDir(), "recognizer.py")
214        )
215
216        self.expect("frame recognizer list", substrs=["no matching results found."])
217
218        self.runCmd(
219            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar"
220        )
221
222        self.expect(
223            "frame recognizer list",
224            substrs=[
225                "recognizer.MyFrameRecognizer, module a.out, demangled symbol foo, bar"
226            ],
227        )
228
229        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
230            self, "foo", exe_name=exe
231        )
232
233        self.expect(
234            "frame recognizer info 0",
235            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
236        )
237
238        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
239            self, "bar", exe_name=exe
240        )
241
242        self.expect(
243            "frame recognizer info 0",
244            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
245        )
246
247    def test_frame_recognizer_target_specific(self):
248        self.build()
249        exe = self.getBuildArtifact("a.out")
250
251        # Clear internal & plugins recognizers that get initialized at launch
252        self.runCmd("frame recognizer clear")
253
254        # Create a target.
255        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
256            self, "foo", exe_name=exe
257        )
258
259        self.runCmd(
260            "command script import "
261            + os.path.join(self.getSourceDir(), "recognizer.py")
262        )
263
264        # Check that this doesn't contain our own FrameRecognizer somehow.
265        self.expect(
266            "frame recognizer list", matching=False, substrs=["MyFrameRecognizer"]
267        )
268
269        # Add a frame recognizer in that target.
270        self.runCmd(
271            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar"
272        )
273
274        self.expect(
275            "frame recognizer list",
276            substrs=[
277                "recognizer.MyFrameRecognizer, module a.out, demangled symbol foo, bar"
278            ],
279        )
280
281        self.expect(
282            "frame recognizer info 0",
283            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
284        )
285
286        # Create a second target. That one shouldn't have the frame recognizer.
287        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
288            self, "bar", exe_name=exe
289        )
290
291        self.expect(
292            "frame recognizer info 0",
293            substrs=["frame 0 not recognized by any recognizer"],
294        )
295
296        # Add a frame recognizer to the new target.
297        self.runCmd(
298            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n bar"
299        )
300
301        self.expect(
302            "frame recognizer list",
303            substrs=[
304                "recognizer.MyFrameRecognizer, module a.out, demangled symbol bar"
305            ],
306        )
307
308        # Now the new target should also recognize the frame.
309        self.expect(
310            "frame recognizer info 0",
311            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
312        )
313
314    def test_frame_recognizer_not_only_first_instruction(self):
315        self.build()
316        exe = self.getBuildArtifact("a.out")
317
318        # Clear internal & plugins recognizers that get initialized at launch.
319        self.runCmd("frame recognizer clear")
320
321        self.runCmd(
322            "command script import "
323            + os.path.join(self.getSourceDir(), "recognizer.py")
324        )
325
326        self.expect("frame recognizer list", substrs=["no matching results found."])
327
328        # Create a target.
329        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
330            self, "foo", exe_name=exe
331        )
332
333        # Move the PC one instruction further.
334        self.runCmd("next")
335
336        # Add a frame recognizer in that target.
337        self.runCmd(
338            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar"
339        )
340
341        # It's not applied to foo(), because frame's PC is not at the first instruction of the function.
342        self.expect(
343            "frame recognizer info 0",
344            substrs=["frame 0 not recognized by any recognizer"],
345        )
346
347        # Add a frame recognizer with --first-instruction-only=true.
348        self.runCmd("frame recognizer clear")
349
350        self.runCmd(
351            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar --first-instruction-only=true"
352        )
353
354        # It's not applied to foo(), because frame's PC is not at the first instruction of the function.
355        self.expect(
356            "frame recognizer info 0",
357            substrs=["frame 0 not recognized by any recognizer"],
358        )
359
360        # Now add a frame recognizer with --first-instruction-only=false.
361        self.runCmd("frame recognizer clear")
362
363        self.runCmd(
364            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar --first-instruction-only=false"
365        )
366
367        # This time it should recognize the frame.
368        self.expect(
369            "frame recognizer info 0",
370            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
371        )
372
373        opts = lldb.SBVariablesOptions()
374        opts.SetIncludeRecognizedArguments(True)
375        frame = thread.selected_frame
376        variables = frame.GetVariables(opts)
377
378        self.assertEqual(variables.GetSize(), 2)
379        self.assertEqual(variables.GetValueAtIndex(0).name, "a")
380        self.assertEqual(variables.GetValueAtIndex(0).signed, 42)
381        self.assertEqual(
382            variables.GetValueAtIndex(0).GetValueType(), lldb.eValueTypeVariableArgument
383        )
384        self.assertEqual(variables.GetValueAtIndex(1).name, "b")
385        self.assertEqual(variables.GetValueAtIndex(1).signed, 56)
386        self.assertEqual(
387            variables.GetValueAtIndex(1).GetValueType(), lldb.eValueTypeVariableArgument
388        )
389
390    def test_frame_recognizer_disable(self):
391        self.build()
392        exe = self.getBuildArtifact("a.out")
393        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
394            self, "foo", exe_name=exe
395        )
396
397        # Clear internal & plugins recognizers that get initialized at launch.
398        self.runCmd("frame recognizer clear")
399
400        self.runCmd(
401            "command script import "
402            + os.path.join(self.getSourceDir(), "recognizer.py")
403        )
404
405        # Add a frame recognizer in that target.
406        self.runCmd(
407            "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo -n bar"
408        )
409
410        # The frame is recognized
411        self.expect(
412            "frame recognizer info 0",
413            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
414        )
415
416        # Disable the recognizer
417        self.runCmd("frame recognizer disable 0")
418
419        self.expect(
420            "frame recognizer list",
421            substrs=[
422                "0: [disabled] recognizer.MyFrameRecognizer, module a.out, demangled symbol foo"
423            ],
424        )
425
426        self.expect(
427            "frame recognizer info 0",
428            substrs=["frame 0 not recognized by any recognizer"],
429        )
430
431        # Re-enable the recognizer
432        self.runCmd("frame recognizer enable 0")
433
434        self.expect(
435            "frame recognizer list",
436            substrs=[
437                "0: recognizer.MyFrameRecognizer, module a.out, demangled symbol foo"
438            ],
439        )
440
441        self.expect(
442            "frame recognizer info 0",
443            substrs=["frame 0 is recognized by recognizer.MyFrameRecognizer"],
444        )
445
446    @no_debug_info_test
447    def test_frame_recognizer_delete_invalid_arg(self):
448        self.expect(
449            "frame recognizer delete a",
450            error=True,
451            substrs=["error: 'a' is not a valid recognizer id."],
452        )
453        self.expect(
454            'frame recognizer delete ""',
455            error=True,
456            substrs=["error: '' is not a valid recognizer id."],
457        )
458        self.expect(
459            "frame recognizer delete -1",
460            error=True,
461            substrs=["error: '-1' is not a valid recognizer id."],
462        )
463        self.expect(
464            "frame recognizer delete 4294967297",
465            error=True,
466            substrs=["error: '4294967297' is not a valid recognizer id."],
467        )
468
469    @no_debug_info_test
470    def test_frame_recognizer_info_invalid_arg(self):
471        self.expect(
472            "frame recognizer info a",
473            error=True,
474            substrs=["error: 'a' is not a valid frame index."],
475        )
476        self.expect(
477            'frame recognizer info ""',
478            error=True,
479            substrs=["error: '' is not a valid frame index."],
480        )
481        self.expect(
482            "frame recognizer info -1",
483            error=True,
484            substrs=["error: '-1' is not a valid frame index."],
485        )
486        self.expect(
487            "frame recognizer info 4294967297",
488            error=True,
489            substrs=["error: '4294967297' is not a valid frame index."],
490        )
491
492    @no_debug_info_test
493    def test_frame_recognizer_add_invalid_arg(self):
494        self.expect(
495            "frame recognizer add -f",
496            error=True,
497            substrs=["error: last option requires an argument"],
498        )
499        self.expect(
500            "frame recognizer add -f -1",
501            error=True,
502            substrs=["error: invalid boolean value '-1' passed for -f option"],
503        )
504        self.expect(
505            "frame recognizer add -f foo",
506            error=True,
507            substrs=["error: invalid boolean value 'foo' passed for -f option"],
508        )
509