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