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