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 None, then
126   ///     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