xref: /llvm-project/lldb/test/API/commands/trace/TestTraceStartStop.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
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