13#include "llvm/Support/FormatVariadic.h"
14#include "llvm/Support/MathExtras.h"
15#include "llvm/Support/MemoryBuffer.h"
16#include <linux/version.h>
19#include <sys/syscall.h>
23using namespace process_linux;
26Expected<LinuxPerfZeroTscConversion>
28#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
31 memset(&attr, 0,
sizeof(attr));
32 attr.size =
sizeof(attr);
33 attr.type = PERF_TYPE_SOFTWARE;
34 attr.config = PERF_COUNT_SW_DUMMY;
38 return perf_event.takeError();
40 perf_event->MmapMetadataAndBuffers(0,
43 return std::move(mmap_err);
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}};
51 !mmap_metada.cap_user_time ?
"cap_user_time" :
"cap_user_time_zero";
53 llvm::formatv(
"Can't get TSC to real time conversion values. "
54 "perf_event capability '{0}' not supported.",
56 return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
59 std::string err_msg =
"PERF_COUNT_SW_DUMMY requires Linux 3.12";
60 return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
75 std::default_delete<long>()(ptr);
79 std::optional<lldb::pid_t> pid,
80 std::optional<lldb::cpu_id_t> cpu,
81 std::optional<long> group_fd,
82 unsigned long flags) {
84 long fd = syscall(SYS_perf_event_open, &attr, pid.value_or(-1),
85 cpu.value_or(-1), group_fd.value_or(-1), flags);
88 llvm::formatv(
"perf event syscall failed: {0}", std::strerror(errno));
89 return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg);
95 std::optional<lldb::pid_t> pid,
96 std::optional<lldb::cpu_id_t> cpu) {
97 return Init(attr, pid, cpu, -1, 0);
100llvm::Expected<resource_handle::MmapUP>
102 long int offset, llvm::StringRef buffer_name) {
104 auto mmap_result = ::mmap(addr, length, prot, flags, GetFd(), offset);
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);
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(
120 MAP_SHARED, 0,
"metadata and data buffer")) {
121 m_metadata_data_base = std::move(mmap_metadata_data.get());
122 return Error::success();
124 return mmap_metadata_data.takeError();
128#ifndef PERF_ATTR_SIZE_VER5
129 return createStringError(inconvertibleErrorCode(),
130 "Intel PT Linux perf event not supported");
132 if (num_aux_pages == 0)
133 return Error::success();
135 perf_event_mmap_page &metadata_page = GetMetadataPage();
137 metadata_page.aux_offset =
138 metadata_page.data_offset + metadata_page.data_size;
139 metadata_page.aux_size = num_aux_pages * getpagesize();
141 if (Expected<resource_handle::MmapUP> mmap_aux =
142 DoMmap(
nullptr, metadata_page.aux_size,
PROT_READ, MAP_SHARED,
143 metadata_page.aux_offset,
"aux buffer")) {
144 m_aux_base = std::move(mmap_aux.get());
145 return Error::success();
147 return mmap_aux.takeError();
152 size_t num_aux_pages,
153 bool data_buffer_write) {
154 if (num_data_pages != 0 && !isPowerOf2_64(num_data_pages))
155 return llvm::createStringError(
156 llvm::inconvertibleErrorCode(),
157 llvm::formatv(
"Number of data pages must be a power of 2, got: {0}",
159 if (num_aux_pages != 0 && !isPowerOf2_64(num_aux_pages))
160 return llvm::createStringError(
161 llvm::inconvertibleErrorCode(),
162 llvm::formatv(
"Number of aux pages must be a power of 2, got: {0}",
164 if (
Error err = MmapMetadataAndDataBuffer(num_data_pages, data_buffer_write))
166 if (
Error err = MmapAuxBuffer(num_aux_pages))
168 return Error::success();
174 return *
reinterpret_cast<perf_event_mmap_page *
>(m_metadata_data_base.get());
178#ifndef PERF_ATTR_SIZE_VER5
179 llvm_unreachable(
"Intel PT Linux perf event not supported");
181 perf_event_mmap_page &mmap_metadata = GetMetadataPage();
182 return {
reinterpret_cast<uint8_t *
>(m_metadata_data_base.get()) +
183 mmap_metadata.data_offset,
184 static_cast<size_t>(mmap_metadata.data_size)};
189#ifndef PERF_ATTR_SIZE_VER5
190 llvm_unreachable(
"Intel PT Linux perf event not supported");
192 perf_event_mmap_page &mmap_metadata = GetMetadataPage();
193 return {
reinterpret_cast<uint8_t *
>(m_aux_base.get()),
194 static_cast<size_t>(mmap_metadata.aux_size)};
204#ifndef PERF_ATTR_SIZE_VER5
205 return createStringError(inconvertibleErrorCode(),
206 "Intel PT Linux perf event not supported");
208 bool was_enabled = m_enabled;
209 if (
Error err = DisableWithIoctl())
210 return std::move(err);
219 perf_event_mmap_page &mmap_metadata = GetMetadataPage();
221 ArrayRef<uint8_t> data = GetDataBuffer();
222 uint64_t data_head = mmap_metadata.data_head;
223 uint64_t data_size = mmap_metadata.data_size;
224 std::vector<uint8_t> output;
225 output.reserve(data.size());
227 if (data_head > data_size) {
228 uint64_t actual_data_head = data_head % data_size;
230 output.insert(output.end(), data.begin() + actual_data_head, data.end());
232 output.insert(output.end(), data.begin(), data.begin() + actual_data_head);
235 output.insert(output.end(), data.begin(), data.begin() + data_head);
239 if (
Error err = EnableWithIoctl())
240 return std::move(err);
253#ifndef PERF_ATTR_SIZE_VER5
254 return createStringError(inconvertibleErrorCode(),
255 "Intel PT Linux perf event not supported");
257 bool was_enabled = m_enabled;
258 if (
Error err = DisableWithIoctl())
259 return std::move(err);
261 perf_event_mmap_page &mmap_metadata = GetMetadataPage();
263 ArrayRef<uint8_t> data = GetAuxBuffer();
264 uint64_t aux_head = mmap_metadata.aux_head;
265 std::vector<uint8_t> output;
266 output.reserve(data.size());
280 output.insert(output.end(), data.begin() + aux_head, data.end());
281 output.insert(output.end(), data.begin(), data.begin() + aux_head);
284 if (
Error err = EnableWithIoctl())
285 return std::move(err);
294 return Error::success();
296 if (ioctl(*m_fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) < 0)
297 return createStringError(inconvertibleErrorCode(),
298 "Can't disable perf event. %s",
299 std::strerror(errno));
302 return Error::success();
309 return Error::success();
311 if (ioctl(*m_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) < 0)
312 return createStringError(inconvertibleErrorCode(),
313 "Can't enable perf event. %s",
314 std::strerror(errno));
317 return Error::success();
321#ifndef PERF_ATTR_SIZE_VER5
322 llvm_unreachable(
"Intel PT Linux perf event not supported");
324 perf_event_mmap_page &mmap_metadata = GetMetadataPage();
325 if (mmap_metadata.data_head < mmap_metadata.data_size)
326 return mmap_metadata.data_head;
328 return mmap_metadata.data_size;
336#ifndef PERF_ATTR_SIZE_VER5
337 return createStringError(inconvertibleErrorCode(),
338 "Intel PT Linux perf event not supported");
340 perf_event_attr attr;
341 memset(&attr, 0,
sizeof(attr));
342 attr.size =
sizeof(attr);
343 attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
344 attr.type = PERF_TYPE_SOFTWARE;
345 attr.context_switch = 1;
346 attr.exclude_kernel = 1;
347 attr.sample_id_all = 1;
349 attr.disabled = parent_perf_event ? !parent_perf_event->
IsEnabled() :
false;
359 uint64_t data_buffer_size = 32768;
360 uint64_t data_buffer_numpages = data_buffer_size / getpagesize();
362 LLDB_LOG(log,
"Will create context switch trace buffer of size {0}",
365 std::optional<long> group_fd;
366 if (parent_perf_event)
367 group_fd = parent_perf_event->
GetFd();
370 attr, std::nullopt, cpu_id, group_fd, 0)) {
371 if (
Error mmap_err = perf_event->MmapMetadataAndBuffers(
372 data_buffer_numpages, 0,
false)) {
373 return std::move(mmap_err);
377 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...
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 ...
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.
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...
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.
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