1 //===-- IntelPTCollector.cpp ------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "IntelPTCollector.h" 10 11 #include "Perf.h" 12 #include "Procfs.h" 13 14 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 15 #include "lldb/Host/linux/Support.h" 16 #include "lldb/Utility/StreamString.h" 17 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/MathExtras.h" 21 22 #include <algorithm> 23 #include <cstddef> 24 #include <fstream> 25 #include <linux/perf_event.h> 26 #include <sstream> 27 #include <sys/ioctl.h> 28 #include <sys/syscall.h> 29 30 using namespace lldb; 31 using namespace lldb_private; 32 using namespace process_linux; 33 using namespace llvm; 34 35 /// IntelPTThreadTraceCollection 36 37 bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const { 38 return m_thread_traces.count(tid); 39 } 40 41 Error IntelPTThreadTraceCollection::TraceStop(lldb::tid_t tid) { 42 auto it = m_thread_traces.find(tid); 43 if (it == m_thread_traces.end()) 44 return createStringError(inconvertibleErrorCode(), 45 "Thread %" PRIu64 " not currently traced", tid); 46 m_total_buffer_size -= it->second->GetTraceBufferSize(); 47 m_thread_traces.erase(tid); 48 return Error::success(); 49 } 50 51 Error IntelPTThreadTraceCollection::TraceStart( 52 lldb::tid_t tid, const TraceIntelPTStartRequest &request) { 53 if (TracesThread(tid)) 54 return createStringError(inconvertibleErrorCode(), 55 "Thread %" PRIu64 " already traced", tid); 56 57 Expected<IntelPTSingleBufferTraceUP> trace_up = 58 IntelPTSingleBufferTrace::Start(request, tid, /*core_id=*/None, 59 TraceCollectionState::Running); 60 if (!trace_up) 61 return trace_up.takeError(); 62 63 m_total_buffer_size += (*trace_up)->GetTraceBufferSize(); 64 m_thread_traces.try_emplace(tid, std::move(*trace_up)); 65 return Error::success(); 66 } 67 68 size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const { 69 return m_total_buffer_size; 70 } 71 72 std::vector<TraceThreadState> 73 IntelPTThreadTraceCollection::GetThreadStates() const { 74 std::vector<TraceThreadState> states; 75 for (const auto &it : m_thread_traces) 76 states.push_back({it.first, 77 {TraceBinaryData{IntelPTDataKinds::kTraceBuffer, 78 it.second->GetTraceBufferSize()}}}); 79 return states; 80 } 81 82 Expected<IntelPTSingleBufferTrace &> 83 IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) { 84 auto it = m_thread_traces.find(tid); 85 if (it == m_thread_traces.end()) 86 return createStringError(inconvertibleErrorCode(), 87 "Thread %" PRIu64 " not currently traced", tid); 88 return *it->second.get(); 89 } 90 91 void IntelPTThreadTraceCollection::Clear() { 92 m_thread_traces.clear(); 93 m_total_buffer_size = 0; 94 } 95 96 size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const { 97 return m_thread_traces.size(); 98 } 99 100 /// IntelPTPerThreadProcessTrace 101 102 bool IntelPTPerThreadProcessTrace::TracesThread(lldb::tid_t tid) const { 103 return m_thread_traces.TracesThread(tid); 104 } 105 106 Error IntelPTPerThreadProcessTrace::TraceStop(lldb::tid_t tid) { 107 return m_thread_traces.TraceStop(tid); 108 } 109 110 Error IntelPTPerThreadProcessTrace::TraceStart(lldb::tid_t tid) { 111 if (m_thread_traces.GetTotalBufferSize() + 112 m_tracing_params.trace_buffer_size > 113 static_cast<size_t>(*m_tracing_params.process_buffer_size_limit)) 114 return createStringError( 115 inconvertibleErrorCode(), 116 "Thread %" PRIu64 " can't be traced as the process trace size limit " 117 "has been reached. Consider retracing with a higher " 118 "limit.", 119 tid); 120 121 return m_thread_traces.TraceStart(tid, m_tracing_params); 122 } 123 124 IntelPTThreadTraceCollection &IntelPTPerThreadProcessTrace::GetThreadTraces() { 125 return m_thread_traces; 126 } 127 128 /// IntelPTCollector 129 130 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process) 131 : m_process(process) { 132 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = 133 LoadPerfTscConversionParameters()) 134 m_tsc_conversion = 135 std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion); 136 else 137 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(), 138 "unable to load TSC to wall time conversion: {0}"); 139 } 140 141 Error IntelPTCollector::TraceStop(lldb::tid_t tid) { 142 if (m_per_thread_process_trace_up && 143 m_per_thread_process_trace_up->TracesThread(tid)) 144 return m_per_thread_process_trace_up->TraceStop(tid); 145 else if (m_per_core_process_trace_up) 146 return createStringError(inconvertibleErrorCode(), 147 "Can't stop tracing an individual thread when " 148 "per-core process tracing is enabled."); 149 return m_thread_traces.TraceStop(tid); 150 } 151 152 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { 153 if (request.IsProcessTracing()) { 154 Clear(); 155 return Error::success(); 156 } else { 157 Error error = Error::success(); 158 for (int64_t tid : *request.tids) 159 error = joinErrors(std::move(error), 160 TraceStop(static_cast<lldb::tid_t>(tid))); 161 return error; 162 } 163 } 164 165 Expected<IntelPTPerThreadProcessTraceUP> 166 IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request, 167 ArrayRef<lldb::tid_t> current_tids) { 168 IntelPTPerThreadProcessTraceUP trace( 169 new IntelPTPerThreadProcessTrace(request)); 170 171 Error error = Error::success(); 172 for (lldb::tid_t tid : current_tids) 173 error = joinErrors(std::move(error), trace->TraceStart(tid)); 174 if (error) 175 return std::move(error); 176 return trace; 177 } 178 179 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { 180 if (request.IsProcessTracing()) { 181 if (IsProcessTracingEnabled()) { 182 return createStringError( 183 inconvertibleErrorCode(), 184 "Process currently traced. Stop process tracing first"); 185 } 186 if (request.IsPerCoreTracing()) { 187 if (m_thread_traces.GetTracedThreadsCount() > 0) 188 return createStringError( 189 inconvertibleErrorCode(), 190 "Threads currently traced. Stop tracing them first."); 191 if (Expected<IntelPTMultiCoreTraceUP> trace = 192 IntelPTMultiCoreTrace::StartOnAllCores(request)) { 193 m_per_core_process_trace_up = std::move(*trace); 194 return Error::success(); 195 } else { 196 return trace.takeError(); 197 } 198 } else { 199 std::vector<lldb::tid_t> process_threads; 200 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 201 process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID()); 202 203 // per-thread process tracing 204 if (Expected<IntelPTPerThreadProcessTraceUP> trace = 205 IntelPTPerThreadProcessTrace::Start(request, process_threads)) { 206 m_per_thread_process_trace_up = std::move(trace.get()); 207 return Error::success(); 208 } else { 209 return trace.takeError(); 210 } 211 } 212 } else { 213 // individual thread tracing 214 if (m_per_core_process_trace_up) 215 return createStringError(inconvertibleErrorCode(), 216 "Process currently traced with per-core " 217 "tracing. Stop process tracing first"); 218 219 Error error = Error::success(); 220 for (int64_t tid : *request.tids) 221 error = joinErrors(std::move(error), 222 m_thread_traces.TraceStart(tid, request)); 223 return error; 224 } 225 } 226 227 void IntelPTCollector::OnProcessStateChanged(lldb::StateType state) { 228 if (m_per_core_process_trace_up) 229 m_per_core_process_trace_up->OnProcessStateChanged(state); 230 } 231 232 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 233 if (m_per_thread_process_trace_up) 234 return m_per_thread_process_trace_up->TraceStart(tid); 235 236 return Error::success(); 237 } 238 239 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 240 if (m_per_thread_process_trace_up && 241 m_per_thread_process_trace_up->TracesThread(tid)) 242 return m_per_thread_process_trace_up->TraceStop(tid); 243 else if (m_thread_traces.TracesThread(tid)) 244 return m_thread_traces.TraceStop(tid); 245 return Error::success(); 246 } 247 248 Expected<json::Value> IntelPTCollector::GetState() const { 249 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 250 if (!cpu_info) 251 return cpu_info.takeError(); 252 253 TraceGetStateResponse state; 254 state.process_binary_data.push_back( 255 {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); 256 257 std::vector<TraceThreadState> thread_states = 258 m_thread_traces.GetThreadStates(); 259 state.traced_threads.insert(state.traced_threads.end(), thread_states.begin(), 260 thread_states.end()); 261 262 if (m_per_thread_process_trace_up) { 263 thread_states = 264 m_per_thread_process_trace_up->GetThreadTraces().GetThreadStates(); 265 state.traced_threads.insert(state.traced_threads.end(), 266 thread_states.begin(), thread_states.end()); 267 } else if (m_per_core_process_trace_up) { 268 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 269 state.traced_threads.push_back( 270 TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}}); 271 272 state.cores.emplace(); 273 m_per_core_process_trace_up->ForEachCore( 274 [&](lldb::core_id_t core_id, 275 const IntelPTSingleBufferTrace &core_trace) { 276 state.cores->push_back({core_id, 277 {{IntelPTDataKinds::kTraceBuffer, 278 core_trace.GetTraceBufferSize()}}}); 279 }); 280 } 281 return toJSON(state); 282 } 283 284 Expected<IntelPTSingleBufferTrace &> 285 IntelPTCollector::GetTracedThread(lldb::tid_t tid) { 286 if (m_per_thread_process_trace_up && 287 m_per_thread_process_trace_up->TracesThread(tid)) 288 return m_per_thread_process_trace_up->GetThreadTraces().GetTracedThread( 289 tid); 290 return m_thread_traces.GetTracedThread(tid); 291 } 292 293 Expected<std::vector<uint8_t>> 294 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { 295 if (request.kind == IntelPTDataKinds::kTraceBuffer) { 296 if (Expected<IntelPTSingleBufferTrace &> trace = 297 GetTracedThread(*request.tid)) 298 return trace->GetTraceBuffer(request.offset, request.size); 299 else 300 return trace.takeError(); 301 } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) { 302 return GetProcfsCpuInfo(); 303 } 304 return createStringError(inconvertibleErrorCode(), 305 "Unsuported trace binary data kind: %s", 306 request.kind.c_str()); 307 } 308 309 void IntelPTCollector::ClearProcessTracing() { 310 m_per_thread_process_trace_up.reset(); 311 m_per_core_process_trace_up.reset(); 312 } 313 314 bool IntelPTCollector::IsSupported() { 315 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 316 return true; 317 } else { 318 llvm::consumeError(intel_pt_type.takeError()); 319 return false; 320 } 321 } 322 323 bool IntelPTCollector::IsProcessTracingEnabled() const { 324 return (bool)m_per_thread_process_trace_up || 325 (bool)m_per_core_process_trace_up; 326 } 327 328 void IntelPTCollector::Clear() { 329 ClearProcessTracing(); 330 m_thread_traces.Clear(); 331 } 332