1import lldb
2from intelpt_testcase import *
3from lldbsuite.test.lldbtest import *
4from lldbsuite.test import lldbutil
5from lldbsuite.test.decorators import *
6
7class TestTraceDumpInstructions(TraceIntelPTTestCaseBase):
8
9    mydir = TestBase.compute_mydir(__file__)
10
11    def testErrorMessages(self):
12        # We first check the output when there are no targets
13        self.expect("thread trace dump instructions",
14            substrs=["error: invalid target, create a target using the 'target create' command"],
15            error=True)
16
17        # We now check the output when there's a non-running target
18        self.expect("target create " +
19            os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
20
21        self.expect("thread trace dump instructions",
22            substrs=["error: Command requires a current process."],
23            error=True)
24
25        # Now we check the output when there's a running target without a trace
26        self.expect("b main")
27        self.expect("run")
28
29        self.expect("thread trace dump instructions",
30            substrs=["error: Process is not being traced"],
31            error=True)
32
33    def testRawDumpInstructions(self):
34        self.expect("trace load -v " +
35            os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
36            substrs=["intel-pt"])
37
38        self.expect("thread trace dump instructions --raw --count 21 --forwards",
39            substrs=['''thread #1: tid = 3842849
40    0: 0x0000000000400511
41    1: 0x0000000000400518
42    2: 0x000000000040051f
43    3: 0x0000000000400529
44    4: 0x000000000040052d
45    5: 0x0000000000400521
46    6: 0x0000000000400525
47    7: 0x0000000000400529
48    8: 0x000000000040052d
49    9: 0x0000000000400521
50    10: 0x0000000000400525
51    11: 0x0000000000400529
52    12: 0x000000000040052d
53    13: 0x0000000000400521
54    14: 0x0000000000400525
55    15: 0x0000000000400529
56    16: 0x000000000040052d
57    17: 0x0000000000400521
58    18: 0x0000000000400525
59    19: 0x0000000000400529
60    20: 0x000000000040052d'''])
61
62        # We check if we can pass count and skip
63        self.expect("thread trace dump instructions --count 5 --skip 6 --raw --forwards",
64            substrs=['''thread #1: tid = 3842849
65    6: 0x0000000000400525
66    7: 0x0000000000400529
67    8: 0x000000000040052d
68    9: 0x0000000000400521
69    10: 0x0000000000400525'''])
70
71        self.expect("thread trace dump instructions --count 5 --skip 6 --raw",
72            substrs=['''thread #1: tid = 3842849
73    14: 0x0000000000400525
74    13: 0x0000000000400521
75    12: 0x000000000040052d
76    11: 0x0000000000400529
77    10: 0x0000000000400525'''])
78
79        # We check if we can pass count and skip and instruction id in hex
80        self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 0xA",
81            substrs=['''thread #1: tid = 3842849
82    4: 0x000000000040052d
83    3: 0x0000000000400529
84    2: 0x000000000040051f
85    1: 0x0000000000400518
86    0: 0x0000000000400511'''])
87
88        # We check if we can pass count and skip and instruction id in decimal
89        self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 10",
90            substrs=['''thread #1: tid = 3842849
91    4: 0x000000000040052d
92    3: 0x0000000000400529
93    2: 0x000000000040051f
94    1: 0x0000000000400518
95    0: 0x0000000000400511'''])
96
97        # We check if we can access the thread by index id
98        self.expect("thread trace dump instructions 1 --raw",
99            substrs=['''thread #1: tid = 3842849
100    20: 0x000000000040052d'''])
101
102        # We check that we get an error when using an invalid thread index id
103        self.expect("thread trace dump instructions 10", error=True,
104            substrs=['error: no thread with index: "10"'])
105
106    def testDumpFullInstructionsWithMultipleThreads(self):
107        # We load a trace with two threads
108        self.expect("trace load -v " +
109            os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json"))
110
111        # We print the instructions of a specific thread
112        self.expect("thread trace dump instructions 2 --count 2",
113            substrs=['''thread #2: tid = 3842850
114  a.out`main + 32 at main.cpp:4
115    20: 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
116    19: 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
117
118        # We use custom --count and --skip, saving the command to history for later
119        self.expect("thread trace dump instructions 2 --count 2 --skip 2", inHistory=True,
120            substrs=['''thread #2: tid = 3842850
121  a.out`main + 24 at main.cpp:4
122    18: 0x0000000000400525    addl   $0x1, -0x8(%rbp)
123  a.out`main + 20 at main.cpp:5
124    17: 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
125
126        # We use a repeat command twice and ensure the previous count is used and the
127        # start position moves with each command.
128        self.expect("", inHistory=True,
129            substrs=['''thread #2: tid = 3842850
130  a.out`main + 32 at main.cpp:4
131    16: 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
132    15: 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
133
134        self.expect("", inHistory=True,
135            substrs=['''thread #2: tid = 3842850
136  a.out`main + 24 at main.cpp:4
137    14: 0x0000000000400525    addl   $0x1, -0x8(%rbp)
138  a.out`main + 20 at main.cpp:5
139    13: 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
140
141    def testInvalidBounds(self):
142        self.expect("trace load -v " +
143            os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
144
145        # The output should be work when too many instructions are asked
146        self.expect("thread trace dump instructions --count 20 --forwards",
147            substrs=['''thread #1: tid = 3842849
148  a.out`main + 4 at main.cpp:2
149    0: 0x0000000000400511    movl   $0x0, -0x4(%rbp)
150  a.out`main + 11 at main.cpp:4
151    1: 0x0000000000400518    movl   $0x0, -0x8(%rbp)
152    2: 0x000000000040051f    jmp    0x400529                  ; <+28> at main.cpp:4'''])
153
154        # Should print no instructions if the position is out of bounds
155        self.expect("thread trace dump instructions --skip 23",
156            endstr='no more data\n')
157
158        # Should fail with negative bounds
159        self.expect("thread trace dump instructions --skip -1", error=True)
160        self.expect("thread trace dump instructions --count -1", error=True)
161
162    def testWrongImage(self):
163        self.expect("trace load " +
164            os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json"))
165        self.expect("thread trace dump instructions --forwards",
166            substrs=['''thread #1: tid = 3842849
167    0: 0x0000000000400511    error: no memory mapped at this address
168    1: 0x0000000000400518    error: no memory mapped at this address'''])
169
170    def testWrongCPU(self):
171        self.expect("trace load " +
172            os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json"))
173        self.expect("thread trace dump instructions --forwards",
174            substrs=['''thread #1: tid = 3842849
175    0: error: unknown cpu'''])
176
177    def testMultiFileTraceWithMissingModule(self):
178        self.expect("trace load " +
179            os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json"))
180
181        # This instructions in this test covers the following flow:
182        #
183        # - The trace starts with a call to libfoo, which triggers the dynamic
184        #   linker, but the dynamic linker is not included in the JSON file,
185        #   thus the trace reports a set of missing instructions after
186        #   instruction [6].
187        # - Then, the dump continues in the next synchronization point showing
188        #   a call to an inlined function, which is displayed as [inlined].
189        # - Finally, a call to libfoo is performed, which invokes libbar inside.
190        #
191        # Whenever there's a line or symbol change, including the inline case, a
192        # line is printed showing the symbol context change.
193        #
194        # Finally, the instruction disassembly is included in the dump.
195        self.expect("thread trace dump instructions --count 50 --forwards",
196            substrs=['''thread #1: tid = 815455
197  a.out`main + 15 at main.cpp:10
198    0: 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()
199  a.out`symbol stub for: foo()
200    1: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
201    2: 0x0000000000400546    pushq  $0x2
202    3: 0x000000000040054b    jmp    0x400510
203  a.out`(none)
204    4: 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
205    5: 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
206    6: 0x00007ffff7df1950    error: no memory mapped at this address
207    ...missing instructions
208  a.out`main + 20 at main.cpp:10
209    7: 0x0000000000400674    movl   %eax, -0xc(%rbp)
210  a.out`main + 23 at main.cpp:12
211    8: 0x0000000000400677    movl   -0xc(%rbp), %eax
212    9: 0x000000000040067a    addl   $0x1, %eax
213    10: 0x000000000040067f    movl   %eax, -0xc(%rbp)
214  a.out`main + 34 [inlined] inline_function() at main.cpp:4
215    11: 0x0000000000400682    movl   $0x0, -0x4(%rbp)
216  a.out`main + 41 [inlined] inline_function() + 7 at main.cpp:5
217    12: 0x0000000000400689    movl   -0x4(%rbp), %eax
218    13: 0x000000000040068c    addl   $0x1, %eax
219    14: 0x0000000000400691    movl   %eax, -0x4(%rbp)
220  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
221    15: 0x0000000000400694    movl   -0x4(%rbp), %eax
222  a.out`main + 55 at main.cpp:14
223    16: 0x0000000000400697    movl   -0xc(%rbp), %ecx
224    17: 0x000000000040069a    addl   %eax, %ecx
225    18: 0x000000000040069c    movl   %ecx, -0xc(%rbp)
226  a.out`main + 63 at main.cpp:16
227    19: 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
228  a.out`symbol stub for: foo()
229    20: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
230  libfoo.so`foo() at foo.cpp:3
231    21: 0x00007ffff7bd96e0    pushq  %rbp
232    22: 0x00007ffff7bd96e1    movq   %rsp, %rbp
233  libfoo.so`foo() + 4 at foo.cpp:4
234    23: 0x00007ffff7bd96e4    subq   $0x10, %rsp
235    24: 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
236  libfoo.so`symbol stub for: bar()
237    25: 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
238  libbar.so`bar() at bar.cpp:1
239    26: 0x00007ffff79d7690    pushq  %rbp
240    27: 0x00007ffff79d7691    movq   %rsp, %rbp
241  libbar.so`bar() + 4 at bar.cpp:2
242    28: 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
243  libbar.so`bar() + 11 at bar.cpp:3
244    29: 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
245    30: 0x00007ffff79d769e    addl   $0x1, %eax
246    31: 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
247  libbar.so`bar() + 22 at bar.cpp:4
248    32: 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
249    33: 0x00007ffff79d76a9    popq   %rbp
250    34: 0x00007ffff79d76aa    retq''',
251  '''libfoo.so`foo() + 13 at foo.cpp:4
252    35: 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
253  libfoo.so`foo() + 16 at foo.cpp:5
254    36: 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
255    37: 0x00007ffff7bd96f3    addl   $0x1, %eax
256    38: 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
257  libfoo.so`foo() + 27 at foo.cpp:6
258    39: 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
259    40: 0x00007ffff7bd96fe    addq   $0x10, %rsp
260    41: 0x00007ffff7bd9702    popq   %rbp
261    42: 0x00007ffff7bd9703    retq''',
262  '''a.out`main + 68 at main.cpp:16
263    43: 0x00000000004006a4    movl   -0xc(%rbp), %ecx
264    44: 0x00000000004006a7    addl   %eax, %ecx
265    45: 0x00000000004006a9    movl   %ecx, -0xc(%rbp)'''])
266
267
268        self.expect("thread trace dump instructions --count 50",
269            substrs=['''thread #1: tid = 815455
270  a.out`main + 73 at main.cpp:16
271    45: 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
272    44: 0x00000000004006a7    addl   %eax, %ecx
273    43: 0x00000000004006a4    movl   -0xc(%rbp), %ecx
274  libfoo.so`foo() + 35 at foo.cpp:6
275    42: 0x00007ffff7bd9703    retq''',
276    '''41: 0x00007ffff7bd9702    popq   %rbp
277    40: 0x00007ffff7bd96fe    addq   $0x10, %rsp
278    39: 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
279  libfoo.so`foo() + 24 at foo.cpp:5
280    38: 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
281    37: 0x00007ffff7bd96f3    addl   $0x1, %eax
282    36: 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
283  libfoo.so`foo() + 13 at foo.cpp:4
284    35: 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
285  libbar.so`bar() + 26 at bar.cpp:4
286    34: 0x00007ffff79d76aa    retq''',
287    '''33: 0x00007ffff79d76a9    popq   %rbp
288    32: 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
289  libbar.so`bar() + 19 at bar.cpp:3
290    31: 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
291    30: 0x00007ffff79d769e    addl   $0x1, %eax
292    29: 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
293  libbar.so`bar() + 4 at bar.cpp:2
294    28: 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
295  libbar.so`bar() + 1 at bar.cpp:1
296    27: 0x00007ffff79d7691    movq   %rsp, %rbp
297    26: 0x00007ffff79d7690    pushq  %rbp
298  libfoo.so`symbol stub for: bar()
299    25: 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
300  libfoo.so`foo() + 8 at foo.cpp:4
301    24: 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
302    23: 0x00007ffff7bd96e4    subq   $0x10, %rsp
303  libfoo.so`foo() + 1 at foo.cpp:3
304    22: 0x00007ffff7bd96e1    movq   %rsp, %rbp
305    21: 0x00007ffff7bd96e0    pushq  %rbp
306  a.out`symbol stub for: foo()
307    20: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
308  a.out`main + 63 at main.cpp:16
309    19: 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
310  a.out`main + 60 at main.cpp:14
311    18: 0x000000000040069c    movl   %ecx, -0xc(%rbp)
312    17: 0x000000000040069a    addl   %eax, %ecx
313    16: 0x0000000000400697    movl   -0xc(%rbp), %ecx
314  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
315    15: 0x0000000000400694    movl   -0x4(%rbp), %eax
316  a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5
317    14: 0x0000000000400691    movl   %eax, -0x4(%rbp)
318    13: 0x000000000040068c    addl   $0x1, %eax
319    12: 0x0000000000400689    movl   -0x4(%rbp), %eax
320  a.out`main + 34 [inlined] inline_function() at main.cpp:4
321    11: 0x0000000000400682    movl   $0x0, -0x4(%rbp)
322  a.out`main + 31 at main.cpp:12
323    10: 0x000000000040067f    movl   %eax, -0xc(%rbp)
324    9: 0x000000000040067a    addl   $0x1, %eax
325    8: 0x0000000000400677    movl   -0xc(%rbp), %eax
326  a.out`main + 20 at main.cpp:10
327    7: 0x0000000000400674    movl   %eax, -0xc(%rbp)
328    ...missing instructions
329    6: 0x00007ffff7df1950    error: no memory mapped at this address
330  a.out`(none)
331    5: 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
332    4: 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
333  a.out`symbol stub for: foo() + 11
334    3: 0x000000000040054b    jmp    0x400510
335    2: 0x0000000000400546    pushq  $0x2
336    1: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
337  a.out`main + 15 at main.cpp:10
338    0: 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()'''])
339
340        self.expect("thread trace dump instructions --skip 100 --forwards", inHistory=True,
341            substrs=['''thread #1: tid = 815455
342    no more data'''])
343
344        self.expect("", substrs=['''thread #1: tid = 815455
345    no more data'''])
346