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