import lldb from intelpt_testcase import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil from lldbsuite.test.decorators import * class TestTraceStartStop(TraceIntelPTTestCaseBase): def expectGenericHelpMessageForStartCommand(self): self.expect("help thread trace start", substrs=["Syntax: thread trace start []"]) @testSBAPIAndCommands def testStartStopSessionFileThreads(self): # it should fail for processes from json session files self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")) # the help command should be the generic one, as it's not a live process self.expectGenericHelpMessageForStartCommand() self.traceStartThread(error=True) self.traceStopThread(error=True) @testSBAPIAndCommands def testStartWithNoProcess(self): self.traceStartThread(error=True) @testSBAPIAndCommands def testStartSessionWithWrongSize(self): self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) self.expect("b main") self.expect("r") self.traceStartThread( error=True, iptTraceSize=2000, substrs=["The intel pt trace size must be a power of 2", "It was 2000"]) self.traceStartThread( error=True, iptTraceSize=5000, substrs=["The intel pt trace size must be a power of 2", "It was 5000"]) self.traceStartThread( error=True, iptTraceSize=0, substrs=["The intel pt trace size must be a power of 2", "It was 0"]) self.traceStartThread(iptTraceSize=1048576) @testSBAPIAndCommands def testStartSessionWithSizeDeclarationInUnits(self): self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) self.expect("b main") self.expect("r") self.traceStartThread( error=True, iptTraceSize="abc", substrs=["invalid bytes expression for 'abc'"]) self.traceStartThread( error=True, iptTraceSize="123.12", substrs=["invalid bytes expression for '123.12'"]) self.traceStartThread( error=True, iptTraceSize="\"\"", substrs=["invalid bytes expression for ''"]) self.traceStartThread( error=True, iptTraceSize="2000B", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 2000"]) self.traceStartThread( error=True, iptTraceSize="3MB", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"]) self.traceStartThread( error=True, iptTraceSize="3MiB", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"]) self.traceStartThread( error=True, iptTraceSize="3mib", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"]) self.traceStartThread( error=True, iptTraceSize="3M", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"]) self.traceStartThread( error=True, iptTraceSize="3KB", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072"]) self.traceStartThread( error=True, iptTraceSize="3KiB", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072"]) self.traceStartThread( error=True, iptTraceSize="3K", substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072"]) self.traceStartThread( error=True, iptTraceSize="3MS", substrs=["invalid bytes expression for '3MS'"]) self.traceStartThread(iptTraceSize="1048576") @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) def testSBAPIHelp(self): self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) self.expect("b main") self.expect("r") help = self.getTraceOrCreate().GetStartConfigurationHelp() self.assertIn("iptTraceSize", help) self.assertIn("processBufferSizeLimit", help) @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) def testStoppingAThread(self): self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) self.expect("b main") self.expect("r") self.expect("thread trace start") self.expect("n") self.expect("thread trace dump instructions", substrs=["""0x0000000000400511 movl $0x0, -0x4(%rbp) no more data"""]) # process stopping should stop the thread self.expect("process trace stop") self.expect("n") self.expect("thread trace dump instructions", substrs=["not traced"], error=True) @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) def testStartStopLiveThreads(self): # The help command should be the generic one if there's no process running self.expectGenericHelpMessageForStartCommand() self.expect("thread trace start", error=True, substrs=["error: Process not available"]) self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")) self.expect("b main") self.expect("thread trace start", error=True, substrs=["error: Process not available"]) # The help command should be the generic one if there's still no process running self.expectGenericHelpMessageForStartCommand() self.expect("r") # This fails because "trace start" hasn't been called yet self.expect("thread trace stop", error=True, substrs=["error: Process is not being traced"]) # the help command should be the intel-pt one now self.expect("help thread trace start", substrs=["Start tracing one or more threads with intel-pt.", "Syntax: thread trace start [ ...] []"]) # We start tracing with a small buffer size self.expect("thread trace start 1 --size 4096") # We fail if we try to trace again self.expect("thread trace start", error=True, substrs=["error: Thread ", "already traced"]) # We can reconstruct the single instruction executed in the first line self.expect("n") self.expect("thread trace dump instructions -f", patterns=[f'''thread #1: tid = .* a.out`main \+ 4 at main.cpp:2 2: {ADDRESS_REGEX} movl''']) # We can reconstruct the instructions up to the second line self.expect("n") self.expect("thread trace dump instructions -f", patterns=[f'''thread #1: tid = .* a.out`main \+ 4 at main.cpp:2 2: {ADDRESS_REGEX} movl .* a.out`main \+ 11 at main.cpp:4 4: {ADDRESS_REGEX} movl .* 6: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 8: {ADDRESS_REGEX} cmpl .* 10: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5''']) self.expect("thread trace dump instructions", patterns=[f'''thread #1: tid = .* a.out`main \+ 32 at main.cpp:4 10: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5 8: {ADDRESS_REGEX} cmpl .* 6: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 4: {ADDRESS_REGEX} movl .* a.out`main \+ 4 at main.cpp:2 2: {ADDRESS_REGEX} movl .* ''']) # We stop tracing self.expect("thread trace stop") # We can't stop twice self.expect("thread trace stop", error=True, substrs=["error: Thread ", "not currently traced"]) # We trace again from scratch, this time letting LLDB to pick the current # thread self.expect("thread trace start") self.expect("n") self.expect("thread trace dump instructions -f", patterns=[f'''thread #1: tid = .* a.out`main \+ 20 at main.cpp:5 2: {ADDRESS_REGEX} xorl''']) self.expect("thread trace dump instructions", patterns=[f'''thread #1: tid = .* a.out`main \+ 20 at main.cpp:5 2: {ADDRESS_REGEX} xorl''']) self.expect("c") # Now the process has finished, so the commands should fail self.expect("thread trace start", error=True, substrs=["error: Process must be launched"]) self.expect("thread trace stop", error=True, substrs=["error: Process must be launched"]) # We should be able to trace the program if we relaunch it # For this, we'll trace starting at a different point in the new # process. self.expect("breakpoint disable") self.expect("b main.cpp:4") self.expect("r") self.expect("thread trace start") # We can reconstruct the single instruction executed in the first line self.expect("si") self.expect("thread trace dump instructions -c 1", patterns=[f'''thread #1: tid = .* a.out`main \+ 11 at main.cpp:4'''])