xref: /llvm-project/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py (revision 5b4100cc354148a1140546e7f5ac2bf380bc5eff)
1"""
2Test lldb-dap stackTrace request
3"""
4
5
6import os
7
8import lldbdap_testcase
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11
12
13class TestDAP_stackTrace(lldbdap_testcase.DAPTestCaseBase):
14    name_key_path = ["name"]
15    source_key_path = ["source", "path"]
16    line_key_path = ["line"]
17
18    # stackTrace additioanl frames for paginated traces
19    page_size = 20
20
21    def verify_stackFrames(self, start_idx, stackFrames):
22        frame_idx = start_idx
23        for stackFrame in stackFrames:
24            # Don't care about frame above main
25            if frame_idx > 40:
26                return
27            self.verify_stackFrame(frame_idx, stackFrame)
28            frame_idx += 1
29
30    def verify_stackFrame(self, frame_idx, stackFrame):
31        frame_name = self.get_dict_value(stackFrame, self.name_key_path)
32        frame_source = self.get_dict_value(stackFrame, self.source_key_path)
33        frame_line = self.get_dict_value(stackFrame, self.line_key_path)
34        if frame_idx == 0:
35            expected_line = self.recurse_end
36            expected_name = "recurse"
37        elif frame_idx < 40:
38            expected_line = self.recurse_call
39            expected_name = "recurse"
40        else:
41            expected_line = self.recurse_invocation
42            expected_name = "main"
43        self.assertEqual(
44            frame_name,
45            expected_name,
46            'frame #%i name "%s" == "%s"' % (frame_idx, frame_name, expected_name),
47        )
48        self.assertEqual(
49            frame_source,
50            self.source_path,
51            'frame #%i source "%s" == "%s"'
52            % (frame_idx, frame_source, self.source_path),
53        )
54        self.assertEqual(
55            frame_line,
56            expected_line,
57            "frame #%i line %i == %i" % (frame_idx, frame_line, expected_line),
58        )
59
60    @skipIfWindows
61    def test_stackTrace(self):
62        """
63        Tests the 'stackTrace' packet and all its variants.
64        """
65        program = self.getBuildArtifact("a.out")
66        self.build_and_launch(program)
67        source = "main.c"
68        self.source_path = os.path.join(os.getcwd(), source)
69        self.recurse_end = line_number(source, "recurse end")
70        self.recurse_call = line_number(source, "recurse call")
71        self.recurse_invocation = line_number(source, "recurse invocation")
72
73        lines = [self.recurse_end]
74
75        # Set breakpoint at a point of deepest recuusion
76        breakpoint_ids = self.set_source_breakpoints(source, lines)
77        self.assertEqual(
78            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
79        )
80
81        self.continue_to_breakpoints(breakpoint_ids)
82        startFrame = 0
83        # Verify we get all stack frames with no arguments
84        (stackFrames, totalFrames) = self.get_stackFrames_and_totalFramesCount()
85        frameCount = len(stackFrames)
86        self.assertGreaterEqual(
87            frameCount, 40, "verify we get at least 40 frames for all frames"
88        )
89        self.assertEqual(
90            totalFrames,
91            frameCount,
92            "verify total frames returns a speculative page size",
93        )
94        self.verify_stackFrames(startFrame, stackFrames)
95
96        # Verify totalFrames contains a speculative page size of additional frames with startFrame = 0 and levels = 0
97        (stackFrames, totalFrames) = self.get_stackFrames_and_totalFramesCount(
98            startFrame=0, levels=10
99        )
100        self.assertEqual(len(stackFrames), 10, "verify we get levels=10 frames")
101        self.assertEqual(
102            totalFrames,
103            len(stackFrames) + self.page_size,
104            "verify total frames returns a speculative page size",
105        )
106        self.verify_stackFrames(startFrame, stackFrames)
107
108        # Verify all stack frames by specifying startFrame = 0 and levels not
109        # specified
110        stackFrames = self.get_stackFrames(startFrame=startFrame)
111        self.assertEqual(
112            frameCount,
113            len(stackFrames),
114            ("verify same number of frames with startFrame=%i") % (startFrame),
115        )
116        self.verify_stackFrames(startFrame, stackFrames)
117
118        # Verify all stack frames by specifying startFrame = 0 and levels = 0
119        levels = 0
120        stackFrames = self.get_stackFrames(startFrame=startFrame, levels=levels)
121        self.assertEqual(
122            frameCount,
123            len(stackFrames),
124            ("verify same number of frames with startFrame=%i and" " levels=%i")
125            % (startFrame, levels),
126        )
127        self.verify_stackFrames(startFrame, stackFrames)
128
129        # Get only the first stack frame by sepcifying startFrame = 0 and
130        # levels = 1
131        levels = 1
132        stackFrames = self.get_stackFrames(startFrame=startFrame, levels=levels)
133        self.assertEqual(
134            levels,
135            len(stackFrames),
136            ("verify one frame with startFrame=%i and" " levels=%i")
137            % (startFrame, levels),
138        )
139        self.verify_stackFrames(startFrame, stackFrames)
140
141        # Get only the first 3 stack frames by sepcifying startFrame = 0 and
142        # levels = 3
143        levels = 3
144        stackFrames = self.get_stackFrames(startFrame=startFrame, levels=levels)
145        self.assertEqual(
146            levels,
147            len(stackFrames),
148            ("verify %i frames with startFrame=%i and" " levels=%i")
149            % (levels, startFrame, levels),
150        )
151        self.verify_stackFrames(startFrame, stackFrames)
152
153        # Get only the first 15 stack frames by sepcifying startFrame = 5 and
154        # levels = 16
155        startFrame = 5
156        levels = 16
157        stackFrames = self.get_stackFrames(startFrame=startFrame, levels=levels)
158        self.assertEqual(
159            levels,
160            len(stackFrames),
161            ("verify %i frames with startFrame=%i and" " levels=%i")
162            % (levels, startFrame, levels),
163        )
164        self.verify_stackFrames(startFrame, stackFrames)
165
166        # Verify we cap things correctly when we ask for too many frames
167        startFrame = 5
168        levels = 1000
169        (stackFrames, totalFrames) = self.get_stackFrames_and_totalFramesCount(
170            startFrame=startFrame, levels=levels
171        )
172        self.assertEqual(
173            len(stackFrames),
174            frameCount - startFrame,
175            ("verify less than 1000 frames with startFrame=%i and" " levels=%i")
176            % (startFrame, levels),
177        )
178        self.assertEqual(
179            totalFrames,
180            frameCount,
181            "verify we get correct value for totalFrames count "
182            "when requested frames not from 0 index",
183        )
184        self.verify_stackFrames(startFrame, stackFrames)
185
186        # Verify level=0 works with non-zerp start frame
187        startFrame = 5
188        levels = 0
189        stackFrames = self.get_stackFrames(startFrame=startFrame, levels=levels)
190        self.assertEqual(
191            len(stackFrames),
192            frameCount - startFrame,
193            ("verify less than 1000 frames with startFrame=%i and" " levels=%i")
194            % (startFrame, levels),
195        )
196        self.verify_stackFrames(startFrame, stackFrames)
197
198        # Verify we get not frames when startFrame is too high
199        startFrame = 1000
200        levels = 1
201        stackFrames = self.get_stackFrames(startFrame=startFrame, levels=levels)
202        self.assertEqual(
203            0, len(stackFrames), "verify zero frames with startFrame out of bounds"
204        )
205
206    @skipIfWindows
207    def test_functionNameWithArgs(self):
208        """
209        Test that the stack frame without a function name is given its pc in the response.
210        """
211        program = self.getBuildArtifact("a.out")
212        self.build_and_launch(program, customFrameFormat="${function.name-with-args}")
213        source = "main.c"
214
215        self.set_source_breakpoints(source, [line_number(source, "recurse end")])
216
217        self.continue_to_next_stop()
218        frame = self.get_stackFrames()[0]
219        self.assertEqual(frame["name"], "recurse(x=1)")
220