//===-- Perf.h --------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// This file contains a thin wrapper of the perf_event_open API /// and classes to handle the destruction of file descriptors /// and mmap pointers. /// //===----------------------------------------------------------------------===// #ifndef LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H #define LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "lldb/lldb-types.h" #include "llvm/Support/Error.h" #include #include #include namespace lldb_private { namespace process_linux { namespace resource_handle { /// Custom deleter for the pointer returned by \a mmap. /// /// This functor type is provided to \a unique_ptr to properly /// unmap the region at destruction time. class MmapDeleter { public: /// Construct new \a MmapDeleter. /// /// \param[in] bytes /// Size of the mmap'ed region in bytes. MmapDeleter(size_t bytes = 0) : m_bytes(bytes) {} /// Unmap the mmap'ed region. /// /// If \a m_bytes==0 or \a ptr==nullptr, nothing is unmmapped. /// /// \param[in] ptr /// pointer to the region to be unmmapped. void operator()(void *ptr); private: /// Size of the mmap'ed region, in bytes, to be unmapped. size_t m_bytes; }; /// Custom deleter for a file descriptor. /// /// This functor type is provided to \a unique_ptr to properly release /// the resources associated with the file descriptor at destruction time. class FileDescriptorDeleter { public: /// Close and free the memory associated with the file descriptor pointer. /// /// Effectively a no-op if \a ptr==nullptr or \a*ptr==-1. /// /// \param[in] ptr /// Pointer to the file descriptor. void operator()(long *ptr); }; using FileDescriptorUP = std::unique_ptr; using MmapUP = std::unique_ptr; } // namespace resource_handle /// Thin wrapper of the perf_event_open API. /// /// Exposes the metadata page and data and aux buffers of a perf event. /// Handles the management of the event's file descriptor and mmap'ed /// regions. class PerfEvent { public: /// Create a new performance monitoring event via the perf_event_open syscall. /// /// The parameters are directly forwarded to a perf_event_open syscall, /// for additional information on the parameters visit /// https://man7.org/linux/man-pages/man2/perf_event_open.2.html. /// /// \param[in] attr /// Configuration information for the event. /// /// \param[in] pid /// The process or thread to be monitored by the event. If \b None, then /// all processes and threads are monitored. /// /// \param[in] cpu /// The cpu to be monitored by the event. If \b None, then all cpus are /// monitored. /// /// \param[in] group_fd /// File descriptor of the group leader. If \b None, then this perf_event /// doesn't belong to a preexisting group. /// /// \param[in] flags /// Bitmask of additional configuration flags. /// /// \return /// If the perf_event_open syscall was successful, a minimal \a PerfEvent /// instance, or an \a llvm::Error otherwise. static llvm::Expected Init(perf_event_attr &attr, std::optional pid, std::optional cpu, std::optional group_fd, unsigned long flags); /// Create a new performance monitoring event via the perf_event_open syscall /// with "default" values for the cpu, group_fd and flags arguments. /// /// Convenience method to be used when the perf event requires minimal /// configuration. It handles the default values of all other arguments. /// /// \param[in] attr /// Configuration information for the event. /// /// \param[in] pid /// The process or thread to be monitored by the event. If \b /// std::nullopt, then all threads and processes are monitored. static llvm::Expected Init(perf_event_attr &attr, std::optional pid, std::optional core = std::nullopt); /// Mmap the metadata page and the data and aux buffers of the perf event and /// expose them through \a PerfEvent::GetMetadataPage() , \a /// PerfEvent::GetDataBuffer() and \a PerfEvent::GetAuxBuffer(). /// /// This uses mmap underneath, which means that the number of pages mmap'ed /// must be less than the actual data available by the kernel. The metadata /// page is always mmap'ed. /// /// Mmap is needed because the underlying data might be changed by the kernel /// dynamically. /// /// \param[in] num_data_pages /// Number of pages in the data buffer to mmap, must be a power of 2. /// A value of 0 is useful for "dummy" events that only want to access /// the metadata, \a perf_event_mmap_page, or the aux buffer. /// /// \param[in] num_aux_pages /// Number of pages in the aux buffer to mmap, must be a power of 2. /// A value of 0 effectively is a no-op and no data is mmap'ed for this /// buffer. /// /// \param[in] data_buffer_write /// Whether to mmap the data buffer with WRITE permissions. This changes /// the behavior of how the kernel writes to the data buffer. /// /// \return /// \a llvm::Error::success if the mmap operations succeeded, /// or an \a llvm::Error otherwise. llvm::Error MmapMetadataAndBuffers(size_t num_data_pages, size_t num_aux_pages, bool data_buffer_write); /// Get the file descriptor associated with the perf event. long GetFd() const; /// Get the metadata page from the data section's mmap buffer. /// /// The metadata page is always mmap'ed, even when \a num_data_pages is 0. /// /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers, /// otherwise a failure might happen. /// /// \return /// The data section's \a perf_event_mmap_page. perf_event_mmap_page &GetMetadataPage() const; /// Get the data buffer from the data section's mmap buffer. /// /// The data buffer is the region of the data section's mmap buffer where /// perf sample data is located. /// /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers, /// otherwise a failure might happen. /// /// \return /// \a ArrayRef extending \a data_size bytes from \a data_offset. llvm::ArrayRef GetDataBuffer() const; /// Get the AUX buffer. /// /// AUX buffer is a region for high-bandwidth data streams /// such as IntelPT. This is separate from the metadata and data buffer. /// /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers, /// otherwise a failure might happen. /// /// \return /// \a ArrayRef extending \a aux_size bytes from \a aux_offset. llvm::ArrayRef GetAuxBuffer() const; /// Read the aux buffer managed by this perf event assuming it was configured /// with PROT_READ permissions only, which indicates that the buffer is /// automatically wrapped and overwritten by the kernel or hardware. To ensure /// that the data is up-to-date and is not corrupted by read-write race /// conditions, the underlying perf_event is paused during read, and later /// it's returned to its initial state. The returned data will be linear, i.e. /// it will fix the circular wrapping the might exist in the buffer. /// /// \return /// A vector with the requested binary data. llvm::Expected> GetReadOnlyAuxBuffer(); /// Read the data buffer managed by this perf even assuming it was configured /// with PROT_READ permissions only, which indicates that the buffer is /// automatically wrapped and overwritten by the kernel or hardware. To ensure /// that the data is up-to-date and is not corrupted by read-write race /// conditions, the underlying perf_event is paused during read, and later /// it's returned to its initial state. The returned data will be linear, i.e. /// it will fix the circular wrapping the might exist int he buffer. /// /// \return /// A vector with the requested binary data. llvm::Expected> GetReadOnlyDataBuffer(); /// Use the ioctl API to disable the perf event and all the events in its /// group. This doesn't terminate the perf event. /// /// This is no-op if the perf event is already disabled. /// /// \return /// An Error if the perf event couldn't be disabled. llvm::Error DisableWithIoctl(); /// Use the ioctl API to enable the perf event and all the events in its /// group. /// /// This is no-op if the perf event is already enabled. /// /// \return /// An Error if the perf event couldn't be enabled. llvm::Error EnableWithIoctl(); /// \return /// The size in bytes of the section of the data buffer that has effective /// data. size_t GetEffectiveDataBufferSize() const; /// \return /// \b true if and only the perf event is enabled and collecting. bool IsEnabled() const; private: /// Create new \a PerfEvent. /// /// \param[in] fd /// File descriptor of the perf event. /// /// \param[in] enabled /// Initial collection state configured for this perf_event. PerfEvent(long fd, bool enabled) : m_fd(new long(fd), resource_handle::FileDescriptorDeleter()), m_enabled(enabled) {} /// Wrapper for \a mmap to provide custom error messages. /// /// The parameters are directly forwarded to a \a mmap syscall, /// for information on the parameters visit /// https://man7.org/linux/man-pages/man2/mmap.2.html. /// /// The value of \a GetFd() is passed as the \a fd argument to \a mmap. llvm::Expected DoMmap(void *addr, size_t length, int prot, int flags, long int offset, llvm::StringRef buffer_name); /// Mmap the data buffer of the perf event. /// /// \param[in] num_data_pages /// Number of pages in the data buffer to mmap, must be a power of 2. /// A value of 0 is useful for "dummy" events that only want to access /// the metadata, \a perf_event_mmap_page, or the aux buffer. /// /// \param[in] data_buffer_write /// Whether to mmap the data buffer with WRITE permissions. This changes /// the behavior of how the kernel writes to the data buffer. llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages, bool data_buffer_write); /// Mmap the aux buffer of the perf event. /// /// \param[in] num_aux_pages /// Number of pages in the aux buffer to mmap, must be a power of 2. /// A value of 0 effectively is a no-op and no data is mmap'ed for this /// buffer. llvm::Error MmapAuxBuffer(size_t num_aux_pages); /// The file descriptor representing the perf event. resource_handle::FileDescriptorUP m_fd; /// Metadata page and data section where perf samples are stored. resource_handle::MmapUP m_metadata_data_base; /// AUX buffer is a separate region for high-bandwidth data streams /// such as IntelPT. resource_handle::MmapUP m_aux_base; /// The state of the underlying perf_event. bool m_enabled; }; /// Create a perf event that tracks context switches on a cpu. /// /// \param[in] cpu_id /// The core to trace. /// /// \param[in] parent_perf_event /// An optional perf event that will be grouped with the /// new perf event. llvm::Expected CreateContextSwitchTracePerfEvent(lldb::cpu_id_t cpu_id, const PerfEvent *parent_perf_event = nullptr); /// Load \a PerfTscConversionParameters from \a perf_event_mmap_page, if /// available. llvm::Expected LoadPerfTscConversionParameters(); } // namespace process_linux } // namespace lldb_private #endif // LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H