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