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    def expectGenericHelpMessageForStartCommand(self):
10        self.expect("help thread trace start",
11            substrs=["Syntax: thread trace start [<trace-options>]"])
12
13    @testSBAPIAndCommands
14    def testStartStopSessionFileThreads(self):
15        # it should fail for processes from json session files
16        self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
17
18        # the help command should be the generic one, as it's not a live process
19        self.expectGenericHelpMessageForStartCommand()
20
21        self.traceStartThread(error=True)
22
23        self.traceStopThread(error=True)
24
25    @testSBAPIAndCommands
26    def testStartWithNoProcess(self):
27        self.traceStartThread(error=True)
28
29    @testSBAPIAndCommands
30    def testStartSessionWithWrongSize(self):
31        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
32        self.expect("b main")
33        self.expect("r")
34
35        self.traceStartThread(
36            error=True, iptTraceSize=2000,
37            substrs=["The intel pt trace size must be a power of 2", "It was 2000"])
38
39        self.traceStartThread(
40            error=True, iptTraceSize=5000,
41            substrs=["The intel pt trace size must be a power of 2", "It was 5000"])
42
43        self.traceStartThread(
44            error=True, iptTraceSize=0,
45            substrs=["The intel pt trace size must be a power of 2", "It was 0"])
46
47        self.traceStartThread(iptTraceSize=1048576)
48
49    @testSBAPIAndCommands
50    def testStartSessionWithSizeDeclarationInUnits(self):
51        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
52        self.expect("b main")
53        self.expect("r")
54
55        self.traceStartThread(
56            error=True, iptTraceSize="abc",
57            substrs=["invalid bytes expression for 'abc'"])
58
59        self.traceStartThread(
60            error=True, iptTraceSize="123.12",
61            substrs=["invalid bytes expression for '123.12'"])
62
63        self.traceStartThread(
64            error=True, iptTraceSize="\"\"",
65            substrs=["invalid bytes expression for ''"])
66
67        self.traceStartThread(
68            error=True, iptTraceSize="2000B",
69            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 2000"])
70
71        self.traceStartThread(
72            error=True, iptTraceSize="3MB",
73            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"])
74
75        self.traceStartThread(
76            error=True, iptTraceSize="3MiB",
77            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"])
78
79        self.traceStartThread(
80            error=True, iptTraceSize="3mib",
81            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"])
82
83        self.traceStartThread(
84            error=True, iptTraceSize="3M",
85            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3145728"])
86
87        self.traceStartThread(
88            error=True, iptTraceSize="3KB",
89            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072"])
90
91        self.traceStartThread(
92            error=True, iptTraceSize="3KiB",
93            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072"])
94
95        self.traceStartThread(
96            error=True, iptTraceSize="3K",
97            substrs=["The intel pt trace size must be a power of 2 greater than or equal to 4096 (2^12) bytes. It was 3072"])
98
99        self.traceStartThread(
100            error=True, iptTraceSize="3MS",
101            substrs=["invalid bytes expression for '3MS'"])
102
103        self.traceStartThread(iptTraceSize="1048576")
104
105    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
106    def testSBAPIHelp(self):
107        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
108        self.expect("b main")
109        self.expect("r")
110
111        help = self.getTraceOrCreate().GetStartConfigurationHelp()
112        self.assertIn("iptTraceSize", help)
113        self.assertIn("processBufferSizeLimit", help)
114
115    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
116    def testStoppingAThread(self):
117        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
118        self.expect("b main")
119        self.expect("r")
120        self.expect("thread trace start")
121        self.expect("n")
122        self.expect("thread trace dump instructions", substrs=["""0x0000000000400511    movl   $0x0, -0x4(%rbp)
123    no more data"""])
124        # process stopping should stop the thread
125        self.expect("process trace stop")
126        self.expect("n")
127        self.expect("thread trace dump instructions", substrs=["not traced"], error=True)
128
129
130    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
131    def testStartStopLiveThreads(self):
132        # The help command should be the generic one if there's no process running
133        self.expectGenericHelpMessageForStartCommand()
134
135        self.expect("thread trace start", error=True,
136            substrs=["error: Process not available"])
137
138        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
139        self.expect("b main")
140
141        self.expect("thread trace start", error=True,
142            substrs=["error: Process not available"])
143
144        # The help command should be the generic one if there's still no process running
145        self.expectGenericHelpMessageForStartCommand()
146
147        self.expect("r")
148
149        # This fails because "trace start" hasn't been called yet
150        self.expect("thread trace stop", error=True,
151            substrs=["error: Process is not being traced"])
152
153
154        # the help command should be the intel-pt one now
155        self.expect("help thread trace start",
156            substrs=["Start tracing one or more threads with intel-pt.",
157                     "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"])
158
159        # We start tracing with a small buffer size
160        self.expect("thread trace start 1 --size 4096")
161
162        # We fail if we try to trace again
163        self.expect("thread trace start", error=True,
164            substrs=["error: Thread ", "already traced"])
165
166        # We can reconstruct the single instruction executed in the first line
167        self.expect("n")
168        self.expect("thread trace dump instructions -f",
169            patterns=[f'''thread #1: tid = .*
170  a.out`main \+ 4 at main.cpp:2
171    0: {ADDRESS_REGEX}    movl'''])
172
173        # We can reconstruct the instructions up to the second line
174        self.expect("n")
175        self.expect("thread trace dump instructions -f",
176            patterns=[f'''thread #1: tid = .*
177  a.out`main \+ 4 at main.cpp:2
178    0: {ADDRESS_REGEX}    movl .*
179  a.out`main \+ 11 at main.cpp:4
180    2: {ADDRESS_REGEX}    movl .*
181    4: {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
182    6: {ADDRESS_REGEX}    cmpl .*
183    8: {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
184
185        self.expect("thread trace dump instructions",
186            patterns=[f'''thread #1: tid = .*
187  a.out`main \+ 32 at main.cpp:4
188    8: {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5
189    6: {ADDRESS_REGEX}    cmpl .*
190    4: {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
191    2: {ADDRESS_REGEX}    movl .*
192  a.out`main \+ 4 at main.cpp:2
193    0: {ADDRESS_REGEX}    movl .* '''])
194
195        # We stop tracing
196        self.expect("thread trace stop")
197
198        # We can't stop twice
199        self.expect("thread trace stop", error=True,
200            substrs=["error: Thread ", "not currently traced"])
201
202        # We trace again from scratch, this time letting LLDB to pick the current
203        # thread
204        self.expect("thread trace start")
205        self.expect("n")
206        self.expect("thread trace dump instructions -f",
207            patterns=[f'''thread #1: tid = .*
208  a.out`main \+ 20 at main.cpp:5
209    0: {ADDRESS_REGEX}    xorl'''])
210
211        self.expect("thread trace dump instructions",
212            patterns=[f'''thread #1: tid = .*
213  a.out`main \+ 20 at main.cpp:5
214    0: {ADDRESS_REGEX}    xorl'''])
215
216        self.expect("c")
217        # Now the process has finished, so the commands should fail
218        self.expect("thread trace start", error=True,
219            substrs=["error: Process must be launched"])
220
221        self.expect("thread trace stop", error=True,
222            substrs=["error: Process must be launched"])
223
224        # We should be able to trace the program if we relaunch it
225        # For this, we'll trace starting at a different point in the new
226        # process.
227        self.expect("breakpoint disable")
228        self.expect("b main.cpp:4")
229        self.expect("r")
230        self.expect("thread trace start")
231        # We can reconstruct the single instruction executed in the first line
232        self.expect("si")
233        self.expect("thread trace dump instructions -c 1",
234            patterns=[f'''thread #1: tid = .*
235  a.out`main \+ 11 at main.cpp:4'''])
236