12#include "llvm/Support/FormatVariadic.h"
13#include "llvm/Support/MathExtras.h"
14#include "llvm/Support/MemoryBuffer.h"
15#include <linux/version.h>
18#include <sys/syscall.h>
25Expected<LinuxPerfZeroTscConversion>
27#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
30 memset(&attr, 0,
sizeof(attr));
31 attr.size =
sizeof(attr);
32 attr.type = PERF_TYPE_SOFTWARE;
33 attr.config = PERF_COUNT_SW_DUMMY;
37 return perf_event.takeError();
39 perf_event->MmapMetadataAndBuffers(0,
42 return std::move(mmap_err);
44 perf_event_mmap_page &mmap_metada = perf_event->GetMetadataPage();
45 if (mmap_metada.cap_user_time && mmap_metada.cap_user_time_zero) {
47 mmap_metada.time_mult, mmap_metada.time_shift, {mmap_metada.time_zero}};
50 !mmap_metada.cap_user_time ?
"cap_user_time" :
"cap_user_time_zero";
52 llvm::formatv(
"Can't get TSC to real time conversion values. "
53 "perf_event capability '{0}' not supported.",
55 return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
58 std::string err_msg =
"PERF_COUNT_SW_DUMMY requires Linux 3.12";
59 return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
74 std::default_delete<long>()(ptr);
78 std::optional<lldb::pid_t> pid,
79 std::optional<lldb::cpu_id_t> cpu,
80 std::optional<long> group_fd,
81 unsigned long flags) {
83 long fd = syscall(SYS_perf_event_open, &attr, pid.value_or(-1),
84 cpu.value_or(-1), group_fd.value_or(-1), flags);
87 llvm::formatv(
"perf event syscall failed: {0}", std::strerror(errno));
88 return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
94 std::optional<lldb::pid_t> pid,
95 std::optional<lldb::cpu_id_t> cpu) {
96 return Init(attr, pid, cpu, -1, 0);
99llvm::Expected<resource_handle::MmapUP>
101 long int offset, llvm::StringRef buffer_name) {
103 auto mmap_result = ::mmap(addr, length, prot, flags,
GetFd(), offset);
105 if (mmap_result == MAP_FAILED) {
106 std::string err_msg =
107 llvm::formatv(
"perf event mmap allocation failed for {0}: {1}",
108 buffer_name, std::strerror(errno));
115 bool data_buffer_write) {
116 size_t mmap_size = (num_data_pages + 1) * getpagesize();
117 if (Expected<resource_handle::MmapUP> mmap_metadata_data =
DoMmap(
119 MAP_SHARED, 0,
"metadata and data buffer")) {
121 return Error::success();
123 return mmap_metadata_data.takeError();
127#ifndef PERF_ATTR_SIZE_VER5
129 "Intel PT Linux perf event not supported");
131 if (num_aux_pages == 0)
132 return Error::success();
136 metadata_page.aux_offset =
137 metadata_page.data_offset + metadata_page.data_size;
138 metadata_page.aux_size = num_aux_pages * getpagesize();
140 if (Expected<resource_handle::MmapUP> mmap_aux =
142 metadata_page.aux_offset,
"aux buffer")) {
144 return Error::success();
146 return mmap_aux.takeError();
151 size_t num_aux_pages,
152 bool data_buffer_write) {
153 if (num_data_pages != 0 && !isPowerOf2_64(num_data_pages))
154 return llvm::createStringError(
155 llvm::inconvertibleErrorCode(),
156 llvm::formatv(
"Number of data pages must be a power of 2, got: {0}",
158 if (num_aux_pages != 0 && !isPowerOf2_64(num_aux_pages))
159 return llvm::createStringError(
160 llvm::inconvertibleErrorCode(),
161 llvm::formatv(
"Number of aux pages must be a power of 2, got: {0}",
167 return Error::success();
177#ifndef PERF_ATTR_SIZE_VER5
178 llvm_unreachable(
"Intel PT Linux perf event not supported");
182 mmap_metadata.data_offset,
183 static_cast<size_t>(mmap_metadata.data_size)};
188#ifndef PERF_ATTR_SIZE_VER5
189 llvm_unreachable(
"Intel PT Linux perf event not supported");
192 return {
reinterpret_cast<uint8_t *
>(
m_aux_base.get()),
193 static_cast<size_t>(mmap_metadata.aux_size)};
203#ifndef PERF_ATTR_SIZE_VER5
205 "Intel PT Linux perf event not supported");
209 return std::move(err);
221 uint64_t data_head = mmap_metadata.data_head;
222 uint64_t data_size = mmap_metadata.data_size;
223 std::vector<uint8_t> output;
224 output.reserve(data.size());
226 if (data_head > data_size) {
227 uint64_t actual_data_head = data_head % data_size;
229 output.insert(output.end(), data.begin() + actual_data_head, data.end());
231 output.insert(output.end(), data.begin(), data.begin() + actual_data_head);
234 output.insert(output.end(), data.begin(), data.begin() + data_head);
239 return std::move(err);
252#ifndef PERF_ATTR_SIZE_VER5
254 "Intel PT Linux perf event not supported");
258 return std::move(err);
263 uint64_t aux_head = mmap_metadata.aux_head;
264 std::vector<uint8_t> output;
265 output.reserve(data.size());
279 output.insert(output.end(), data.begin() + aux_head, data.end());
280 output.insert(output.end(), data.begin(), data.begin() + aux_head);
284 return std::move(err);
293 return Error::success();
295 if (ioctl(*
m_fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) < 0)
297 "Can't disable perf event. %s",
298 std::strerror(errno));
301 return Error::success();
308 return Error::success();
310 if (ioctl(*
m_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) < 0)
312 "Can't enable perf event. %s",
313 std::strerror(errno));
316 return Error::success();
320#ifndef PERF_ATTR_SIZE_VER5
321 llvm_unreachable(
"Intel PT Linux perf event not supported");
324 if (mmap_metadata.data_head < mmap_metadata.data_size)
325 return mmap_metadata.data_head;
327 return mmap_metadata.data_size;
335#ifndef PERF_ATTR_SIZE_VER5
337 "Intel PT Linux perf event not supported");
339 perf_event_attr attr;
340 memset(&attr, 0,
sizeof(attr));
341 attr.size =
sizeof(attr);
342 attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
343 attr.type = PERF_TYPE_SOFTWARE;
344 attr.context_switch = 1;
345 attr.exclude_kernel = 1;
346 attr.sample_id_all = 1;
348 attr.disabled = parent_perf_event ? !parent_perf_event->
IsEnabled() :
false;
358 uint64_t data_buffer_size = 32768;
359 uint64_t data_buffer_numpages = data_buffer_size / getpagesize();
361 LLDB_LOG(log,
"Will create context switch trace buffer of size {0}",
364 std::optional<long> group_fd;
365 if (parent_perf_event)
366 group_fd = parent_perf_event->
GetFd();
369 attr, std::nullopt, cpu_id, group_fd, 0)) {
370 if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
371 data_buffer_numpages, 0,
false)) {
372 return std::move(mmap_err);
376 return perf_event.takeError();
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
This file contains a thin wrapper of the perf_event_open API and classes to handle the destruction of...
static llvm::Error createStringError(const char *format, Args &&...args)
Thin wrapper of the perf_event_open API.
llvm::Error MmapAuxBuffer(size_t num_aux_pages)
Mmap the aux buffer of the perf event.
llvm::Expected< std::vector< uint8_t > > GetReadOnlyAuxBuffer()
Read the aux buffer managed by this perf event assuming it was configured with PROT_READ permissions ...
resource_handle::MmapUP m_metadata_data_base
Metadata page and data section where perf samples are stored.
bool m_enabled
The state of the underlying perf_event.
llvm::Expected< std::vector< uint8_t > > GetReadOnlyDataBuffer()
Read the data buffer managed by this perf even assuming it was configured with PROT_READ permissions ...
perf_event_mmap_page & GetMetadataPage() const
Get the metadata page from the data section's mmap buffer.
resource_handle::MmapUP m_aux_base
AUX buffer is a separate region for high-bandwidth data streams such as IntelPT.
llvm::Error DisableWithIoctl()
Use the ioctl API to disable the perf event and all the events in its group.
size_t GetEffectiveDataBufferSize() const
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...
resource_handle::FileDescriptorUP m_fd
The file descriptor representing the perf event.
llvm::Error EnableWithIoctl()
Use the ioctl API to enable the perf event and all the events in its group.
long GetFd() const
Get the file descriptor associated with the perf event.
llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages, bool data_buffer_write)
Mmap the data buffer of the perf event.
llvm::ArrayRef< uint8_t > GetAuxBuffer() const
Get the AUX buffer.
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.
llvm::ArrayRef< uint8_t > GetDataBuffer() const
Get the data buffer from the data section's mmap buffer.
static llvm::Expected< PerfEvent > Init(perf_event_attr &attr, std::optional< lldb::pid_t > pid, std::optional< lldb::cpu_id_t > cpu, std::optional< long > group_fd, unsigned long flags)
Create a new performance monitoring event via the perf_event_open syscall.
PerfEvent(long fd, bool enabled)
Create new PerfEvent.
void operator()(long *ptr)
Close and free the memory associated with the file descriptor pointer.
void operator()(void *ptr)
Unmap the mmap'ed region.
size_t m_bytes
Size of the mmap'ed region, in bytes, to be unmapped.
std::unique_ptr< void, resource_handle::MmapDeleter > MmapUP
llvm::Expected< LinuxPerfZeroTscConversion > LoadPerfTscConversionParameters()
Load PerfTscConversionParameters from perf_event_mmap_page, if available.
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.
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
jLLDBTraceGetState gdb-remote packet