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