LLDB mainline
IntelPTSingleBufferTrace.cpp
Go to the documentation of this file.
1//===-- IntelPTSingleBufferTrace.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
10
12#include "lldb/Utility/Status.h"
14#include "llvm/Support/MemoryBuffer.h"
15#include "llvm/TargetParser/Host.h"
16
17#include <linux/perf_event.h>
18#include <sstream>
19#include <sys/syscall.h>
20#include <unistd.h>
21
22using namespace lldb;
23using namespace lldb_private;
24using namespace process_linux;
25using namespace llvm;
26
28 "/sys/bus/event_source/devices/intel_pt/type";
29
30const char kPSBPeriodCapFile[] =
31 "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";
32
34 "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";
35
37 "/sys/bus/event_source/devices/intel_pt/format/psb_period";
38
39const char kTSCBitOffsetFile[] =
40 "/sys/bus/event_source/devices/intel_pt/format/tsc";
41
43 Hex = 0,
44 // 0 or 1
47 // a bit index file always starts with the prefix config: following by an int,
48 // which represents the offset of the perf_event_attr.config value where to
49 // store a given configuration.
51};
52
53static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
55 ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
56 MemoryBuffer::getFileAsStream(file);
57
58 if (!stream)
59 return createStringError(inconvertibleErrorCode(),
60 "Can't open the file '%s'", file);
61
62 uint32_t value = 0;
63 StringRef text_buffer = stream.get()->getBuffer();
64
65 if (type == BitOffset) {
66 const char *prefix = "config:";
67 if (!text_buffer.starts_with(prefix))
68 return createStringError(inconvertibleErrorCode(),
69 "The file '%s' contents doesn't start with '%s'",
70 file, prefix);
71 text_buffer = text_buffer.substr(strlen(prefix));
72 }
73
74 auto getRadix = [&]() {
75 switch (type) {
76 case Hex:
77 return 16;
78 case ZeroOne:
79 case Decimal:
80 case BitOffset:
81 return 10;
82 }
83 llvm_unreachable("Fully covered switch above!");
84 };
85
86 auto createError = [&](const char *expected_value_message) {
87 return createStringError(
88 inconvertibleErrorCode(),
89 "The file '%s' has an invalid value. It should be %s.", file,
90 expected_value_message);
91 };
92
93 if (text_buffer.trim().consumeInteger(getRadix(), value) ||
94 (type == ZeroOne && value != 0 && value != 1)) {
95 switch (type) {
96 case Hex:
97 return createError("an unsigned hexadecimal int");
98 case ZeroOne:
99 return createError("0 or 1");
100 case Decimal:
101 case BitOffset:
102 return createError("an unsigned decimal int");
103 }
104 }
105 return value;
106}
107
108/// Return the Linux perf event type for Intel PT.
113
114static Error CheckPsbPeriod(size_t psb_period) {
115 Expected<uint32_t> cap =
117 if (!cap)
118 return cap.takeError();
119 if (*cap == 0)
120 return createStringError(inconvertibleErrorCode(),
121 "psb_period is unsupported in the system.");
122
123 Expected<uint32_t> valid_values = ReadIntelPTConfigFile(
125 if (!valid_values)
126 return valid_values.takeError();
127
128 if (valid_values.get() & (1 << psb_period))
129 return Error::success();
130
131 std::ostringstream error;
132 // 0 is always a valid value
133 error << "Invalid psb_period. Valid values are: 0";
134 uint32_t mask = valid_values.get();
135 while (mask) {
136 int index = __builtin_ctz(mask);
137 if (index > 0)
138 error << ", " << index;
139 // clear the lowest bit
140 mask &= mask - 1;
141 }
142 error << ".";
143 return createStringError(inconvertibleErrorCode(), error.str().c_str());
144}
145
146#ifdef PERF_ATTR_SIZE_VER5
147static Expected<uint64_t>
148GeneratePerfEventConfigValue(bool enable_tsc,
149 std::optional<uint64_t> psb_period) {
150 uint64_t config = 0;
151 // tsc is always supported
152 if (enable_tsc) {
153 if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
155 config |= 1 << *offset;
156 else
157 return offset.takeError();
158 }
159 if (psb_period) {
160 if (Error error = CheckPsbPeriod(*psb_period))
161 return std::move(error);
162
163 if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
165 config |= *psb_period << *offset;
166 else
167 return offset.takeError();
168 }
169 return config;
170}
171
172/// Create a \a perf_event_attr configured for
173/// an IntelPT event.
174///
175/// \return
176/// A \a perf_event_attr if successful,
177/// or an \a llvm::Error otherwise.
178static Expected<perf_event_attr>
179CreateIntelPTPerfEventConfiguration(bool enable_tsc,
180 std::optional<uint64_t> psb_period) {
181 perf_event_attr attr;
182 memset(&attr, 0, sizeof(attr));
183 attr.size = sizeof(attr);
184 attr.exclude_kernel = 1;
185 attr.exclude_hv = 1;
186 attr.exclude_idle = 1;
187
188 if (Expected<uint64_t> config_value =
189 GeneratePerfEventConfigValue(enable_tsc, psb_period))
190 attr.config = *config_value;
191 else
192 return config_value.takeError();
193
194 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType())
195 attr.type = *intel_pt_type;
196 else
197 return intel_pt_type.takeError();
198
199 return attr;
200}
201#endif
202
204 return m_perf_event.GetAuxBuffer().size();
205}
206
208 return m_perf_event.DisableWithIoctl();
209}
210
212 return m_perf_event.EnableWithIoctl();
213}
214
215Expected<std::vector<uint8_t>> IntelPTSingleBufferTrace::GetIptTrace() {
216 // Disable the perf event to force a flush out of the CPU's internal buffer.
217 // Besides, we can guarantee that the CPU won't override any data as we are
218 // reading the buffer.
219 // The Intel documentation says:
220 //
221 // Packets are first buffered internally and then written out
222 // asynchronously. To collect packet output for postprocessing, a collector
223 // needs first to ensure that all packet data has been flushed from internal
224 // buffers. Software can ensure this by stopping packet generation by
225 // clearing IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
226 // Section 35.2.7.2).
227 //
228 // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as
229 // mentioned in the man page of perf_event_open.
230 return m_perf_event.GetReadOnlyAuxBuffer();
231}
232
233Expected<IntelPTSingleBufferTrace>
235 std::optional<lldb::tid_t> tid,
236 std::optional<cpu_id_t> cpu_id, bool disabled,
237 std::optional<int> cgroup_fd) {
238#ifndef PERF_ATTR_SIZE_VER5
239 return createStringError(inconvertibleErrorCode(),
240 "Intel PT Linux perf event not supported");
241#else
242 Log *log = GetLog(POSIXLog::Trace);
243
244 LLDB_LOG(log, "Will start tracing thread id {0} and cpu id {1}", tid, cpu_id);
245
246 if (__builtin_popcount(request.ipt_trace_size) != 1 ||
247 request.ipt_trace_size < 4096) {
248 return createStringError(
249 inconvertibleErrorCode(),
250 "The intel pt trace size must be a power of 2 greater than or equal to "
251 "4096 (2^12) bytes. It was %" PRIu64 ".",
252 request.ipt_trace_size);
253 }
254 uint64_t page_size = getpagesize();
255 uint64_t aux_buffer_numpages = static_cast<uint64_t>(llvm::bit_floor(
256 (request.ipt_trace_size + page_size - 1) / page_size));
257
258 Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration(
259 request.enable_tsc,
260 llvm::transformOptional(request.psb_period, [](int value) {
261 return static_cast<uint64_t>(value);
262 }));
263 if (!attr)
264 return attr.takeError();
265 attr->disabled = disabled;
266
267 LLDB_LOG(log, "Will create intel pt trace buffer of size {0}",
268 request.ipt_trace_size);
269 unsigned long flags = 0;
270 if (cgroup_fd) {
271 tid = *cgroup_fd;
272 flags |= PERF_FLAG_PID_CGROUP;
273 }
274
275 if (Expected<PerfEvent> perf_event =
276 PerfEvent::Init(*attr, tid, cpu_id, -1, flags)) {
277 if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
278 /*num_data_pages=*/0, aux_buffer_numpages,
279 /*data_buffer_write=*/true)) {
280 return std::move(mmap_err);
281 }
282 return IntelPTSingleBufferTrace(std::move(*perf_event));
283 } else {
284 return perf_event.takeError();
285 }
286#endif
287}
288
static llvm::raw_ostream & error(Stream &strm)
static Error CheckPsbPeriod(size_t psb_period)
const char kTSCBitOffsetFile[]
static Expected< uint32_t > ReadIntelPTConfigFile(const char *file, IntelPTConfigFileType type)
const char kPSBPeriodValidValuesFile[]
const char kPSBPeriodBitOffsetFile[]
const char kPSBPeriodCapFile[]
const char kOSEventIntelPTTypeFile[]
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
llvm::Error Error
static llvm::Expected< IntelPTSingleBufferTrace > Start(const TraceIntelPTStartRequest &request, std::optional< lldb::tid_t > tid, std::optional< lldb::cpu_id_t > cpu_id=std::nullopt, bool disabled=false, std::optional< int > cgroup_fd=std::nullopt)
Start tracing using a single Intel PT trace buffer.
PerfEvent m_perf_event
perf event configured for IntelPT.
llvm::Error Resume()
Resume the collection of this trace.
IntelPTSingleBufferTrace(PerfEvent &&perf_event)
Construct new IntelPTSingleBufferThreadTrace.
llvm::Error Pause()
Pause the collection of this trace.
llvm::Expected< std::vector< uint8_t > > GetIptTrace()
Read the intel pt trace buffer managed by this trace instance.
Thin wrapper of the perf_event_open API.
Definition Perf.h:80
static llvm::Expected< PerfEvent > Init(perf_event_attr &attr, std::optional< lldb::pid_t > pid, std::optional< lldb::cpu_id_t > cpu, std::optional< long > group_fd, unsigned long flags)
Create a new performance monitoring event via the perf_event_open syscall.
Definition Perf.cpp:77
llvm::Expected< uint32_t > GetIntelPTOSEventType()
Return the Linux perf event type for Intel PT.
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
std::optional< uint64_t > psb_period
PSB packet period.
uint64_t ipt_trace_size
Size in bytes to use for each thread's trace buffer.