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