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