brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.3 KiB · 658a5c3 Raw
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