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/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 else
41 return createStringError(EC: inconvertibleErrorCode(),
42 Fmt: "Unable to load TSC to wall time conversion: %s",
43 Vals: toString(E: 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(E1: std::move(error),
60 E2: TraceStop(tid: static_cast<lldb::tid_t>(tid)));
61 return error;
62 }
63}
64
65/// \return
66/// some file descriptor in /sys/fs/ associated with the cgroup of the given
67/// 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(s: formatv(Fmt: "/proc/{0}/cgroup", Vals&: pid));
75 if (!ifile)
76 return std::nullopt;
77
78 std::string line;
79 while (std::getline(is&: ifile, str&: line)) {
80 if (line.find(s: "0:") != 0)
81 continue;
82
83 std::string slice = line.substr(pos: line.find_first_of(c: '/'));
84 if (slice.empty())
85 return std::nullopt;
86 std::string cgroup_file = formatv(Fmt: "/sys/fs/cgroup/{0}", Vals&: slice);
87 // This cgroup should for the duration of the target, so we don't need to
88 // invoke close ourselves.
89 int maybe_fd = open(file: 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 EC: inconvertibleErrorCode(),
103 S: "Process currently traced. Stop process tracing first");
104 }
105 if (request.IsPerCpuTracing()) {
106 if (m_thread_traces.GetTracedThreadsCount() > 0)
107 return createStringError(
108 EC: inconvertibleErrorCode(),
109 S: "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 the
117 // cpu traces.
118 TraceIntelPTStartRequest effective_request = request;
119 effective_request.enable_tsc = true;
120
121 // We try to use cgroup filtering whenever possible
122 std::optional<int> cgroup_fd;
123 if (!request.disable_cgroup_filtering.value_or(u: false))
124 cgroup_fd = GetCGroupFileDescriptor(pid: m_process.GetID());
125
126 if (Expected<IntelPTProcessTraceUP> trace =
127 IntelPTMultiCoreTrace::StartOnAllCores(request: effective_request,
128 process&: 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(x: thread.GetID());
138
139 // per-thread process tracing
140 if (Expected<IntelPTProcessTraceUP> trace =
141 IntelPTPerThreadProcessTrace::Start(request, current_tids: 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 tracing
150 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 E1: std::move(error),
155 E2: createStringError(EC: inconvertibleErrorCode(),
156 S: formatv(Fmt: "Thread with tid {0} is currently "
157 "traced. Stop tracing it first.",
158 Vals&: tid)
159 .str()
160 .c_str()));
161 else
162 error = joinErrors(E1: std::move(error),
163 E2: 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 x: {.kind: IntelPTDataKinds::kProcFsCpuInfo, .size: cpu_info->size()});
205
206 m_thread_traces.ForEachThread(
207 callback: [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) {
208 state.traced_threads.push_back(
209 x: {.tid: tid,
210 .binary_data: {{.kind: IntelPTDataKinds::kIptTrace, .size: thread_trace.GetIptTraceSize()}}});
211 });
212
213 if (Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
214 FetchPerfTscConversionParameters())
215 state.tsc_perf_zero_conversion = *tsc_conversion;
216 else
217 state.AddWarning(warning: toString(E: tsc_conversion.takeError()));
218 return toJSON(packet: 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 EC: inconvertibleErrorCode(),
246 S: formatv(Fmt: "Can't fetch data kind {0} for cpu_id {1}, tid {2} and "
247 "\"process tracing\" mode {3}",
248 Vals: request.kind, Vals: request.cpu_id, Vals: request.tid,
249 Vals: 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(Err: 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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp