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