xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/perf/sample.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/importers/perf/sample.h"
18 
19 #include <cstdint>
20 #include <optional>
21 #include <utility>
22 #include <vector>
23 
24 #include "perfetto/base/logging.h"
25 #include "perfetto/base/status.h"
26 #include "perfetto/public/compiler.h"
27 #include "src/trace_processor/importers/perf/perf_event.h"
28 #include "src/trace_processor/importers/perf/reader.h"
29 #include "src/trace_processor/importers/perf/record.h"
30 
31 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
32 
33 namespace perfetto::trace_processor::perf_importer {
34 namespace {
35 
ParseSampleReadGroup(Reader & reader,uint64_t read_format,uint64_t num_records,std::vector<Sample::ReadGroup> & out)36 bool ParseSampleReadGroup(Reader& reader,
37                           uint64_t read_format,
38                           uint64_t num_records,
39                           std::vector<Sample::ReadGroup>& out) {
40   out.resize(num_records);
41   for (auto& read : out) {
42     if (PERFETTO_UNLIKELY(!reader.Read(read.value))) {
43       return false;
44     }
45 
46     if (read_format & PERF_FORMAT_ID) {
47       if (PERFETTO_UNLIKELY(!reader.ReadOptional(read.event_id))) {
48         return false;
49       }
50     }
51 
52     if (read_format & PERF_FORMAT_LOST) {
53       uint64_t lost;
54       if (PERFETTO_UNLIKELY(!reader.Read(lost))) {
55         return false;
56       }
57     }
58   }
59 
60   return true;
61 }
62 
ParseSampleRead(Reader & reader,uint64_t read_format,std::vector<Sample::ReadGroup> & out)63 bool ParseSampleRead(Reader& reader,
64                      uint64_t read_format,
65                      std::vector<Sample::ReadGroup>& out) {
66   uint64_t value_or_nr;
67 
68   if (PERFETTO_UNLIKELY(!reader.Read(value_or_nr))) {
69     return false;
70   }
71 
72   if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
73     uint64_t total_time_enabled;
74     if (PERFETTO_UNLIKELY(!reader.Read(total_time_enabled))) {
75       return false;
76     }
77   }
78 
79   if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
80     uint64_t total_time_running;
81     if (PERFETTO_UNLIKELY(!reader.Read(total_time_running))) {
82       return false;
83     }
84   }
85 
86   if (read_format & PERF_FORMAT_GROUP) {
87     return ParseSampleReadGroup(reader, read_format, value_or_nr, out);
88   }
89 
90   std::optional<uint64_t> event_id;
91   if (read_format & PERF_FORMAT_ID) {
92     event_id.emplace(0);
93     if (PERFETTO_UNLIKELY(!reader.ReadOptional(event_id))) {
94       return false;
95     }
96   }
97 
98   if (read_format & PERF_FORMAT_LOST) {
99     uint64_t lost;
100     if (PERFETTO_UNLIKELY(!reader.Read(lost))) {
101       return false;
102     }
103   }
104 
105   out.push_back({event_id, value_or_nr});
106 
107   return true;
108 }
109 
PerfCallchainContextToCpuMode(uint64_t ip)110 protos::pbzero::Profiling::CpuMode PerfCallchainContextToCpuMode(uint64_t ip) {
111   switch (ip) {
112     case PERF_CONTEXT_HV:
113       return protos::pbzero::Profiling::MODE_HYPERVISOR;
114     case PERF_CONTEXT_KERNEL:
115       return protos::pbzero::Profiling::MODE_KERNEL;
116     case PERF_CONTEXT_USER:
117       return protos::pbzero::Profiling::MODE_USER;
118     case PERF_CONTEXT_GUEST_KERNEL:
119       return protos::pbzero::Profiling::MODE_GUEST_KERNEL;
120     case PERF_CONTEXT_GUEST_USER:
121       return protos::pbzero::Profiling::MODE_GUEST_USER;
122     case PERF_CONTEXT_GUEST:
123     default:
124       return protos::pbzero::Profiling::MODE_UNKNOWN;
125   }
126   PERFETTO_FATAL("For GCC");
127 }
128 
IsPerfContextMark(uint64_t ip)129 bool IsPerfContextMark(uint64_t ip) {
130   return ip >= PERF_CONTEXT_MAX;
131 }
132 
ParseSampleCallchain(Reader & reader,protos::pbzero::Profiling::CpuMode cpu_mode,std::vector<Sample::Frame> & out)133 bool ParseSampleCallchain(Reader& reader,
134                           protos::pbzero::Profiling::CpuMode cpu_mode,
135                           std::vector<Sample::Frame>& out) {
136   uint64_t nr;
137   if (PERFETTO_UNLIKELY(!reader.Read(nr))) {
138     return false;
139   }
140 
141   std::vector<Sample::Frame> frames;
142   frames.reserve(nr);
143   for (; nr != 0; --nr) {
144     uint64_t ip;
145     if (PERFETTO_UNLIKELY(!reader.Read(ip))) {
146       return false;
147     }
148     if (PERFETTO_UNLIKELY(IsPerfContextMark(ip))) {
149       cpu_mode = PerfCallchainContextToCpuMode(ip);
150       continue;
151     }
152     frames.push_back({cpu_mode, ip});
153   }
154 
155   out = std::move(frames);
156   return true;
157 }
158 }  // namespace
159 
Parse(int64_t in_trace_ts,const Record & record)160 base::Status Sample::Parse(int64_t in_trace_ts, const Record& record) {
161   PERFETTO_CHECK(record.attr);
162   const uint64_t sample_type = record.attr->sample_type();
163 
164   trace_ts = in_trace_ts;
165   cpu_mode = record.GetCpuMode();
166   perf_session = record.session;
167   attr = record.attr;
168 
169   Reader reader(record.payload.copy());
170 
171   std::optional<uint64_t> identifier;
172   if (sample_type & PERF_SAMPLE_IDENTIFIER) {
173     if (PERFETTO_UNLIKELY(!reader.ReadOptional(identifier))) {
174       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_IDENTIFIER");
175     }
176   }
177 
178   if (sample_type & PERF_SAMPLE_IP) {
179     if (PERFETTO_UNLIKELY(!reader.ReadOptional(ip))) {
180       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_IP");
181     }
182   }
183 
184   if (sample_type & PERF_SAMPLE_TID) {
185     if (PERFETTO_UNLIKELY(!reader.ReadOptional(pid_tid))) {
186       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_TID");
187     }
188   }
189 
190   if (sample_type & PERF_SAMPLE_TIME) {
191     if (PERFETTO_UNLIKELY(!reader.ReadOptional(time))) {
192       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_TIME");
193     }
194   }
195 
196   if (sample_type & PERF_SAMPLE_ADDR) {
197     if (PERFETTO_UNLIKELY(!reader.ReadOptional(addr))) {
198       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_ADDR");
199     }
200   }
201 
202   if (sample_type & PERF_SAMPLE_ID) {
203     if (PERFETTO_UNLIKELY(!reader.ReadOptional(id))) {
204       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_ID");
205     }
206   }
207 
208   if (identifier.has_value()) {
209     if (!id.has_value()) {
210       id = identifier;
211     } else if (PERFETTO_UNLIKELY(*identifier != *id)) {
212       return base::ErrStatus("ID and IDENTIFIER mismatch");
213     }
214   }
215 
216   if (sample_type & PERF_SAMPLE_STREAM_ID) {
217     if (PERFETTO_UNLIKELY(!reader.ReadOptional(stream_id))) {
218       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_STREAM_ID");
219     }
220   }
221 
222   if (sample_type & PERF_SAMPLE_CPU) {
223     struct {
224       int32_t cpu;
225       int32_t unused;
226     } tmp;
227     if (PERFETTO_UNLIKELY(!reader.Read(tmp))) {
228       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_CPU");
229     }
230     cpu = tmp.cpu;
231   }
232 
233   if (sample_type & PERF_SAMPLE_PERIOD) {
234     if (PERFETTO_UNLIKELY(!reader.ReadOptional(period))) {
235       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_PERIOD");
236     }
237   }
238 
239   if (sample_type & PERF_SAMPLE_READ) {
240     if (PERFETTO_UNLIKELY(
241             !ParseSampleRead(reader, attr->read_format(), read_groups))) {
242       return base::ErrStatus("Failed to read PERF_SAMPLE_READ field");
243     }
244     if (read_groups.empty()) {
245       return base::ErrStatus("No data in PERF_SAMPLE_READ field");
246     }
247   }
248 
249   if (sample_type & PERF_SAMPLE_CALLCHAIN) {
250     if (PERFETTO_UNLIKELY(!ParseSampleCallchain(reader, cpu_mode, callchain))) {
251       return base::ErrStatus("Failed to read PERF_SAMPLE_CALLCHAIN field");
252     }
253   }
254 
255   return base::OkStatus();
256 }
257 
258 }  // namespace perfetto::trace_processor::perf_importer
259