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/lldb-types.h"
19 
20 #include "llvm/Support/Error.h"
21 
22 #include <chrono>
23 #include <cstdint>
24 #include <linux/perf_event.h>
25 
26 namespace lldb_private {
27 namespace process_linux {
28 namespace resource_handle {
29 
30 /// Custom deleter for the pointer returned by \a mmap.
31 ///
32 /// This functor type is provided to \a unique_ptr to properly
33 /// unmap the region at destruction time.
34 class MmapDeleter {
35 public:
36   /// Construct new \a MmapDeleter.
37   ///
38   /// \param[in] bytes
39   ///   Size of the mmap'ed region in bytes.
40   MmapDeleter(size_t bytes = 0) : m_bytes(bytes) {}
41 
42   /// Unmap the mmap'ed region.
43   ///
44   /// If \a m_bytes==0 or \a ptr==nullptr, nothing is unmmapped.
45   ///
46   /// \param[in] ptr
47   ///   pointer to the region to be unmmapped.
48   void operator()(void *ptr);
49 
50 private:
51   /// Size of the mmap'ed region, in bytes, to be unmapped.
52   size_t m_bytes;
53 };
54 
55 /// Custom deleter for a file descriptor.
56 ///
57 /// This functor type is provided to \a unique_ptr to properly release
58 /// the resources associated with the file descriptor at destruction time.
59 class FileDescriptorDeleter {
60 public:
61   /// Close and free the memory associated with the file descriptor pointer.
62   ///
63   /// Effectively a no-op if \a ptr==nullptr or \a*ptr==-1.
64   ///
65   /// \param[in] ptr
66   ///   Pointer to the file descriptor.
67   void operator()(long *ptr);
68 };
69 
70 using FileDescriptorUP =
71     std::unique_ptr<long, resource_handle::FileDescriptorDeleter>;
72 using MmapUP = std::unique_ptr<void, resource_handle::MmapDeleter>;
73 
74 } // namespace resource_handle
75 
76 /// Thin wrapper of the perf_event_open API.
77 ///
78 /// Exposes the metadata page and data and aux buffers of a perf event.
79 /// Handles the management of the event's file descriptor and mmap'ed
80 /// regions.
81 class PerfEvent {
82 public:
83   /// Create a new performance monitoring event via the perf_event_open syscall.
84   ///
85   /// The parameters are directly forwarded to a perf_event_open syscall,
86   /// for additional information on the parameters visit
87   /// https://man7.org/linux/man-pages/man2/perf_event_open.2.html.
88   ///
89   /// \param[in] attr
90   ///     Configuration information for the event.
91   ///
92   /// \param[in] pid
93   ///     The process to be monitored by the event.
94   ///
95   /// \param[in] cpu
96   ///     The cpu to be monitored by the event.
97   ///
98   /// \param[in] group_fd
99   ///     File descriptor of the group leader.
100   ///
101   /// \param[in] flags
102   ///     Bitmask of additional configuration flags.
103   ///
104   /// \return
105   ///     If the perf_event_open syscall was successful, a minimal \a PerfEvent
106   ///     instance, or an \a llvm::Error otherwise.
107   static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, lldb::pid_t pid,
108                                         int cpu, int group_fd,
109                                         unsigned long flags);
110 
111   /// Create a new performance monitoring event via the perf_event_open syscall
112   /// with "default" values for the cpu, group_fd and flags arguments.
113   ///
114   /// Convenience method to be used when the perf event requires minimal
115   /// configuration. It handles the default values of all other arguments.
116   ///
117   /// \param[in] attr
118   ///     Configuration information for the event.
119   ///
120   /// \param[in] pid
121   ///     The process to be monitored by the event.
122   static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, lldb::pid_t pid);
123 
124   /// Mmap the metadata page and the data and aux buffers of the perf event and
125   /// expose them through \a PerfEvent::GetMetadataPage() , \a
126   /// PerfEvent::GetDataBuffer() and \a PerfEvent::GetAuxBuffer().
127   ///
128   /// This uses mmap underneath, which means that the number of pages mmap'ed
129   /// must be less than the actual data available by the kernel. The metadata
130   /// page is always mmap'ed.
131   ///
132   /// Mmap is needed because the underlying data might be changed by the kernel
133   /// dynamically.
134   ///
135   /// \param[in] num_data_pages
136   ///     Number of pages in the data buffer to mmap, must be a power of 2.
137   ///     A value of 0 is useful for "dummy" events that only want to access
138   ///     the metadata, \a perf_event_mmap_page, or the aux buffer.
139   ///
140   /// \param[in] num_aux_pages
141   ///     Number of pages in the aux buffer to mmap, must be a power of 2.
142   ///     A value of 0 effectively is a no-op and no data is mmap'ed for this
143   ///     buffer.
144   ///
145   /// \return
146   ///   \a llvm::Error::success if the mmap operations succeeded,
147   ///   or an \a llvm::Error otherwise.
148   llvm::Error MmapMetadataAndBuffers(size_t num_data_pages,
149                                      size_t num_aux_pages);
150 
151   /// Get the file descriptor associated with the perf event.
152   long GetFd() const;
153 
154   /// Get the metadata page from the data section's mmap buffer.
155   ///
156   /// The metadata page is always mmap'ed, even when \a num_data_pages is 0.
157   ///
158   /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
159   /// otherwise a failure might happen.
160   ///
161   /// \return
162   ///   The data section's \a perf_event_mmap_page.
163   perf_event_mmap_page &GetMetadataPage() const;
164 
165   /// Get the data buffer from the data section's mmap buffer.
166   ///
167   /// The data buffer is the region of the data section's mmap buffer where
168   /// perf sample data is located.
169   ///
170   /// This should be called only after \a PerfEvent::MmapMetadataAndBuffers,
171   /// otherwise a failure might happen.
172   ///
173   /// \return
174   ///   \a ArrayRef<uint8_t> extending \a data_size bytes from \a data_offset.
175   llvm::ArrayRef<uint8_t> GetDataBuffer() const;
176 
177   /// Get the AUX buffer.
178   ///
179   /// AUX buffer is a region for high-bandwidth data streams
180   /// such as IntelPT. This is separate from the metadata and data buffer.
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 aux_size bytes from \a aux_offset.
187   llvm::ArrayRef<uint8_t> GetAuxBuffer() const;
188 
189 private:
190   /// Create new \a PerfEvent.
191   ///
192   /// \param[in] fd
193   ///   File descriptor of the perf event.
194   PerfEvent(long fd)
195       : m_fd(new long(fd), resource_handle::FileDescriptorDeleter()),
196         m_metadata_data_base(), m_aux_base() {}
197 
198   /// Wrapper for \a mmap to provide custom error messages.
199   ///
200   /// The parameters are directly forwarded to a \a mmap syscall,
201   /// for information on the parameters visit
202   /// https://man7.org/linux/man-pages/man2/mmap.2.html.
203   ///
204   /// The value of \a GetFd() is passed as the \a fd argument to \a mmap.
205   llvm::Expected<resource_handle::MmapUP> DoMmap(void *addr, size_t length,
206                                                  int prot, int flags,
207                                                  long int offset,
208                                                  llvm::StringRef buffer_name);
209 
210   /// Mmap the data buffer of the perf event.
211   ///
212   /// \param[in] num_data_pages
213   ///     Number of pages in the data buffer to mmap, must be a power of 2.
214   ///     A value of 0 is useful for "dummy" events that only want to access
215   ///     the metadata, \a perf_event_mmap_page, or the aux buffer.
216   llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages);
217 
218   /// Mmap the aux buffer of the perf event.
219   ///
220   /// \param[in] num_aux_pages
221   ///   Number of pages in the aux buffer to mmap, must be a power of 2.
222   ///   A value of 0 effectively is a no-op and no data is mmap'ed for this
223   ///   buffer.
224   llvm::Error MmapAuxBuffer(size_t num_aux_pages);
225 
226   /// The file descriptor representing the perf event.
227   resource_handle::FileDescriptorUP m_fd;
228   /// Metadata page and data section where perf samples are stored.
229   resource_handle::MmapUP m_metadata_data_base;
230   /// AUX buffer is a separate region for high-bandwidth data streams
231   /// such as IntelPT.
232   resource_handle::MmapUP m_aux_base;
233 };
234 
235 /// TSC to nanoseconds conversion values defined by the Linux perf_event API
236 /// when the capibilities cap_user_time and cap_user_time_zero are set. See the
237 /// documentation of `time_zero` in
238 /// https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more
239 /// information.
240 struct PerfTscConversionParameters {
241   uint32_t m_time_mult;
242   uint16_t m_time_shift;
243   uint64_t m_time_zero;
244 
245   /// Convert TSC value to nanosecond wall time.
246   ///
247   /// \a param[in] tsc
248   ///   The TSC value to be converted.
249   ///
250   /// \return
251   ///   Nanosecond wall time.
252   std::chrono::nanoseconds ToWallTime(uint64_t tsc);
253 };
254 
255 /// Fetch \a PerfTscConversionParameters from \a perf_event_mmap_page, if
256 /// available.
257 llvm::Expected<PerfTscConversionParameters> FetchPerfTscConversionParameters();
258 
259 } // namespace process_linux
260 } // namespace lldb_private
261 
262 #endif // LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H
263