xref: /llvm-project/lldb/test/API/commands/trace/TestTraceDumpFunctionCalls.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1from intelpt_testcase import *
2from lldbsuite.test.lldbtest import *
3from lldbsuite.test.decorators import *
4
5
6class TestTraceDumpInfo(TraceIntelPTTestCaseBase):
7    def testDumpSimpleFunctionCalls(self):
8        self.expect(
9            "trace load -v "
10            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")
11        )
12
13        self.expect(
14            "thread trace dump function-calls 2",
15            error=True,
16            substrs=['error: no thread with index: "2"'],
17        )
18
19        self.expect(
20            "thread trace dump function-calls 1 -j",
21            substrs=[
22                '[{"tracedSegments":[{"firstInstructionId":"3","lastInstructionId":"26"}]}]'
23            ],
24        )
25
26        self.expect(
27            "thread trace dump function-calls 1 -J",
28            substrs=[
29                """[
30  {
31    "tracedSegments": [
32      {
33        "firstInstructionId": "3",
34        "lastInstructionId": "26"
35      }
36    ]
37  }
38]"""
39            ],
40        )
41
42        # We test first some code without function call
43        self.expect(
44            "thread trace dump function-calls 1",
45            substrs=[
46                """thread #1: tid = 3842849
47
48[call tree #0]
49a.out`main + 4 at main.cpp:2 to 4:0  [3, 26]"""
50            ],
51        )
52
53    def testFunctionCallsWithErrors(self):
54        self.expect(
55            "trace load -v "
56            + os.path.join(
57                self.getSourceDir(), "intelpt-multi-core-trace", "trace.json"
58            )
59        )
60
61        # We expect that tracing errors appear as a different tree
62        self.expect(
63            "thread trace dump function-calls 2",
64            substrs=[
65                """thread #2: tid = 3497496
66
67[call tree #0]
68m.out`foo() + 65 at multi_thread.cpp:12:21 to 12:21  [4, 19524]
69
70[call tree #1]
71<tracing errors>  [19526, 19526]"""
72            ],
73        )
74
75        self.expect(
76            "thread trace dump function-calls 2 -J",
77            substrs=[
78                """[
79  {
80    "tracedSegments": [
81      {
82        "firstInstructionId": "4",
83        "lastInstructionId": "19524"
84      }
85    ]
86  },
87  {
88    "tracedSegments": [
89      {
90        "firstInstructionId": "19526",
91        "lastInstructionId": "19526"
92      }
93    ]
94  }
95]"""
96            ],
97        )
98
99        self.expect(
100            "thread trace dump function-calls 3",
101            substrs=[
102                """thread #3: tid = 3497497
103
104[call tree #0]
105m.out`bar() + 30 at multi_thread.cpp:19:3 to 20:6  [5, 61831]
106
107[call tree #1]
108<tracing errors>  [61833, 61833]"""
109            ],
110        )
111
112        self.expect(
113            "thread trace dump function-calls 3 -J",
114            substrs=[
115                """[
116  {
117    "tracedSegments": [
118      {
119        "firstInstructionId": "5",
120        "lastInstructionId": "61831"
121      }
122    ]
123  },
124  {
125    "tracedSegments": [
126      {
127        "firstInstructionId": "61833",
128        "lastInstructionId": "61833"
129      }
130    ]
131  }
132]"""
133            ],
134        )
135
136    def testInlineFunctionCalls(self):
137        self.expect(
138            "file " + os.path.join(self.getSourceDir(), "inline-function", "a.out")
139        )
140        self.expect("b main")  # we'll trace from the beginning of main
141        self.expect("b 17")
142        self.expect("r")
143        self.expect("thread trace start")
144        self.expect("c")
145        self.expect(
146            "thread trace dump function-calls",
147            substrs=[
148                """[call tree #0]
149a.out`main + 8 at inline.cpp:15:7 to 16:14  [2, 6]
150  a.out`foo(int) at inline.cpp:8:16 to 9:15  [7, 14]
151    a.out`foo(int) + 22 [inlined] mult(int, int) at inline.cpp:2:7 to 5:10  [15, 22]
152  a.out`foo(int) + 49 at inline.cpp:9:15 to 12:1  [23, 27]
153a.out`main + 25 at inline.cpp:16:14 to 16:14  [28, 28]"""
154            ],
155        )
156
157        self.expect(
158            "thread trace dump function-calls -J",
159            substrs=[
160                """[
161  {
162    "tracedSegments": [
163      {
164        "firstInstructionId": "2",
165        "lastInstructionId": "6",
166        "nestedCall": {
167          "tracedSegments": [
168            {
169              "firstInstructionId": "7",
170              "lastInstructionId": "14",
171              "nestedCall": {
172                "tracedSegments": [
173                  {
174                    "firstInstructionId": "15",
175                    "lastInstructionId": "22"
176                  }
177                ]
178              }
179            },
180            {
181              "firstInstructionId": "23",
182              "lastInstructionId": "27"
183            }
184          ]
185        }
186      },
187      {
188        "firstInstructionId": "28",
189        "lastInstructionId": "28"
190      }
191    ]
192  }
193]"""
194            ],
195        )
196
197    def testIncompleteInlineFunctionCalls(self):
198        self.expect(
199            "file " + os.path.join(self.getSourceDir(), "inline-function", "a.out")
200        )
201        self.expect("b 4")  # we'll trace from the middle of the inline method
202        self.expect("b 17")
203        self.expect("r")
204        self.expect("thread trace start")
205        self.expect("c")
206        self.expect(
207            "thread trace dump function-calls",
208            substrs=[
209                """[call tree #0]
210a.out`main
211  a.out`foo(int)
212    a.out`foo(int) + 36 [inlined] mult(int, int) + 14 at inline.cpp:4:5 to 5:10  [2, 6]
213  a.out`foo(int) + 49 at inline.cpp:9:15 to 12:1  [7, 11]
214a.out`main + 25 at inline.cpp:16:14 to 16:14  [12, 12]"""
215            ],
216        )
217
218        self.expect(
219            "thread trace dump function-calls -J",
220            substrs=[
221                """[
222  {
223    "untracedPrefixSegment": {
224      "nestedCall": {
225        "untracedPrefixSegment": {
226          "nestedCall": {
227            "tracedSegments": [
228              {
229                "firstInstructionId": "2",
230                "lastInstructionId": "6"
231              }
232            ]
233          }
234        },
235        "tracedSegments": [
236          {
237            "firstInstructionId": "7",
238            "lastInstructionId": "11"
239          }
240        ]
241      }
242    },
243    "tracedSegments": [
244      {
245        "firstInstructionId": "12",
246        "lastInstructionId": "12"
247      }
248    ]
249  }
250]"""
251            ],
252        )
253
254    def testMultifileFunctionCalls(self):
255        # This test is extremely important because it first calls the method foo() which requires going through the dynamic linking.
256        # You'll see the entry "a.out`symbol stub for: foo()" which will invoke the ld linker, which will in turn find the actual foo
257        # function and eventually invoke it.  However, we don't have the image of the linker in the trace bundle, so we'll see errors
258        # because the decoder couldn't find the linker binary! After those failures, the linker will resume right where we return to
259        # main after foo() finished.
260        # Then, we call foo() again, but because it has already been loaded by the linker, we don't invoke the linker anymore! And
261        # we'll see a nice tree without errors in this second invocation. Something interesting happens here. We still have an
262        # invocation to the symbol stub for foo(), but it modifies the stack so that when we return from foo() we don't stop again
263        # at the symbol stub, but instead we return directly to main. This is an example of returning several levels up in the
264        # call stack.
265        # Not only that, we also have an inline method in between.
266        self.expect(
267            "trace load "
268            + os.path.join(
269                self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json"
270            )
271        )
272        self.expect(
273            "thread trace dump function-calls",
274            substrs=[
275                """thread #1: tid = 815455
276
277[call tree #0]
278a.out`main + 15 at main.cpp:10 to 10:0  [3, 3]
279  a.out`symbol stub for: foo() to <+11>  [7, 9]
280    a.out`a.out[0x0000000000400510] to a.out[0x0000000000400516]  [10, 11]
281
282[call tree #1]
283<tracing errors>  [12, 12]
284
285[call tree #2]
286a.out`main + 20 at main.cpp:10 to 12:0  [16, 22]
287  a.out`main + 34 [inlined] inline_function() at main.cpp:4 to 6:0  [26, 30]
288a.out`main + 55 at main.cpp:14 to 16:0  [31, 37]
289  a.out`symbol stub for: foo() to <+0>  [38, 38]
290    libfoo.so`foo() at foo.cpp:3 to 4:0  [39, 42]
291      libfoo.so`symbol stub for: bar() to <+0>  [43, 43]
292        libbar.so`bar() at bar.cpp:1 to 4:0  [44, 52]
293    libfoo.so`foo() + 13 at foo.cpp:4 to 6:0  [53, 60]
294a.out`main + 68 at main.cpp:16 to 16:0  [61, 63]"""
295            ],
296        )
297
298        self.expect(
299            "thread trace dump function-calls -J",
300            substrs=[
301                """[
302  {
303    "tracedSegments": [
304      {
305        "firstInstructionId": "3",
306        "lastInstructionId": "3",
307        "nestedCall": {
308          "tracedSegments": [
309            {
310              "firstInstructionId": "7",
311              "lastInstructionId": "9",
312              "nestedCall": {
313                "tracedSegments": [
314                  {
315                    "firstInstructionId": "10",
316                    "lastInstructionId": "11"
317                  }
318                ]
319              }
320            }
321          ]
322        }
323      }
324    ]
325  },
326  {
327    "tracedSegments": [
328      {
329        "firstInstructionId": "12",
330        "lastInstructionId": "12"
331      }
332    ]
333  },
334  {
335    "tracedSegments": [
336      {
337        "firstInstructionId": "16",
338        "lastInstructionId": "22",
339        "nestedCall": {
340          "tracedSegments": [
341            {
342              "firstInstructionId": "26",
343              "lastInstructionId": "30"
344            }
345          ]
346        }
347      },
348      {
349        "firstInstructionId": "31",
350        "lastInstructionId": "37",
351        "nestedCall": {
352          "tracedSegments": [
353            {
354              "firstInstructionId": "38",
355              "lastInstructionId": "38",
356              "nestedCall": {
357                "tracedSegments": [
358                  {
359                    "firstInstructionId": "39",
360                    "lastInstructionId": "42",
361                    "nestedCall": {
362                      "tracedSegments": [
363                        {
364                          "firstInstructionId": "43",
365                          "lastInstructionId": "43",
366                          "nestedCall": {
367                            "tracedSegments": [
368                              {
369                                "firstInstructionId": "44",
370                                "lastInstructionId": "52"
371                              }
372                            ]
373                          }
374                        }
375                      ]
376                    }
377                  },
378                  {
379                    "firstInstructionId": "53",
380                    "lastInstructionId": "60"
381                  }
382                ]
383              }
384            }
385          ]
386        }
387      },
388      {
389        "firstInstructionId": "61",
390        "lastInstructionId": "63"
391      }
392    ]
393  }
394]"""
395            ],
396        )
397