LLDB mainline
IntelPTCollector.cpp
Go to the documentation of this file.
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"
12#include "Procfs.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
34
35llvm::Expected<LinuxPerfZeroTscConversion &>
37 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
39 return *tsc_conversion;
40 else
41 return createStringError(inconvertibleErrorCode(),
42 "Unable to load TSC to wall time conversion: %s",
43 toString(tsc_conversion.takeError()).c_str());
44}
45
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
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/// \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(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 to
88 // 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
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 =
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(false))
124 cgroup_fd = GetCGroupFileDescriptor(m_process.GetID());
125
126 if (Expected<IntelPTProcessTraceUP> trace =
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 tracing
140 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 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 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 else
162 error = joinErrors(std::move(error),
163 m_thread_traces.TraceStart(tid, request));
164 }
165 return error;
166 }
167}
168
171 m_process_trace_up->ProcessWillResume();
172}
173
176 m_process_trace_up->ProcessDidStop();
177}
178
181 return m_process_trace_up->TraceStart(tid);
182
183 return Error::success();
184}
185
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
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 =
215 state.tsc_perf_zero_conversion = *tsc_conversion;
216 else
217 state.AddWarning(toString(tsc_conversion.takeError()));
218 return toJSON(state);
219}
220
221Expected<std::vector<uint8_t>>
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
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
262 m_process_trace_up.reset();
263 m_thread_traces.Clear();
264}
static llvm::raw_ostream & error(Stream &strm)
static std::optional< int > GetCGroupFileDescriptor(lldb::pid_t pid)
This file contains a thin wrapper of the perf_event_open API and classes to handle the destruction of...
static llvm::Error createStringError(const char *format, Args &&...args)
Definition Resource.cpp:59
llvm::Expected< LinuxPerfZeroTscConversion & > FetchPerfTscConversionParameters()
llvm::Expected< std::vector< uint8_t > > GetBinaryData(const TraceGetBinaryDataRequest &request)
Implementation of the jLLDBTraceGetBinaryData packet.
llvm::Error TraceStop(const TraceStopRequest &request)
Implementation of the jLLDBTraceStop packet.
IntelPTProcessTraceUP m_process_trace_up
Only one instance of "process trace" can be active at a given time.
NativeProcessProtocol & m_process
The target process.
llvm::Error TraceStart(const TraceIntelPTStartRequest &request)
Implementation of the jLLDBTraceStart packet.
IntelPTThreadTraceCollection m_thread_traces
Threads traced due to "thread tracing".
llvm::Error OnThreadCreated(lldb::tid_t tid)
If "process tracing" is enabled, then trace the given thread.
IntelPTCollector(NativeProcessProtocol &process)
llvm::Error OnThreadDestroyed(lldb::tid_t tid)
Stops tracing a tracing upon a destroy event.
llvm::Expected< llvm::json::Value > GetState()
Implementation of the jLLDBTraceGetState packet.
void ProcessWillResume()
To be invoked before the process will resume, so that we can capture the first instructions after the...
void ProcessDidStop()
To be invoked as soon as we know the process stopped.
static llvm::Expected< std::unique_ptr< IntelPTMultiCoreTrace > > StartOnAllCores(const TraceIntelPTStartRequest &request, NativeProcessProtocol &process, std::optional< int > cgroup_fd=std::nullopt)
Start tracing all CPU cores.
static llvm::Expected< std::unique_ptr< IntelPTPerThreadProcessTrace > > Start(const TraceIntelPTStartRequest &request, llvm::ArrayRef< lldb::tid_t > current_tids)
Start tracing the current process by tracing each of its tids individually.
This class wraps a single perf event collecting intel pt data in a single buffer.
llvm::Expected< uint32_t > GetIntelPTOSEventType()
Return the Linux perf event type for Intel PT.
llvm::Expected< LinuxPerfZeroTscConversion > LoadPerfTscConversionParameters()
Load PerfTscConversionParameters from perf_event_mmap_page, if available.
Definition Perf.cpp:26
llvm::Expected< llvm::ArrayRef< uint8_t > > GetProcfsCpuInfo()
Definition Procfs.cpp:22
A class that represents a running process on the host machine.
const char * toString(AppleArm64ExceptionClass EC)
llvm::json::Value toJSON(const TraceSupportedResponse &packet)
uint64_t pid_t
Definition lldb-types.h:83
uint64_t tid_t
Definition lldb-types.h:84
jLLDBTraceGetBinaryData gdb-remote packet
std::optional< lldb::cpu_id_t > cpu_id
Optional core id if the data is related to a cpu core.
std::optional< lldb::tid_t > tid
Optional tid if the data is related to a thread.
std::string kind
Identifier for the data.
std::vector< TraceThreadState > traced_threads
std::vector< TraceBinaryData > process_binary_data
void AddWarning(llvm::StringRef warning)
std::optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
The TSC to wall time conversion if it exists, otherwise nullptr.
std::optional< bool > disable_cgroup_filtering
Disable the cgroup filtering that is automatically applied in per cpu mode.
bool IsProcessTracing() const
jLLDBTraceStart
std::optional< std::vector< lldb::tid_t > > tids
If std::nullopt, then this starts tracing the whole process.
jLLDBTraceStop gdb-remote packet
std::optional< std::vector< lldb::tid_t > > tids
If std::nullopt, then this stops tracing the whole process.