122077627SJakob Johnson //===-- IntelPTCollector.cpp ------------------------------------------------===//
222077627SJakob Johnson //
322077627SJakob Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
422077627SJakob Johnson // See https://llvm.org/LICENSE.txt for license information.
522077627SJakob Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
622077627SJakob Johnson //
722077627SJakob Johnson //===----------------------------------------------------------------------===//
822077627SJakob Johnson
9e6c84f82SJakob Johnson #include "IntelPTCollector.h"
10e6c84f82SJakob Johnson
11e6c84f82SJakob Johnson #include "Perf.h"
125de0a3e9SWalter Erquinigo #include "Procfs.h"
13e6c84f82SJakob Johnson
14e6c84f82SJakob Johnson #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
15e6c84f82SJakob Johnson #include "lldb/Host/linux/Support.h"
16e6c84f82SJakob Johnson #include "lldb/Utility/StreamString.h"
1722077627SJakob Johnson
1822077627SJakob Johnson #include "llvm/ADT/StringRef.h"
1922077627SJakob Johnson #include "llvm/Support/Error.h"
2022077627SJakob Johnson #include "llvm/Support/MathExtras.h"
2122077627SJakob Johnson
22e6c84f82SJakob Johnson #include <algorithm>
23e6c84f82SJakob Johnson #include <cstddef>
24d30fd5c3SGaurav Gaur #include <fcntl.h>
25e6c84f82SJakob Johnson #include <fstream>
26e6c84f82SJakob Johnson #include <linux/perf_event.h>
27e6c84f82SJakob Johnson #include <sstream>
2822077627SJakob Johnson #include <sys/ioctl.h>
2922077627SJakob Johnson #include <sys/syscall.h>
3022077627SJakob Johnson
3122077627SJakob Johnson using namespace lldb;
3222077627SJakob Johnson using namespace lldb_private;
3322077627SJakob Johnson using namespace process_linux;
3422077627SJakob Johnson using namespace llvm;
3522077627SJakob Johnson
IntelPTCollector(NativeProcessProtocol & process)361f49714dSWalter Erquinigo IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process)
371f2d49a8SWalter Erquinigo : m_process(process) {}
381f2d49a8SWalter Erquinigo
391f2d49a8SWalter Erquinigo llvm::Expected<LinuxPerfZeroTscConversion &>
FetchPerfTscConversionParameters()401f2d49a8SWalter Erquinigo IntelPTCollector::FetchPerfTscConversionParameters() {
419b79187cSJakob Johnson if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
429b79187cSJakob Johnson LoadPerfTscConversionParameters())
4303cc58ffSWalter Erquinigo return *tsc_conversion;
449b79187cSJakob Johnson else
451f2d49a8SWalter Erquinigo return createStringError(inconvertibleErrorCode(),
461f2d49a8SWalter Erquinigo "Unable to load TSC to wall time conversion: %s",
471f2d49a8SWalter Erquinigo toString(tsc_conversion.takeError()).c_str());
481f2d49a8SWalter Erquinigo }
499b79187cSJakob Johnson
TraceStop(lldb::tid_t tid)5022077627SJakob Johnson Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
511f56f7fcSWalter Erquinigo if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
521f56f7fcSWalter Erquinigo return m_process_trace_up->TraceStop(tid);
5322077627SJakob Johnson return m_thread_traces.TraceStop(tid);
5422077627SJakob Johnson }
5522077627SJakob Johnson
TraceStop(const TraceStopRequest & request)5622077627SJakob Johnson Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
5722077627SJakob Johnson if (request.IsProcessTracing()) {
5822077627SJakob Johnson Clear();
5922077627SJakob Johnson return Error::success();
6022077627SJakob Johnson } else {
6122077627SJakob Johnson Error error = Error::success();
6222077627SJakob Johnson for (int64_t tid : *request.tids)
6322077627SJakob Johnson error = joinErrors(std::move(error),
6422077627SJakob Johnson TraceStop(static_cast<lldb::tid_t>(tid)));
6522077627SJakob Johnson return error;
6622077627SJakob Johnson }
6722077627SJakob Johnson }
6822077627SJakob Johnson
69d30fd5c3SGaurav Gaur /// \return
70d30fd5c3SGaurav Gaur /// some file descriptor in /sys/fs/ associated with the cgroup of the given
71d30fd5c3SGaurav Gaur /// pid, or \a llvm::None if the pid is not part of a cgroup.
GetCGroupFileDescriptor(lldb::pid_t pid)72d30fd5c3SGaurav Gaur static Optional<int> GetCGroupFileDescriptor(lldb::pid_t pid) {
73d30fd5c3SGaurav Gaur static Optional<int> fd;
74d30fd5c3SGaurav Gaur if (fd)
75d30fd5c3SGaurav Gaur return fd;
76d30fd5c3SGaurav Gaur
77d30fd5c3SGaurav Gaur std::ifstream ifile;
78d30fd5c3SGaurav Gaur ifile.open(formatv("/proc/{0}/cgroup", pid));
79d30fd5c3SGaurav Gaur if (!ifile)
80d30fd5c3SGaurav Gaur return None;
81d30fd5c3SGaurav Gaur
82d30fd5c3SGaurav Gaur std::string line;
83d30fd5c3SGaurav Gaur while (std::getline(ifile, line)) {
84d30fd5c3SGaurav Gaur if (line.find("0:") != 0)
85d30fd5c3SGaurav Gaur continue;
86d30fd5c3SGaurav Gaur
87d30fd5c3SGaurav Gaur std::string slice = line.substr(line.find_first_of("/"));
88d30fd5c3SGaurav Gaur if (slice.empty())
89d30fd5c3SGaurav Gaur return None;
90d30fd5c3SGaurav Gaur std::string cgroup_file = formatv("/sys/fs/cgroup/{0}", slice);
91d30fd5c3SGaurav Gaur // This cgroup should for the duration of the target, so we don't need to
92d30fd5c3SGaurav Gaur // invoke close ourselves.
93d30fd5c3SGaurav Gaur int maybe_fd = open(cgroup_file.c_str(), O_RDONLY);
94d30fd5c3SGaurav Gaur if (maybe_fd != -1) {
95d30fd5c3SGaurav Gaur fd = maybe_fd;
96d30fd5c3SGaurav Gaur return fd;
97d30fd5c3SGaurav Gaur }
98d30fd5c3SGaurav Gaur }
99d30fd5c3SGaurav Gaur return None;
100d30fd5c3SGaurav Gaur }
101d30fd5c3SGaurav Gaur
TraceStart(const TraceIntelPTStartRequest & request)1021f49714dSWalter Erquinigo Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) {
10322077627SJakob Johnson if (request.IsProcessTracing()) {
1041f56f7fcSWalter Erquinigo if (m_process_trace_up) {
10522077627SJakob Johnson return createStringError(
10622077627SJakob Johnson inconvertibleErrorCode(),
10722077627SJakob Johnson "Process currently traced. Stop process tracing first");
10822077627SJakob Johnson }
1096a5355e8SWalter Erquinigo if (request.IsPerCpuTracing()) {
1101f49714dSWalter Erquinigo if (m_thread_traces.GetTracedThreadsCount() > 0)
1111f49714dSWalter Erquinigo return createStringError(
1121f49714dSWalter Erquinigo inconvertibleErrorCode(),
1131f49714dSWalter Erquinigo "Threads currently traced. Stop tracing them first.");
1141f2d49a8SWalter Erquinigo // CPU tracing is useless if we can't convert tsc to nanos.
1151f2d49a8SWalter Erquinigo Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
1161f2d49a8SWalter Erquinigo FetchPerfTscConversionParameters();
1171f2d49a8SWalter Erquinigo if (!tsc_conversion)
1181f2d49a8SWalter Erquinigo return tsc_conversion.takeError();
1191f2d49a8SWalter Erquinigo
120d30fd5c3SGaurav Gaur // We force the enablement of TSCs, which is needed for correlating the
1211f2d49a8SWalter Erquinigo // cpu traces.
1221f2d49a8SWalter Erquinigo TraceIntelPTStartRequest effective_request = request;
1231f2d49a8SWalter Erquinigo effective_request.enable_tsc = true;
1241f2d49a8SWalter Erquinigo
125d30fd5c3SGaurav Gaur // We try to use cgroup filtering whenever possible
126d30fd5c3SGaurav Gaur Optional<int> cgroup_fd;
127*3c849d0aSFangrui Song if (!request.disable_cgroup_filtering.value_or(false))
128d30fd5c3SGaurav Gaur cgroup_fd = GetCGroupFileDescriptor(m_process.GetID());
129d30fd5c3SGaurav Gaur
1301f56f7fcSWalter Erquinigo if (Expected<IntelPTProcessTraceUP> trace =
1311f2d49a8SWalter Erquinigo IntelPTMultiCoreTrace::StartOnAllCores(effective_request,
132d30fd5c3SGaurav Gaur m_process, cgroup_fd)) {
1331f56f7fcSWalter Erquinigo m_process_trace_up = std::move(*trace);
1341f49714dSWalter Erquinigo return Error::success();
13522077627SJakob Johnson } else {
1361f49714dSWalter Erquinigo return trace.takeError();
1371f49714dSWalter Erquinigo }
1381f49714dSWalter Erquinigo } else {
1391f49714dSWalter Erquinigo std::vector<lldb::tid_t> process_threads;
140e095cddbSMichał Górny for (NativeThreadProtocol &thread : m_process.Threads())
141e095cddbSMichał Górny process_threads.push_back(thread.GetID());
1421f49714dSWalter Erquinigo
1431f49714dSWalter Erquinigo // per-thread process tracing
1441f56f7fcSWalter Erquinigo if (Expected<IntelPTProcessTraceUP> trace =
1451f49714dSWalter Erquinigo IntelPTPerThreadProcessTrace::Start(request, process_threads)) {
1461f56f7fcSWalter Erquinigo m_process_trace_up = std::move(trace.get());
1471f49714dSWalter Erquinigo return Error::success();
1481f49714dSWalter Erquinigo } else {
1491f49714dSWalter Erquinigo return trace.takeError();
1501f49714dSWalter Erquinigo }
1511f49714dSWalter Erquinigo }
1521f49714dSWalter Erquinigo } else {
1531f49714dSWalter Erquinigo // individual thread tracing
15422077627SJakob Johnson Error error = Error::success();
1551f56f7fcSWalter Erquinigo for (int64_t tid : *request.tids) {
1561f56f7fcSWalter Erquinigo if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
1571f56f7fcSWalter Erquinigo error = joinErrors(
1581f56f7fcSWalter Erquinigo std::move(error),
1591f56f7fcSWalter Erquinigo createStringError(inconvertibleErrorCode(),
1601f56f7fcSWalter Erquinigo formatv("Thread with tid {0} is currently "
1611f56f7fcSWalter Erquinigo "traced. Stop tracing it first.",
1621f56f7fcSWalter Erquinigo tid)
1631f56f7fcSWalter Erquinigo .str()
1641f56f7fcSWalter Erquinigo .c_str()));
1651f56f7fcSWalter Erquinigo else
16622077627SJakob Johnson error = joinErrors(std::move(error),
16722077627SJakob Johnson m_thread_traces.TraceStart(tid, request));
1681f56f7fcSWalter Erquinigo }
16922077627SJakob Johnson return error;
17022077627SJakob Johnson }
17122077627SJakob Johnson }
17222077627SJakob Johnson
ProcessWillResume()173a7582059SWalter Erquinigo void IntelPTCollector::ProcessWillResume() {
1741f56f7fcSWalter Erquinigo if (m_process_trace_up)
175a7582059SWalter Erquinigo m_process_trace_up->ProcessWillResume();
176a7582059SWalter Erquinigo }
177a7582059SWalter Erquinigo
ProcessDidStop()178a7582059SWalter Erquinigo void IntelPTCollector::ProcessDidStop() {
179a7582059SWalter Erquinigo if (m_process_trace_up)
180a7582059SWalter Erquinigo m_process_trace_up->ProcessDidStop();
1811637545fSWalter Erquinigo }
1821637545fSWalter Erquinigo
OnThreadCreated(lldb::tid_t tid)18322077627SJakob Johnson Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
1841f56f7fcSWalter Erquinigo if (m_process_trace_up)
1851f56f7fcSWalter Erquinigo return m_process_trace_up->TraceStart(tid);
1861f49714dSWalter Erquinigo
18722077627SJakob Johnson return Error::success();
18822077627SJakob Johnson }
18922077627SJakob Johnson
OnThreadDestroyed(lldb::tid_t tid)19022077627SJakob Johnson Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
1911f56f7fcSWalter Erquinigo if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
1921f56f7fcSWalter Erquinigo return m_process_trace_up->TraceStop(tid);
19322077627SJakob Johnson else if (m_thread_traces.TracesThread(tid))
19422077627SJakob Johnson return m_thread_traces.TraceStop(tid);
19522077627SJakob Johnson return Error::success();
19622077627SJakob Johnson }
19722077627SJakob Johnson
GetState()1981188faa7SWalter Erquinigo Expected<json::Value> IntelPTCollector::GetState() {
1995de0a3e9SWalter Erquinigo Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo();
20022077627SJakob Johnson if (!cpu_info)
20122077627SJakob Johnson return cpu_info.takeError();
20222077627SJakob Johnson
2031f2d49a8SWalter Erquinigo TraceIntelPTGetStateResponse state;
2041f56f7fcSWalter Erquinigo if (m_process_trace_up)
2051f56f7fcSWalter Erquinigo state = m_process_trace_up->GetState();
2061f56f7fcSWalter Erquinigo
2071f49714dSWalter Erquinigo state.process_binary_data.push_back(
20826d83a43SWalter Erquinigo {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()});
20922077627SJakob Johnson
2101188faa7SWalter Erquinigo m_thread_traces.ForEachThread(
2111188faa7SWalter Erquinigo [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) {
2126a5355e8SWalter Erquinigo state.traced_threads.push_back(
2136a5355e8SWalter Erquinigo {tid,
2146a5355e8SWalter Erquinigo {{IntelPTDataKinds::kIptTrace, thread_trace.GetIptTraceSize()}}});
2151188faa7SWalter Erquinigo });
2161f2d49a8SWalter Erquinigo
2171f2d49a8SWalter Erquinigo if (Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
2181f2d49a8SWalter Erquinigo FetchPerfTscConversionParameters())
2191f2d49a8SWalter Erquinigo state.tsc_perf_zero_conversion = *tsc_conversion;
2201f2d49a8SWalter Erquinigo else
221fc5ef57cSWalter Erquinigo state.AddWarning(toString(tsc_conversion.takeError()));
22222077627SJakob Johnson return toJSON(state);
22322077627SJakob Johnson }
22422077627SJakob Johnson
22522077627SJakob Johnson Expected<std::vector<uint8_t>>
GetBinaryData(const TraceGetBinaryDataRequest & request)2261637545fSWalter Erquinigo IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) {
227fc5ef57cSWalter Erquinigo if (request.kind == IntelPTDataKinds::kProcFsCpuInfo)
228fc5ef57cSWalter Erquinigo return GetProcfsCpuInfo();
229fc5ef57cSWalter Erquinigo
230fc5ef57cSWalter Erquinigo if (m_process_trace_up) {
231fc5ef57cSWalter Erquinigo Expected<Optional<std::vector<uint8_t>>> data =
232fc5ef57cSWalter Erquinigo m_process_trace_up->TryGetBinaryData(request);
233fc5ef57cSWalter Erquinigo if (!data)
234fc5ef57cSWalter Erquinigo return data.takeError();
235fc5ef57cSWalter Erquinigo if (*data)
236fc5ef57cSWalter Erquinigo return **data;
237fc5ef57cSWalter Erquinigo }
238fc5ef57cSWalter Erquinigo
239fc5ef57cSWalter Erquinigo {
240fc5ef57cSWalter Erquinigo Expected<Optional<std::vector<uint8_t>>> data =
241fc5ef57cSWalter Erquinigo m_thread_traces.TryGetBinaryData(request);
242fc5ef57cSWalter Erquinigo if (!data)
243fc5ef57cSWalter Erquinigo return data.takeError();
244fc5ef57cSWalter Erquinigo if (*data)
245fc5ef57cSWalter Erquinigo return **data;
246fc5ef57cSWalter Erquinigo }
247fc5ef57cSWalter Erquinigo
2481f56f7fcSWalter Erquinigo return createStringError(
2491f56f7fcSWalter Erquinigo inconvertibleErrorCode(),
2506a5355e8SWalter Erquinigo formatv("Can't fetch data kind {0} for cpu_id {1}, tid {2} and "
251fc5ef57cSWalter Erquinigo "\"process tracing\" mode {3}",
2526a5355e8SWalter Erquinigo request.kind, request.cpu_id, request.tid,
253fc5ef57cSWalter Erquinigo m_process_trace_up ? "enabled" : "not enabled"));
25422077627SJakob Johnson }
25522077627SJakob Johnson
IsSupported()25622077627SJakob Johnson bool IntelPTCollector::IsSupported() {
2577b73de9eSWalter Erquinigo if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
2587b73de9eSWalter Erquinigo return true;
2597b73de9eSWalter Erquinigo } else {
26022077627SJakob Johnson llvm::consumeError(intel_pt_type.takeError());
26122077627SJakob Johnson return false;
26222077627SJakob Johnson }
26322077627SJakob Johnson }
26422077627SJakob Johnson
Clear()26522077627SJakob Johnson void IntelPTCollector::Clear() {
2661f56f7fcSWalter Erquinigo m_process_trace_up.reset();
26722077627SJakob Johnson m_thread_traces.Clear();
26822077627SJakob Johnson }
269