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 
21 #include "llvm/Support/Error.h"
22 
23 #include <chrono>
24 #include <cstdint>
25 #include <linux/perf_event.h>
26 
27 namespace lldb_private {
28 namespace process_linux {
29 namespace resource_handle {
30 
31 /// Custom deleter for the pointer returned by \a mmap.
32 ///
33 /// This functor type is provided to \a unique_ptr to properly
34 /// unmap the region at destruction time.
35 class MmapDeleter {
36 public:
37   /// Construct new \a MmapDeleter.
38   ///
39   /// \param[in] bytes
40   ///   Size of the mmap'ed region in bytes.
m_bytes(bytes)41   MmapDeleter(size_t bytes = 0) : m_bytes(bytes) {}
42 
43   /// Unmap the mmap'ed region.
44   ///
45   /// If \a m_bytes==0 or \a ptr==nullptr, nothing is unmmapped.
46   ///
47   /// \param[in] ptr
48   ///   pointer to the region to be unmmapped.
49   void operator()(void *ptr);
50 
51 private:
52   /// Size of the mmap'ed region, in bytes, to be unmapped.
53   size_t m_bytes;
54 };
55 
56 /// Custom deleter for a file descriptor.
57 ///
58 /// This functor type is provided to \a unique_ptr to properly release
59 /// the resources associated with the file descriptor at destruction time.
60 class FileDescriptorDeleter {
61 public:
62   /// Close and free the memory associated with the file descriptor pointer.
63   ///
64   /// Effectively a no-op if \a ptr==nullptr or \a*ptr==-1.
65   ///
66   /// \param[in] ptr
67   ///   Pointer to the file descriptor.
68   void operator()(long *ptr);
69 };
70 
71 using FileDescriptorUP =
72     std::unique_ptr<long, resource_handle::FileDescriptorDeleter>;
73 using MmapUP = std::unique_ptr<void, resource_handle::MmapDeleter>;
74 
75 } // namespace resource_handle
76 
77 /// Thin wrapper of the perf_event_open API.
78 ///
79 /// Exposes the metadata page and data and aux buffers of a perf event.
80 /// Handles the management of the event's file descriptor and mmap'ed
81 /// regions.
82 class PerfEvent {
83 public:
84   /// Create a new performance monitoring event via the perf_event_open syscall.
85   ///
86   /// The parameters are directly forwarded to a perf_event_open syscall,
87   /// for additional information on the parameters visit
88   /// https://man7.org/linux/man-pages/man2/perf_event_open.2.html.
89   ///
90   /// \param[in] attr
91   ///     Configuration information for the event.
92   ///
93   /// \param[in] pid
94   ///     The process or thread to be monitored by the event. If \b None, then
95   ///     all processes and threads are monitored.
96   ///
97   /// \param[in] cpu
98   ///     The cpu to be monitored by the event. If \b None, then all cpus are
99   ///     monitored.
100   ///
101   /// \param[in] group_fd
102   ///     File descriptor of the group leader. If \b None, then this perf_event
103   ///     doesn't belong to a preexisting group.
104   ///
105   /// \param[in] flags
106   ///     Bitmask of additional configuration flags.
107   ///
108   /// \return
109   ///     If the perf_event_open syscall was successful, a minimal \a PerfEvent
110   ///     instance, or an \a llvm::Error otherwise.
111   static llvm::Expected<PerfEvent> Init(perf_event_attr &attr,
112                                         llvm::Optional<lldb::pid_t> pid,
113                                         llvm::Optional<lldb::cpu_id_t> cpu,
114                                         llvm::Optional<long> group_fd,
115                                         unsigned long flags);
116 
117   /// Create a new performance monitoring event via the perf_event_open syscall
118   /// with "default" values for the cpu, group_fd and flags arguments.
119   ///
120   /// Convenience method to be used when the perf event requires minimal
121   /// configuration. It handles the default values of all other arguments.
122   ///
123   /// \param[in] attr
124   ///     Configuration information for the event.
125   ///
126   /// \param[in] pid
127   ///     The process or thread to be monitored by the event. If \b None, then
128   ///     all threads and processes are monitored.
129   static llvm::Expected<PerfEvent>
130   Init(perf_event_attr &attr, llvm::Optional<lldb::pid_t> pid,
131        llvm::Optional<lldb::cpu_id_t> core = llvm::None);
132 
133   /// Mmap the metadata page and the data and aux buffers of the perf event and
134   /// expose them through \a PerfEvent::GetMetadataPage() , \a
135   /// PerfEvent::GetDataBuffer() and \a PerfEvent::GetAuxBuffer().
136   ///
137   /// This uses mmap underneath, which means that the number of pages mmap'ed
138   /// must be less than the actual data available by the kernel. The metadata
139   /// page is always mmap'ed.
140   ///
141   /// Mmap is needed because the underlying data might be changed by the kernel
142   /// dynamically.
143   ///
144   /// \param[in] num_data_pages
145   ///     Number of pages in the data buffer to mmap, must be a power of 2.
146   ///     A value of 0 is useful for "dummy" events that only want to access
147   ///     the metadata, \a perf_event_mmap_page, or the aux buffer.
148   ///
149   /// \param[in] num_aux_pages
150   ///     Number of pages in the aux buffer to mmap, must be a power of 2.
151   ///     A value of 0 effectively is a no-op and no data is mmap'ed for this
152   ///     buffer.
153   ///
154   /// \param[in] data_buffer_write
155   ///     Whether to mmap the data buffer with WRITE permissions. This changes
156   ///     the behavior of how the kernel writes to the data buffer.
157   ///
158   /// \return
159   ///   \a llvm::Error::success if the mmap operations succeeded,
160   ///   or an \a llvm::Error otherwise.
161   llvm::Error MmapMetadataAndBuffers(size_t num_data_pages,
162                                      size_t num_aux_pages,
163                                      bool data_buffer_write);
164 
165   /// Get the file descriptor associated with the perf event.
166   long GetFd() const;
167 
168   /// Get the metadata page from the data section's mmap buffer.
169   ///
170   /// The metadata page is always mmap'ed, even when \a num_data_pages is 0.
171   ///
172   /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
173   /// otherwise a failure might happen.
174   ///
175   /// \return
176   ///   The data section's \a perf_event_mmap_page.
177   perf_event_mmap_page &GetMetadataPage() const;
178 
179   /// Get the data buffer from the data section's mmap buffer.
180   ///
181   /// The data buffer is the region of the data section's mmap buffer where
182   /// perf sample data is located.
183   ///
184   /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
185   /// otherwise a failure might happen.
186   ///
187   /// \return
188   ///   \a ArrayRef<uint8_t> extending \a data_size bytes from \a data_offset.
189   llvm::ArrayRef<uint8_t> GetDataBuffer() const;
190 
191   /// Get the AUX buffer.
192   ///
193   /// AUX buffer is a region for high-bandwidth data streams
194   /// such as IntelPT. This is separate from the metadata and data buffer.
195   ///
196   /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
197   /// otherwise a failure might happen.
198   ///
199   /// \return
200   ///   \a ArrayRef<uint8_t> extending \a aux_size bytes from \a aux_offset.
201   llvm::ArrayRef<uint8_t> GetAuxBuffer() const;
202 
203   /// Read the aux buffer managed by this perf event assuming it was configured
204   /// with PROT_READ permissions only, which indicates that the buffer is
205   /// automatically wrapped and overwritten by the kernel or hardware. To ensure
206   /// that the data is up-to-date and is not corrupted by read-write race
207   /// conditions, the underlying perf_event is paused during read, and later
208   /// it's returned to its initial state. The returned data will be linear, i.e.
209   /// it will fix the circular wrapping the might exist in the buffer.
210   ///
211   /// \return
212   ///     A vector with the requested binary data.
213   llvm::Expected<std::vector<uint8_t>> GetReadOnlyAuxBuffer();
214 
215   /// Read the data buffer managed by this perf even assuming it was configured
216   /// with PROT_READ permissions only, which indicates that the buffer is
217   /// automatically wrapped and overwritten by the kernel or hardware. To ensure
218   /// that the data is up-to-date and is not corrupted by read-write race
219   /// conditions, the underlying perf_event is paused during read, and later
220   /// it's returned to its initial state. The returned data will be linear, i.e.
221   /// it will fix the circular wrapping the might exist int he buffer.
222   ///
223   /// \return
224   ///     A vector with the requested binary data.
225   llvm::Expected<std::vector<uint8_t>> GetReadOnlyDataBuffer();
226 
227   /// Use the ioctl API to disable the perf event and all the events in its
228   /// group. This doesn't terminate the perf event.
229   ///
230   /// This is no-op if the perf event is already disabled.
231   ///
232   /// \return
233   ///   An Error if the perf event couldn't be disabled.
234   llvm::Error DisableWithIoctl();
235 
236   /// Use the ioctl API to enable the perf event and all the events in its
237   /// group.
238   ///
239   /// This is no-op if the perf event is already enabled.
240   ///
241   /// \return
242   ///   An Error if the perf event couldn't be enabled.
243   llvm::Error EnableWithIoctl();
244 
245   /// \return
246   ///   The size in bytes of the section of the data buffer that has effective
247   ///   data.
248   size_t GetEffectiveDataBufferSize() const;
249 
250   /// \return
251   ///   \b true if and only the perf event is enabled and collecting.
252   bool IsEnabled() const;
253 
254 private:
255   /// Create new \a PerfEvent.
256   ///
257   /// \param[in] fd
258   ///   File descriptor of the perf event.
259   ///
260   /// \param[in] enabled
261   ///   Initial collection state configured for this perf_event.
PerfEvent(long fd,bool enabled)262   PerfEvent(long fd, bool enabled)
263       : m_fd(new long(fd), resource_handle::FileDescriptorDeleter()),
264         m_enabled(enabled) {}
265 
266   /// Wrapper for \a mmap to provide custom error messages.
267   ///
268   /// The parameters are directly forwarded to a \a mmap syscall,
269   /// for information on the parameters visit
270   /// https://man7.org/linux/man-pages/man2/mmap.2.html.
271   ///
272   /// The value of \a GetFd() is passed as the \a fd argument to \a mmap.
273   llvm::Expected<resource_handle::MmapUP> DoMmap(void *addr, size_t length,
274                                                  int prot, int flags,
275                                                  long int offset,
276                                                  llvm::StringRef buffer_name);
277 
278   /// Mmap the data buffer of the perf event.
279   ///
280   /// \param[in] num_data_pages
281   ///     Number of pages in the data buffer to mmap, must be a power of 2.
282   ///     A value of 0 is useful for "dummy" events that only want to access
283   ///     the metadata, \a perf_event_mmap_page, or the aux buffer.
284   ///
285   /// \param[in] data_buffer_write
286   ///     Whether to mmap the data buffer with WRITE permissions. This changes
287   ///     the behavior of how the kernel writes to the data buffer.
288   llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages,
289                                         bool data_buffer_write);
290 
291   /// Mmap the aux buffer of the perf event.
292   ///
293   /// \param[in] num_aux_pages
294   ///   Number of pages in the aux buffer to mmap, must be a power of 2.
295   ///   A value of 0 effectively is a no-op and no data is mmap'ed for this
296   ///   buffer.
297   llvm::Error MmapAuxBuffer(size_t num_aux_pages);
298 
299   /// The file descriptor representing the perf event.
300   resource_handle::FileDescriptorUP m_fd;
301   /// Metadata page and data section where perf samples are stored.
302   resource_handle::MmapUP m_metadata_data_base;
303   /// AUX buffer is a separate region for high-bandwidth data streams
304   /// such as IntelPT.
305   resource_handle::MmapUP m_aux_base;
306   /// The state of the underlying perf_event.
307   bool m_enabled;
308 };
309 
310 /// Create a perf event that tracks context switches on a cpu.
311 ///
312 /// \param[in] cpu_id
313 ///   The core to trace.
314 ///
315 /// \param[in] parent_perf_event
316 ///   An optional perf event that will be grouped with the
317 ///   new perf event.
318 llvm::Expected<PerfEvent>
319 CreateContextSwitchTracePerfEvent(lldb::cpu_id_t cpu_id,
320                                   const PerfEvent *parent_perf_event = nullptr);
321 
322 /// Load \a PerfTscConversionParameters from \a perf_event_mmap_page, if
323 /// available.
324 llvm::Expected<LinuxPerfZeroTscConversion> LoadPerfTscConversionParameters();
325 
326 } // namespace process_linux
327 } // namespace lldb_private
328 
329 #endif // LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H
330