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