328 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8/// \file9/// This file contains a thin wrapper of the perf_event_open API10/// and classes to handle the destruction of file descriptors11/// and mmap pointers.12///13//===----------------------------------------------------------------------===//14 15#ifndef LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H16#define LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H17 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 properly32/// unmap the region at destruction time.33class MmapDeleter {34public:35 /// Construct new \a MmapDeleter.36 ///37 /// \param[in] bytes38 /// 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] ptr46 /// 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 release57/// 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] ptr65 /// 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_handle74 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'ed79/// 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 visit86 /// https://man7.org/linux/man-pages/man2/perf_event_open.2.html.87 ///88 /// \param[in] attr89 /// Configuration information for the event.90 ///91 /// \param[in] pid92 /// The process or thread to be monitored by the event. If \b None, then93 /// all processes and threads are monitored.94 ///95 /// \param[in] cpu96 /// The cpu to be monitored by the event. If \b None, then all cpus are97 /// monitored.98 ///99 /// \param[in] group_fd100 /// File descriptor of the group leader. If \b None, then this perf_event101 /// doesn't belong to a preexisting group.102 ///103 /// \param[in] flags104 /// Bitmask of additional configuration flags.105 ///106 /// \return107 /// If the perf_event_open syscall was successful, a minimal \a PerfEvent108 /// 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 syscall116 /// with "default" values for the cpu, group_fd and flags arguments.117 ///118 /// Convenience method to be used when the perf event requires minimal119 /// configuration. It handles the default values of all other arguments.120 ///121 /// \param[in] attr122 /// Configuration information for the event.123 ///124 /// \param[in] pid125 /// The process or thread to be monitored by the event. If \b126 /// 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 and132 /// expose them through \a PerfEvent::GetMetadataPage() , \a133 /// PerfEvent::GetDataBuffer() and \a PerfEvent::GetAuxBuffer().134 ///135 /// This uses mmap underneath, which means that the number of pages mmap'ed136 /// must be less than the actual data available by the kernel. The metadata137 /// page is always mmap'ed.138 ///139 /// Mmap is needed because the underlying data might be changed by the kernel140 /// dynamically.141 ///142 /// \param[in] num_data_pages143 /// 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 access145 /// the metadata, \a perf_event_mmap_page, or the aux buffer.146 ///147 /// \param[in] num_aux_pages148 /// 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 this150 /// buffer.151 ///152 /// \param[in] data_buffer_write153 /// Whether to mmap the data buffer with WRITE permissions. This changes154 /// the behavior of how the kernel writes to the data buffer.155 ///156 /// \return157 /// \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 /// \return174 /// 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 where180 /// perf sample data is located.181 ///182 /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,183 /// otherwise a failure might happen.184 ///185 /// \return186 /// \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 streams192 /// 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 /// \return198 /// \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 configured202 /// with PROT_READ permissions only, which indicates that the buffer is203 /// automatically wrapped and overwritten by the kernel or hardware. To ensure204 /// that the data is up-to-date and is not corrupted by read-write race205 /// conditions, the underlying perf_event is paused during read, and later206 /// 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 /// \return210 /// 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 configured214 /// with PROT_READ permissions only, which indicates that the buffer is215 /// automatically wrapped and overwritten by the kernel or hardware. To ensure216 /// that the data is up-to-date and is not corrupted by read-write race217 /// conditions, the underlying perf_event is paused during read, and later218 /// 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 /// \return222 /// 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 its226 /// group. This doesn't terminate the perf event.227 ///228 /// This is no-op if the perf event is already disabled.229 ///230 /// \return231 /// 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 its235 /// group.236 ///237 /// This is no-op if the perf event is already enabled.238 ///239 /// \return240 /// An Error if the perf event couldn't be enabled.241 llvm::Error EnableWithIoctl();242 243 /// \return244 /// The size in bytes of the section of the data buffer that has effective245 /// data.246 size_t GetEffectiveDataBufferSize() const;247 248 /// \return249 /// \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] fd256 /// File descriptor of the perf event.257 ///258 /// \param[in] enabled259 /// 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 visit268 /// 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_pages279 /// 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 access281 /// the metadata, \a perf_event_mmap_page, or the aux buffer.282 ///283 /// \param[in] data_buffer_write284 /// Whether to mmap the data buffer with WRITE permissions. This changes285 /// 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_pages292 /// 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 this294 /// 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 streams302 /// 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_id311/// The core to trace.312///313/// \param[in] parent_perf_event314/// An optional perf event that will be grouped with the315/// 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, if321/// available.322llvm::Expected<LinuxPerfZeroTscConversion> LoadPerfTscConversionParameters();323 324} // namespace process_linux325} // namespace lldb_private326 327#endif // LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H328