1a19fcc2bSWalter Erquinigo //===-- PerfContextSwitchDecoder.cpp --======------------------------------===//
2a19fcc2bSWalter Erquinigo // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3a19fcc2bSWalter Erquinigo // See https://llvm.org/LICENSE.txt for license information.
4a19fcc2bSWalter Erquinigo // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5a19fcc2bSWalter Erquinigo //
6a19fcc2bSWalter Erquinigo //===----------------------------------------------------------------------===//
7a19fcc2bSWalter Erquinigo 
8a19fcc2bSWalter Erquinigo #include "PerfContextSwitchDecoder.h"
9a19fcc2bSWalter Erquinigo 
10a19fcc2bSWalter Erquinigo using namespace lldb;
11a19fcc2bSWalter Erquinigo using namespace lldb_private;
12a19fcc2bSWalter Erquinigo using namespace lldb_private::trace_intel_pt;
13a19fcc2bSWalter Erquinigo using namespace llvm;
14a19fcc2bSWalter Erquinigo 
15a19fcc2bSWalter Erquinigo /// Copied from <linux/perf_event.h> to avoid depending on perf_event.h on
16a19fcc2bSWalter Erquinigo /// non-linux platforms.
17a19fcc2bSWalter Erquinigo /// \{
1867c24051SWalter Erquinigo #define PERF_RECORD_MISC_SWITCH_OUT (1 << 13)
19*b532dd54SWalter Erquinigo 
20*b532dd54SWalter Erquinigo #define PERF_RECORD_LOST 2
21*b532dd54SWalter Erquinigo #define PERF_RECORD_THROTTLE 5
22*b532dd54SWalter Erquinigo #define PERF_RECORD_UNTHROTTLE 6
23*b532dd54SWalter Erquinigo #define PERF_RECORD_LOST_SAMPLES 13
2467c24051SWalter Erquinigo #define PERF_RECORD_SWITCH_CPU_WIDE 15
25*b532dd54SWalter Erquinigo #define PERF_RECORD_MAX 19
2667c24051SWalter Erquinigo 
27a19fcc2bSWalter Erquinigo struct perf_event_header {
28a19fcc2bSWalter Erquinigo   uint32_t type;
29a19fcc2bSWalter Erquinigo   uint16_t misc;
30a19fcc2bSWalter Erquinigo   uint16_t size;
31a19fcc2bSWalter Erquinigo 
3267c24051SWalter Erquinigo   /// \return
3367c24051SWalter Erquinigo   ///   An \a llvm::Error if the record looks obviously wrong, or \a
3467c24051SWalter Erquinigo   ///   llvm::Error::success() otherwise.
SanityCheckperf_event_header3567c24051SWalter Erquinigo   Error SanityCheck() const {
3667c24051SWalter Erquinigo     // The following checks are based on visual inspection of the records and
3767c24051SWalter Erquinigo     // enums in
3867c24051SWalter Erquinigo     // https://elixir.bootlin.com/linux/v4.8/source/include/uapi/linux/perf_event.h
3967c24051SWalter Erquinigo     // See PERF_RECORD_MAX, PERF_RECORD_SWITCH and the data similar records
4067c24051SWalter Erquinigo     // hold.
4167c24051SWalter Erquinigo 
4267c24051SWalter Erquinigo     // A record of too many uint64_t's or more should mean that the data is
4367c24051SWalter Erquinigo     // wrong
4467c24051SWalter Erquinigo     const uint64_t max_valid_size_bytes = 8000;
4567c24051SWalter Erquinigo     if (size == 0 || size > max_valid_size_bytes)
4667c24051SWalter Erquinigo       return createStringError(
4767c24051SWalter Erquinigo           inconvertibleErrorCode(),
4867c24051SWalter Erquinigo           formatv("A record of {0} bytes was found.", size));
4967c24051SWalter Erquinigo 
5067c24051SWalter Erquinigo     // We add some numbers to PERF_RECORD_MAX because some systems might have
5167c24051SWalter Erquinigo     // custom records. In any case, we are looking only for abnormal data.
5267c24051SWalter Erquinigo     if (type >= PERF_RECORD_MAX + 100)
5367c24051SWalter Erquinigo       return createStringError(
5467c24051SWalter Erquinigo           inconvertibleErrorCode(),
5567c24051SWalter Erquinigo           formatv("Invalid record type {0} was found.", type));
5667c24051SWalter Erquinigo     return Error::success();
5767c24051SWalter Erquinigo   }
5867c24051SWalter Erquinigo 
IsContextSwitchRecordperf_event_header5967c24051SWalter Erquinigo   bool IsContextSwitchRecord() const {
6067c24051SWalter Erquinigo     return type == PERF_RECORD_SWITCH_CPU_WIDE;
6167c24051SWalter Erquinigo   }
62*b532dd54SWalter Erquinigo 
IsErrorRecordperf_event_header63*b532dd54SWalter Erquinigo   bool IsErrorRecord() const {
64*b532dd54SWalter Erquinigo     return type == PERF_RECORD_LOST || type == PERF_RECORD_THROTTLE ||
65*b532dd54SWalter Erquinigo            type == PERF_RECORD_UNTHROTTLE || type == PERF_RECORD_LOST_SAMPLES;
66*b532dd54SWalter Erquinigo   }
6767c24051SWalter Erquinigo };
68a19fcc2bSWalter Erquinigo /// \}
69a19fcc2bSWalter Erquinigo 
70a19fcc2bSWalter Erquinigo /// Record found in the perf_event context switch traces. It might contain
71a19fcc2bSWalter Erquinigo /// additional fields in memory, but header.size should have the actual size
72a19fcc2bSWalter Erquinigo /// of the record.
73a19fcc2bSWalter Erquinigo struct PerfContextSwitchRecord {
74a19fcc2bSWalter Erquinigo   struct perf_event_header header;
75a19fcc2bSWalter Erquinigo   uint32_t next_prev_pid;
76a19fcc2bSWalter Erquinigo   uint32_t next_prev_tid;
77a19fcc2bSWalter Erquinigo   uint32_t pid, tid;
78a19fcc2bSWalter Erquinigo   uint64_t time_in_nanos;
79a19fcc2bSWalter Erquinigo 
IsOutPerfContextSwitchRecord80a19fcc2bSWalter Erquinigo   bool IsOut() const { return header.misc & PERF_RECORD_MISC_SWITCH_OUT; }
81a19fcc2bSWalter Erquinigo };
82a19fcc2bSWalter Erquinigo 
83a19fcc2bSWalter Erquinigo /// Record produced after parsing the raw context switch trace produce by
84a19fcc2bSWalter Erquinigo /// perf_event. A major difference between this struct and
85a19fcc2bSWalter Erquinigo /// PerfContextSwitchRecord is that this one uses tsc instead of nanos.
86a19fcc2bSWalter Erquinigo struct ContextSwitchRecord {
87a19fcc2bSWalter Erquinigo   uint64_t tsc;
88a19fcc2bSWalter Erquinigo   /// Whether the switch is in or out
89a19fcc2bSWalter Erquinigo   bool is_out;
90a19fcc2bSWalter Erquinigo   /// pid = 0 and tid = 0 indicate the swapper or idle process, which normally
91a19fcc2bSWalter Erquinigo   /// runs after a context switch out of a normal user thread.
92a19fcc2bSWalter Erquinigo   lldb::pid_t pid;
93a19fcc2bSWalter Erquinigo   lldb::tid_t tid;
94a19fcc2bSWalter Erquinigo 
IsOutContextSwitchRecord95a19fcc2bSWalter Erquinigo   bool IsOut() const { return is_out; }
96a19fcc2bSWalter Erquinigo 
IsInContextSwitchRecord97a19fcc2bSWalter Erquinigo   bool IsIn() const { return !is_out; }
98a19fcc2bSWalter Erquinigo };
99a19fcc2bSWalter Erquinigo 
GetLowestKnownTSC() const100a19fcc2bSWalter Erquinigo uint64_t ThreadContinuousExecution::GetLowestKnownTSC() const {
101a19fcc2bSWalter Erquinigo   switch (variant) {
102a19fcc2bSWalter Erquinigo   case Variant::Complete:
103a19fcc2bSWalter Erquinigo     return tscs.complete.start;
104a19fcc2bSWalter Erquinigo   case Variant::OnlyStart:
105a19fcc2bSWalter Erquinigo     return tscs.only_start.start;
106a19fcc2bSWalter Erquinigo   case Variant::OnlyEnd:
107a19fcc2bSWalter Erquinigo     return tscs.only_end.end;
108a19fcc2bSWalter Erquinigo   case Variant::HintedEnd:
109a19fcc2bSWalter Erquinigo     return tscs.hinted_end.start;
110a19fcc2bSWalter Erquinigo   case Variant::HintedStart:
111a19fcc2bSWalter Erquinigo     return tscs.hinted_start.end;
112a19fcc2bSWalter Erquinigo   }
113a19fcc2bSWalter Erquinigo }
114a19fcc2bSWalter Erquinigo 
GetStartTSC() const115a19fcc2bSWalter Erquinigo uint64_t ThreadContinuousExecution::GetStartTSC() const {
116a19fcc2bSWalter Erquinigo   switch (variant) {
117a19fcc2bSWalter Erquinigo   case Variant::Complete:
118a19fcc2bSWalter Erquinigo     return tscs.complete.start;
119a19fcc2bSWalter Erquinigo   case Variant::OnlyStart:
120a19fcc2bSWalter Erquinigo     return tscs.only_start.start;
121a19fcc2bSWalter Erquinigo   case Variant::OnlyEnd:
122a19fcc2bSWalter Erquinigo     return 0;
123a19fcc2bSWalter Erquinigo   case Variant::HintedEnd:
124a19fcc2bSWalter Erquinigo     return tscs.hinted_end.start;
125a19fcc2bSWalter Erquinigo   case Variant::HintedStart:
126a19fcc2bSWalter Erquinigo     return tscs.hinted_start.hinted_start;
127a19fcc2bSWalter Erquinigo   }
128a19fcc2bSWalter Erquinigo }
129a19fcc2bSWalter Erquinigo 
GetEndTSC() const130a19fcc2bSWalter Erquinigo uint64_t ThreadContinuousExecution::GetEndTSC() const {
131a19fcc2bSWalter Erquinigo   switch (variant) {
132a19fcc2bSWalter Erquinigo   case Variant::Complete:
133a19fcc2bSWalter Erquinigo     return tscs.complete.end;
134a19fcc2bSWalter Erquinigo   case Variant::OnlyStart:
135a19fcc2bSWalter Erquinigo     return std::numeric_limits<uint64_t>::max();
136a19fcc2bSWalter Erquinigo   case Variant::OnlyEnd:
137a19fcc2bSWalter Erquinigo     return tscs.only_end.end;
138a19fcc2bSWalter Erquinigo   case Variant::HintedEnd:
139a19fcc2bSWalter Erquinigo     return tscs.hinted_end.hinted_end;
140a19fcc2bSWalter Erquinigo   case Variant::HintedStart:
141a19fcc2bSWalter Erquinigo     return tscs.hinted_start.end;
142a19fcc2bSWalter Erquinigo   }
143a19fcc2bSWalter Erquinigo }
144a19fcc2bSWalter Erquinigo 
CreateCompleteExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t start,uint64_t end)145a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateCompleteExecution(
1466a5355e8SWalter Erquinigo     lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
147a19fcc2bSWalter Erquinigo     uint64_t end) {
1486a5355e8SWalter Erquinigo   ThreadContinuousExecution o(cpu_id, tid, pid);
149a19fcc2bSWalter Erquinigo   o.variant = Variant::Complete;
150a19fcc2bSWalter Erquinigo   o.tscs.complete.start = start;
151a19fcc2bSWalter Erquinigo   o.tscs.complete.end = end;
152a19fcc2bSWalter Erquinigo   return o;
153a19fcc2bSWalter Erquinigo }
154a19fcc2bSWalter Erquinigo 
CreateHintedStartExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t hinted_start,uint64_t end)155a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateHintedStartExecution(
1566a5355e8SWalter Erquinigo     lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid,
157a19fcc2bSWalter Erquinigo     uint64_t hinted_start, uint64_t end) {
1586a5355e8SWalter Erquinigo   ThreadContinuousExecution o(cpu_id, tid, pid);
159a19fcc2bSWalter Erquinigo   o.variant = Variant::HintedStart;
160a19fcc2bSWalter Erquinigo   o.tscs.hinted_start.hinted_start = hinted_start;
161a19fcc2bSWalter Erquinigo   o.tscs.hinted_start.end = end;
162a19fcc2bSWalter Erquinigo   return o;
163a19fcc2bSWalter Erquinigo }
164a19fcc2bSWalter Erquinigo 
CreateHintedEndExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t start,uint64_t hinted_end)165a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateHintedEndExecution(
1666a5355e8SWalter Erquinigo     lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
167a19fcc2bSWalter Erquinigo     uint64_t hinted_end) {
1686a5355e8SWalter Erquinigo   ThreadContinuousExecution o(cpu_id, tid, pid);
169a19fcc2bSWalter Erquinigo   o.variant = Variant::HintedEnd;
170a19fcc2bSWalter Erquinigo   o.tscs.hinted_end.start = start;
171a19fcc2bSWalter Erquinigo   o.tscs.hinted_end.hinted_end = hinted_end;
172a19fcc2bSWalter Erquinigo   return o;
173a19fcc2bSWalter Erquinigo }
174a19fcc2bSWalter Erquinigo 
CreateOnlyEndExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t end)175a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyEndExecution(
1766a5355e8SWalter Erquinigo     lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t end) {
1776a5355e8SWalter Erquinigo   ThreadContinuousExecution o(cpu_id, tid, pid);
178a19fcc2bSWalter Erquinigo   o.variant = Variant::OnlyEnd;
179a19fcc2bSWalter Erquinigo   o.tscs.only_end.end = end;
180a19fcc2bSWalter Erquinigo   return o;
181a19fcc2bSWalter Erquinigo }
182a19fcc2bSWalter Erquinigo 
CreateOnlyStartExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t start)183a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyStartExecution(
1846a5355e8SWalter Erquinigo     lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start) {
1856a5355e8SWalter Erquinigo   ThreadContinuousExecution o(cpu_id, tid, pid);
186a19fcc2bSWalter Erquinigo   o.variant = Variant::OnlyStart;
187a19fcc2bSWalter Erquinigo   o.tscs.only_start.start = start;
188a19fcc2bSWalter Erquinigo   return o;
189a19fcc2bSWalter Erquinigo }
190a19fcc2bSWalter Erquinigo 
RecoverExecutionsFromConsecutiveRecords(cpu_id_t cpu_id,const LinuxPerfZeroTscConversion & tsc_conversion,const ContextSwitchRecord & current_record,const Optional<ContextSwitchRecord> & prev_record,std::function<void (const ThreadContinuousExecution & execution)> on_new_execution)191a19fcc2bSWalter Erquinigo static Error RecoverExecutionsFromConsecutiveRecords(
1926a5355e8SWalter Erquinigo     cpu_id_t cpu_id, const LinuxPerfZeroTscConversion &tsc_conversion,
193a19fcc2bSWalter Erquinigo     const ContextSwitchRecord &current_record,
194a19fcc2bSWalter Erquinigo     const Optional<ContextSwitchRecord> &prev_record,
195a19fcc2bSWalter Erquinigo     std::function<void(const ThreadContinuousExecution &execution)>
196a19fcc2bSWalter Erquinigo         on_new_execution) {
197a19fcc2bSWalter Erquinigo   if (!prev_record) {
198a19fcc2bSWalter Erquinigo     if (current_record.IsOut()) {
199a19fcc2bSWalter Erquinigo       on_new_execution(ThreadContinuousExecution::CreateOnlyEndExecution(
2006a5355e8SWalter Erquinigo           cpu_id, current_record.tid, current_record.pid, current_record.tsc));
201a19fcc2bSWalter Erquinigo     }
202a19fcc2bSWalter Erquinigo     // The 'in' case will be handled later when we try to look for its end
203a19fcc2bSWalter Erquinigo     return Error::success();
204a19fcc2bSWalter Erquinigo   }
205a19fcc2bSWalter Erquinigo 
206a19fcc2bSWalter Erquinigo   const ContextSwitchRecord &prev = *prev_record;
207a19fcc2bSWalter Erquinigo   if (prev.tsc >= current_record.tsc)
208a19fcc2bSWalter Erquinigo     return createStringError(
209a19fcc2bSWalter Erquinigo         inconvertibleErrorCode(),
210a19fcc2bSWalter Erquinigo         formatv("A context switch record doesn't happen after the previous "
211a19fcc2bSWalter Erquinigo                 "record. Previous TSC= {0}, current TSC = {1}.",
212a19fcc2bSWalter Erquinigo                 prev.tsc, current_record.tsc));
213a19fcc2bSWalter Erquinigo 
214a19fcc2bSWalter Erquinigo   if (current_record.IsIn() && prev.IsIn()) {
215a19fcc2bSWalter Erquinigo     // We found two consecutive ins, which means that we didn't capture
216a19fcc2bSWalter Erquinigo     // the end of the previous execution.
217a19fcc2bSWalter Erquinigo     on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
2186a5355e8SWalter Erquinigo         cpu_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
219a19fcc2bSWalter Erquinigo   } else if (current_record.IsOut() && prev.IsOut()) {
220a19fcc2bSWalter Erquinigo     // We found two consecutive outs, that means that we didn't capture
221a19fcc2bSWalter Erquinigo     // the beginning of the current execution.
222a19fcc2bSWalter Erquinigo     on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
2236a5355e8SWalter Erquinigo         cpu_id, current_record.tid, current_record.pid, prev.tsc + 1,
224a19fcc2bSWalter Erquinigo         current_record.tsc));
225a19fcc2bSWalter Erquinigo   } else if (current_record.IsOut() && prev.IsIn()) {
226a19fcc2bSWalter Erquinigo     if (current_record.pid == prev.pid && current_record.tid == prev.tid) {
227a19fcc2bSWalter Erquinigo       /// A complete execution
228a19fcc2bSWalter Erquinigo       on_new_execution(ThreadContinuousExecution::CreateCompleteExecution(
2296a5355e8SWalter Erquinigo           cpu_id, current_record.tid, current_record.pid, prev.tsc,
230a19fcc2bSWalter Erquinigo           current_record.tsc));
231a19fcc2bSWalter Erquinigo     } else {
232a19fcc2bSWalter Erquinigo       // An out after the in of a different thread. The first one doesn't
233a19fcc2bSWalter Erquinigo       // have an end, and the second one doesn't have a start.
234a19fcc2bSWalter Erquinigo       on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
2356a5355e8SWalter Erquinigo           cpu_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
236a19fcc2bSWalter Erquinigo       on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
2376a5355e8SWalter Erquinigo           cpu_id, current_record.tid, current_record.pid, prev.tsc + 1,
238a19fcc2bSWalter Erquinigo           current_record.tsc));
239a19fcc2bSWalter Erquinigo     }
240a19fcc2bSWalter Erquinigo   }
241a19fcc2bSWalter Erquinigo   return Error::success();
242a19fcc2bSWalter Erquinigo }
243a19fcc2bSWalter Erquinigo 
244a19fcc2bSWalter Erquinigo Expected<std::vector<ThreadContinuousExecution>>
DecodePerfContextSwitchTrace(ArrayRef<uint8_t> data,cpu_id_t cpu_id,const LinuxPerfZeroTscConversion & tsc_conversion)245a19fcc2bSWalter Erquinigo lldb_private::trace_intel_pt::DecodePerfContextSwitchTrace(
2466a5355e8SWalter Erquinigo     ArrayRef<uint8_t> data, cpu_id_t cpu_id,
247a19fcc2bSWalter Erquinigo     const LinuxPerfZeroTscConversion &tsc_conversion) {
248a19fcc2bSWalter Erquinigo 
249a19fcc2bSWalter Erquinigo   std::vector<ThreadContinuousExecution> executions;
250a19fcc2bSWalter Erquinigo 
251a19fcc2bSWalter Erquinigo   // This offset is used to create the error message in case of failures.
252a19fcc2bSWalter Erquinigo   size_t offset = 0;
253a19fcc2bSWalter Erquinigo 
254a19fcc2bSWalter Erquinigo   auto do_decode = [&]() -> Error {
255a19fcc2bSWalter Erquinigo     Optional<ContextSwitchRecord> prev_record;
256a19fcc2bSWalter Erquinigo     while (offset < data.size()) {
25767c24051SWalter Erquinigo       const perf_event_header &perf_record =
25867c24051SWalter Erquinigo           *reinterpret_cast<const perf_event_header *>(data.data() + offset);
259a19fcc2bSWalter Erquinigo       if (Error err = perf_record.SanityCheck())
260a19fcc2bSWalter Erquinigo         return err;
261a19fcc2bSWalter Erquinigo 
262a19fcc2bSWalter Erquinigo       if (perf_record.IsContextSwitchRecord()) {
26367c24051SWalter Erquinigo         const PerfContextSwitchRecord &context_switch_record =
26467c24051SWalter Erquinigo             *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
26567c24051SWalter Erquinigo                                                                offset);
266a19fcc2bSWalter Erquinigo         ContextSwitchRecord record{
26767c24051SWalter Erquinigo             tsc_conversion.ToTSC(context_switch_record.time_in_nanos),
26867c24051SWalter Erquinigo             context_switch_record.IsOut(),
26967c24051SWalter Erquinigo             static_cast<lldb::pid_t>(context_switch_record.pid),
27067c24051SWalter Erquinigo             static_cast<lldb::tid_t>(context_switch_record.tid)};
271a19fcc2bSWalter Erquinigo 
272a19fcc2bSWalter Erquinigo         if (Error err = RecoverExecutionsFromConsecutiveRecords(
2736a5355e8SWalter Erquinigo                 cpu_id, tsc_conversion, record, prev_record,
274a19fcc2bSWalter Erquinigo                 [&](const ThreadContinuousExecution &execution) {
275a19fcc2bSWalter Erquinigo                   executions.push_back(execution);
276a19fcc2bSWalter Erquinigo                 }))
277a19fcc2bSWalter Erquinigo           return err;
278a19fcc2bSWalter Erquinigo 
279a19fcc2bSWalter Erquinigo         prev_record = record;
280a19fcc2bSWalter Erquinigo       }
28167c24051SWalter Erquinigo       offset += perf_record.size;
282a19fcc2bSWalter Erquinigo     }
283a19fcc2bSWalter Erquinigo 
284a19fcc2bSWalter Erquinigo     // We might have an incomplete last record
285a19fcc2bSWalter Erquinigo     if (prev_record && prev_record->IsIn())
286a19fcc2bSWalter Erquinigo       executions.push_back(ThreadContinuousExecution::CreateOnlyStartExecution(
2876a5355e8SWalter Erquinigo           cpu_id, prev_record->tid, prev_record->pid, prev_record->tsc));
288a19fcc2bSWalter Erquinigo     return Error::success();
289a19fcc2bSWalter Erquinigo   };
290a19fcc2bSWalter Erquinigo 
291a19fcc2bSWalter Erquinigo   if (Error err = do_decode())
292a19fcc2bSWalter Erquinigo     return createStringError(inconvertibleErrorCode(),
293a19fcc2bSWalter Erquinigo                              formatv("Malformed perf context switch trace for "
294a19fcc2bSWalter Erquinigo                                      "cpu {0} at offset {1}. {2}",
2956a5355e8SWalter Erquinigo                                      cpu_id, offset, toString(std::move(err))));
296a19fcc2bSWalter Erquinigo 
297a19fcc2bSWalter Erquinigo   return executions;
298a19fcc2bSWalter Erquinigo }
299*b532dd54SWalter Erquinigo 
300*b532dd54SWalter Erquinigo Expected<std::vector<uint8_t>>
FilterProcessesFromContextSwitchTrace(llvm::ArrayRef<uint8_t> data,const std::set<lldb::pid_t> & pids)301*b532dd54SWalter Erquinigo lldb_private::trace_intel_pt::FilterProcessesFromContextSwitchTrace(
302*b532dd54SWalter Erquinigo     llvm::ArrayRef<uint8_t> data, const std::set<lldb::pid_t> &pids) {
303*b532dd54SWalter Erquinigo   size_t offset = 0;
304*b532dd54SWalter Erquinigo   std::vector<uint8_t> out_data;
305*b532dd54SWalter Erquinigo 
306*b532dd54SWalter Erquinigo   while (offset < data.size()) {
307*b532dd54SWalter Erquinigo     const perf_event_header &perf_record =
308*b532dd54SWalter Erquinigo         *reinterpret_cast<const perf_event_header *>(data.data() + offset);
309*b532dd54SWalter Erquinigo     if (Error err = perf_record.SanityCheck())
310*b532dd54SWalter Erquinigo       return std::move(err);
311*b532dd54SWalter Erquinigo     bool should_copy = false;
312*b532dd54SWalter Erquinigo     if (perf_record.IsContextSwitchRecord()) {
313*b532dd54SWalter Erquinigo       const PerfContextSwitchRecord &context_switch_record =
314*b532dd54SWalter Erquinigo           *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
315*b532dd54SWalter Erquinigo                                                              offset);
316*b532dd54SWalter Erquinigo       if (pids.count(context_switch_record.pid))
317*b532dd54SWalter Erquinigo         should_copy = true;
318*b532dd54SWalter Erquinigo     } else if (perf_record.IsErrorRecord()) {
319*b532dd54SWalter Erquinigo       should_copy = true;
320*b532dd54SWalter Erquinigo     }
321*b532dd54SWalter Erquinigo 
322*b532dd54SWalter Erquinigo     if (should_copy) {
323*b532dd54SWalter Erquinigo       for (size_t i = 0; i < perf_record.size; i++) {
324*b532dd54SWalter Erquinigo         out_data.push_back(data[offset + i]);
325*b532dd54SWalter Erquinigo       }
326*b532dd54SWalter Erquinigo     }
327*b532dd54SWalter Erquinigo 
328*b532dd54SWalter Erquinigo     offset += perf_record.size;
329*b532dd54SWalter Erquinigo   }
330*b532dd54SWalter Erquinigo   return out_data;
331*b532dd54SWalter Erquinigo }
332