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

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