1import lldb 2from intelpt_testcase import * 3from lldbsuite.test.lldbtest import * 4from lldbsuite.test import lldbutil 5from lldbsuite.test.decorators import * 6 7 8class TestTraceStartStop(TraceIntelPTTestCaseBase): 9 def expectGenericHelpMessageForStartCommand(self): 10 self.expect( 11 "help thread trace start", 12 substrs=["Syntax: thread trace start [<trace-options>]"], 13 ) 14 15 @testSBAPIAndCommands 16 def testStartStopSessionFileThreads(self): 17 # it should fail for processes from json session files 18 self.expect( 19 "trace load -v " 20 + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json") 21 ) 22 23 # the help command should be the generic one, as it's not a live process 24 self.expectGenericHelpMessageForStartCommand() 25 26 self.traceStartThread(error=True) 27 28 self.traceStopThread(error=True) 29 30 @testSBAPIAndCommands 31 def testStartWithNoProcess(self): 32 self.traceStartThread(error=True) 33 34 @testSBAPIAndCommands 35 def testStartSessionWithWrongSize(self): 36 self.expect( 37 "file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") 38 ) 39 self.expect("b main") 40 self.expect("r") 41 42 self.traceStartThread( 43 error=True, 44 iptTraceSize=2000, 45 substrs=["The intel pt trace size must be a power of 2", "It was 2000"], 46 ) 47 48 self.traceStartThread( 49 error=True, 50 iptTraceSize=5000, 51 substrs=["The intel pt trace size must be a power of 2", "It was 5000"], 52 ) 53 54 self.traceStartThread( 55 error=True, 56 iptTraceSize=0, 57 substrs=["The intel pt trace size must be a power of 2", "It was 0"], 58 ) 59 60 self.traceStartThread(iptTraceSize=1048576) 61 62 @testSBAPIAndCommands 63 def testStartSessionWithSizeDeclarationInUnits(self): 64 self.expect( 65 "file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") 66 ) 67 self.expect("b main") 68 self.expect("r") 69 70 self.traceStartThread( 71 error=True, 72 iptTraceSize="abc", 73 substrs=["invalid bytes expression for 'abc'"], 74 ) 75 76 self.traceStartThread( 77 error=True, 78 iptTraceSize="123.12", 79 substrs=["invalid bytes expression for '123.12'"], 80 ) 81 82 self.traceStartThread( 83 error=True, iptTraceSize='""', substrs=["invalid bytes expression for ''"] 84 ) 85 86 self.traceStartThread( 87 error=True, 88 iptTraceSize="2000B", 89 substrs=[ 90 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 2000" 91 ], 92 ) 93 94 self.traceStartThread( 95 error=True, 96 iptTraceSize="3MB", 97 substrs=[ 98 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728" 99 ], 100 ) 101 102 self.traceStartThread( 103 error=True, 104 iptTraceSize="3MiB", 105 substrs=[ 106 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728" 107 ], 108 ) 109 110 self.traceStartThread( 111 error=True, 112 iptTraceSize="3mib", 113 substrs=[ 114 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728" 115 ], 116 ) 117 118 self.traceStartThread( 119 error=True, 120 iptTraceSize="3M", 121 substrs=[ 122 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728" 123 ], 124 ) 125 126 self.traceStartThread( 127 error=True, 128 iptTraceSize="3KB", 129 substrs=[ 130 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072" 131 ], 132 ) 133 134 self.traceStartThread( 135 error=True, 136 iptTraceSize="3KiB", 137 substrs=[ 138 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072" 139 ], 140 ) 141 142 self.traceStartThread( 143 error=True, 144 iptTraceSize="3K", 145 substrs=[ 146 "The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072" 147 ], 148 ) 149 150 self.traceStartThread( 151 error=True, 152 iptTraceSize="3MS", 153 substrs=["invalid bytes expression for '3MS'"], 154 ) 155 156 self.traceStartThread(iptTraceSize="1048576") 157 158 @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) 159 def testSBAPIHelp(self): 160 self.expect( 161 "file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") 162 ) 163 self.expect("b main") 164 self.expect("r") 165 166 help = self.getTraceOrCreate().GetStartConfigurationHelp() 167 self.assertIn("iptTraceSize", help) 168 self.assertIn("processBufferSizeLimit", help) 169 170 @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) 171 def testStoppingAThread(self): 172 self.expect( 173 "file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") 174 ) 175 self.expect("b main") 176 self.expect("r") 177 self.expect("thread trace start") 178 self.expect("n") 179 self.expect( 180 "thread trace dump instructions", 181 substrs=[ 182 """0x0000000000400511 movl $0x0, -0x4(%rbp) 183 no more data""" 184 ], 185 ) 186 # process stopping should stop the thread 187 self.expect("process trace stop") 188 self.expect("n") 189 self.expect( 190 "thread trace dump instructions", substrs=["not traced"], error=True 191 ) 192 193 @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) 194 def testStartStopLiveThreads(self): 195 # The help command should be the generic one if there's no process running 196 self.expectGenericHelpMessageForStartCommand() 197 198 self.expect( 199 "thread trace start", error=True, substrs=["error: Process not available"] 200 ) 201 202 self.expect( 203 "file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") 204 ) 205 self.expect("b main") 206 207 self.expect( 208 "thread trace start", error=True, substrs=["error: Process not available"] 209 ) 210 211 # The help command should be the generic one if there's still no process running 212 self.expectGenericHelpMessageForStartCommand() 213 214 self.expect("r") 215 216 # This fails because "trace start" hasn't been called yet 217 self.expect( 218 "thread trace stop", 219 error=True, 220 substrs=["error: Process is not being traced"], 221 ) 222 223 # the help command should be the intel-pt one now 224 self.expect( 225 "help thread trace start", 226 substrs=[ 227 "Start tracing one or more threads with intel-pt.", 228 "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]", 229 ], 230 ) 231 232 # We start tracing with a small buffer size 233 self.expect("thread trace start 1 --size 4096") 234 235 # We fail if we try to trace again 236 self.expect( 237 "thread trace start", 238 error=True, 239 substrs=["error: Thread ", "already traced"], 240 ) 241 242 # We can reconstruct the single instruction executed in the first line 243 self.expect("n") 244 self.expect( 245 "thread trace dump instructions -f", 246 patterns=[ 247 f"""thread #1: tid = .* 248 a.out`main \+ 4 at main.cpp:2 249 2: {ADDRESS_REGEX} movl""" 250 ], 251 ) 252 253 # We can reconstruct the instructions up to the second line 254 self.expect("n") 255 self.expect( 256 "thread trace dump instructions -f", 257 patterns=[ 258 f"""thread #1: tid = .* 259 a.out`main \+ 4 at main.cpp:2 260 2: {ADDRESS_REGEX} movl .* 261 a.out`main \+ 11 at main.cpp:4 262 4: {ADDRESS_REGEX} movl .* 263 6: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 264 8: {ADDRESS_REGEX} cmpl .* 265 10: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5""" 266 ], 267 ) 268 269 self.expect( 270 "thread trace dump instructions", 271 patterns=[ 272 f"""thread #1: tid = .* 273 a.out`main \+ 32 at main.cpp:4 274 10: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5 275 8: {ADDRESS_REGEX} cmpl .* 276 6: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 277 4: {ADDRESS_REGEX} movl .* 278 a.out`main \+ 4 at main.cpp:2 279 2: {ADDRESS_REGEX} movl .* """ 280 ], 281 ) 282 283 # We stop tracing 284 self.expect("thread trace stop") 285 286 # We can't stop twice 287 self.expect( 288 "thread trace stop", 289 error=True, 290 substrs=["error: Thread ", "not currently traced"], 291 ) 292 293 # We trace again from scratch, this time letting LLDB to pick the current 294 # thread 295 self.expect("thread trace start") 296 self.expect("n") 297 self.expect( 298 "thread trace dump instructions -f", 299 patterns=[ 300 f"""thread #1: tid = .* 301 a.out`main \+ 20 at main.cpp:5 302 2: {ADDRESS_REGEX} xorl""" 303 ], 304 ) 305 306 self.expect( 307 "thread trace dump instructions", 308 patterns=[ 309 f"""thread #1: tid = .* 310 a.out`main \+ 20 at main.cpp:5 311 2: {ADDRESS_REGEX} xorl""" 312 ], 313 ) 314 315 self.expect("c") 316 # Now the process has finished, so the commands should fail 317 self.expect( 318 "thread trace start", 319 error=True, 320 substrs=["error: Process must be launched"], 321 ) 322 323 self.expect( 324 "thread trace stop", error=True, substrs=["error: Process must be launched"] 325 ) 326 327 # We should be able to trace the program if we relaunch it 328 # For this, we'll trace starting at a different point in the new 329 # process. 330 self.expect("breakpoint disable") 331 self.expect("b main.cpp:4") 332 self.expect("r") 333 self.expect("thread trace start") 334 # We can reconstruct the single instruction executed in the first line 335 self.expect("si") 336 self.expect( 337 "thread trace dump instructions -c 1", 338 patterns=[ 339 f"""thread #1: tid = .* 340 a.out`main \+ 11 at main.cpp:4""" 341 ], 342 ) 343