| 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 | |
| 25 | namespace lldb_private { |
| 26 | namespace process_linux { |
| 27 | namespace 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. |
| 33 | class MmapDeleter { |
| 34 | public: |
| 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 | |
| 49 | private: |
| 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. |
| 58 | class FileDescriptorDeleter { |
| 59 | public: |
| 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 | |
| 69 | using FileDescriptorUP = |
| 70 | std::unique_ptr<long, resource_handle::FileDescriptorDeleter>; |
| 71 | using 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. |
| 80 | class PerfEvent { |
| 81 | public: |
| 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 | |
| 252 | private: |
| 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. |
| 316 | llvm::Expected<PerfEvent> |
| 317 | CreateContextSwitchTracePerfEvent(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. |
| 322 | llvm::Expected<LinuxPerfZeroTscConversion> LoadPerfTscConversionParameters(); |
| 323 | |
| 324 | } // namespace process_linux |
| 325 | } // namespace lldb_private |
| 326 | |
| 327 | #endif // LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H |
| 328 | |