LLDB  mainline
Perf.cpp
Go to the documentation of this file.
1 //===-- Perf.cpp ----------------------------------------------------------===//
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 
9 #include "Perf.h"
10 
13 #include "llvm/Support/FormatVariadic.h"
14 #include "llvm/Support/MathExtras.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include <linux/version.h>
17 #include <sys/ioctl.h>
18 #include <sys/mman.h>
19 #include <sys/syscall.h>
20 #include <unistd.h>
21 
22 using namespace lldb_private;
23 using namespace process_linux;
24 using namespace llvm;
25 
26 Expected<LinuxPerfZeroTscConversion>
28 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
29  lldb::pid_t pid = getpid();
30  perf_event_attr attr;
31  memset(&attr, 0, sizeof(attr));
32  attr.size = sizeof(attr);
33  attr.type = PERF_TYPE_SOFTWARE;
34  attr.config = PERF_COUNT_SW_DUMMY;
35 
36  Expected<PerfEvent> perf_event = PerfEvent::Init(attr, pid);
37  if (!perf_event)
38  return perf_event.takeError();
39  if (Error mmap_err =
40  perf_event->MmapMetadataAndBuffers(/*num_data_pages=*/0,
41  /*num_aux_pages=*/0,
42  /*data_buffer_write=*/false))
43  return std::move(mmap_err);
44 
45  perf_event_mmap_page &mmap_metada = perf_event->GetMetadataPage();
46  if (mmap_metada.cap_user_time && mmap_metada.cap_user_time_zero) {
48  mmap_metada.time_mult, mmap_metada.time_shift, {mmap_metada.time_zero}};
49  } else {
50  auto err_cap =
51  !mmap_metada.cap_user_time ? "cap_user_time" : "cap_user_time_zero";
52  std::string err_msg =
53  llvm::formatv("Can't get TSC to real time conversion values. "
54  "perf_event capability '{0}' not supported.",
55  err_cap);
56  return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
57  }
58 #else
59  std::string err_msg = "PERF_COUNT_SW_DUMMY requires Linux 3.12";
60  return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
61 #endif
62 }
63 
65  if (m_bytes && ptr != nullptr)
66  munmap(ptr, m_bytes);
67 }
68 
70  if (ptr == nullptr)
71  return;
72  if (*ptr == -1)
73  return;
74  close(*ptr);
75  std::default_delete<long>()(ptr);
76 }
77 
78 llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr,
79  Optional<lldb::pid_t> pid,
80  Optional<lldb::cpu_id_t> cpu,
81  Optional<long> group_fd,
82  unsigned long flags) {
83  errno = 0;
84  long fd = syscall(SYS_perf_event_open, &attr, pid.value_or(-1),
85  cpu.value_or(-1), group_fd.value_or(-1), flags);
86  if (fd == -1) {
87  std::string err_msg =
88  llvm::formatv("perf event syscall failed: {0}", std::strerror(errno));
89  return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
90  }
91  return PerfEvent(fd, !attr.disabled);
92 }
93 
94 llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr,
95  Optional<lldb::pid_t> pid,
96  Optional<lldb::cpu_id_t> cpu) {
97  return Init(attr, pid, cpu, -1, 0);
98 }
99 
100 llvm::Expected<resource_handle::MmapUP>
101 PerfEvent::DoMmap(void *addr, size_t length, int prot, int flags,
102  long int offset, llvm::StringRef buffer_name) {
103  errno = 0;
104  auto mmap_result = ::mmap(addr, length, prot, flags, GetFd(), offset);
105 
106  if (mmap_result == MAP_FAILED) {
107  std::string err_msg =
108  llvm::formatv("perf event mmap allocation failed for {0}: {1}",
109  buffer_name, std::strerror(errno));
110  return createStringError(inconvertibleErrorCode(), err_msg);
111  }
112  return resource_handle::MmapUP(mmap_result, length);
113 }
114 
116  bool data_buffer_write) {
117  size_t mmap_size = (num_data_pages + 1) * getpagesize();
118  if (Expected<resource_handle::MmapUP> mmap_metadata_data = DoMmap(
119  nullptr, mmap_size, PROT_READ | (data_buffer_write ? PROT_WRITE : 0),
120  MAP_SHARED, 0, "metadata and data buffer")) {
121  m_metadata_data_base = std::move(mmap_metadata_data.get());
122  return Error::success();
123  } else
124  return mmap_metadata_data.takeError();
125 }
126 
127 llvm::Error PerfEvent::MmapAuxBuffer(size_t num_aux_pages) {
128  if (num_aux_pages == 0)
129  return Error::success();
130 
131  perf_event_mmap_page &metadata_page = GetMetadataPage();
132 
133  metadata_page.aux_offset =
134  metadata_page.data_offset + metadata_page.data_size;
135  metadata_page.aux_size = num_aux_pages * getpagesize();
136 
137  if (Expected<resource_handle::MmapUP> mmap_aux =
138  DoMmap(nullptr, metadata_page.aux_size, PROT_READ, MAP_SHARED,
139  metadata_page.aux_offset, "aux buffer")) {
140  m_aux_base = std::move(mmap_aux.get());
141  return Error::success();
142  } else
143  return mmap_aux.takeError();
144 }
145 
147  size_t num_aux_pages,
148  bool data_buffer_write) {
149  if (num_data_pages != 0 && !isPowerOf2_64(num_data_pages))
150  return llvm::createStringError(
151  llvm::inconvertibleErrorCode(),
152  llvm::formatv("Number of data pages must be a power of 2, got: {0}",
153  num_data_pages));
154  if (num_aux_pages != 0 && !isPowerOf2_64(num_aux_pages))
155  return llvm::createStringError(
156  llvm::inconvertibleErrorCode(),
157  llvm::formatv("Number of aux pages must be a power of 2, got: {0}",
158  num_aux_pages));
159  if (Error err = MmapMetadataAndDataBuffer(num_data_pages, data_buffer_write))
160  return err;
161  if (Error err = MmapAuxBuffer(num_aux_pages))
162  return err;
163  return Error::success();
164 }
165 
166 long PerfEvent::GetFd() const { return *(m_fd.get()); }
167 
168 perf_event_mmap_page &PerfEvent::GetMetadataPage() const {
169  return *reinterpret_cast<perf_event_mmap_page *>(m_metadata_data_base.get());
170 }
171 
172 ArrayRef<uint8_t> PerfEvent::GetDataBuffer() const {
173  perf_event_mmap_page &mmap_metadata = GetMetadataPage();
174  return {reinterpret_cast<uint8_t *>(m_metadata_data_base.get()) +
175  mmap_metadata.data_offset,
176  static_cast<size_t>(mmap_metadata.data_size)};
177 }
178 
179 ArrayRef<uint8_t> PerfEvent::GetAuxBuffer() const {
180  perf_event_mmap_page &mmap_metadata = GetMetadataPage();
181  return {reinterpret_cast<uint8_t *>(m_aux_base.get()),
182  static_cast<size_t>(mmap_metadata.aux_size)};
183 }
184 
185 Expected<std::vector<uint8_t>> PerfEvent::GetReadOnlyDataBuffer() {
186  // The following code assumes that the protection level of the DATA page
187  // is PROT_READ. If PROT_WRITE is used, then reading would require that
188  // this piece of code updates some pointers. See more about data_tail
189  // in https://man7.org/linux/man-pages/man2/perf_event_open.2.html.
190 
191  bool was_enabled = m_enabled;
192  if (Error err = DisableWithIoctl())
193  return std::move(err);
194 
195  /**
196  * The data buffer and aux buffer have different implementations
197  * with respect to their definition of head pointer when using PROD_READ only.
198  * In the case of Aux data buffer the head always wraps around the aux buffer
199  * and we don't need to care about it, whereas the data_head keeps
200  * increasing and needs to be wrapped by modulus operator
201  */
202  perf_event_mmap_page &mmap_metadata = GetMetadataPage();
203 
204  ArrayRef<uint8_t> data = GetDataBuffer();
205  uint64_t data_head = mmap_metadata.data_head;
206  uint64_t data_size = mmap_metadata.data_size;
207  std::vector<uint8_t> output;
208  output.reserve(data.size());
209 
210  if (data_head > data_size) {
211  uint64_t actual_data_head = data_head % data_size;
212  // The buffer has wrapped, so we first the oldest chunk of data
213  output.insert(output.end(), data.begin() + actual_data_head, data.end());
214  // And we we read the most recent chunk of data
215  output.insert(output.end(), data.begin(), data.begin() + actual_data_head);
216  } else {
217  // There's been no wrapping, so we just read linearly
218  output.insert(output.end(), data.begin(), data.begin() + data_head);
219  }
220 
221  if (was_enabled) {
222  if (Error err = EnableWithIoctl())
223  return std::move(err);
224  }
225 
226  return output;
227 }
228 
229 Expected<std::vector<uint8_t>> PerfEvent::GetReadOnlyAuxBuffer() {
230  // The following code assumes that the protection level of the AUX page
231  // is PROT_READ. If PROT_WRITE is used, then reading would require that
232  // this piece of code updates some pointers. See more about aux_tail
233  // in https://man7.org/linux/man-pages/man2/perf_event_open.2.html.
234 
235  bool was_enabled = m_enabled;
236  if (Error err = DisableWithIoctl())
237  return std::move(err);
238 
239  perf_event_mmap_page &mmap_metadata = GetMetadataPage();
240 
241  ArrayRef<uint8_t> data = GetAuxBuffer();
242  uint64_t aux_head = mmap_metadata.aux_head;
243  std::vector<uint8_t> output;
244  output.reserve(data.size());
245 
246  /**
247  * When configured as ring buffer, the aux buffer keeps wrapping around
248  * the buffer and its not possible to detect how many times the buffer
249  * wrapped. Initially the buffer is filled with zeros,as shown below
250  * so in order to get complete buffer we first copy firstpartsize, followed
251  * by any left over part from beginning to aux_head
252  *
253  * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
254  * aux_head->||<- firstpartsize ->|
255  *
256  * */
257 
258  output.insert(output.end(), data.begin() + aux_head, data.end());
259  output.insert(output.end(), data.begin(), data.begin() + aux_head);
260 
261  if (was_enabled) {
262  if (Error err = EnableWithIoctl())
263  return std::move(err);
264  }
265 
266  return output;
267 }
268 
270  if (!m_enabled)
271  return Error::success();
272 
273  if (ioctl(*m_fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) < 0)
274  return createStringError(inconvertibleErrorCode(),
275  "Can't disable perf event. %s",
276  std::strerror(errno));
277 
278  m_enabled = false;
279  return Error::success();
280 }
281 
282 bool PerfEvent::IsEnabled() const { return m_enabled; }
283 
285  if (m_enabled)
286  return Error::success();
287 
288  if (ioctl(*m_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) < 0)
289  return createStringError(inconvertibleErrorCode(),
290  "Can't enable perf event. %s",
291  std::strerror(errno));
292 
293  m_enabled = true;
294  return Error::success();
295 }
296 
298  perf_event_mmap_page &mmap_metadata = GetMetadataPage();
299  if (mmap_metadata.data_head < mmap_metadata.data_size)
300  return mmap_metadata.data_head;
301  else
302  return mmap_metadata.data_size; // The buffer has wrapped.
303 }
304 
305 Expected<PerfEvent>
307  lldb::cpu_id_t cpu_id, const PerfEvent *parent_perf_event) {
308  Log *log = GetLog(POSIXLog::Trace);
309 #ifndef PERF_ATTR_SIZE_VER5
310  return createStringError(inconvertibleErrorCode(),
311  "Intel PT Linux perf event not supported");
312 #else
313  perf_event_attr attr;
314  memset(&attr, 0, sizeof(attr));
315  attr.size = sizeof(attr);
316  attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
317  attr.type = PERF_TYPE_SOFTWARE;
318  attr.context_switch = 1;
319  attr.exclude_kernel = 1;
320  attr.sample_id_all = 1;
321  attr.exclude_hv = 1;
322  attr.disabled = parent_perf_event ? !parent_perf_event->IsEnabled() : false;
323 
324  // The given perf configuration will produce context switch records of 32
325  // bytes each. Assuming that every context switch will be emitted twice (one
326  // for context switch ins and another one for context switch outs), and that a
327  // context switch will happen at least every half a millisecond per core, we
328  // need 500 * 32 bytes (~16 KB) for a trace of one second, which is much more
329  // than what a regular intel pt trace can get. Pessimistically we pick as
330  // 32KiB for the size of our context switch trace.
331 
332  uint64_t data_buffer_size = 32768;
333  uint64_t data_buffer_numpages = data_buffer_size / getpagesize();
334 
335  LLDB_LOG(log, "Will create context switch trace buffer of size {0}",
336  data_buffer_size);
337 
338  Optional<long> group_fd;
339  if (parent_perf_event)
340  group_fd = parent_perf_event->GetFd();
341 
342  if (Expected<PerfEvent> perf_event =
343  PerfEvent::Init(attr, /*pid=*/None, cpu_id, group_fd, /*flags=*/0)) {
344  if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
345  data_buffer_numpages, 0, /*data_buffer_write=*/false)) {
346  return std::move(mmap_err);
347  }
348  return perf_event;
349  } else {
350  return perf_event.takeError();
351  }
352 #endif
353 }
lldb_private::process_linux::PerfEvent::GetAuxBuffer
llvm::ArrayRef< uint8_t > GetAuxBuffer() const
Get the AUX buffer.
Definition: Perf.cpp:179
llvm
Definition: Debugger.h:50
lldb_private::process_linux::LoadPerfTscConversionParameters
llvm::Expected< LinuxPerfZeroTscConversion > LoadPerfTscConversionParameters()
Load PerfTscConversionParameters from perf_event_mmap_page, if available.
Definition: Perf.cpp:27
lldb_private::POSIXLog::Trace
@ Trace
lldb_private::process_linux::PerfEvent::IsEnabled
bool IsEnabled() const
Definition: Perf.cpp:282
lldb_private::process_linux::PerfEvent::GetEffectiveDataBufferSize
size_t GetEffectiveDataBufferSize() const
Definition: Perf.cpp:297
PROT_WRITE
#define PROT_WRITE
Definition: InferiorCallPOSIX.cpp:30
lldb_private::process_linux::PerfEvent::GetReadOnlyDataBuffer
llvm::Expected< std::vector< uint8_t > > GetReadOnlyDataBuffer()
Read the data buffer managed by this perf even assuming it was configured with PROT_READ permissions ...
Definition: Perf.cpp:185
lldb_private::LinuxPerfZeroTscConversion::time_mult
uint32_t time_mult
Definition: TraceIntelPTGDBRemotePackets.h:106
lldb_private::process_linux::PerfEvent
Thin wrapper of the perf_event_open API.
Definition: Perf.h:80
lldb_private::process_linux::PerfEvent::EnableWithIoctl
llvm::Error EnableWithIoctl()
Use the ioctl API to enable the perf event and all the events in its group.
Definition: Perf.cpp:284
lldb_private::process_linux::PerfEvent::Init
static llvm::Expected< PerfEvent > Init(perf_event_attr &attr, llvm::Optional< lldb::pid_t > pid, llvm::Optional< lldb::cpu_id_t > cpu, llvm::Optional< long > group_fd, unsigned long flags)
Create a new performance monitoring event via the perf_event_open syscall.
ProcessPOSIXLog.h
lldb_private::process_linux::PerfEvent::MmapAuxBuffer
llvm::Error MmapAuxBuffer(size_t num_aux_pages)
Mmap the aux buffer of the perf event.
Definition: Perf.cpp:127
PROT_READ
#define PROT_READ
Definition: InferiorCallPOSIX.cpp:29
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::process_linux::PerfEvent::MmapMetadataAndBuffers
llvm::Error MmapMetadataAndBuffers(size_t num_data_pages, size_t num_aux_pages, bool data_buffer_write)
Mmap the metadata page and the data and aux buffers of the perf event and expose them through PerfEve...
Definition: Perf.cpp:146
lldb_private::process_linux::PerfEvent::DisableWithIoctl
llvm::Error DisableWithIoctl()
Use the ioctl API to disable the perf event and all the events in its group.
Definition: Perf.cpp:269
uint32_t
lldb_private::process_linux::resource_handle::FileDescriptorDeleter::operator()
void operator()(long *ptr)
Close and free the memory associated with the file descriptor pointer.
Definition: Perf.cpp:69
lldb::pid_t
uint64_t pid_t
Definition: lldb-types.h:85
lldb_private::process_linux::CreateContextSwitchTracePerfEvent
llvm::Expected< PerfEvent > CreateContextSwitchTracePerfEvent(lldb::cpu_id_t cpu_id, const PerfEvent *parent_perf_event=nullptr)
Create a perf event that tracks context switches on a cpu.
Definition: Perf.cpp:306
Support.h
lldb_private::LinuxPerfZeroTscConversion
jLLDBTraceGetState gdb-remote packet
Definition: TraceIntelPTGDBRemotePackets.h:89
LLDB_LOG
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:336
Perf.h
lldb_private::process_linux::resource_handle::MmapDeleter::operator()
void operator()(void *ptr)
Unmap the mmap'ed region.
Definition: Perf.cpp:64
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
Error
llvm::Error Error
Definition: UdtRecordCompleter.cpp:30
lldb_private::process_linux::resource_handle::MmapUP
std::unique_ptr< void, resource_handle::MmapDeleter > MmapUP
Definition: Perf.h:71
lldb_private::process_linux::PerfEvent::MmapMetadataAndDataBuffer
llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages, bool data_buffer_write)
Mmap the data buffer of the perf event.
Definition: Perf.cpp:115
lldb_private::Log
Definition: Log.h:115
lldb_private::process_linux::PerfEvent::GetReadOnlyAuxBuffer
llvm::Expected< std::vector< uint8_t > > GetReadOnlyAuxBuffer()
Read the aux buffer managed by this perf event assuming it was configured with PROT_READ permissions ...
Definition: Perf.cpp:229
lldb_private::process_linux::PerfEvent::DoMmap
llvm::Expected< resource_handle::MmapUP > DoMmap(void *addr, size_t length, int prot, int flags, long int offset, llvm::StringRef buffer_name)
Wrapper for mmap to provide custom error messages.
Definition: Perf.cpp:101
lldb_private::process_linux::PerfEvent::GetMetadataPage
perf_event_mmap_page & GetMetadataPage() const
Get the metadata page from the data section's mmap buffer.
Definition: Perf.cpp:168
lldb_private::GetLog
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:308
lldb_private::process_linux::PerfEvent::GetFd
long GetFd() const
Get the file descriptor associated with the perf event.
Definition: Perf.cpp:166
lldb_private::process_linux::PerfEvent::GetDataBuffer
llvm::ArrayRef< uint8_t > GetDataBuffer() const
Get the data buffer from the data section's mmap buffer.
Definition: Perf.cpp:172