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