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 38 llvm::Expected<LinuxPerfZeroTscConversion &> 39 IntelPTCollector::FetchPerfTscConversionParameters() { 40 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = 41 LoadPerfTscConversionParameters()) 42 return *tsc_conversion; 43 else 44 return createStringError(inconvertibleErrorCode(), 45 "Unable to load TSC to wall time conversion: %s", 46 toString(tsc_conversion.takeError()).c_str()); 47 } 48 49 Error IntelPTCollector::TraceStop(lldb::tid_t tid) { 50 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 51 return m_process_trace_up->TraceStop(tid); 52 return m_thread_traces.TraceStop(tid); 53 } 54 55 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { 56 if (request.IsProcessTracing()) { 57 Clear(); 58 return Error::success(); 59 } else { 60 Error error = Error::success(); 61 for (int64_t tid : *request.tids) 62 error = joinErrors(std::move(error), 63 TraceStop(static_cast<lldb::tid_t>(tid))); 64 return error; 65 } 66 } 67 68 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { 69 if (request.IsProcessTracing()) { 70 if (m_process_trace_up) { 71 return createStringError( 72 inconvertibleErrorCode(), 73 "Process currently traced. Stop process tracing first"); 74 } 75 if (request.IsPerCpuTracing()) { 76 if (m_thread_traces.GetTracedThreadsCount() > 0) 77 return createStringError( 78 inconvertibleErrorCode(), 79 "Threads currently traced. Stop tracing them first."); 80 // CPU tracing is useless if we can't convert tsc to nanos. 81 Expected<LinuxPerfZeroTscConversion &> tsc_conversion = 82 FetchPerfTscConversionParameters(); 83 if (!tsc_conversion) 84 return tsc_conversion.takeError(); 85 86 // We force the enabledment of TSCs, which is needed for correlating the 87 // cpu traces. 88 TraceIntelPTStartRequest effective_request = request; 89 effective_request.enable_tsc = true; 90 91 if (Expected<IntelPTProcessTraceUP> trace = 92 IntelPTMultiCoreTrace::StartOnAllCores(effective_request, 93 m_process)) { 94 m_process_trace_up = std::move(*trace); 95 return Error::success(); 96 } else { 97 return trace.takeError(); 98 } 99 } else { 100 std::vector<lldb::tid_t> process_threads; 101 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 102 process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID()); 103 104 // per-thread process tracing 105 if (Expected<IntelPTProcessTraceUP> trace = 106 IntelPTPerThreadProcessTrace::Start(request, process_threads)) { 107 m_process_trace_up = std::move(trace.get()); 108 return Error::success(); 109 } else { 110 return trace.takeError(); 111 } 112 } 113 } else { 114 // individual thread tracing 115 Error error = Error::success(); 116 for (int64_t tid : *request.tids) { 117 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 118 error = joinErrors( 119 std::move(error), 120 createStringError(inconvertibleErrorCode(), 121 formatv("Thread with tid {0} is currently " 122 "traced. Stop tracing it first.", 123 tid) 124 .str() 125 .c_str())); 126 else 127 error = joinErrors(std::move(error), 128 m_thread_traces.TraceStart(tid, request)); 129 } 130 return error; 131 } 132 } 133 134 void IntelPTCollector::ProcessWillResume() { 135 if (m_process_trace_up) 136 m_process_trace_up->ProcessWillResume(); 137 } 138 139 void IntelPTCollector::ProcessDidStop() { 140 if (m_process_trace_up) 141 m_process_trace_up->ProcessDidStop(); 142 } 143 144 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 145 if (m_process_trace_up) 146 return m_process_trace_up->TraceStart(tid); 147 148 return Error::success(); 149 } 150 151 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 152 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 153 return m_process_trace_up->TraceStop(tid); 154 else if (m_thread_traces.TracesThread(tid)) 155 return m_thread_traces.TraceStop(tid); 156 return Error::success(); 157 } 158 159 Expected<json::Value> IntelPTCollector::GetState() { 160 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 161 if (!cpu_info) 162 return cpu_info.takeError(); 163 164 TraceIntelPTGetStateResponse state; 165 if (m_process_trace_up) 166 state = m_process_trace_up->GetState(); 167 168 state.process_binary_data.push_back( 169 {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); 170 171 m_thread_traces.ForEachThread( 172 [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { 173 state.traced_threads.push_back( 174 {tid, 175 {{IntelPTDataKinds::kIptTrace, thread_trace.GetIptTraceSize()}}}); 176 }); 177 178 if (Expected<LinuxPerfZeroTscConversion &> tsc_conversion = 179 FetchPerfTscConversionParameters()) 180 state.tsc_perf_zero_conversion = *tsc_conversion; 181 else 182 state.AddWarning(toString(tsc_conversion.takeError())); 183 return toJSON(state); 184 } 185 186 Expected<std::vector<uint8_t>> 187 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { 188 if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) 189 return GetProcfsCpuInfo(); 190 191 if (m_process_trace_up) { 192 Expected<Optional<std::vector<uint8_t>>> data = 193 m_process_trace_up->TryGetBinaryData(request); 194 if (!data) 195 return data.takeError(); 196 if (*data) 197 return **data; 198 } 199 200 { 201 Expected<Optional<std::vector<uint8_t>>> data = 202 m_thread_traces.TryGetBinaryData(request); 203 if (!data) 204 return data.takeError(); 205 if (*data) 206 return **data; 207 } 208 209 return createStringError( 210 inconvertibleErrorCode(), 211 formatv("Can't fetch data kind {0} for cpu_id {1}, tid {2} and " 212 "\"process tracing\" mode {3}", 213 request.kind, request.cpu_id, request.tid, 214 m_process_trace_up ? "enabled" : "not enabled")); 215 } 216 217 bool IntelPTCollector::IsSupported() { 218 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 219 return true; 220 } else { 221 llvm::consumeError(intel_pt_type.takeError()); 222 return false; 223 } 224 } 225 226 void IntelPTCollector::Clear() { 227 m_process_trace_up.reset(); 228 m_thread_traces.Clear(); 229 } 230