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