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 | |
20 | using namespace lldb; |
21 | using namespace lldb_private; |
22 | using namespace process_linux; |
23 | using namespace llvm; |
24 | |
25 | const char kOSEventIntelPTTypeFile[] = |
26 | "/sys/bus/event_source/devices/intel_pt/type" ; |
27 | |
28 | const char kPSBPeriodCapFile[] = |
29 | "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc" ; |
30 | |
31 | const char kPSBPeriodValidValuesFile[] = |
32 | "/sys/bus/event_source/devices/intel_pt/caps/psb_periods" ; |
33 | |
34 | const char kPSBPeriodBitOffsetFile[] = |
35 | "/sys/bus/event_source/devices/intel_pt/format/psb_period" ; |
36 | |
37 | const char kTSCBitOffsetFile[] = |
38 | "/sys/bus/event_source/devices/intel_pt/format/tsc" ; |
39 | |
40 | enum 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 | |
51 | static 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. |
107 | Expected<uint32_t> process_linux::GetIntelPTOSEventType() { |
108 | return ReadIntelPTConfigFile(file: kOSEventIntelPTTypeFile, |
109 | type: IntelPTConfigFileType::Decimal); |
110 | } |
111 | |
112 | static 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 |
145 | static Expected<uint64_t> |
146 | GeneratePerfEventConfigValue(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. |
176 | static Expected<perf_event_attr> |
177 | CreateIntelPTPerfEventConfiguration(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 | |
201 | size_t IntelPTSingleBufferTrace::GetIptTraceSize() const { |
202 | return m_perf_event.GetAuxBuffer().size(); |
203 | } |
204 | |
205 | Error IntelPTSingleBufferTrace::Pause() { |
206 | return m_perf_event.DisableWithIoctl(); |
207 | } |
208 | |
209 | Error IntelPTSingleBufferTrace::Resume() { |
210 | return m_perf_event.EnableWithIoctl(); |
211 | } |
212 | |
213 | Expected<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 | |
231 | Expected<IntelPTSingleBufferTrace> |
232 | IntelPTSingleBufferTrace::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 | |
287 | const PerfEvent &IntelPTSingleBufferTrace::GetPerfEvent() const { |
288 | return m_perf_event; |
289 | } |
290 | |