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