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