1import lldb 2from intelpt_testcase import * 3from lldbsuite.test.lldbtest import * 4from lldbsuite.test import lldbutil 5from lldbsuite.test.decorators import * 6 7class TestTraceStartStop(TraceIntelPTTestCaseBase): 8 9 mydir = TestBase.compute_mydir(__file__) 10 11 def expectGenericHelpMessageForStartCommand(self): 12 self.expect("help thread trace start", 13 substrs=["Syntax: thread trace start [<trace-options>]"]) 14 15 @testSBAPIAndCommands 16 def testStartStopSessionFileThreads(self): 17 # it should fail for processes from json session files 18 self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")) 19 20 # the help command should be the generic one, as it's not a live process 21 self.expectGenericHelpMessageForStartCommand() 22 23 self.traceStartThread(error=True) 24 25 self.traceStopThread(error=True) 26 27 @testSBAPIAndCommands 28 def testStartWithNoProcess(self): 29 self.traceStartThread(error=True) 30 31 @testSBAPIAndCommands 32 def testStartSessionWithWrongSize(self): 33 self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) 34 self.expect("b main") 35 self.expect("r") 36 37 self.traceStartThread( 38 error=True, threadBufferSize=2000, 39 substrs=["The trace buffer size must be a power of 2", "It was 2000"]) 40 41 self.traceStartThread( 42 error=True, threadBufferSize=5000, 43 substrs=["The trace buffer size must be a power of 2", "It was 5000"]) 44 45 self.traceStartThread( 46 error=True, threadBufferSize=0, 47 substrs=["The trace buffer size must be a power of 2", "It was 0"]) 48 49 self.traceStartThread(threadBufferSize=1048576) 50 51 @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) 52 def testSBAPIHelp(self): 53 self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) 54 self.expect("b main") 55 self.expect("r") 56 57 help = self.getTraceOrCreate().GetStartConfigurationHelp() 58 self.assertIn("threadBufferSize", help) 59 self.assertIn("processBufferSizeLimit", help) 60 61 @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) 62 def testStoppingAThread(self): 63 self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) 64 self.expect("b main") 65 self.expect("r") 66 self.expect("thread trace start") 67 self.expect("n") 68 self.expect("thread trace dump instructions", substrs=["""0x0000000000400511 movl $0x0, -0x4(%rbp) 69 no more data"""]) 70 # process stopping should stop the thread 71 self.expect("process trace stop") 72 self.expect("n") 73 self.expect("thread trace dump instructions", substrs=["not traced"]) 74 75 76 @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) 77 def testStartStopLiveThreads(self): 78 # The help command should be the generic one if there's no process running 79 self.expectGenericHelpMessageForStartCommand() 80 81 self.expect("thread trace start", error=True, 82 substrs=["error: Process not available"]) 83 84 self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) 85 self.expect("b main") 86 87 self.expect("thread trace start", error=True, 88 substrs=["error: Process not available"]) 89 90 # The help command should be the generic one if there's still no process running 91 self.expectGenericHelpMessageForStartCommand() 92 93 self.expect("r") 94 95 # This fails because "trace start" hasn't been called yet 96 self.expect("thread trace stop", error=True, 97 substrs=["error: Process is not being traced"]) 98 99 100 # the help command should be the intel-pt one now 101 self.expect("help thread trace start", 102 substrs=["Start tracing one or more threads with intel-pt.", 103 "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"]) 104 105 # We start tracing with a small buffer size 106 self.expect("thread trace start 1 --size 4096") 107 108 # We fail if we try to trace again 109 self.expect("thread trace start", error=True, 110 substrs=["error: Thread ", "already traced"]) 111 112 # We can reconstruct the single instruction executed in the first line 113 self.expect("n") 114 self.expect("thread trace dump instructions -f", 115 patterns=[f'''thread #1: tid = .* 116 a.out`main \+ 4 at main.cpp:2 117 \[ 0\] {ADDRESS_REGEX} movl''']) 118 119 # We can reconstruct the instructions up to the second line 120 self.expect("n") 121 self.expect("thread trace dump instructions -f", 122 patterns=[f'''thread #1: tid = .* 123 a.out`main \+ 4 at main.cpp:2 124 \[ 0\] {ADDRESS_REGEX} movl .* 125 a.out`main \+ 11 at main.cpp:4 126 \[ 1\] {ADDRESS_REGEX} movl .* 127 \[ 2\] {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 128 \[ 3\] {ADDRESS_REGEX} cmpl .* 129 \[ 4\] {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5''']) 130 131 self.expect("thread trace dump instructions", 132 patterns=[f'''thread #1: tid = .* 133 a.out`main \+ 32 at main.cpp:4 134 \[ 0\] {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5 135 \[ -1\] {ADDRESS_REGEX} cmpl .* 136 \[ -2\] {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 137 \[ -3\] {ADDRESS_REGEX} movl .* 138 a.out`main \+ 4 at main.cpp:2 139 \[ -4\] {ADDRESS_REGEX} movl .* ''']) 140 141 # We stop tracing 142 self.expect("thread trace stop") 143 144 # We can't stop twice 145 self.expect("thread trace stop", error=True, 146 substrs=["error: Thread ", "not currently traced"]) 147 148 # We trace again from scratch, this time letting LLDB to pick the current 149 # thread 150 self.expect("thread trace start") 151 self.expect("n") 152 self.expect("thread trace dump instructions -f", 153 patterns=[f'''thread #1: tid = .* 154 a.out`main \+ 20 at main.cpp:5 155 \[ 0\] {ADDRESS_REGEX} xorl''']) 156 157 self.expect("thread trace dump instructions", 158 patterns=[f'''thread #1: tid = .* 159 a.out`main \+ 20 at main.cpp:5 160 \[ 0\] {ADDRESS_REGEX} xorl''']) 161 162 self.expect("c") 163 # Now the process has finished, so the commands should fail 164 self.expect("thread trace start", error=True, 165 substrs=["error: Process must be launched"]) 166 167 self.expect("thread trace stop", error=True, 168 substrs=["error: Process must be launched"]) 169