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"
9f190ce62SKazu Hirata #include <optional>
10a19fcc2bSWalter Erquinigo
11a19fcc2bSWalter Erquinigo using namespace lldb;
12a19fcc2bSWalter Erquinigo using namespace lldb_private;
13a19fcc2bSWalter Erquinigo using namespace lldb_private::trace_intel_pt;
14a19fcc2bSWalter Erquinigo using namespace llvm;
15a19fcc2bSWalter Erquinigo
16a19fcc2bSWalter Erquinigo /// Copied from <linux/perf_event.h> to avoid depending on perf_event.h on
17a19fcc2bSWalter Erquinigo /// non-linux platforms.
18a19fcc2bSWalter Erquinigo /// \{
1967c24051SWalter Erquinigo #define PERF_RECORD_MISC_SWITCH_OUT (1 << 13)
20b532dd54SWalter Erquinigo
21b532dd54SWalter Erquinigo #define PERF_RECORD_LOST 2
22b532dd54SWalter Erquinigo #define PERF_RECORD_THROTTLE 5
23b532dd54SWalter Erquinigo #define PERF_RECORD_UNTHROTTLE 6
24b532dd54SWalter Erquinigo #define PERF_RECORD_LOST_SAMPLES 13
2567c24051SWalter Erquinigo #define PERF_RECORD_SWITCH_CPU_WIDE 15
26b532dd54SWalter Erquinigo #define PERF_RECORD_MAX 19
2767c24051SWalter Erquinigo
28a19fcc2bSWalter Erquinigo struct perf_event_header {
29a19fcc2bSWalter Erquinigo uint32_t type;
30a19fcc2bSWalter Erquinigo uint16_t misc;
31a19fcc2bSWalter Erquinigo uint16_t size;
32a19fcc2bSWalter Erquinigo
3367c24051SWalter Erquinigo /// \return
3467c24051SWalter Erquinigo /// An \a llvm::Error if the record looks obviously wrong, or \a
3567c24051SWalter Erquinigo /// llvm::Error::success() otherwise.
SanityCheckperf_event_header3667c24051SWalter Erquinigo Error SanityCheck() const {
3767c24051SWalter Erquinigo // The following checks are based on visual inspection of the records and
3867c24051SWalter Erquinigo // enums in
3967c24051SWalter Erquinigo // https://elixir.bootlin.com/linux/v4.8/source/include/uapi/linux/perf_event.h
4067c24051SWalter Erquinigo // See PERF_RECORD_MAX, PERF_RECORD_SWITCH and the data similar records
4167c24051SWalter Erquinigo // hold.
4267c24051SWalter Erquinigo
4367c24051SWalter Erquinigo // A record of too many uint64_t's or more should mean that the data is
4467c24051SWalter Erquinigo // wrong
4567c24051SWalter Erquinigo const uint64_t max_valid_size_bytes = 8000;
4667c24051SWalter Erquinigo if (size == 0 || size > max_valid_size_bytes)
4767c24051SWalter Erquinigo return createStringError(
4867c24051SWalter Erquinigo inconvertibleErrorCode(),
4967c24051SWalter Erquinigo formatv("A record of {0} bytes was found.", size));
5067c24051SWalter Erquinigo
5167c24051SWalter Erquinigo // We add some numbers to PERF_RECORD_MAX because some systems might have
5267c24051SWalter Erquinigo // custom records. In any case, we are looking only for abnormal data.
5367c24051SWalter Erquinigo if (type >= PERF_RECORD_MAX + 100)
5467c24051SWalter Erquinigo return createStringError(
5567c24051SWalter Erquinigo inconvertibleErrorCode(),
5667c24051SWalter Erquinigo formatv("Invalid record type {0} was found.", type));
5767c24051SWalter Erquinigo return Error::success();
5867c24051SWalter Erquinigo }
5967c24051SWalter Erquinigo
IsContextSwitchRecordperf_event_header6067c24051SWalter Erquinigo bool IsContextSwitchRecord() const {
6167c24051SWalter Erquinigo return type == PERF_RECORD_SWITCH_CPU_WIDE;
6267c24051SWalter Erquinigo }
63b532dd54SWalter Erquinigo
IsErrorRecordperf_event_header64b532dd54SWalter Erquinigo bool IsErrorRecord() const {
65b532dd54SWalter Erquinigo return type == PERF_RECORD_LOST || type == PERF_RECORD_THROTTLE ||
66b532dd54SWalter Erquinigo type == PERF_RECORD_UNTHROTTLE || type == PERF_RECORD_LOST_SAMPLES;
67b532dd54SWalter Erquinigo }
6867c24051SWalter Erquinigo };
69a19fcc2bSWalter Erquinigo /// \}
70a19fcc2bSWalter Erquinigo
71a19fcc2bSWalter Erquinigo /// Record found in the perf_event context switch traces. It might contain
72a19fcc2bSWalter Erquinigo /// additional fields in memory, but header.size should have the actual size
73a19fcc2bSWalter Erquinigo /// of the record.
74a19fcc2bSWalter Erquinigo struct PerfContextSwitchRecord {
75a19fcc2bSWalter Erquinigo struct perf_event_header header;
76a19fcc2bSWalter Erquinigo uint32_t next_prev_pid;
77a19fcc2bSWalter Erquinigo uint32_t next_prev_tid;
78a19fcc2bSWalter Erquinigo uint32_t pid, tid;
79a19fcc2bSWalter Erquinigo uint64_t time_in_nanos;
80a19fcc2bSWalter Erquinigo
IsOutPerfContextSwitchRecord81a19fcc2bSWalter Erquinigo bool IsOut() const { return header.misc & PERF_RECORD_MISC_SWITCH_OUT; }
82a19fcc2bSWalter Erquinigo };
83a19fcc2bSWalter Erquinigo
84a19fcc2bSWalter Erquinigo /// Record produced after parsing the raw context switch trace produce by
85a19fcc2bSWalter Erquinigo /// perf_event. A major difference between this struct and
86a19fcc2bSWalter Erquinigo /// PerfContextSwitchRecord is that this one uses tsc instead of nanos.
87a19fcc2bSWalter Erquinigo struct ContextSwitchRecord {
88a19fcc2bSWalter Erquinigo uint64_t tsc;
89a19fcc2bSWalter Erquinigo /// Whether the switch is in or out
90a19fcc2bSWalter Erquinigo bool is_out;
91a19fcc2bSWalter Erquinigo /// pid = 0 and tid = 0 indicate the swapper or idle process, which normally
92a19fcc2bSWalter Erquinigo /// runs after a context switch out of a normal user thread.
93a19fcc2bSWalter Erquinigo lldb::pid_t pid;
94a19fcc2bSWalter Erquinigo lldb::tid_t tid;
95a19fcc2bSWalter Erquinigo
IsOutContextSwitchRecord96a19fcc2bSWalter Erquinigo bool IsOut() const { return is_out; }
97a19fcc2bSWalter Erquinigo
IsInContextSwitchRecord98a19fcc2bSWalter Erquinigo bool IsIn() const { return !is_out; }
99a19fcc2bSWalter Erquinigo };
100a19fcc2bSWalter Erquinigo
GetLowestKnownTSC() const101a19fcc2bSWalter Erquinigo uint64_t ThreadContinuousExecution::GetLowestKnownTSC() const {
102a19fcc2bSWalter Erquinigo switch (variant) {
103a19fcc2bSWalter Erquinigo case Variant::Complete:
104a19fcc2bSWalter Erquinigo return tscs.complete.start;
105a19fcc2bSWalter Erquinigo case Variant::OnlyStart:
106a19fcc2bSWalter Erquinigo return tscs.only_start.start;
107a19fcc2bSWalter Erquinigo case Variant::OnlyEnd:
108a19fcc2bSWalter Erquinigo return tscs.only_end.end;
109a19fcc2bSWalter Erquinigo case Variant::HintedEnd:
110a19fcc2bSWalter Erquinigo return tscs.hinted_end.start;
111a19fcc2bSWalter Erquinigo case Variant::HintedStart:
112a19fcc2bSWalter Erquinigo return tscs.hinted_start.end;
113a19fcc2bSWalter Erquinigo }
114a19fcc2bSWalter Erquinigo }
115a19fcc2bSWalter Erquinigo
GetStartTSC() const116a19fcc2bSWalter Erquinigo uint64_t ThreadContinuousExecution::GetStartTSC() const {
117a19fcc2bSWalter Erquinigo switch (variant) {
118a19fcc2bSWalter Erquinigo case Variant::Complete:
119a19fcc2bSWalter Erquinigo return tscs.complete.start;
120a19fcc2bSWalter Erquinigo case Variant::OnlyStart:
121a19fcc2bSWalter Erquinigo return tscs.only_start.start;
122a19fcc2bSWalter Erquinigo case Variant::OnlyEnd:
123a19fcc2bSWalter Erquinigo return 0;
124a19fcc2bSWalter Erquinigo case Variant::HintedEnd:
125a19fcc2bSWalter Erquinigo return tscs.hinted_end.start;
126a19fcc2bSWalter Erquinigo case Variant::HintedStart:
127a19fcc2bSWalter Erquinigo return tscs.hinted_start.hinted_start;
128a19fcc2bSWalter Erquinigo }
129a19fcc2bSWalter Erquinigo }
130a19fcc2bSWalter Erquinigo
GetEndTSC() const131a19fcc2bSWalter Erquinigo uint64_t ThreadContinuousExecution::GetEndTSC() const {
132a19fcc2bSWalter Erquinigo switch (variant) {
133a19fcc2bSWalter Erquinigo case Variant::Complete:
134a19fcc2bSWalter Erquinigo return tscs.complete.end;
135a19fcc2bSWalter Erquinigo case Variant::OnlyStart:
136a19fcc2bSWalter Erquinigo return std::numeric_limits<uint64_t>::max();
137a19fcc2bSWalter Erquinigo case Variant::OnlyEnd:
138a19fcc2bSWalter Erquinigo return tscs.only_end.end;
139a19fcc2bSWalter Erquinigo case Variant::HintedEnd:
140a19fcc2bSWalter Erquinigo return tscs.hinted_end.hinted_end;
141a19fcc2bSWalter Erquinigo case Variant::HintedStart:
142a19fcc2bSWalter Erquinigo return tscs.hinted_start.end;
143a19fcc2bSWalter Erquinigo }
144a19fcc2bSWalter Erquinigo }
145a19fcc2bSWalter Erquinigo
CreateCompleteExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t start,uint64_t end)146a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateCompleteExecution(
1476a5355e8SWalter Erquinigo lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
148a19fcc2bSWalter Erquinigo uint64_t end) {
1496a5355e8SWalter Erquinigo ThreadContinuousExecution o(cpu_id, tid, pid);
150a19fcc2bSWalter Erquinigo o.variant = Variant::Complete;
151a19fcc2bSWalter Erquinigo o.tscs.complete.start = start;
152a19fcc2bSWalter Erquinigo o.tscs.complete.end = end;
153a19fcc2bSWalter Erquinigo return o;
154a19fcc2bSWalter Erquinigo }
155a19fcc2bSWalter Erquinigo
CreateHintedStartExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t hinted_start,uint64_t end)156a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateHintedStartExecution(
1576a5355e8SWalter Erquinigo lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid,
158a19fcc2bSWalter Erquinigo uint64_t hinted_start, uint64_t end) {
1596a5355e8SWalter Erquinigo ThreadContinuousExecution o(cpu_id, tid, pid);
160a19fcc2bSWalter Erquinigo o.variant = Variant::HintedStart;
161a19fcc2bSWalter Erquinigo o.tscs.hinted_start.hinted_start = hinted_start;
162a19fcc2bSWalter Erquinigo o.tscs.hinted_start.end = end;
163a19fcc2bSWalter Erquinigo return o;
164a19fcc2bSWalter Erquinigo }
165a19fcc2bSWalter Erquinigo
CreateHintedEndExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t start,uint64_t hinted_end)166a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateHintedEndExecution(
1676a5355e8SWalter Erquinigo lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
168a19fcc2bSWalter Erquinigo uint64_t hinted_end) {
1696a5355e8SWalter Erquinigo ThreadContinuousExecution o(cpu_id, tid, pid);
170a19fcc2bSWalter Erquinigo o.variant = Variant::HintedEnd;
171a19fcc2bSWalter Erquinigo o.tscs.hinted_end.start = start;
172a19fcc2bSWalter Erquinigo o.tscs.hinted_end.hinted_end = hinted_end;
173a19fcc2bSWalter Erquinigo return o;
174a19fcc2bSWalter Erquinigo }
175a19fcc2bSWalter Erquinigo
CreateOnlyEndExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t end)176a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyEndExecution(
1776a5355e8SWalter Erquinigo lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t end) {
1786a5355e8SWalter Erquinigo ThreadContinuousExecution o(cpu_id, tid, pid);
179a19fcc2bSWalter Erquinigo o.variant = Variant::OnlyEnd;
180a19fcc2bSWalter Erquinigo o.tscs.only_end.end = end;
181a19fcc2bSWalter Erquinigo return o;
182a19fcc2bSWalter Erquinigo }
183a19fcc2bSWalter Erquinigo
CreateOnlyStartExecution(lldb::cpu_id_t cpu_id,lldb::tid_t tid,lldb::pid_t pid,uint64_t start)184a19fcc2bSWalter Erquinigo ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyStartExecution(
1856a5355e8SWalter Erquinigo lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start) {
1866a5355e8SWalter Erquinigo ThreadContinuousExecution o(cpu_id, tid, pid);
187a19fcc2bSWalter Erquinigo o.variant = Variant::OnlyStart;
188a19fcc2bSWalter Erquinigo o.tscs.only_start.start = start;
189a19fcc2bSWalter Erquinigo return o;
190a19fcc2bSWalter Erquinigo }
191a19fcc2bSWalter Erquinigo
RecoverExecutionsFromConsecutiveRecords(cpu_id_t cpu_id,const LinuxPerfZeroTscConversion & tsc_conversion,const ContextSwitchRecord & current_record,const std::optional<ContextSwitchRecord> & prev_record,std::function<void (const ThreadContinuousExecution & execution)> on_new_execution)192a19fcc2bSWalter Erquinigo static Error RecoverExecutionsFromConsecutiveRecords(
1936a5355e8SWalter Erquinigo cpu_id_t cpu_id, const LinuxPerfZeroTscConversion &tsc_conversion,
194a19fcc2bSWalter Erquinigo const ContextSwitchRecord ¤t_record,
195*2fe83274SKazu Hirata const std::optional<ContextSwitchRecord> &prev_record,
196a19fcc2bSWalter Erquinigo std::function<void(const ThreadContinuousExecution &execution)>
197a19fcc2bSWalter Erquinigo on_new_execution) {
198a19fcc2bSWalter Erquinigo if (!prev_record) {
199a19fcc2bSWalter Erquinigo if (current_record.IsOut()) {
200a19fcc2bSWalter Erquinigo on_new_execution(ThreadContinuousExecution::CreateOnlyEndExecution(
2016a5355e8SWalter Erquinigo cpu_id, current_record.tid, current_record.pid, current_record.tsc));
202a19fcc2bSWalter Erquinigo }
203a19fcc2bSWalter Erquinigo // The 'in' case will be handled later when we try to look for its end
204a19fcc2bSWalter Erquinigo return Error::success();
205a19fcc2bSWalter Erquinigo }
206a19fcc2bSWalter Erquinigo
207a19fcc2bSWalter Erquinigo const ContextSwitchRecord &prev = *prev_record;
208a19fcc2bSWalter Erquinigo if (prev.tsc >= current_record.tsc)
209a19fcc2bSWalter Erquinigo return createStringError(
210a19fcc2bSWalter Erquinigo inconvertibleErrorCode(),
211a19fcc2bSWalter Erquinigo formatv("A context switch record doesn't happen after the previous "
212a19fcc2bSWalter Erquinigo "record. Previous TSC= {0}, current TSC = {1}.",
213a19fcc2bSWalter Erquinigo prev.tsc, current_record.tsc));
214a19fcc2bSWalter Erquinigo
215a19fcc2bSWalter Erquinigo if (current_record.IsIn() && prev.IsIn()) {
216a19fcc2bSWalter Erquinigo // We found two consecutive ins, which means that we didn't capture
217a19fcc2bSWalter Erquinigo // the end of the previous execution.
218a19fcc2bSWalter Erquinigo on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
2196a5355e8SWalter Erquinigo cpu_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
220a19fcc2bSWalter Erquinigo } else if (current_record.IsOut() && prev.IsOut()) {
221a19fcc2bSWalter Erquinigo // We found two consecutive outs, that means that we didn't capture
222a19fcc2bSWalter Erquinigo // the beginning of the current execution.
223a19fcc2bSWalter Erquinigo on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
2246a5355e8SWalter Erquinigo cpu_id, current_record.tid, current_record.pid, prev.tsc + 1,
225a19fcc2bSWalter Erquinigo current_record.tsc));
226a19fcc2bSWalter Erquinigo } else if (current_record.IsOut() && prev.IsIn()) {
227a19fcc2bSWalter Erquinigo if (current_record.pid == prev.pid && current_record.tid == prev.tid) {
228a19fcc2bSWalter Erquinigo /// A complete execution
229a19fcc2bSWalter Erquinigo on_new_execution(ThreadContinuousExecution::CreateCompleteExecution(
2306a5355e8SWalter Erquinigo cpu_id, current_record.tid, current_record.pid, prev.tsc,
231a19fcc2bSWalter Erquinigo current_record.tsc));
232a19fcc2bSWalter Erquinigo } else {
233a19fcc2bSWalter Erquinigo // An out after the in of a different thread. The first one doesn't
234a19fcc2bSWalter Erquinigo // have an end, and the second one doesn't have a start.
235a19fcc2bSWalter Erquinigo on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
2366a5355e8SWalter Erquinigo cpu_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
237a19fcc2bSWalter Erquinigo on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
2386a5355e8SWalter Erquinigo cpu_id, current_record.tid, current_record.pid, prev.tsc + 1,
239a19fcc2bSWalter Erquinigo current_record.tsc));
240a19fcc2bSWalter Erquinigo }
241a19fcc2bSWalter Erquinigo }
242a19fcc2bSWalter Erquinigo return Error::success();
243a19fcc2bSWalter Erquinigo }
244a19fcc2bSWalter Erquinigo
245a19fcc2bSWalter Erquinigo Expected<std::vector<ThreadContinuousExecution>>
DecodePerfContextSwitchTrace(ArrayRef<uint8_t> data,cpu_id_t cpu_id,const LinuxPerfZeroTscConversion & tsc_conversion)246a19fcc2bSWalter Erquinigo lldb_private::trace_intel_pt::DecodePerfContextSwitchTrace(
2476a5355e8SWalter Erquinigo ArrayRef<uint8_t> data, cpu_id_t cpu_id,
248a19fcc2bSWalter Erquinigo const LinuxPerfZeroTscConversion &tsc_conversion) {
249a19fcc2bSWalter Erquinigo
250a19fcc2bSWalter Erquinigo std::vector<ThreadContinuousExecution> executions;
251a19fcc2bSWalter Erquinigo
252a19fcc2bSWalter Erquinigo // This offset is used to create the error message in case of failures.
253a19fcc2bSWalter Erquinigo size_t offset = 0;
254a19fcc2bSWalter Erquinigo
255a19fcc2bSWalter Erquinigo auto do_decode = [&]() -> Error {
256*2fe83274SKazu Hirata std::optional<ContextSwitchRecord> prev_record;
257a19fcc2bSWalter Erquinigo while (offset < data.size()) {
25867c24051SWalter Erquinigo const perf_event_header &perf_record =
25967c24051SWalter Erquinigo *reinterpret_cast<const perf_event_header *>(data.data() + offset);
260a19fcc2bSWalter Erquinigo if (Error err = perf_record.SanityCheck())
261a19fcc2bSWalter Erquinigo return err;
262a19fcc2bSWalter Erquinigo
263a19fcc2bSWalter Erquinigo if (perf_record.IsContextSwitchRecord()) {
26467c24051SWalter Erquinigo const PerfContextSwitchRecord &context_switch_record =
26567c24051SWalter Erquinigo *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
26667c24051SWalter Erquinigo offset);
267a19fcc2bSWalter Erquinigo ContextSwitchRecord record{
26867c24051SWalter Erquinigo tsc_conversion.ToTSC(context_switch_record.time_in_nanos),
26967c24051SWalter Erquinigo context_switch_record.IsOut(),
27067c24051SWalter Erquinigo static_cast<lldb::pid_t>(context_switch_record.pid),
27167c24051SWalter Erquinigo static_cast<lldb::tid_t>(context_switch_record.tid)};
272a19fcc2bSWalter Erquinigo
273a19fcc2bSWalter Erquinigo if (Error err = RecoverExecutionsFromConsecutiveRecords(
2746a5355e8SWalter Erquinigo cpu_id, tsc_conversion, record, prev_record,
275a19fcc2bSWalter Erquinigo [&](const ThreadContinuousExecution &execution) {
276a19fcc2bSWalter Erquinigo executions.push_back(execution);
277a19fcc2bSWalter Erquinigo }))
278a19fcc2bSWalter Erquinigo return err;
279a19fcc2bSWalter Erquinigo
280a19fcc2bSWalter Erquinigo prev_record = record;
281a19fcc2bSWalter Erquinigo }
28267c24051SWalter Erquinigo offset += perf_record.size;
283a19fcc2bSWalter Erquinigo }
284a19fcc2bSWalter Erquinigo
285a19fcc2bSWalter Erquinigo // We might have an incomplete last record
286a19fcc2bSWalter Erquinigo if (prev_record && prev_record->IsIn())
287a19fcc2bSWalter Erquinigo executions.push_back(ThreadContinuousExecution::CreateOnlyStartExecution(
2886a5355e8SWalter Erquinigo cpu_id, prev_record->tid, prev_record->pid, prev_record->tsc));
289a19fcc2bSWalter Erquinigo return Error::success();
290a19fcc2bSWalter Erquinigo };
291a19fcc2bSWalter Erquinigo
292a19fcc2bSWalter Erquinigo if (Error err = do_decode())
293a19fcc2bSWalter Erquinigo return createStringError(inconvertibleErrorCode(),
294a19fcc2bSWalter Erquinigo formatv("Malformed perf context switch trace for "
295a19fcc2bSWalter Erquinigo "cpu {0} at offset {1}. {2}",
2966a5355e8SWalter Erquinigo cpu_id, offset, toString(std::move(err))));
297a19fcc2bSWalter Erquinigo
298a19fcc2bSWalter Erquinigo return executions;
299a19fcc2bSWalter Erquinigo }
300b532dd54SWalter Erquinigo
301b532dd54SWalter Erquinigo Expected<std::vector<uint8_t>>
FilterProcessesFromContextSwitchTrace(llvm::ArrayRef<uint8_t> data,const std::set<lldb::pid_t> & pids)302b532dd54SWalter Erquinigo lldb_private::trace_intel_pt::FilterProcessesFromContextSwitchTrace(
303b532dd54SWalter Erquinigo llvm::ArrayRef<uint8_t> data, const std::set<lldb::pid_t> &pids) {
304b532dd54SWalter Erquinigo size_t offset = 0;
305b532dd54SWalter Erquinigo std::vector<uint8_t> out_data;
306b532dd54SWalter Erquinigo
307b532dd54SWalter Erquinigo while (offset < data.size()) {
308b532dd54SWalter Erquinigo const perf_event_header &perf_record =
309b532dd54SWalter Erquinigo *reinterpret_cast<const perf_event_header *>(data.data() + offset);
310b532dd54SWalter Erquinigo if (Error err = perf_record.SanityCheck())
311b532dd54SWalter Erquinigo return std::move(err);
312b532dd54SWalter Erquinigo bool should_copy = false;
313b532dd54SWalter Erquinigo if (perf_record.IsContextSwitchRecord()) {
314b532dd54SWalter Erquinigo const PerfContextSwitchRecord &context_switch_record =
315b532dd54SWalter Erquinigo *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
316b532dd54SWalter Erquinigo offset);
317b532dd54SWalter Erquinigo if (pids.count(context_switch_record.pid))
318b532dd54SWalter Erquinigo should_copy = true;
319b532dd54SWalter Erquinigo } else if (perf_record.IsErrorRecord()) {
320b532dd54SWalter Erquinigo should_copy = true;
321b532dd54SWalter Erquinigo }
322b532dd54SWalter Erquinigo
323b532dd54SWalter Erquinigo if (should_copy) {
324b532dd54SWalter Erquinigo for (size_t i = 0; i < perf_record.size; i++) {
325b532dd54SWalter Erquinigo out_data.push_back(data[offset + i]);
326b532dd54SWalter Erquinigo }
327b532dd54SWalter Erquinigo }
328b532dd54SWalter Erquinigo
329b532dd54SWalter Erquinigo offset += perf_record.size;
330b532dd54SWalter Erquinigo }
331b532dd54SWalter Erquinigo return out_data;
332b532dd54SWalter Erquinigo }
333