1 //===-- IntelPTCollector.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 "IntelPTCollector.h"
10 
11 #include "Perf.h"
12 
13 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
14 #include "lldb/Host/linux/Support.h"
15 #include "lldb/Utility/StreamString.h"
16 
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/MathExtras.h"
20 
21 #include <algorithm>
22 #include <cstddef>
23 #include <fstream>
24 #include <linux/perf_event.h>
25 #include <sstream>
26 #include <sys/ioctl.h>
27 #include <sys/syscall.h>
28 
29 using namespace lldb;
30 using namespace lldb_private;
31 using namespace process_linux;
32 using namespace llvm;
33 
34 const char *kOSEventIntelPTTypeFile =
35     "/sys/bus/event_source/devices/intel_pt/type";
36 
37 const char *kPSBPeriodCapFile =
38     "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";
39 
40 const char *kPSBPeriodValidValuesFile =
41     "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";
42 
43 const char *kTSCBitOffsetFile =
44     "/sys/bus/event_source/devices/intel_pt/format/tsc";
45 
46 const char *kPSBPeriodBitOffsetFile =
47     "/sys/bus/event_source/devices/intel_pt/format/psb_period";
48 
49 enum IntelPTConfigFileType {
50   Hex = 0,
51   // 0 or 1
52   ZeroOne,
53   Decimal,
54   // a bit index file always starts with the prefix config: following by an int,
55   // which represents the offset of the perf_event_attr.config value where to
56   // store a given configuration.
57   BitOffset
58 };
59 
60 /// Get the content of /proc/cpuinfo that can be later used to decode traces.
61 static Expected<ArrayRef<uint8_t>> GetCPUInfo() {
62   static llvm::Optional<std::vector<uint8_t>> cpu_info;
63   if (!cpu_info) {
64     auto buffer_or_error = errorOrToExpected(getProcFile("cpuinfo"));
65     if (!buffer_or_error)
66       return buffer_or_error.takeError();
67     MemoryBuffer &buffer = **buffer_or_error;
68     cpu_info = std::vector<uint8_t>(
69         reinterpret_cast<const uint8_t *>(buffer.getBufferStart()),
70         reinterpret_cast<const uint8_t *>(buffer.getBufferEnd()));
71   }
72   return *cpu_info;
73 }
74 
75 static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
76                                                 IntelPTConfigFileType type) {
77   ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
78       MemoryBuffer::getFileAsStream(file);
79 
80   if (!stream)
81     return createStringError(inconvertibleErrorCode(),
82                              "Can't open the file '%s'", file);
83 
84   uint32_t value = 0;
85   StringRef text_buffer = stream.get()->getBuffer();
86 
87   if (type == BitOffset) {
88     const char *prefix = "config:";
89     if (!text_buffer.startswith(prefix))
90       return createStringError(inconvertibleErrorCode(),
91                                "The file '%s' contents doesn't start with '%s'",
92                                file, prefix);
93     text_buffer = text_buffer.substr(strlen(prefix));
94   }
95 
96   auto getRadix = [&]() {
97     switch (type) {
98     case Hex:
99       return 16;
100     case ZeroOne:
101     case Decimal:
102     case BitOffset:
103       return 10;
104     }
105   };
106 
107   auto createError = [&](const char *expected_value_message) {
108     return createStringError(
109         inconvertibleErrorCode(),
110         "The file '%s' has an invalid value. It should be %s.", file,
111         expected_value_message);
112   };
113 
114   if (text_buffer.trim().consumeInteger(getRadix(), value) ||
115       (type == ZeroOne && value != 0 && value != 1)) {
116     switch (type) {
117     case Hex:
118       return createError("an unsigned hexadecimal int");
119     case ZeroOne:
120       return createError("0 or 1");
121     case Decimal:
122     case BitOffset:
123       return createError("an unsigned decimal int");
124     }
125   }
126   return value;
127 }
128 
129 /// Return the Linux perf event type for Intel PT.
130 static Expected<uint32_t> GetOSEventType() {
131   return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
132                                IntelPTConfigFileType::Decimal);
133 }
134 
135 static Error CheckPsbPeriod(size_t psb_period) {
136   Expected<uint32_t> cap =
137       ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne);
138   if (!cap)
139     return cap.takeError();
140   if (*cap == 0)
141     return createStringError(inconvertibleErrorCode(),
142                              "psb_period is unsupported in the system.");
143 
144   Expected<uint32_t> valid_values = ReadIntelPTConfigFile(
145       kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex);
146   if (!valid_values)
147     return valid_values.takeError();
148 
149   if (valid_values.get() & (1 << psb_period))
150     return Error::success();
151 
152   std::ostringstream error;
153   // 0 is always a valid value
154   error << "Invalid psb_period. Valid values are: 0";
155   uint32_t mask = valid_values.get();
156   while (mask) {
157     int index = __builtin_ctz(mask);
158     if (index > 0)
159       error << ", " << index;
160     // clear the lowest bit
161     mask &= mask - 1;
162   }
163   error << ".";
164   return createStringError(inconvertibleErrorCode(), error.str().c_str());
165 }
166 
167 size_t IntelPTThreadTrace::GetTraceBufferSize() const {
168 #ifndef PERF_ATTR_SIZE_VER5
169   llvm_unreachable("Intel PT Linux perf event not supported");
170 #else
171   return m_perf_event.GetAuxBuffer().size();
172 #endif
173 }
174 
175 static Expected<uint64_t>
176 GeneratePerfEventConfigValue(bool enable_tsc, Optional<size_t> psb_period) {
177   uint64_t config = 0;
178   // tsc is always supported
179   if (enable_tsc) {
180     if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
181             kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))
182       config |= 1 << *offset;
183     else
184       return offset.takeError();
185   }
186   if (psb_period) {
187     if (Error error = CheckPsbPeriod(*psb_period))
188       return std::move(error);
189 
190     if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
191             kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset))
192       config |= *psb_period << *offset;
193     else
194       return offset.takeError();
195   }
196   return config;
197 }
198 
199 llvm::Expected<perf_event_attr>
200 IntelPTThreadTrace::CreateIntelPTPerfEventConfiguration(
201     bool enable_tsc, Optional<size_t> psb_period) {
202   perf_event_attr attr;
203   memset(&attr, 0, sizeof(attr));
204   attr.size = sizeof(attr);
205   attr.exclude_kernel = 1;
206   attr.sample_type = PERF_SAMPLE_TIME;
207   attr.sample_id_all = 1;
208   attr.exclude_hv = 1;
209   attr.exclude_idle = 1;
210   attr.mmap = 1;
211 
212   if (Expected<uint64_t> config_value =
213           GeneratePerfEventConfigValue(enable_tsc, psb_period)) {
214     attr.config = *config_value;
215   } else {
216     return config_value.takeError();
217   }
218 
219   if (Expected<uint32_t> intel_pt_type = GetOSEventType()) {
220     attr.type = *intel_pt_type;
221   } else {
222     return intel_pt_type.takeError();
223   }
224 
225   return attr;
226 }
227 
228 llvm::Expected<IntelPTThreadTraceUP>
229 IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size,
230                            bool enable_tsc, Optional<size_t> psb_period) {
231 #ifndef PERF_ATTR_SIZE_VER5
232   llvm_unreachable("Intel PT Linux perf event not supported");
233 #else
234   Log *log = GetLog(POSIXLog::Ptrace);
235 
236   LLDB_LOG(log, "called thread id {0}", tid);
237 
238   if (__builtin_popcount(buffer_size) != 1 || buffer_size < 4096) {
239     return createStringError(
240         inconvertibleErrorCode(),
241         "The trace buffer size must be a power of 2 greater than or equal to "
242         "4096 (2^12) bytes. It was %" PRIu64 ".",
243         buffer_size);
244   }
245   uint64_t page_size = getpagesize();
246   uint64_t buffer_numpages = static_cast<uint64_t>(
247       llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
248 
249   Expected<perf_event_attr> attr =
250       IntelPTThreadTrace::CreateIntelPTPerfEventConfiguration(enable_tsc,
251                                                               psb_period);
252   if (!attr)
253     return attr.takeError();
254 
255   LLDB_LOG(log, "buffer size {0} ", buffer_size);
256 
257   if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
258     if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
259                                                             buffer_numpages)) {
260       return std::move(mmap_err);
261     }
262     return IntelPTThreadTraceUP(
263         new IntelPTThreadTrace(std::move(*perf_event), tid));
264   } else {
265     return perf_event.takeError();
266   }
267 #endif
268 }
269 
270 Expected<std::vector<uint8_t>>
271 IntelPTThreadTrace::GetIntelPTBuffer(size_t offset, size_t size) const {
272   std::vector<uint8_t> data(size, 0);
273   MutableArrayRef<uint8_t> buffer_ref(data);
274   Status error = ReadPerfTraceAux(buffer_ref, 0);
275   if (error.Fail())
276     return error.ToError();
277   return data;
278 }
279 
280 Status
281 IntelPTThreadTrace::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
282                                      size_t offset) const {
283 #ifndef PERF_ATTR_SIZE_VER5
284   llvm_unreachable("perf event not supported");
285 #else
286   auto fd = m_perf_event.GetFd();
287   perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
288   // Disable the perf event to force a flush out of the CPU's internal buffer.
289   // Besides, we can guarantee that the CPU won't override any data as we are
290   // reading the buffer.
291   //
292   // The Intel documentation says:
293   //
294   // Packets are first buffered internally and then written out asynchronously.
295   // To collect packet output for postprocessing, a collector needs first to
296   // ensure that all packet data has been flushed from internal buffers.
297   // Software can ensure this by stopping packet generation by clearing
298   // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
299   // Section 35.2.7.2).
300   //
301   // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
302   // in the man page of perf_event_open.
303   ioctl(fd, PERF_EVENT_IOC_DISABLE);
304 
305   Log *log = GetLog(POSIXLog::Ptrace);
306   Status error;
307   uint64_t head = mmap_metadata.aux_head;
308 
309   LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.aux_size, head);
310 
311   /**
312    * When configured as ring buffer, the aux buffer keeps wrapping around
313    * the buffer and its not possible to detect how many times the buffer
314    * wrapped. Initially the buffer is filled with zeros,as shown below
315    * so in order to get complete buffer we first copy firstpartsize, followed
316    * by any left over part from beginning to aux_head
317    *
318    * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
319    *                 aux_head->||<- firstpartsize  ->|
320    *
321    * */
322 
323   ReadCyclicBuffer(buffer, m_perf_event.GetAuxBuffer(),
324                    static_cast<size_t>(head), offset);
325   LLDB_LOG(log, "ReadCyclic Buffer Done");
326 
327   // Reenable tracing now we have read the buffer
328   ioctl(fd, PERF_EVENT_IOC_ENABLE);
329   return error;
330 #endif
331 }
332 
333 Status
334 IntelPTThreadTrace::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
335                                       size_t offset) const {
336 #ifndef PERF_ATTR_SIZE_VER5
337   llvm_unreachable("perf event not supported");
338 #else
339   Log *log = GetLog(POSIXLog::Ptrace);
340   uint64_t bytes_remaining = buffer.size();
341   Status error;
342 
343   perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
344   uint64_t head = mmap_metadata.data_head;
345 
346   /*
347    * The data buffer and aux buffer have different implementations
348    * with respect to their definition of head pointer. In the case
349    * of Aux data buffer the head always wraps around the aux buffer
350    * and we don't need to care about it, whereas the data_head keeps
351    * increasing and needs to be wrapped by modulus operator
352    */
353 
354   LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
355 
356   auto data_buffer = m_perf_event.GetDataBuffer();
357 
358   if (head > data_buffer.size()) {
359     head = head % data_buffer.size();
360     LLDB_LOG(log, "Data size -{0} Head - {1}", mmap_metadata.data_size, head);
361 
362     ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
363     bytes_remaining -= buffer.size();
364   } else {
365     LLDB_LOG(log, "Head - {0}", head);
366     if (offset >= head) {
367       LLDB_LOG(log, "Invalid Offset ");
368       error.SetErrorString("invalid offset");
369       buffer = buffer.slice(buffer.size());
370       return error;
371     }
372 
373     auto data = data_buffer.slice(offset, (head - offset));
374     auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
375     bytes_remaining -= (remaining - buffer.begin());
376   }
377   buffer = buffer.drop_back(bytes_remaining);
378   return error;
379 #endif
380 }
381 
382 void IntelPTThreadTrace::ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
383                                           llvm::ArrayRef<uint8_t> src,
384                                           size_t src_cyc_index, size_t offset) {
385 
386   Log *log = GetLog(POSIXLog::Ptrace);
387 
388   if (dst.empty() || src.empty()) {
389     dst = dst.drop_back(dst.size());
390     return;
391   }
392 
393   if (dst.data() == nullptr || src.data() == nullptr) {
394     dst = dst.drop_back(dst.size());
395     return;
396   }
397 
398   if (src_cyc_index > src.size()) {
399     dst = dst.drop_back(dst.size());
400     return;
401   }
402 
403   if (offset >= src.size()) {
404     LLDB_LOG(log, "Too Big offset ");
405     dst = dst.drop_back(dst.size());
406     return;
407   }
408 
409   llvm::SmallVector<ArrayRef<uint8_t>, 2> parts = {
410       src.slice(src_cyc_index), src.take_front(src_cyc_index)};
411 
412   if (offset > parts[0].size()) {
413     parts[1] = parts[1].slice(offset - parts[0].size());
414     parts[0] = parts[0].drop_back(parts[0].size());
415   } else if (offset == parts[0].size()) {
416     parts[0] = parts[0].drop_back(parts[0].size());
417   } else {
418     parts[0] = parts[0].slice(offset);
419   }
420   auto next = dst.begin();
421   auto bytes_left = dst.size();
422   for (auto part : parts) {
423     size_t chunk_size = std::min(part.size(), bytes_left);
424     next = std::copy_n(part.begin(), chunk_size, next);
425     bytes_left -= chunk_size;
426   }
427   dst = dst.drop_back(bytes_left);
428 }
429 
430 TraceThreadState IntelPTThreadTrace::GetState() const {
431   return {static_cast<int64_t>(m_tid),
432           {TraceBinaryData{"threadTraceBuffer",
433                            static_cast<int64_t>(GetTraceBufferSize())}}};
434 }
435 
436 /// IntelPTThreadTraceCollection
437 
438 bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const {
439   return m_thread_traces.count(tid);
440 }
441 
442 Error IntelPTThreadTraceCollection::TraceStop(lldb::tid_t tid) {
443   auto it = m_thread_traces.find(tid);
444   if (it == m_thread_traces.end())
445     return createStringError(inconvertibleErrorCode(),
446                              "Thread %" PRIu64 " not currently traced", tid);
447   m_total_buffer_size -= it->second->GetTraceBufferSize();
448   m_thread_traces.erase(tid);
449   return Error::success();
450 }
451 
452 Error IntelPTThreadTraceCollection::TraceStart(
453     lldb::tid_t tid, const TraceIntelPTStartRequest &request) {
454   if (TracesThread(tid))
455     return createStringError(inconvertibleErrorCode(),
456                              "Thread %" PRIu64 " already traced", tid);
457 
458   Expected<IntelPTThreadTraceUP> trace_up = IntelPTThreadTrace::Create(
459       m_pid, tid, request.threadBufferSize, request.enableTsc,
460       request.psbPeriod.map([](int64_t period) { return (size_t)period; }));
461   if (!trace_up)
462     return trace_up.takeError();
463 
464   m_total_buffer_size += (*trace_up)->GetTraceBufferSize();
465   m_thread_traces.try_emplace(tid, std::move(*trace_up));
466   return Error::success();
467 }
468 
469 size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const {
470   return m_total_buffer_size;
471 }
472 
473 std::vector<TraceThreadState>
474 IntelPTThreadTraceCollection::GetThreadStates() const {
475   std::vector<TraceThreadState> states;
476   for (const auto &it : m_thread_traces)
477     states.push_back(it.second->GetState());
478   return states;
479 }
480 
481 Expected<const IntelPTThreadTrace &>
482 IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) const {
483   auto it = m_thread_traces.find(tid);
484   if (it == m_thread_traces.end())
485     return createStringError(inconvertibleErrorCode(),
486                              "Thread %" PRIu64 " not currently traced", tid);
487   return *it->second.get();
488 }
489 
490 void IntelPTThreadTraceCollection::Clear() {
491   m_thread_traces.clear();
492   m_total_buffer_size = 0;
493 }
494 
495 /// IntelPTProcessTrace
496 
497 bool IntelPTProcessTrace::TracesThread(lldb::tid_t tid) const {
498   return m_thread_traces.TracesThread(tid);
499 }
500 
501 Error IntelPTProcessTrace::TraceStop(lldb::tid_t tid) {
502   return m_thread_traces.TraceStop(tid);
503 }
504 
505 Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) {
506   if (m_thread_traces.GetTotalBufferSize() + m_tracing_params.threadBufferSize >
507       static_cast<size_t>(*m_tracing_params.processBufferSizeLimit))
508     return createStringError(
509         inconvertibleErrorCode(),
510         "Thread %" PRIu64 " can't be traced as the process trace size limit "
511         "has been reached. Consider retracing with a higher "
512         "limit.",
513         tid);
514 
515   return m_thread_traces.TraceStart(tid, m_tracing_params);
516 }
517 
518 const IntelPTThreadTraceCollection &
519 IntelPTProcessTrace::GetThreadTraces() const {
520   return m_thread_traces;
521 }
522 
523 /// IntelPTCollector
524 
525 IntelPTCollector::IntelPTCollector(lldb::pid_t pid)
526     : m_pid(pid), m_thread_traces(pid) {
527   if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
528           LoadPerfTscConversionParameters())
529     m_tsc_conversion =
530         std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion);
531   else
532     LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(),
533                    "unable to load TSC to wall time conversion: {0}");
534 }
535 
536 Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
537   if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
538     return m_process_trace->TraceStop(tid);
539   return m_thread_traces.TraceStop(tid);
540 }
541 
542 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
543   if (request.IsProcessTracing()) {
544     Clear();
545     return Error::success();
546   } else {
547     Error error = Error::success();
548     for (int64_t tid : *request.tids)
549       error = joinErrors(std::move(error),
550                          TraceStop(static_cast<lldb::tid_t>(tid)));
551     return error;
552   }
553 }
554 
555 Error IntelPTCollector::TraceStart(
556     const TraceIntelPTStartRequest &request,
557     const std::vector<lldb::tid_t> &process_threads) {
558   if (request.IsProcessTracing()) {
559     if (IsProcessTracingEnabled()) {
560       return createStringError(
561           inconvertibleErrorCode(),
562           "Process currently traced. Stop process tracing first");
563     }
564     m_process_trace = IntelPTProcessTrace(m_pid, request);
565 
566     Error error = Error::success();
567     for (lldb::tid_t tid : process_threads)
568       error = joinErrors(std::move(error), m_process_trace->TraceStart(tid));
569     return error;
570   } else {
571     Error error = Error::success();
572     for (int64_t tid : *request.tids)
573       error = joinErrors(std::move(error),
574                          m_thread_traces.TraceStart(tid, request));
575     return error;
576   }
577 }
578 
579 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
580   if (!IsProcessTracingEnabled())
581     return Error::success();
582   return m_process_trace->TraceStart(tid);
583 }
584 
585 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
586   if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
587     return m_process_trace->TraceStop(tid);
588   else if (m_thread_traces.TracesThread(tid))
589     return m_thread_traces.TraceStop(tid);
590   return Error::success();
591 }
592 
593 Expected<json::Value> IntelPTCollector::GetState() const {
594   Expected<ArrayRef<uint8_t>> cpu_info = GetCPUInfo();
595   if (!cpu_info)
596     return cpu_info.takeError();
597 
598   TraceGetStateResponse state;
599   state.processBinaryData.push_back(
600       {"cpuInfo", static_cast<int64_t>(cpu_info->size())});
601 
602   std::vector<TraceThreadState> thread_states =
603       m_thread_traces.GetThreadStates();
604   state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(),
605                              thread_states.end());
606 
607   if (IsProcessTracingEnabled()) {
608     thread_states = m_process_trace->GetThreadTraces().GetThreadStates();
609     state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(),
610                                thread_states.end());
611   }
612   return toJSON(state);
613 }
614 
615 Expected<const IntelPTThreadTrace &>
616 IntelPTCollector::GetTracedThread(lldb::tid_t tid) const {
617   if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
618     return m_process_trace->GetThreadTraces().GetTracedThread(tid);
619   return m_thread_traces.GetTracedThread(tid);
620 }
621 
622 Expected<std::vector<uint8_t>>
623 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const {
624   if (request.kind == "threadTraceBuffer") {
625     if (Expected<const IntelPTThreadTrace &> trace =
626             GetTracedThread(*request.tid))
627       return trace->GetIntelPTBuffer(request.offset, request.size);
628     else
629       return trace.takeError();
630   } else if (request.kind == "cpuInfo") {
631     return GetCPUInfo();
632   }
633   return createStringError(inconvertibleErrorCode(),
634                            "Unsuported trace binary data kind: %s",
635                            request.kind.c_str());
636 }
637 
638 void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; }
639 
640 bool IntelPTCollector::IsSupported() {
641   Expected<uint32_t> intel_pt_type = GetOSEventType();
642   if (!intel_pt_type) {
643     llvm::consumeError(intel_pt_type.takeError());
644     return false;
645   }
646   return true;
647 }
648 
649 bool IntelPTCollector::IsProcessTracingEnabled() const {
650   return (bool)m_process_trace;
651 }
652 
653 void IntelPTCollector::Clear() {
654   ClearProcessTracing();
655   m_thread_traces.Clear();
656 }
657