xref: /llvm-project/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py (revision a4c18137d84bc48df49ee0101bef465a955e62ac)
1import lldb
2import json
3from intelpt_testcase import *
4from lldbsuite.test.lldbtest import *
5from lldbsuite.test import lldbutil
6from lldbsuite.test.decorators import *
7
8
9class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
10    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
11    @testSBAPIAndCommands
12    def testStartMultipleLiveThreads(self):
13        self.build()
14        exe = self.getBuildArtifact("a.out")
15
16        self.dbg.CreateTarget(exe)
17
18        self.expect("b main")
19        self.expect("b 6")
20        self.expect("b 11")
21
22        self.expect("r")
23        self.traceStartProcess()
24
25        self.expect("continue")
26        self.expect("thread trace dump instructions", substrs=["main.cpp:9"])
27
28        # We'll see here the second thread
29        self.expect("continue")
30        self.expect("thread trace dump instructions", substrs=["main.cpp:4"])
31
32        self.traceStopProcess()
33
34    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
35    @testSBAPIAndCommands
36    def testStartMultipleLiveThreadsWithStops(self):
37        self.build()
38        exe = self.getBuildArtifact("a.out")
39        self.dbg.CreateTarget(exe)
40
41        self.expect("b main")
42        self.expect("b 6")
43        self.expect("b 11")
44
45        self.expect("r")
46
47        self.traceStartProcess()
48
49        # We'll see here the first thread
50        self.expect("continue")
51
52        # We are in thread 2
53        self.expect("thread trace dump instructions", substrs=["main.cpp:9"])
54        self.expect("thread trace dump instructions 2", substrs=["main.cpp:9"])
55
56        # We stop tracing all
57        self.expect("thread trace stop all")
58
59        # The trace is still in memory
60        self.expect("thread trace dump instructions 2", substrs=["main.cpp:9"])
61
62        # We'll stop at the next breakpoint in thread 3, thread 2 and 3 will be alive, but only 3 traced.
63        self.expect("continue")
64        self.expect("thread trace dump instructions", substrs=["main.cpp:4"])
65        self.expect("thread trace dump instructions 3", substrs=["main.cpp:4"])
66        self.expect(
67            "thread trace dump instructions 1", substrs=["not traced"], error=True
68        )
69        self.expect(
70            "thread trace dump instructions 2", substrs=["not traced"], error=True
71        )
72
73        self.traceStopProcess()
74
75    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
76    def testStartMultipleLiveThreadsWithThreadStartAll(self):
77        self.build()
78        exe = self.getBuildArtifact("a.out")
79        target = self.dbg.CreateTarget(exe)
80
81        self.expect("b main")
82        self.expect("b 6")
83        self.expect("b 11")
84
85        self.expect("r")
86
87        self.expect("continue")
88        # We are in thread 2
89        self.expect("thread trace start all")
90        # Now we have instructions in thread's 2 trace
91        self.expect("n")
92
93        self.expect("thread trace dump instructions 2", substrs=["main.cpp:11"])
94
95        # We stop tracing all
96        self.runCmd("thread trace stop all")
97
98        # The trace is still in memory
99        self.expect("thread trace dump instructions 2", substrs=["main.cpp:11"])
100
101        # We'll stop at the next breakpoint in thread 3, and nothing should be traced
102        self.expect("continue")
103        self.expect(
104            "thread trace dump instructions 3", substrs=["not traced"], error=True
105        )
106        self.expect(
107            "thread trace dump instructions 1", substrs=["not traced"], error=True
108        )
109        self.expect(
110            "thread trace dump instructions 2", substrs=["not traced"], error=True
111        )
112
113    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
114    @testSBAPIAndCommands
115    def testStartMultipleLiveThreadsWithSmallTotalLimit(self):
116        self.build()
117        exe = self.getBuildArtifact("a.out")
118
119        self.dbg.CreateTarget(exe)
120
121        self.expect("b main")
122        self.expect("r")
123
124        # trace the entire process with enough total size for 1 thread trace
125        self.traceStartProcess(processBufferSizeLimit=5000)
126
127        # we get the stop event when trace 2 appears and can't be traced
128        self.expect("c", substrs=["Thread", "can't be traced"])
129        # we get the stop event when trace 3 appears and can't be traced
130        self.expect("c", substrs=["Thread", "can't be traced"])
131
132        self.traceStopProcess()
133
134    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
135    @testSBAPIAndCommands
136    def testStartPerCpuSession(self):
137        self.skipIfPerCpuTracingIsNotSupported()
138
139        self.build()
140        exe = self.getBuildArtifact("a.out")
141        self.dbg.CreateTarget(exe)
142
143        self.expect("b main")
144        self.expect("r")
145
146        # We should fail if we hit the total buffer limit. Useful if the number
147        # of cpus is huge.
148        self.traceStartProcess(
149            error="True",
150            processBufferSizeLimit=100,
151            perCpuTracing=True,
152            substrs=[
153                "The process can't be traced because the process trace size "
154                "limit has been reached. Consider retracing with a higher limit."
155            ],
156        )
157
158        self.traceStartProcess(perCpuTracing=True)
159        self.traceStopProcess()
160
161        self.traceStartProcess(perCpuTracing=True)
162        # We can't support multiple per-cpu tracing sessions.
163        self.traceStartProcess(
164            error=True,
165            perCpuTracing=True,
166            substrs=["Process currently traced. Stop process tracing first"],
167        )
168
169        # We can't support tracing per thread is per cpu is enabled.
170        self.traceStartThread(
171            error="True", substrs=["Thread with tid ", "is currently traced"]
172        )
173
174        # We can't stop individual thread when per cpu is enabled.
175        self.traceStopThread(
176            error="True",
177            substrs=[
178                "Can't stop tracing an individual thread when per-cpu process tracing is enabled"
179            ],
180        )
181
182        # We move forward a little bit to collect some data
183        self.expect("b 19")
184        self.expect("c")
185
186        # We will assert that the trace state will contain valid context switch and intel pt trace buffer entries.
187        # Besides that, we need to get tsc-to-nanos conversion information.
188
189        # We first parse the json response from the custom packet
190        self.runCmd(
191            """process plugin packet send 'jLLDBTraceGetState:{"type":"intel-pt"}]'"""
192        )
193        response_header = "response: "
194        output = None
195        for line in self.res.GetOutput().splitlines():
196            if line.find(response_header) != -1:
197                response = line[
198                    line.find(response_header) + len(response_header) :
199                ].strip()
200                output = json.loads(response)
201
202        self.assertIsNotNone(output)
203        self.assertIn("cpus", output)
204        self.assertIn("tscPerfZeroConversion", output)
205        found_non_empty_context_switch = False
206
207        for cpu in output["cpus"]:
208            context_switch_size = None
209            ipt_trace_size = None
210            for binary_data in cpu["binaryData"]:
211                if binary_data["kind"] == "iptTrace":
212                    ipt_trace_size = binary_data["size"]
213                elif binary_data["kind"] == "perfContextSwitchTrace":
214                    context_switch_size = binary_data["size"]
215            self.assertIsNotNone(context_switch_size)
216            self.assertIsNotNone(ipt_trace_size)
217            if context_switch_size > 0:
218                found_non_empty_context_switch = True
219
220        # We must have captured the context switch of when the target resumed
221        self.assertTrue(found_non_empty_context_switch)
222
223        self.expect("thread trace dump instructions")
224
225        self.traceStopProcess()
226