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 testRawDumpInstructionsInJSON(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 5 --forwards --json", 37 substrs=['''[{"id":0,"loadAddress":"0x400511"},{"id":1,"loadAddress":"0x400518"},{"id":2,"loadAddress":"0x40051f"},{"id":3,"loadAddress":"0x400529"},{"id":4,"loadAddress":"0x40052d"}]''']) 38 39 self.expect("thread trace dump instructions --raw --count 5 --forwards --pretty-json", 40 substrs=['''[ 41 { 42 "id": 0, 43 "loadAddress": "0x400511" 44 }, 45 { 46 "id": 1, 47 "loadAddress": "0x400518" 48 }, 49 { 50 "id": 2, 51 "loadAddress": "0x40051f" 52 }, 53 { 54 "id": 3, 55 "loadAddress": "0x400529" 56 }, 57 { 58 "id": 4, 59 "loadAddress": "0x40052d" 60 } 61]''']) 62 63 def testRawDumpInstructionsInJSONToFile(self): 64 self.expect("trace load -v " + 65 os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"), 66 substrs=["intel-pt"]) 67 68 outfile = os.path.join(self.getBuildDir(), "output.json") 69 70 self.expect("thread trace dump instructions --raw --count 5 --forwards --pretty-json --file " + outfile) 71 72 with open(outfile, "r") as out: 73 self.assertEqual(out.read(), '''[ 74 { 75 "id": 0, 76 "loadAddress": "0x400511" 77 }, 78 { 79 "id": 1, 80 "loadAddress": "0x400518" 81 }, 82 { 83 "id": 2, 84 "loadAddress": "0x40051f" 85 }, 86 { 87 "id": 3, 88 "loadAddress": "0x400529" 89 }, 90 { 91 "id": 4, 92 "loadAddress": "0x40052d" 93 } 94]''') 95 96 def testRawDumpInstructions(self): 97 self.expect("trace load -v " + 98 os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"), 99 substrs=["intel-pt"]) 100 101 self.expect("thread trace dump instructions --raw --count 21 --forwards", 102 substrs=['''thread #1: tid = 3842849 103 0: 0x0000000000400511 104 1: 0x0000000000400518 105 2: 0x000000000040051f 106 3: 0x0000000000400529 107 4: 0x000000000040052d 108 5: 0x0000000000400521 109 6: 0x0000000000400525 110 7: 0x0000000000400529 111 8: 0x000000000040052d 112 9: 0x0000000000400521 113 10: 0x0000000000400525 114 11: 0x0000000000400529 115 12: 0x000000000040052d 116 13: 0x0000000000400521 117 14: 0x0000000000400525 118 15: 0x0000000000400529 119 16: 0x000000000040052d 120 17: 0x0000000000400521 121 18: 0x0000000000400525 122 19: 0x0000000000400529 123 20: 0x000000000040052d''']) 124 125 # We check if we can pass count and skip 126 self.expect("thread trace dump instructions --count 5 --skip 6 --raw --forwards", 127 substrs=['''thread #1: tid = 3842849 128 6: 0x0000000000400525 129 7: 0x0000000000400529 130 8: 0x000000000040052d 131 9: 0x0000000000400521 132 10: 0x0000000000400525''']) 133 134 self.expect("thread trace dump instructions --count 5 --skip 6 --raw", 135 substrs=['''thread #1: tid = 3842849 136 14: 0x0000000000400525 137 13: 0x0000000000400521 138 12: 0x000000000040052d 139 11: 0x0000000000400529 140 10: 0x0000000000400525''']) 141 142 # We check if we can pass count and skip and instruction id in hex 143 self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 0xA", 144 substrs=['''thread #1: tid = 3842849 145 4: 0x000000000040052d 146 3: 0x0000000000400529 147 2: 0x000000000040051f 148 1: 0x0000000000400518 149 0: 0x0000000000400511''']) 150 151 # We check if we can pass count and skip and instruction id in decimal 152 self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 10", 153 substrs=['''thread #1: tid = 3842849 154 4: 0x000000000040052d 155 3: 0x0000000000400529 156 2: 0x000000000040051f 157 1: 0x0000000000400518 158 0: 0x0000000000400511''']) 159 160 # We check if we can access the thread by index id 161 self.expect("thread trace dump instructions 1 --raw", 162 substrs=['''thread #1: tid = 3842849 163 20: 0x000000000040052d''']) 164 165 # We check that we get an error when using an invalid thread index id 166 self.expect("thread trace dump instructions 10", error=True, 167 substrs=['error: no thread with index: "10"']) 168 169 def testDumpFullInstructionsWithMultipleThreads(self): 170 # We load a trace with two threads 171 self.expect("trace load -v " + 172 os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json")) 173 174 # We print the instructions of a specific thread 175 self.expect("thread trace dump instructions 2 --count 2", 176 substrs=['''thread #2: tid = 3842850 177 a.out`main + 32 at main.cpp:4 178 20: 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 179 19: 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) 180 181 # We use custom --count and --skip, saving the command to history for later 182 self.expect("thread trace dump instructions 2 --count 2 --skip 2", inHistory=True, 183 substrs=['''thread #2: tid = 3842850 184 a.out`main + 24 at main.cpp:4 185 18: 0x0000000000400525 addl $0x1, -0x8(%rbp) 186 a.out`main + 20 at main.cpp:5 187 17: 0x0000000000400521 xorl $0x1, -0x4(%rbp)''']) 188 189 # We use a repeat command twice and ensure the previous count is used and the 190 # start position moves with each command. 191 self.expect("", inHistory=True, 192 substrs=['''thread #2: tid = 3842850 193 a.out`main + 32 at main.cpp:4 194 16: 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 195 15: 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) 196 197 self.expect("", inHistory=True, 198 substrs=['''thread #2: tid = 3842850 199 a.out`main + 24 at main.cpp:4 200 14: 0x0000000000400525 addl $0x1, -0x8(%rbp) 201 a.out`main + 20 at main.cpp:5 202 13: 0x0000000000400521 xorl $0x1, -0x4(%rbp)''']) 203 204 def testInvalidBounds(self): 205 self.expect("trace load -v " + 206 os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")) 207 208 # The output should be work when too many instructions are asked 209 self.expect("thread trace dump instructions --count 20 --forwards", 210 substrs=['''thread #1: tid = 3842849 211 a.out`main + 4 at main.cpp:2 212 0: 0x0000000000400511 movl $0x0, -0x4(%rbp) 213 a.out`main + 11 at main.cpp:4 214 1: 0x0000000000400518 movl $0x0, -0x8(%rbp) 215 2: 0x000000000040051f jmp 0x400529 ; <+28> at main.cpp:4''']) 216 217 # Should print no instructions if the position is out of bounds 218 self.expect("thread trace dump instructions --skip 23", 219 endstr='no more data\n') 220 221 # Should fail with negative bounds 222 self.expect("thread trace dump instructions --skip -1", error=True) 223 self.expect("thread trace dump instructions --count -1", error=True) 224 225 def testWrongImage(self): 226 self.expect("trace load " + 227 os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json")) 228 self.expect("thread trace dump instructions --forwards", 229 substrs=['''thread #1: tid = 3842849 230 ...missing instructions 231 0: 0x0000000000400511 error: no memory mapped at this address 232 1: 0x0000000000400518 error: no memory mapped at this address''']) 233 234 def testWrongCPU(self): 235 self.expect("trace load " + 236 os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")) 237 self.expect("thread trace dump instructions --forwards", 238 substrs=['''thread #1: tid = 3842849 239 ...missing instructions 240 0: error: unknown cpu''']) 241 242 def testMultiFileTraceWithMissingModuleInJSON(self): 243 self.expect("trace load " + 244 os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json")) 245 246 self.expect("thread trace dump instructions --count 3 --id 4 --forwards --pretty-json", 247 substrs=['''[ 248 { 249 "id": 4, 250 "loadAddress": "0x400510", 251 "module": "a.out", 252 "symbol": null, 253 "mnemonic": "pushq" 254 }, 255 { 256 "id": 5, 257 "loadAddress": "0x400516", 258 "module": "a.out", 259 "symbol": null, 260 "mnemonic": "jmpq" 261 }, 262 { 263 "id": 6, 264 "error": "0x00007ffff7df1950 error: no memory mapped at this address" 265 } 266]''']) 267 268 self.expect("thread trace dump instructions --count 4 --id 20 --forwards --pretty-json", 269 substrs=['''[ 270 { 271 "id": 20, 272 "loadAddress": "0x400540", 273 "module": "a.out", 274 "symbol": "foo()", 275 "mnemonic": "jmpq" 276 }, 277 { 278 "id": 21, 279 "loadAddress": "0x7ffff7bd96e0", 280 "module": "libfoo.so", 281 "symbol": "foo()", 282 "mnemonic": "pushq", 283 "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp", 284 "line": 3, 285 "column": 0 286 }, 287 { 288 "id": 22, 289 "loadAddress": "0x7ffff7bd96e1", 290 "module": "libfoo.so", 291 "symbol": "foo()", 292 "mnemonic": "movq", 293 "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp", 294 "line": 3, 295 "column": 0 296 }, 297 { 298 "id": 23, 299 "loadAddress": "0x7ffff7bd96e4", 300 "module": "libfoo.so", 301 "symbol": "foo()", 302 "mnemonic": "subq", 303 "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp", 304 "line": 4, 305 "column": 0 306 } 307]''']) 308 309 def testMultiFileTraceWithMissingModule(self): 310 self.expect("trace load " + 311 os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json")) 312 313 # This instructions in this test covers the following flow: 314 # 315 # - The trace starts with a call to libfoo, which triggers the dynamic 316 # linker, but the dynamic linker is not included in the JSON file, 317 # thus the trace reports a set of missing instructions after 318 # instruction [6]. 319 # - Then, the dump continues in the next synchronization point showing 320 # a call to an inlined function, which is displayed as [inlined]. 321 # - Finally, a call to libfoo is performed, which invokes libbar inside. 322 # 323 # Whenever there's a line or symbol change, including the inline case, a 324 # line is printed showing the symbol context change. 325 # 326 # Finally, the instruction disassembly is included in the dump. 327 self.expect("thread trace dump instructions --count 50 --forwards", 328 substrs=['''thread #1: tid = 815455 329 a.out`main + 15 at main.cpp:10 330 0: 0x000000000040066f callq 0x400540 ; symbol stub for: foo() 331 a.out`symbol stub for: foo() 332 1: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 333 2: 0x0000000000400546 pushq $0x2 334 3: 0x000000000040054b jmp 0x400510 335 a.out`(none) 336 4: 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 337 5: 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 338 ...missing instructions 339 6: 0x00007ffff7df1950 error: no memory mapped at this address 340 a.out`main + 20 at main.cpp:10 341 7: 0x0000000000400674 movl %eax, -0xc(%rbp) 342 a.out`main + 23 at main.cpp:12 343 8: 0x0000000000400677 movl -0xc(%rbp), %eax 344 9: 0x000000000040067a addl $0x1, %eax 345 10: 0x000000000040067f movl %eax, -0xc(%rbp) 346 a.out`main + 34 [inlined] inline_function() at main.cpp:4 347 11: 0x0000000000400682 movl $0x0, -0x4(%rbp) 348 a.out`main + 41 [inlined] inline_function() + 7 at main.cpp:5 349 12: 0x0000000000400689 movl -0x4(%rbp), %eax 350 13: 0x000000000040068c addl $0x1, %eax 351 14: 0x0000000000400691 movl %eax, -0x4(%rbp) 352 a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6 353 15: 0x0000000000400694 movl -0x4(%rbp), %eax 354 a.out`main + 55 at main.cpp:14 355 16: 0x0000000000400697 movl -0xc(%rbp), %ecx 356 17: 0x000000000040069a addl %eax, %ecx 357 18: 0x000000000040069c movl %ecx, -0xc(%rbp) 358 a.out`main + 63 at main.cpp:16 359 19: 0x000000000040069f callq 0x400540 ; symbol stub for: foo() 360 a.out`symbol stub for: foo() 361 20: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 362 libfoo.so`foo() at foo.cpp:3 363 21: 0x00007ffff7bd96e0 pushq %rbp 364 22: 0x00007ffff7bd96e1 movq %rsp, %rbp 365 libfoo.so`foo() + 4 at foo.cpp:4 366 23: 0x00007ffff7bd96e4 subq $0x10, %rsp 367 24: 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() 368 libfoo.so`symbol stub for: bar() 369 25: 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 370 libbar.so`bar() at bar.cpp:1 371 26: 0x00007ffff79d7690 pushq %rbp 372 27: 0x00007ffff79d7691 movq %rsp, %rbp 373 libbar.so`bar() + 4 at bar.cpp:2 374 28: 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) 375 libbar.so`bar() + 11 at bar.cpp:3 376 29: 0x00007ffff79d769b movl -0x4(%rbp), %eax 377 30: 0x00007ffff79d769e addl $0x1, %eax 378 31: 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) 379 libbar.so`bar() + 22 at bar.cpp:4 380 32: 0x00007ffff79d76a6 movl -0x4(%rbp), %eax 381 33: 0x00007ffff79d76a9 popq %rbp 382 34: 0x00007ffff79d76aa retq''', 383 '''libfoo.so`foo() + 13 at foo.cpp:4 384 35: 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) 385 libfoo.so`foo() + 16 at foo.cpp:5 386 36: 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax 387 37: 0x00007ffff7bd96f3 addl $0x1, %eax 388 38: 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) 389 libfoo.so`foo() + 27 at foo.cpp:6 390 39: 0x00007ffff7bd96fb movl -0x4(%rbp), %eax 391 40: 0x00007ffff7bd96fe addq $0x10, %rsp 392 41: 0x00007ffff7bd9702 popq %rbp 393 42: 0x00007ffff7bd9703 retq''', 394 '''a.out`main + 68 at main.cpp:16 395 43: 0x00000000004006a4 movl -0xc(%rbp), %ecx 396 44: 0x00000000004006a7 addl %eax, %ecx 397 45: 0x00000000004006a9 movl %ecx, -0xc(%rbp)''']) 398 399 400 self.expect("thread trace dump instructions --count 50", 401 substrs=['''thread #1: tid = 815455 402 a.out`main + 73 at main.cpp:16 403 45: 0x00000000004006a9 movl %ecx, -0xc(%rbp) 404 44: 0x00000000004006a7 addl %eax, %ecx 405 43: 0x00000000004006a4 movl -0xc(%rbp), %ecx 406 libfoo.so`foo() + 35 at foo.cpp:6 407 42: 0x00007ffff7bd9703 retq''', 408 '''41: 0x00007ffff7bd9702 popq %rbp 409 40: 0x00007ffff7bd96fe addq $0x10, %rsp 410 39: 0x00007ffff7bd96fb movl -0x4(%rbp), %eax 411 libfoo.so`foo() + 24 at foo.cpp:5 412 38: 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) 413 37: 0x00007ffff7bd96f3 addl $0x1, %eax 414 36: 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax 415 libfoo.so`foo() + 13 at foo.cpp:4 416 35: 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) 417 libbar.so`bar() + 26 at bar.cpp:4 418 34: 0x00007ffff79d76aa retq''', 419 '''33: 0x00007ffff79d76a9 popq %rbp 420 32: 0x00007ffff79d76a6 movl -0x4(%rbp), %eax 421 libbar.so`bar() + 19 at bar.cpp:3 422 31: 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) 423 30: 0x00007ffff79d769e addl $0x1, %eax 424 29: 0x00007ffff79d769b movl -0x4(%rbp), %eax 425 libbar.so`bar() + 4 at bar.cpp:2 426 28: 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) 427 libbar.so`bar() + 1 at bar.cpp:1 428 27: 0x00007ffff79d7691 movq %rsp, %rbp 429 26: 0x00007ffff79d7690 pushq %rbp 430 libfoo.so`symbol stub for: bar() 431 25: 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 432 libfoo.so`foo() + 8 at foo.cpp:4 433 24: 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() 434 23: 0x00007ffff7bd96e4 subq $0x10, %rsp 435 libfoo.so`foo() + 1 at foo.cpp:3 436 22: 0x00007ffff7bd96e1 movq %rsp, %rbp 437 21: 0x00007ffff7bd96e0 pushq %rbp 438 a.out`symbol stub for: foo() 439 20: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 440 a.out`main + 63 at main.cpp:16 441 19: 0x000000000040069f callq 0x400540 ; symbol stub for: foo() 442 a.out`main + 60 at main.cpp:14 443 18: 0x000000000040069c movl %ecx, -0xc(%rbp) 444 17: 0x000000000040069a addl %eax, %ecx 445 16: 0x0000000000400697 movl -0xc(%rbp), %ecx 446 a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6 447 15: 0x0000000000400694 movl -0x4(%rbp), %eax 448 a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5 449 14: 0x0000000000400691 movl %eax, -0x4(%rbp) 450 13: 0x000000000040068c addl $0x1, %eax 451 12: 0x0000000000400689 movl -0x4(%rbp), %eax 452 a.out`main + 34 [inlined] inline_function() at main.cpp:4 453 11: 0x0000000000400682 movl $0x0, -0x4(%rbp) 454 a.out`main + 31 at main.cpp:12 455 10: 0x000000000040067f movl %eax, -0xc(%rbp) 456 9: 0x000000000040067a addl $0x1, %eax 457 8: 0x0000000000400677 movl -0xc(%rbp), %eax 458 a.out`main + 20 at main.cpp:10 459 7: 0x0000000000400674 movl %eax, -0xc(%rbp) 460 ...missing instructions 461 6: 0x00007ffff7df1950 error: no memory mapped at this address 462 a.out`(none) 463 5: 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 464 4: 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 465 a.out`symbol stub for: foo() + 11 466 3: 0x000000000040054b jmp 0x400510 467 2: 0x0000000000400546 pushq $0x2 468 1: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 469 a.out`main + 15 at main.cpp:10 470 0: 0x000000000040066f callq 0x400540 ; symbol stub for: foo()''']) 471 472 self.expect("thread trace dump instructions --skip 100 --forwards", inHistory=True, 473 substrs=['''thread #1: tid = 815455 474 no more data''']) 475 476 self.expect("", substrs=['''thread #1: tid = 815455 477 no more data''']) 478 479 480 self.expect("thread trace dump instructions --raw --all --forwards", 481 substrs=['''thread #1: tid = 815455 482 0: 0x000000000040066f 483 1: 0x0000000000400540''', '''5: 0x0000000000400516 484 ...missing instructions 485 6: 0x00007ffff7df1950 error: no memory mapped at this address 486 7: 0x0000000000400674''', '''43: 0x00000000004006a4 487 44: 0x00000000004006a7 488 45: 0x00000000004006a9 489 no more data''']) 490