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'''])