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""" ], )