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