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: invalid 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    [ -6] 0x0000000000400525
74    [ -7] 0x0000000000400521
75    [ -8] 0x000000000040052d
76    [ -9] 0x0000000000400529
77    [-10] 0x0000000000400525'''])
78
79        # We check if we can access the thread by index id
80        self.expect("thread trace dump instructions 1 --raw",
81            substrs=['''thread #1: tid = 3842849
82    [  0] 0x000000000040052d'''])
83
84        # We check that we get an error when using an invalid thread index id
85        self.expect("thread trace dump instructions 10", error=True,
86            substrs=['error: no thread with index: "10"'])
87
88    def testDumpFullInstructionsWithMultipleThreads(self):
89        # We load a trace with two threads
90        self.expect("trace load -v " +
91            os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json"))
92
93        # We print the instructions of two threads simultaneously
94        self.expect("thread trace dump instructions 1 2 --count 2",
95            substrs=['''thread #1: tid = 3842849
96  a.out`main + 32 at main.cpp:4
97    [ 0] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
98    [-1] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
99thread #2: tid = 3842850
100  a.out`main + 32 at main.cpp:4
101    [ 0] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
102    [-1] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
103
104        # We use custom --count and --skip, saving the command to history for later
105        self.expect("thread trace dump instructions 1 2 --count 2 --skip 2", inHistory=True,
106            substrs=['''thread #1: tid = 3842849
107  a.out`main + 24 at main.cpp:4
108    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
109  a.out`main + 20 at main.cpp:5
110    [-3] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
111thread #2: tid = 3842850
112  a.out`main + 24 at main.cpp:4
113    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
114  a.out`main + 20 at main.cpp:5
115    [-3] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
116
117        # We use a repeat command twice and ensure the previous count is used and the
118        # start position moves with each command.
119        self.expect("", inHistory=True,
120            substrs=['''thread #1: tid = 3842849
121  a.out`main + 32 at main.cpp:4
122    [-4] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
123    [-5] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
124thread #2: tid = 3842850
125  a.out`main + 32 at main.cpp:4
126    [-4] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
127    [-5] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
128
129        self.expect("", inHistory=True,
130            substrs=['''thread #1: tid = 3842849
131  a.out`main + 24 at main.cpp:4
132    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
133  a.out`main + 20 at main.cpp:5
134    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
135thread #2: tid = 3842850
136  a.out`main + 24 at main.cpp:4
137    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
138  a.out`main + 20 at main.cpp:5
139    [-7] 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    [  0] 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
272    [ -1] 0x00000000004006a7    addl   %eax, %ecx
273    [ -2] 0x00000000004006a4    movl   -0xc(%rbp), %ecx
274  libfoo.so`foo() + 35 at foo.cpp:6
275    [ -3] 0x00007ffff7bd9703    retq''',
276    '''[ -4] 0x00007ffff7bd9702    popq   %rbp
277    [ -5] 0x00007ffff7bd96fe    addq   $0x10, %rsp
278    [ -6] 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
279  libfoo.so`foo() + 24 at foo.cpp:5
280    [ -7] 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
281    [ -8] 0x00007ffff7bd96f3    addl   $0x1, %eax
282    [ -9] 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
283  libfoo.so`foo() + 13 at foo.cpp:4
284    [-10] 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
285  libbar.so`bar() + 26 at bar.cpp:4
286    [-11] 0x00007ffff79d76aa    retq''',
287    '''[-12] 0x00007ffff79d76a9    popq   %rbp
288    [-13] 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
289  libbar.so`bar() + 19 at bar.cpp:3
290    [-14] 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
291    [-15] 0x00007ffff79d769e    addl   $0x1, %eax
292    [-16] 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
293  libbar.so`bar() + 4 at bar.cpp:2
294    [-17] 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
295  libbar.so`bar() + 1 at bar.cpp:1
296    [-18] 0x00007ffff79d7691    movq   %rsp, %rbp
297    [-19] 0x00007ffff79d7690    pushq  %rbp
298  libfoo.so`symbol stub for: bar()
299    [-20] 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
300  libfoo.so`foo() + 8 at foo.cpp:4
301    [-21] 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
302    [-22] 0x00007ffff7bd96e4    subq   $0x10, %rsp
303  libfoo.so`foo() + 1 at foo.cpp:3
304    [-23] 0x00007ffff7bd96e1    movq   %rsp, %rbp
305    [-24] 0x00007ffff7bd96e0    pushq  %rbp
306  a.out`symbol stub for: foo()
307    [-25] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
308  a.out`main + 63 at main.cpp:16
309    [-26] 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
310  a.out`main + 60 at main.cpp:14
311    [-27] 0x000000000040069c    movl   %ecx, -0xc(%rbp)
312    [-28] 0x000000000040069a    addl   %eax, %ecx
313    [-29] 0x0000000000400697    movl   -0xc(%rbp), %ecx
314  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
315    [-30] 0x0000000000400694    movl   -0x4(%rbp), %eax
316  a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5
317    [-31] 0x0000000000400691    movl   %eax, -0x4(%rbp)
318    [-32] 0x000000000040068c    addl   $0x1, %eax
319    [-33] 0x0000000000400689    movl   -0x4(%rbp), %eax
320  a.out`main + 34 [inlined] inline_function() at main.cpp:4
321    [-34] 0x0000000000400682    movl   $0x0, -0x4(%rbp)
322  a.out`main + 31 at main.cpp:12
323    [-35] 0x000000000040067f    movl   %eax, -0xc(%rbp)
324    [-36] 0x000000000040067a    addl   $0x1, %eax
325    [-37] 0x0000000000400677    movl   -0xc(%rbp), %eax
326  a.out`main + 20 at main.cpp:10
327    [-38] 0x0000000000400674    movl   %eax, -0xc(%rbp)
328    ...missing instructions
329    [-39] 0x00007ffff7df1950    error: no memory mapped at this address
330  a.out`(none)
331    [-40] 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
332    [-41] 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
333  a.out`symbol stub for: foo() + 11
334    [-42] 0x000000000040054b    jmp    0x400510
335    [-43] 0x0000000000400546    pushq  $0x2
336    [-44] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
337  a.out`main + 15 at main.cpp:10
338    [-45] 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