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=["total instructions"]) 69 # process stopping should stop the thread 70 self.expect("process trace stop") 71 self.expect("n") 72 self.expect("thread trace dump instructions", error=True, substrs=["not traced"]) 73 74 75 @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) 76 def testStartStopLiveThreads(self): 77 # The help command should be the generic one if there's no process running 78 self.expectGenericHelpMessageForStartCommand() 79 80 self.expect("thread trace start", error=True, 81 substrs=["error: Process not available"]) 82 83 self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) 84 self.expect("b main") 85 86 self.expect("thread trace start", error=True, 87 substrs=["error: Process not available"]) 88 89 # The help command should be the generic one if there's still no process running 90 self.expectGenericHelpMessageForStartCommand() 91 92 self.expect("r") 93 94 # This fails because "trace start" hasn't been called yet 95 self.expect("thread trace stop", error=True, 96 substrs=["error: Process is not being traced"]) 97 98 99 # the help command should be the intel-pt one now 100 self.expect("help thread trace start", 101 substrs=["Start tracing one or more threads with intel-pt.", 102 "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"]) 103 104 # We start tracing with a small buffer size 105 self.expect("thread trace start 1 --size 4096") 106 107 # We fail if we try to trace again 108 self.expect("thread trace start", error=True, 109 substrs=["error: Thread ", "already traced"]) 110 111 # We can reconstruct the single instruction executed in the first line 112 self.expect("n") 113 self.expect("thread trace dump instructions", 114 patterns=[f'''thread #1: tid = .*, total instructions = 1 115 a.out`main \+ 4 at main.cpp:2 116 \[0\] {ADDRESS_REGEX} movl''']) 117 118 # We can reconstruct the instructions up to the second line 119 self.expect("n") 120 self.expect("thread trace dump instructions", 121 patterns=[f'''thread #1: tid = .*, total instructions = 5 122 a.out`main \+ 4 at main.cpp:2 123 \[0\] {ADDRESS_REGEX} movl .* 124 a.out`main \+ 11 at main.cpp:4 125 \[1\] {ADDRESS_REGEX} movl .* 126 \[2\] {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 127 \[3\] {ADDRESS_REGEX} cmpl .* 128 \[4\] {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5''']) 129 130 # We stop tracing 131 self.expect("thread trace stop") 132 133 # We can't stop twice 134 self.expect("thread trace stop", error=True, 135 substrs=["error: Thread ", "not currently traced"]) 136 137 # We trace again from scratch, this time letting LLDB to pick the current 138 # thread 139 self.expect("thread trace start") 140 self.expect("n") 141 self.expect("thread trace dump instructions", 142 patterns=[f'''thread #1: tid = .*, total instructions = 1 143 a.out`main \+ 20 at main.cpp:5 144 \[0\] {ADDRESS_REGEX} xorl''']) 145 146 self.expect("c") 147 # Now the process has finished, so the commands should fail 148 self.expect("thread trace start", error=True, 149 substrs=["error: Process must be launched"]) 150 151 self.expect("thread trace stop", error=True, 152 substrs=["error: Process must be launched"]) 153