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); 59 if (!trace_up) 60 return trace_up.takeError(); 61 62 m_total_buffer_size += (*trace_up)->GetTraceBufferSize(); 63 m_thread_traces.try_emplace(tid, std::move(*trace_up)); 64 return Error::success(); 65 } 66 67 size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const { 68 return m_total_buffer_size; 69 } 70 71 std::vector<TraceThreadState> 72 IntelPTThreadTraceCollection::GetThreadStates() const { 73 std::vector<TraceThreadState> states; 74 for (const auto &it : m_thread_traces) 75 states.push_back({static_cast<int64_t>(it.first), 76 {TraceBinaryData{IntelPTDataKinds::kTraceBuffer, 77 static_cast<int64_t>( 78 it.second->GetTraceBufferSize())}}}); 79 return states; 80 } 81 82 Expected<const IntelPTSingleBufferTrace &> 83 IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) const { 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 /// IntelPTProcessTrace 97 98 bool IntelPTProcessTrace::TracesThread(lldb::tid_t tid) const { 99 return m_thread_traces.TracesThread(tid); 100 } 101 102 Error IntelPTProcessTrace::TraceStop(lldb::tid_t tid) { 103 return m_thread_traces.TraceStop(tid); 104 } 105 106 Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) { 107 if (m_thread_traces.GetTotalBufferSize() + 108 m_tracing_params.trace_buffer_size > 109 static_cast<size_t>(*m_tracing_params.process_buffer_size_limit)) 110 return createStringError( 111 inconvertibleErrorCode(), 112 "Thread %" PRIu64 " can't be traced as the process trace size limit " 113 "has been reached. Consider retracing with a higher " 114 "limit.", 115 tid); 116 117 return m_thread_traces.TraceStart(tid, m_tracing_params); 118 } 119 120 const IntelPTThreadTraceCollection & 121 IntelPTProcessTrace::GetThreadTraces() const { 122 return m_thread_traces; 123 } 124 125 /// IntelPTCollector 126 127 IntelPTCollector::IntelPTCollector() { 128 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = 129 LoadPerfTscConversionParameters()) 130 m_tsc_conversion = 131 std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion); 132 else 133 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(), 134 "unable to load TSC to wall time conversion: {0}"); 135 } 136 137 Error IntelPTCollector::TraceStop(lldb::tid_t tid) { 138 if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) 139 return m_process_trace->TraceStop(tid); 140 return m_thread_traces.TraceStop(tid); 141 } 142 143 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { 144 if (request.IsProcessTracing()) { 145 Clear(); 146 return Error::success(); 147 } else { 148 Error error = Error::success(); 149 for (int64_t tid : *request.tids) 150 error = joinErrors(std::move(error), 151 TraceStop(static_cast<lldb::tid_t>(tid))); 152 return error; 153 } 154 } 155 156 Error IntelPTCollector::TraceStart( 157 const TraceIntelPTStartRequest &request, 158 const std::vector<lldb::tid_t> &process_threads) { 159 if (request.IsProcessTracing()) { 160 if (IsProcessTracingEnabled()) { 161 return createStringError( 162 inconvertibleErrorCode(), 163 "Process currently traced. Stop process tracing first"); 164 } 165 if (request.per_core_tracing.getValueOr(false)) { 166 return createStringError(inconvertibleErrorCode(), 167 "Per-core tracing is not supported."); 168 } 169 m_process_trace = IntelPTProcessTrace(request); 170 171 Error error = Error::success(); 172 for (lldb::tid_t tid : process_threads) 173 error = joinErrors(std::move(error), m_process_trace->TraceStart(tid)); 174 return error; 175 } else { 176 Error error = Error::success(); 177 for (int64_t tid : *request.tids) 178 error = joinErrors(std::move(error), 179 m_thread_traces.TraceStart(tid, request)); 180 return error; 181 } 182 } 183 184 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 185 if (!IsProcessTracingEnabled()) 186 return Error::success(); 187 return m_process_trace->TraceStart(tid); 188 } 189 190 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 191 if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) 192 return m_process_trace->TraceStop(tid); 193 else if (m_thread_traces.TracesThread(tid)) 194 return m_thread_traces.TraceStop(tid); 195 return Error::success(); 196 } 197 198 Expected<json::Value> IntelPTCollector::GetState() const { 199 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 200 if (!cpu_info) 201 return cpu_info.takeError(); 202 203 TraceGetStateResponse state; 204 state.processBinaryData.push_back({IntelPTDataKinds::kProcFsCpuInfo, 205 static_cast<int64_t>(cpu_info->size())}); 206 207 std::vector<TraceThreadState> thread_states = 208 m_thread_traces.GetThreadStates(); 209 state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), 210 thread_states.end()); 211 212 if (IsProcessTracingEnabled()) { 213 thread_states = m_process_trace->GetThreadTraces().GetThreadStates(); 214 state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), 215 thread_states.end()); 216 } 217 return toJSON(state); 218 } 219 220 Expected<const IntelPTSingleBufferTrace &> 221 IntelPTCollector::GetTracedThread(lldb::tid_t tid) const { 222 if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) 223 return m_process_trace->GetThreadTraces().GetTracedThread(tid); 224 return m_thread_traces.GetTracedThread(tid); 225 } 226 227 Expected<std::vector<uint8_t>> 228 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const { 229 if (request.kind == IntelPTDataKinds::kTraceBuffer) { 230 if (Expected<const IntelPTSingleBufferTrace &> trace = 231 GetTracedThread(*request.tid)) 232 return trace->GetTraceBuffer(request.offset, request.size); 233 else 234 return trace.takeError(); 235 } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) { 236 return GetProcfsCpuInfo(); 237 } 238 return createStringError(inconvertibleErrorCode(), 239 "Unsuported trace binary data kind: %s", 240 request.kind.c_str()); 241 } 242 243 void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; } 244 245 bool IntelPTCollector::IsSupported() { 246 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 247 return true; 248 } else { 249 llvm::consumeError(intel_pt_type.takeError()); 250 return false; 251 } 252 } 253 254 bool IntelPTCollector::IsProcessTracingEnabled() const { 255 return (bool)m_process_trace; 256 } 257 258 void IntelPTCollector::Clear() { 259 ClearProcessTracing(); 260 m_thread_traces.Clear(); 261 } 262