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