| import lldb |
| from intelpt_testcase import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| from lldbsuite.test.decorators import * |
| |
| class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): |
| |
| mydir = TestBase.compute_mydir(__file__) |
| |
| def testErrorMessages(self): |
| # We first check the output when there are no targets |
| self.expect("thread trace dump instructions", |
| substrs=["error: invalid target, create a target using the 'target create' command"], |
| error=True) |
| |
| # We now check the output when there's a non-running target |
| self.expect("target create " + |
| os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) |
| |
| self.expect("thread trace dump instructions", |
| substrs=["error: invalid process"], |
| error=True) |
| |
| # Now we check the output when there's a running target without a trace |
| self.expect("b main") |
| self.expect("run") |
| |
| self.expect("thread trace dump instructions", |
| substrs=["error: Process is not being traced"], |
| error=True) |
| |
| def testRawDumpInstructions(self): |
| self.expect("trace load -v " + |
| os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"), |
| substrs=["intel-pt"]) |
| |
| self.expect("thread trace dump instructions --raw --count 21 --forwards", |
| substrs=['''thread #1: tid = 3842849 |
| [ 0] 0x0000000000400511 |
| [ 1] 0x0000000000400518 |
| [ 2] 0x000000000040051f |
| [ 3] 0x0000000000400529 |
| [ 4] 0x000000000040052d |
| [ 5] 0x0000000000400521 |
| [ 6] 0x0000000000400525 |
| [ 7] 0x0000000000400529 |
| [ 8] 0x000000000040052d |
| [ 9] 0x0000000000400521 |
| [10] 0x0000000000400525 |
| [11] 0x0000000000400529 |
| [12] 0x000000000040052d |
| [13] 0x0000000000400521 |
| [14] 0x0000000000400525 |
| [15] 0x0000000000400529 |
| [16] 0x000000000040052d |
| [17] 0x0000000000400521 |
| [18] 0x0000000000400525 |
| [19] 0x0000000000400529 |
| [20] 0x000000000040052d''']) |
| |
| # We check if we can pass count and skip |
| self.expect("thread trace dump instructions --count 5 --skip 6 --raw --forwards", |
| substrs=['''thread #1: tid = 3842849 |
| [ 6] 0x0000000000400525 |
| [ 7] 0x0000000000400529 |
| [ 8] 0x000000000040052d |
| [ 9] 0x0000000000400521 |
| [10] 0x0000000000400525''']) |
| |
| self.expect("thread trace dump instructions --count 5 --skip 6 --raw", |
| substrs=['''thread #1: tid = 3842849 |
| [ -6] 0x0000000000400525 |
| [ -7] 0x0000000000400521 |
| [ -8] 0x000000000040052d |
| [ -9] 0x0000000000400529 |
| [-10] 0x0000000000400525''']) |
| |
| # We check if we can access the thread by index id |
| self.expect("thread trace dump instructions 1 --raw", |
| substrs=['''thread #1: tid = 3842849 |
| [ 0] 0x000000000040052d''']) |
| |
| # We check that we get an error when using an invalid thread index id |
| self.expect("thread trace dump instructions 10", error=True, |
| substrs=['error: no thread with index: "10"']) |
| |
| def testDumpFullInstructionsWithMultipleThreads(self): |
| # We load a trace with two threads |
| self.expect("trace load -v " + |
| os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json")) |
| |
| # We print the instructions of two threads simultaneously |
| self.expect("thread trace dump instructions 1 2 --count 2", |
| substrs=['''thread #1: tid = 3842849 |
| a.out`main + 32 at main.cpp:4 |
| [ 0] 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 |
| [-1] 0x0000000000400529 cmpl $0x3, -0x8(%rbp) |
| thread #2: tid = 3842850 |
| a.out`main + 32 at main.cpp:4 |
| [ 0] 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 |
| [-1] 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) |
| |
| # We use custom --count and --skip, saving the command to history for later |
| self.expect("thread trace dump instructions 1 2 --count 2 --skip 2", inHistory=True, |
| substrs=['''thread #1: tid = 3842849 |
| a.out`main + 24 at main.cpp:4 |
| [-2] 0x0000000000400525 addl $0x1, -0x8(%rbp) |
| a.out`main + 20 at main.cpp:5 |
| [-3] 0x0000000000400521 xorl $0x1, -0x4(%rbp) |
| thread #2: tid = 3842850 |
| a.out`main + 24 at main.cpp:4 |
| [-2] 0x0000000000400525 addl $0x1, -0x8(%rbp) |
| a.out`main + 20 at main.cpp:5 |
| [-3] 0x0000000000400521 xorl $0x1, -0x4(%rbp)''']) |
| |
| # We use a repeat command twice and ensure the previous count is used and the |
| # start position moves with each command. |
| self.expect("", inHistory=True, |
| substrs=['''thread #1: tid = 3842849 |
| a.out`main + 32 at main.cpp:4 |
| [-4] 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 |
| [-5] 0x0000000000400529 cmpl $0x3, -0x8(%rbp) |
| thread #2: tid = 3842850 |
| a.out`main + 32 at main.cpp:4 |
| [-4] 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 |
| [-5] 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) |
| |
| self.expect("", inHistory=True, |
| substrs=['''thread #1: tid = 3842849 |
| a.out`main + 24 at main.cpp:4 |
| [-6] 0x0000000000400525 addl $0x1, -0x8(%rbp) |
| a.out`main + 20 at main.cpp:5 |
| [-7] 0x0000000000400521 xorl $0x1, -0x4(%rbp) |
| thread #2: tid = 3842850 |
| a.out`main + 24 at main.cpp:4 |
| [-6] 0x0000000000400525 addl $0x1, -0x8(%rbp) |
| a.out`main + 20 at main.cpp:5 |
| [-7] 0x0000000000400521 xorl $0x1, -0x4(%rbp)''']) |
| |
| def testInvalidBounds(self): |
| self.expect("trace load -v " + |
| os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")) |
| |
| # The output should be work when too many instructions are asked |
| self.expect("thread trace dump instructions --count 20 --forwards", |
| substrs=['''thread #1: tid = 3842849 |
| a.out`main + 4 at main.cpp:2 |
| [ 0] 0x0000000000400511 movl $0x0, -0x4(%rbp) |
| a.out`main + 11 at main.cpp:4 |
| [ 1] 0x0000000000400518 movl $0x0, -0x8(%rbp) |
| [ 2] 0x000000000040051f jmp 0x400529 ; <+28> at main.cpp:4''']) |
| |
| # Should print no instructions if the position is out of bounds |
| self.expect("thread trace dump instructions --skip 23", |
| endstr='no more data\n') |
| |
| # Should fail with negative bounds |
| self.expect("thread trace dump instructions --skip -1", error=True) |
| self.expect("thread trace dump instructions --count -1", error=True) |
| |
| def testWrongImage(self): |
| self.expect("trace load " + |
| os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json")) |
| self.expect("thread trace dump instructions --forwards", |
| substrs=['''thread #1: tid = 3842849 |
| [ 0] 0x0000000000400511 error: no memory mapped at this address |
| [ 1] 0x0000000000400518 error: no memory mapped at this address''']) |
| |
| def testWrongCPU(self): |
| self.expect("trace load " + |
| os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")) |
| self.expect("thread trace dump instructions --forwards", |
| substrs=['''thread #1: tid = 3842849 |
| [ 0] error: unknown cpu''']) |
| |
| def testMultiFileTraceWithMissingModule(self): |
| self.expect("trace load " + |
| os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json")) |
| |
| # This instructions in this test covers the following flow: |
| # |
| # - The trace starts with a call to libfoo, which triggers the dynamic |
| # linker, but the dynamic linker is not included in the JSON file, |
| # thus the trace reports a set of missing instructions after |
| # instruction [6]. |
| # - Then, the dump continues in the next synchronization point showing |
| # a call to an inlined function, which is displayed as [inlined]. |
| # - Finally, a call to libfoo is performed, which invokes libbar inside. |
| # |
| # Whenever there's a line or symbol change, including the inline case, a |
| # line is printed showing the symbol context change. |
| # |
| # Finally, the instruction disassembly is included in the dump. |
| self.expect("thread trace dump instructions --count 50 --forwards", |
| substrs=['''thread #1: tid = 815455 |
| a.out`main + 15 at main.cpp:10 |
| [ 0] 0x000000000040066f callq 0x400540 ; symbol stub for: foo() |
| a.out`symbol stub for: foo() |
| [ 1] 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 |
| [ 2] 0x0000000000400546 pushq $0x2 |
| [ 3] 0x000000000040054b jmp 0x400510 |
| a.out`(none) |
| [ 4] 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 |
| [ 5] 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 |
| [ 6] 0x00007ffff7df1950 error: no memory mapped at this address |
| ...missing instructions |
| a.out`main + 20 at main.cpp:10 |
| [ 7] 0x0000000000400674 movl %eax, -0xc(%rbp) |
| a.out`main + 23 at main.cpp:12 |
| [ 8] 0x0000000000400677 movl -0xc(%rbp), %eax |
| [ 9] 0x000000000040067a addl $0x1, %eax |
| [10] 0x000000000040067f movl %eax, -0xc(%rbp) |
| a.out`main + 34 [inlined] inline_function() at main.cpp:4 |
| [11] 0x0000000000400682 movl $0x0, -0x4(%rbp) |
| a.out`main + 41 [inlined] inline_function() + 7 at main.cpp:5 |
| [12] 0x0000000000400689 movl -0x4(%rbp), %eax |
| [13] 0x000000000040068c addl $0x1, %eax |
| [14] 0x0000000000400691 movl %eax, -0x4(%rbp) |
| a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6 |
| [15] 0x0000000000400694 movl -0x4(%rbp), %eax |
| a.out`main + 55 at main.cpp:14 |
| [16] 0x0000000000400697 movl -0xc(%rbp), %ecx |
| [17] 0x000000000040069a addl %eax, %ecx |
| [18] 0x000000000040069c movl %ecx, -0xc(%rbp) |
| a.out`main + 63 at main.cpp:16 |
| [19] 0x000000000040069f callq 0x400540 ; symbol stub for: foo() |
| a.out`symbol stub for: foo() |
| [20] 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 |
| libfoo.so`foo() at foo.cpp:3 |
| [21] 0x00007ffff7bd96e0 pushq %rbp |
| [22] 0x00007ffff7bd96e1 movq %rsp, %rbp |
| libfoo.so`foo() + 4 at foo.cpp:4 |
| [23] 0x00007ffff7bd96e4 subq $0x10, %rsp |
| [24] 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() |
| libfoo.so`symbol stub for: bar() |
| [25] 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 |
| libbar.so`bar() at bar.cpp:1 |
| [26] 0x00007ffff79d7690 pushq %rbp |
| [27] 0x00007ffff79d7691 movq %rsp, %rbp |
| libbar.so`bar() + 4 at bar.cpp:2 |
| [28] 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) |
| libbar.so`bar() + 11 at bar.cpp:3 |
| [29] 0x00007ffff79d769b movl -0x4(%rbp), %eax |
| [30] 0x00007ffff79d769e addl $0x1, %eax |
| [31] 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) |
| libbar.so`bar() + 22 at bar.cpp:4 |
| [32] 0x00007ffff79d76a6 movl -0x4(%rbp), %eax |
| [33] 0x00007ffff79d76a9 popq %rbp |
| [34] 0x00007ffff79d76aa retq''', |
| '''libfoo.so`foo() + 13 at foo.cpp:4 |
| [35] 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) |
| libfoo.so`foo() + 16 at foo.cpp:5 |
| [36] 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax |
| [37] 0x00007ffff7bd96f3 addl $0x1, %eax |
| [38] 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) |
| libfoo.so`foo() + 27 at foo.cpp:6 |
| [39] 0x00007ffff7bd96fb movl -0x4(%rbp), %eax |
| [40] 0x00007ffff7bd96fe addq $0x10, %rsp |
| [41] 0x00007ffff7bd9702 popq %rbp |
| [42] 0x00007ffff7bd9703 retq''', |
| '''a.out`main + 68 at main.cpp:16 |
| [43] 0x00000000004006a4 movl -0xc(%rbp), %ecx |
| [44] 0x00000000004006a7 addl %eax, %ecx |
| [45] 0x00000000004006a9 movl %ecx, -0xc(%rbp)''']) |
| |
| |
| self.expect("thread trace dump instructions --count 50", |
| substrs=['''thread #1: tid = 815455 |
| a.out`main + 73 at main.cpp:16 |
| [ 0] 0x00000000004006a9 movl %ecx, -0xc(%rbp) |
| [ -1] 0x00000000004006a7 addl %eax, %ecx |
| [ -2] 0x00000000004006a4 movl -0xc(%rbp), %ecx |
| libfoo.so`foo() + 35 at foo.cpp:6 |
| [ -3] 0x00007ffff7bd9703 retq''', |
| '''[ -4] 0x00007ffff7bd9702 popq %rbp |
| [ -5] 0x00007ffff7bd96fe addq $0x10, %rsp |
| [ -6] 0x00007ffff7bd96fb movl -0x4(%rbp), %eax |
| libfoo.so`foo() + 24 at foo.cpp:5 |
| [ -7] 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) |
| [ -8] 0x00007ffff7bd96f3 addl $0x1, %eax |
| [ -9] 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax |
| libfoo.so`foo() + 13 at foo.cpp:4 |
| [-10] 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) |
| libbar.so`bar() + 26 at bar.cpp:4 |
| [-11] 0x00007ffff79d76aa retq''', |
| '''[-12] 0x00007ffff79d76a9 popq %rbp |
| [-13] 0x00007ffff79d76a6 movl -0x4(%rbp), %eax |
| libbar.so`bar() + 19 at bar.cpp:3 |
| [-14] 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) |
| [-15] 0x00007ffff79d769e addl $0x1, %eax |
| [-16] 0x00007ffff79d769b movl -0x4(%rbp), %eax |
| libbar.so`bar() + 4 at bar.cpp:2 |
| [-17] 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) |
| libbar.so`bar() + 1 at bar.cpp:1 |
| [-18] 0x00007ffff79d7691 movq %rsp, %rbp |
| [-19] 0x00007ffff79d7690 pushq %rbp |
| libfoo.so`symbol stub for: bar() |
| [-20] 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 |
| libfoo.so`foo() + 8 at foo.cpp:4 |
| [-21] 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() |
| [-22] 0x00007ffff7bd96e4 subq $0x10, %rsp |
| libfoo.so`foo() + 1 at foo.cpp:3 |
| [-23] 0x00007ffff7bd96e1 movq %rsp, %rbp |
| [-24] 0x00007ffff7bd96e0 pushq %rbp |
| a.out`symbol stub for: foo() |
| [-25] 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 |
| a.out`main + 63 at main.cpp:16 |
| [-26] 0x000000000040069f callq 0x400540 ; symbol stub for: foo() |
| a.out`main + 60 at main.cpp:14 |
| [-27] 0x000000000040069c movl %ecx, -0xc(%rbp) |
| [-28] 0x000000000040069a addl %eax, %ecx |
| [-29] 0x0000000000400697 movl -0xc(%rbp), %ecx |
| a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6 |
| [-30] 0x0000000000400694 movl -0x4(%rbp), %eax |
| a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5 |
| [-31] 0x0000000000400691 movl %eax, -0x4(%rbp) |
| [-32] 0x000000000040068c addl $0x1, %eax |
| [-33] 0x0000000000400689 movl -0x4(%rbp), %eax |
| a.out`main + 34 [inlined] inline_function() at main.cpp:4 |
| [-34] 0x0000000000400682 movl $0x0, -0x4(%rbp) |
| a.out`main + 31 at main.cpp:12 |
| [-35] 0x000000000040067f movl %eax, -0xc(%rbp) |
| [-36] 0x000000000040067a addl $0x1, %eax |
| [-37] 0x0000000000400677 movl -0xc(%rbp), %eax |
| a.out`main + 20 at main.cpp:10 |
| [-38] 0x0000000000400674 movl %eax, -0xc(%rbp) |
| ...missing instructions |
| [-39] 0x00007ffff7df1950 error: no memory mapped at this address |
| a.out`(none) |
| [-40] 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 |
| [-41] 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 |
| a.out`symbol stub for: foo() + 11 |
| [-42] 0x000000000040054b jmp 0x400510 |
| [-43] 0x0000000000400546 pushq $0x2 |
| [-44] 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 |
| a.out`main + 15 at main.cpp:10 |
| [-45] 0x000000000040066f callq 0x400540 ; symbol stub for: foo()''']) |
| |
| self.expect("thread trace dump instructions --skip 100 --forwards", inHistory=True, |
| substrs=['''thread #1: tid = 815455 |
| no more data''']) |
| |
| self.expect("", substrs=['''thread #1: tid = 815455 |
| no more data''']) |