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