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