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