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