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 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process) 36 : m_process(process) { 37 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = 38 LoadPerfTscConversionParameters()) 39 m_tsc_conversion = 40 std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion); 41 else 42 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(), 43 "unable to load TSC to wall time conversion: {0}"); 44 } 45 46 Error IntelPTCollector::TraceStop(lldb::tid_t tid) { 47 if (m_per_thread_process_trace_up && 48 m_per_thread_process_trace_up->TracesThread(tid)) 49 return m_per_thread_process_trace_up->TraceStop(tid); 50 else if (m_per_core_process_trace_up) 51 return createStringError(inconvertibleErrorCode(), 52 "Can't stop tracing an individual thread when " 53 "per-core process tracing is enabled."); 54 return m_thread_traces.TraceStop(tid); 55 } 56 57 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { 58 if (request.IsProcessTracing()) { 59 Clear(); 60 return Error::success(); 61 } else { 62 Error error = Error::success(); 63 for (int64_t tid : *request.tids) 64 error = joinErrors(std::move(error), 65 TraceStop(static_cast<lldb::tid_t>(tid))); 66 return error; 67 } 68 } 69 70 Expected<IntelPTPerThreadProcessTraceUP> 71 IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request, 72 ArrayRef<lldb::tid_t> current_tids) { 73 IntelPTPerThreadProcessTraceUP trace( 74 new IntelPTPerThreadProcessTrace(request)); 75 76 Error error = Error::success(); 77 for (lldb::tid_t tid : current_tids) 78 error = joinErrors(std::move(error), trace->TraceStart(tid)); 79 if (error) 80 return std::move(error); 81 return std::move(trace); 82 } 83 84 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { 85 if (request.IsProcessTracing()) { 86 if (IsProcessTracingEnabled()) { 87 return createStringError( 88 inconvertibleErrorCode(), 89 "Process currently traced. Stop process tracing first"); 90 } 91 if (request.IsPerCoreTracing()) { 92 if (m_thread_traces.GetTracedThreadsCount() > 0) 93 return createStringError( 94 inconvertibleErrorCode(), 95 "Threads currently traced. Stop tracing them first."); 96 if (Expected<IntelPTMultiCoreTraceUP> trace = 97 IntelPTMultiCoreTrace::StartOnAllCores(request)) { 98 m_per_core_process_trace_up = std::move(*trace); 99 return Error::success(); 100 } else { 101 return trace.takeError(); 102 } 103 } else { 104 std::vector<lldb::tid_t> process_threads; 105 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 106 process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID()); 107 108 // per-thread process tracing 109 if (Expected<IntelPTPerThreadProcessTraceUP> trace = 110 IntelPTPerThreadProcessTrace::Start(request, process_threads)) { 111 m_per_thread_process_trace_up = std::move(trace.get()); 112 return Error::success(); 113 } else { 114 return trace.takeError(); 115 } 116 } 117 } else { 118 // individual thread tracing 119 if (m_per_core_process_trace_up) 120 return createStringError(inconvertibleErrorCode(), 121 "Process currently traced with per-core " 122 "tracing. Stop process tracing first"); 123 124 Error error = Error::success(); 125 for (int64_t tid : *request.tids) 126 error = joinErrors(std::move(error), 127 m_thread_traces.TraceStart(tid, request)); 128 return error; 129 } 130 } 131 132 void IntelPTCollector::OnProcessStateChanged(lldb::StateType state) { 133 if (m_per_core_process_trace_up) 134 m_per_core_process_trace_up->OnProcessStateChanged(state); 135 } 136 137 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 138 if (m_per_thread_process_trace_up) 139 return m_per_thread_process_trace_up->TraceStart(tid); 140 141 return Error::success(); 142 } 143 144 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 145 if (m_per_thread_process_trace_up && 146 m_per_thread_process_trace_up->TracesThread(tid)) 147 return m_per_thread_process_trace_up->TraceStop(tid); 148 else if (m_thread_traces.TracesThread(tid)) 149 return m_thread_traces.TraceStop(tid); 150 return Error::success(); 151 } 152 153 Expected<json::Value> IntelPTCollector::GetState() { 154 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 155 if (!cpu_info) 156 return cpu_info.takeError(); 157 158 TraceGetStateResponse state; 159 state.process_binary_data.push_back( 160 {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); 161 162 m_thread_traces.ForEachThread( 163 [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { 164 state.traced_threads.push_back({tid, 165 {{IntelPTDataKinds::kTraceBuffer, 166 thread_trace.GetTraceBufferSize()}}}); 167 }); 168 169 if (m_per_thread_process_trace_up) { 170 m_per_thread_process_trace_up->GetThreadTraces().ForEachThread( 171 [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { 172 state.traced_threads.push_back( 173 {tid, 174 {{IntelPTDataKinds::kTraceBuffer, 175 thread_trace.GetTraceBufferSize()}}}); 176 }); 177 } 178 179 if (m_per_core_process_trace_up) { 180 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 181 state.traced_threads.push_back( 182 TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}}); 183 184 state.cores.emplace(); 185 m_per_core_process_trace_up->ForEachCore( 186 [&](lldb::core_id_t core_id, 187 const IntelPTSingleBufferTrace &core_trace) { 188 state.cores->push_back({core_id, 189 {{IntelPTDataKinds::kTraceBuffer, 190 core_trace.GetTraceBufferSize()}}}); 191 }); 192 } 193 return toJSON(state); 194 } 195 196 Expected<IntelPTSingleBufferTrace &> 197 IntelPTCollector::GetTracedThread(lldb::tid_t tid) { 198 if (m_per_thread_process_trace_up && 199 m_per_thread_process_trace_up->TracesThread(tid)) 200 return m_per_thread_process_trace_up->GetThreadTraces().GetTracedThread( 201 tid); 202 return m_thread_traces.GetTracedThread(tid); 203 } 204 205 Expected<std::vector<uint8_t>> 206 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { 207 if (request.kind == IntelPTDataKinds::kTraceBuffer) { 208 if (Expected<IntelPTSingleBufferTrace &> trace = 209 GetTracedThread(*request.tid)) 210 return trace->GetTraceBuffer(request.offset, request.size); 211 else 212 return trace.takeError(); 213 } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) { 214 return GetProcfsCpuInfo(); 215 } 216 return createStringError(inconvertibleErrorCode(), 217 "Unsuported trace binary data kind: %s", 218 request.kind.c_str()); 219 } 220 221 void IntelPTCollector::ClearProcessTracing() { 222 m_per_thread_process_trace_up.reset(); 223 m_per_core_process_trace_up.reset(); 224 } 225 226 bool IntelPTCollector::IsSupported() { 227 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 228 return true; 229 } else { 230 llvm::consumeError(intel_pt_type.takeError()); 231 return false; 232 } 233 } 234 235 bool IntelPTCollector::IsProcessTracingEnabled() const { 236 return (bool)m_per_thread_process_trace_up || 237 (bool)m_per_core_process_trace_up; 238 } 239 240 void IntelPTCollector::Clear() { 241 ClearProcessTracing(); 242 m_thread_traces.Clear(); 243 } 244