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 #include "Perf.h" 11 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 12 #include "Procfs.h" 13 #include "lldb/Host/linux/Support.h" 14 #include "lldb/Utility/StreamString.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/MathExtras.h" 18 #include <algorithm> 19 #include <cstddef> 20 #include <fcntl.h> 21 #include <fstream> 22 #include <linux/perf_event.h> 23 #include <optional> 24 #include <sstream> 25 #include <sys/ioctl.h> 26 #include <sys/syscall.h> 27 28 using namespace lldb; 29 using namespace lldb_private; 30 using namespace process_linux; 31 using namespace llvm; 32 33 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process) 34 : m_process(process) {} 35 36 llvm::Expected<LinuxPerfZeroTscConversion &> 37 IntelPTCollector::FetchPerfTscConversionParameters() { 38 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = 39 LoadPerfTscConversionParameters()) 40 return *tsc_conversion; 41 else 42 return createStringError(inconvertibleErrorCode(), 43 "Unable to load TSC to wall time conversion: %s", 44 toString(tsc_conversion.takeError()).c_str()); 45 } 46 47 Error IntelPTCollector::TraceStop(lldb::tid_t tid) { 48 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 49 return m_process_trace_up->TraceStop(tid); 50 return m_thread_traces.TraceStop(tid); 51 } 52 53 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { 54 if (request.IsProcessTracing()) { 55 Clear(); 56 return Error::success(); 57 } else { 58 Error error = Error::success(); 59 for (int64_t tid : *request.tids) 60 error = joinErrors(std::move(error), 61 TraceStop(static_cast<lldb::tid_t>(tid))); 62 return error; 63 } 64 } 65 66 /// \return 67 /// some file descriptor in /sys/fs/ associated with the cgroup of the given 68 /// pid, or \a std::nullopt if the pid is not part of a cgroup. 69 static std::optional<int> GetCGroupFileDescriptor(lldb::pid_t pid) { 70 static std::optional<int> fd; 71 if (fd) 72 return fd; 73 74 std::ifstream ifile; 75 ifile.open(formatv("/proc/{0}/cgroup", pid)); 76 if (!ifile) 77 return std::nullopt; 78 79 std::string line; 80 while (std::getline(ifile, line)) { 81 if (line.find("0:") != 0) 82 continue; 83 84 std::string slice = line.substr(line.find_first_of("/")); 85 if (slice.empty()) 86 return std::nullopt; 87 std::string cgroup_file = formatv("/sys/fs/cgroup/{0}", slice); 88 // This cgroup should for the duration of the target, so we don't need to 89 // invoke close ourselves. 90 int maybe_fd = open(cgroup_file.c_str(), O_RDONLY); 91 if (maybe_fd != -1) { 92 fd = maybe_fd; 93 return fd; 94 } 95 } 96 return std::nullopt; 97 } 98 99 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { 100 if (request.IsProcessTracing()) { 101 if (m_process_trace_up) { 102 return createStringError( 103 inconvertibleErrorCode(), 104 "Process currently traced. Stop process tracing first"); 105 } 106 if (request.IsPerCpuTracing()) { 107 if (m_thread_traces.GetTracedThreadsCount() > 0) 108 return createStringError( 109 inconvertibleErrorCode(), 110 "Threads currently traced. Stop tracing them first."); 111 // CPU tracing is useless if we can't convert tsc to nanos. 112 Expected<LinuxPerfZeroTscConversion &> tsc_conversion = 113 FetchPerfTscConversionParameters(); 114 if (!tsc_conversion) 115 return tsc_conversion.takeError(); 116 117 // We force the enablement of TSCs, which is needed for correlating the 118 // cpu traces. 119 TraceIntelPTStartRequest effective_request = request; 120 effective_request.enable_tsc = true; 121 122 // We try to use cgroup filtering whenever possible 123 std::optional<int> cgroup_fd; 124 if (!request.disable_cgroup_filtering.value_or(false)) 125 cgroup_fd = GetCGroupFileDescriptor(m_process.GetID()); 126 127 if (Expected<IntelPTProcessTraceUP> trace = 128 IntelPTMultiCoreTrace::StartOnAllCores(effective_request, 129 m_process, cgroup_fd)) { 130 m_process_trace_up = std::move(*trace); 131 return Error::success(); 132 } else { 133 return trace.takeError(); 134 } 135 } else { 136 std::vector<lldb::tid_t> process_threads; 137 for (NativeThreadProtocol &thread : m_process.Threads()) 138 process_threads.push_back(thread.GetID()); 139 140 // per-thread process tracing 141 if (Expected<IntelPTProcessTraceUP> trace = 142 IntelPTPerThreadProcessTrace::Start(request, process_threads)) { 143 m_process_trace_up = std::move(trace.get()); 144 return Error::success(); 145 } else { 146 return trace.takeError(); 147 } 148 } 149 } else { 150 // individual thread tracing 151 Error error = Error::success(); 152 for (int64_t tid : *request.tids) { 153 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 154 error = joinErrors( 155 std::move(error), 156 createStringError(inconvertibleErrorCode(), 157 formatv("Thread with tid {0} is currently " 158 "traced. Stop tracing it first.", 159 tid) 160 .str() 161 .c_str())); 162 else 163 error = joinErrors(std::move(error), 164 m_thread_traces.TraceStart(tid, request)); 165 } 166 return error; 167 } 168 } 169 170 void IntelPTCollector::ProcessWillResume() { 171 if (m_process_trace_up) 172 m_process_trace_up->ProcessWillResume(); 173 } 174 175 void IntelPTCollector::ProcessDidStop() { 176 if (m_process_trace_up) 177 m_process_trace_up->ProcessDidStop(); 178 } 179 180 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 181 if (m_process_trace_up) 182 return m_process_trace_up->TraceStart(tid); 183 184 return Error::success(); 185 } 186 187 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 188 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 189 return m_process_trace_up->TraceStop(tid); 190 else if (m_thread_traces.TracesThread(tid)) 191 return m_thread_traces.TraceStop(tid); 192 return Error::success(); 193 } 194 195 Expected<json::Value> IntelPTCollector::GetState() { 196 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 197 if (!cpu_info) 198 return cpu_info.takeError(); 199 200 TraceIntelPTGetStateResponse state; 201 if (m_process_trace_up) 202 state = m_process_trace_up->GetState(); 203 204 state.process_binary_data.push_back( 205 {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); 206 207 m_thread_traces.ForEachThread( 208 [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { 209 state.traced_threads.push_back( 210 {tid, 211 {{IntelPTDataKinds::kIptTrace, thread_trace.GetIptTraceSize()}}}); 212 }); 213 214 if (Expected<LinuxPerfZeroTscConversion &> tsc_conversion = 215 FetchPerfTscConversionParameters()) 216 state.tsc_perf_zero_conversion = *tsc_conversion; 217 else 218 state.AddWarning(toString(tsc_conversion.takeError())); 219 return toJSON(state); 220 } 221 222 Expected<std::vector<uint8_t>> 223 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { 224 if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) 225 return GetProcfsCpuInfo(); 226 227 if (m_process_trace_up) { 228 Expected<std::optional<std::vector<uint8_t>>> data = 229 m_process_trace_up->TryGetBinaryData(request); 230 if (!data) 231 return data.takeError(); 232 if (*data) 233 return **data; 234 } 235 236 { 237 Expected<std::optional<std::vector<uint8_t>>> data = 238 m_thread_traces.TryGetBinaryData(request); 239 if (!data) 240 return data.takeError(); 241 if (*data) 242 return **data; 243 } 244 245 return createStringError( 246 inconvertibleErrorCode(), 247 formatv("Can't fetch data kind {0} for cpu_id {1}, tid {2} and " 248 "\"process tracing\" mode {3}", 249 request.kind, request.cpu_id, request.tid, 250 m_process_trace_up ? "enabled" : "not enabled")); 251 } 252 253 bool IntelPTCollector::IsSupported() { 254 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 255 return true; 256 } else { 257 llvm::consumeError(intel_pt_type.takeError()); 258 return false; 259 } 260 } 261 262 void IntelPTCollector::Clear() { 263 m_process_trace_up.reset(); 264 m_thread_traces.Clear(); 265 } 266