1//===-- Perf.h --------------------------------------------------*- C++ -*-===//
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/// \file
9/// This file contains a thin wrapper of the perf_event_open API
10/// and classes to handle the destruction of file descriptors
11/// and mmap pointers.
12///
13//===----------------------------------------------------------------------===//
14
15#ifndef LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H
16#define LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H
17
18#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
19#include "lldb/lldb-types.h"
20#include "llvm/Support/Error.h"
21#include <chrono>
22#include <cstdint>
23#include <linux/perf_event.h>
24
25namespace lldb_private {
26namespace process_linux {
27namespace resource_handle {
28
29/// Custom deleter for the pointer returned by \a mmap.
30///
31/// This functor type is provided to \a unique_ptr to properly
32/// unmap the region at destruction time.
33class MmapDeleter {
34public:
35 /// Construct new \a MmapDeleter.
36 ///
37 /// \param[in] bytes
38 /// Size of the mmap'ed region in bytes.
39 MmapDeleter(size_t bytes = 0) : m_bytes(bytes) {}
40
41 /// Unmap the mmap'ed region.
42 ///
43 /// If \a m_bytes==0 or \a ptr==nullptr, nothing is unmmapped.
44 ///
45 /// \param[in] ptr
46 /// pointer to the region to be unmmapped.
47 void operator()(void *ptr);
48
49private:
50 /// Size of the mmap'ed region, in bytes, to be unmapped.
51 size_t m_bytes;
52};
53
54/// Custom deleter for a file descriptor.
55///
56/// This functor type is provided to \a unique_ptr to properly release
57/// the resources associated with the file descriptor at destruction time.
58class FileDescriptorDeleter {
59public:
60 /// Close and free the memory associated with the file descriptor pointer.
61 ///
62 /// Effectively a no-op if \a ptr==nullptr or \a*ptr==-1.
63 ///
64 /// \param[in] ptr
65 /// Pointer to the file descriptor.
66 void operator()(long *ptr);
67};
68
69using FileDescriptorUP =
70 std::unique_ptr<long, resource_handle::FileDescriptorDeleter>;
71using MmapUP = std::unique_ptr<void, resource_handle::MmapDeleter>;
72
73} // namespace resource_handle
74
75/// Thin wrapper of the perf_event_open API.
76///
77/// Exposes the metadata page and data and aux buffers of a perf event.
78/// Handles the management of the event's file descriptor and mmap'ed
79/// regions.
80class PerfEvent {
81public:
82 /// Create a new performance monitoring event via the perf_event_open syscall.
83 ///
84 /// The parameters are directly forwarded to a perf_event_open syscall,
85 /// for additional information on the parameters visit
86 /// https://man7.org/linux/man-pages/man2/perf_event_open.2.html.
87 ///
88 /// \param[in] attr
89 /// Configuration information for the event.
90 ///
91 /// \param[in] pid
92 /// The process or thread to be monitored by the event. If \b None, then
93 /// all processes and threads are monitored.
94 ///
95 /// \param[in] cpu
96 /// The cpu to be monitored by the event. If \b None, then all cpus are
97 /// monitored.
98 ///
99 /// \param[in] group_fd
100 /// File descriptor of the group leader. If \b None, then this perf_event
101 /// doesn't belong to a preexisting group.
102 ///
103 /// \param[in] flags
104 /// Bitmask of additional configuration flags.
105 ///
106 /// \return
107 /// If the perf_event_open syscall was successful, a minimal \a PerfEvent
108 /// instance, or an \a llvm::Error otherwise.
109 static llvm::Expected<PerfEvent> Init(perf_event_attr &attr,
110 std::optional<lldb::pid_t> pid,
111 std::optional<lldb::cpu_id_t> cpu,
112 std::optional<long> group_fd,
113 unsigned long flags);
114
115 /// Create a new performance monitoring event via the perf_event_open syscall
116 /// with "default" values for the cpu, group_fd and flags arguments.
117 ///
118 /// Convenience method to be used when the perf event requires minimal
119 /// configuration. It handles the default values of all other arguments.
120 ///
121 /// \param[in] attr
122 /// Configuration information for the event.
123 ///
124 /// \param[in] pid
125 /// The process or thread to be monitored by the event. If \b
126 /// std::nullopt, then all threads and processes are monitored.
127 static llvm::Expected<PerfEvent>
128 Init(perf_event_attr &attr, std::optional<lldb::pid_t> pid,
129 std::optional<lldb::cpu_id_t> core = std::nullopt);
130
131 /// Mmap the metadata page and the data and aux buffers of the perf event and
132 /// expose them through \a PerfEvent::GetMetadataPage() , \a
133 /// PerfEvent::GetDataBuffer() and \a PerfEvent::GetAuxBuffer().
134 ///
135 /// This uses mmap underneath, which means that the number of pages mmap'ed
136 /// must be less than the actual data available by the kernel. The metadata
137 /// page is always mmap'ed.
138 ///
139 /// Mmap is needed because the underlying data might be changed by the kernel
140 /// dynamically.
141 ///
142 /// \param[in] num_data_pages
143 /// Number of pages in the data buffer to mmap, must be a power of 2.
144 /// A value of 0 is useful for "dummy" events that only want to access
145 /// the metadata, \a perf_event_mmap_page, or the aux buffer.
146 ///
147 /// \param[in] num_aux_pages
148 /// Number of pages in the aux buffer to mmap, must be a power of 2.
149 /// A value of 0 effectively is a no-op and no data is mmap'ed for this
150 /// buffer.
151 ///
152 /// \param[in] data_buffer_write
153 /// Whether to mmap the data buffer with WRITE permissions. This changes
154 /// the behavior of how the kernel writes to the data buffer.
155 ///
156 /// \return
157 /// \a llvm::Error::success if the mmap operations succeeded,
158 /// or an \a llvm::Error otherwise.
159 llvm::Error MmapMetadataAndBuffers(size_t num_data_pages,
160 size_t num_aux_pages,
161 bool data_buffer_write);
162
163 /// Get the file descriptor associated with the perf event.
164 long GetFd() const;
165
166 /// Get the metadata page from the data section's mmap buffer.
167 ///
168 /// The metadata page is always mmap'ed, even when \a num_data_pages is 0.
169 ///
170 /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
171 /// otherwise a failure might happen.
172 ///
173 /// \return
174 /// The data section's \a perf_event_mmap_page.
175 perf_event_mmap_page &GetMetadataPage() const;
176
177 /// Get the data buffer from the data section's mmap buffer.
178 ///
179 /// The data buffer is the region of the data section's mmap buffer where
180 /// perf sample data is located.
181 ///
182 /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
183 /// otherwise a failure might happen.
184 ///
185 /// \return
186 /// \a ArrayRef<uint8_t> extending \a data_size bytes from \a data_offset.
187 llvm::ArrayRef<uint8_t> GetDataBuffer() const;
188
189 /// Get the AUX buffer.
190 ///
191 /// AUX buffer is a region for high-bandwidth data streams
192 /// such as IntelPT. This is separate from the metadata and data buffer.
193 ///
194 /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
195 /// otherwise a failure might happen.
196 ///
197 /// \return
198 /// \a ArrayRef<uint8_t> extending \a aux_size bytes from \a aux_offset.
199 llvm::ArrayRef<uint8_t> GetAuxBuffer() const;
200
201 /// Read the aux buffer managed by this perf event assuming it was configured
202 /// with PROT_READ permissions only, which indicates that the buffer is
203 /// automatically wrapped and overwritten by the kernel or hardware. To ensure
204 /// that the data is up-to-date and is not corrupted by read-write race
205 /// conditions, the underlying perf_event is paused during read, and later
206 /// it's returned to its initial state. The returned data will be linear, i.e.
207 /// it will fix the circular wrapping the might exist in the buffer.
208 ///
209 /// \return
210 /// A vector with the requested binary data.
211 llvm::Expected<std::vector<uint8_t>> GetReadOnlyAuxBuffer();
212
213 /// Read the data buffer managed by this perf even assuming it was configured
214 /// with PROT_READ permissions only, which indicates that the buffer is
215 /// automatically wrapped and overwritten by the kernel or hardware. To ensure
216 /// that the data is up-to-date and is not corrupted by read-write race
217 /// conditions, the underlying perf_event is paused during read, and later
218 /// it's returned to its initial state. The returned data will be linear, i.e.
219 /// it will fix the circular wrapping the might exist int he buffer.
220 ///
221 /// \return
222 /// A vector with the requested binary data.
223 llvm::Expected<std::vector<uint8_t>> GetReadOnlyDataBuffer();
224
225 /// Use the ioctl API to disable the perf event and all the events in its
226 /// group. This doesn't terminate the perf event.
227 ///
228 /// This is no-op if the perf event is already disabled.
229 ///
230 /// \return
231 /// An Error if the perf event couldn't be disabled.
232 llvm::Error DisableWithIoctl();
233
234 /// Use the ioctl API to enable the perf event and all the events in its
235 /// group.
236 ///
237 /// This is no-op if the perf event is already enabled.
238 ///
239 /// \return
240 /// An Error if the perf event couldn't be enabled.
241 llvm::Error EnableWithIoctl();
242
243 /// \return
244 /// The size in bytes of the section of the data buffer that has effective
245 /// data.
246 size_t GetEffectiveDataBufferSize() const;
247
248 /// \return
249 /// \b true if and only the perf event is enabled and collecting.
250 bool IsEnabled() const;
251
252private:
253 /// Create new \a PerfEvent.
254 ///
255 /// \param[in] fd
256 /// File descriptor of the perf event.
257 ///
258 /// \param[in] enabled
259 /// Initial collection state configured for this perf_event.
260 PerfEvent(long fd, bool enabled)
261 : m_fd(new long(fd), resource_handle::FileDescriptorDeleter()),
262 m_enabled(enabled) {}
263
264 /// Wrapper for \a mmap to provide custom error messages.
265 ///
266 /// The parameters are directly forwarded to a \a mmap syscall,
267 /// for information on the parameters visit
268 /// https://man7.org/linux/man-pages/man2/mmap.2.html.
269 ///
270 /// The value of \a GetFd() is passed as the \a fd argument to \a mmap.
271 llvm::Expected<resource_handle::MmapUP> DoMmap(void *addr, size_t length,
272 int prot, int flags,
273 long int offset,
274 llvm::StringRef buffer_name);
275
276 /// Mmap the data buffer of the perf event.
277 ///
278 /// \param[in] num_data_pages
279 /// Number of pages in the data buffer to mmap, must be a power of 2.
280 /// A value of 0 is useful for "dummy" events that only want to access
281 /// the metadata, \a perf_event_mmap_page, or the aux buffer.
282 ///
283 /// \param[in] data_buffer_write
284 /// Whether to mmap the data buffer with WRITE permissions. This changes
285 /// the behavior of how the kernel writes to the data buffer.
286 llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages,
287 bool data_buffer_write);
288
289 /// Mmap the aux buffer of the perf event.
290 ///
291 /// \param[in] num_aux_pages
292 /// Number of pages in the aux buffer to mmap, must be a power of 2.
293 /// A value of 0 effectively is a no-op and no data is mmap'ed for this
294 /// buffer.
295 llvm::Error MmapAuxBuffer(size_t num_aux_pages);
296
297 /// The file descriptor representing the perf event.
298 resource_handle::FileDescriptorUP m_fd;
299 /// Metadata page and data section where perf samples are stored.
300 resource_handle::MmapUP m_metadata_data_base;
301 /// AUX buffer is a separate region for high-bandwidth data streams
302 /// such as IntelPT.
303 resource_handle::MmapUP m_aux_base;
304 /// The state of the underlying perf_event.
305 bool m_enabled;
306};
307
308/// Create a perf event that tracks context switches on a cpu.
309///
310/// \param[in] cpu_id
311/// The core to trace.
312///
313/// \param[in] parent_perf_event
314/// An optional perf event that will be grouped with the
315/// new perf event.
316llvm::Expected<PerfEvent>
317CreateContextSwitchTracePerfEvent(lldb::cpu_id_t cpu_id,
318 const PerfEvent *parent_perf_event = nullptr);
319
320/// Load \a PerfTscConversionParameters from \a perf_event_mmap_page, if
321/// available.
322llvm::Expected<LinuxPerfZeroTscConversion> LoadPerfTscConversionParameters();
323
324} // namespace process_linux
325} // namespace lldb_private
326
327#endif // LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H
328

source code of lldb/source/Plugins/Process/Linux/Perf.h