xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Process/Linux/Perf.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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