• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 #include "perfetto/base/logging.h"
22 #include "perfetto/base/status.h"
23 #include "perfetto/public/compiler.h"
24 #include "src/trace_processor/importers/perf/reader.h"
25 #include "src/trace_processor/importers/perf/record.h"
26 
27 namespace perfetto::trace_processor::perf_importer {
28 namespace {
29 
ParseSampleReadGroup(Reader & reader,uint64_t read_format,uint64_t num_records,std::vector<Sample::ReadGroup> & out)30 bool ParseSampleReadGroup(Reader& reader,
31                           uint64_t read_format,
32                           uint64_t num_records,
33                           std::vector<Sample::ReadGroup>& out) {
34   out.resize(num_records);
35   for (auto& read : out) {
36     if (PERFETTO_UNLIKELY(!reader.Read(read.value))) {
37       return false;
38     }
39 
40     if (read_format & PERF_FORMAT_ID) {
41       if (PERFETTO_UNLIKELY(!reader.ReadOptional(read.event_id))) {
42         return false;
43       }
44     }
45 
46     if (read_format & PERF_FORMAT_LOST) {
47       uint64_t lost;
48       if (PERFETTO_UNLIKELY(!reader.Read(lost))) {
49         return false;
50       }
51     }
52   }
53 
54   return true;
55 }
56 
ParseSampleRead(Reader & reader,uint64_t read_format,std::vector<Sample::ReadGroup> & out)57 bool ParseSampleRead(Reader& reader,
58                      uint64_t read_format,
59                      std::vector<Sample::ReadGroup>& out) {
60   uint64_t value_or_nr;
61 
62   if (PERFETTO_UNLIKELY(!reader.Read(value_or_nr))) {
63     return false;
64   }
65 
66   if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
67     uint64_t total_time_enabled;
68     if (PERFETTO_UNLIKELY(!reader.Read(total_time_enabled))) {
69       return false;
70     }
71   }
72 
73   if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
74     uint64_t total_time_running;
75     if (PERFETTO_UNLIKELY(!reader.Read(total_time_running))) {
76       return false;
77     }
78   }
79 
80   if (read_format & PERF_FORMAT_GROUP) {
81     return ParseSampleReadGroup(reader, read_format, value_or_nr, out);
82   }
83 
84   std::optional<uint64_t> event_id;
85   if (read_format & PERF_FORMAT_ID) {
86     event_id.emplace(0);
87     if (PERFETTO_UNLIKELY(!reader.ReadOptional(event_id))) {
88       return false;
89     }
90   }
91 
92   if (read_format & PERF_FORMAT_LOST) {
93     uint64_t lost;
94     if (PERFETTO_UNLIKELY(!reader.Read(lost))) {
95       return false;
96     }
97   }
98 
99   out.push_back({event_id, value_or_nr});
100 
101   return true;
102 }
103 
PerfCallchainContextToCpuMode(uint64_t ip)104 protos::pbzero::Profiling::CpuMode PerfCallchainContextToCpuMode(uint64_t ip) {
105   switch (ip) {
106     case PERF_CONTEXT_HV:
107       return protos::pbzero::Profiling::MODE_HYPERVISOR;
108     case PERF_CONTEXT_KERNEL:
109       return protos::pbzero::Profiling::MODE_KERNEL;
110     case PERF_CONTEXT_USER:
111       return protos::pbzero::Profiling::MODE_USER;
112     case PERF_CONTEXT_GUEST_KERNEL:
113       return protos::pbzero::Profiling::MODE_GUEST_KERNEL;
114     case PERF_CONTEXT_GUEST_USER:
115       return protos::pbzero::Profiling::MODE_GUEST_USER;
116     case PERF_CONTEXT_GUEST:
117     default:
118       return protos::pbzero::Profiling::MODE_UNKNOWN;
119   }
120   PERFETTO_FATAL("For GCC");
121 }
122 
IsPerfContextMark(uint64_t ip)123 bool IsPerfContextMark(uint64_t ip) {
124   return ip >= PERF_CONTEXT_MAX;
125 }
126 
ParseSampleCallchain(Reader & reader,protos::pbzero::Profiling::CpuMode cpu_mode,std::vector<Sample::Frame> & out)127 bool ParseSampleCallchain(Reader& reader,
128                           protos::pbzero::Profiling::CpuMode cpu_mode,
129                           std::vector<Sample::Frame>& out) {
130   uint64_t nr;
131   if (PERFETTO_UNLIKELY(!reader.Read(nr))) {
132     return false;
133   }
134 
135   std::vector<Sample::Frame> frames;
136   frames.reserve(nr);
137   for (; nr != 0; --nr) {
138     uint64_t ip;
139     if (PERFETTO_UNLIKELY(!reader.Read(ip))) {
140       return false;
141     }
142     if (PERFETTO_UNLIKELY(IsPerfContextMark(ip))) {
143       cpu_mode = PerfCallchainContextToCpuMode(ip);
144       continue;
145     }
146     frames.push_back({cpu_mode, ip});
147   }
148 
149   out = std::move(frames);
150   return true;
151 }
152 }  // namespace
153 
Parse(int64_t in_trace_ts,const Record & record)154 base::Status Sample::Parse(int64_t in_trace_ts, const Record& record) {
155   PERFETTO_CHECK(record.attr);
156   const uint64_t sample_type = record.attr->sample_type();
157 
158   trace_ts = in_trace_ts;
159   cpu_mode = record.GetCpuMode();
160   perf_session = record.session;
161   attr = record.attr;
162 
163   Reader reader(record.payload.copy());
164 
165   std::optional<uint64_t> identifier;
166   if (sample_type & PERF_SAMPLE_IDENTIFIER) {
167     if (PERFETTO_UNLIKELY(!reader.ReadOptional(identifier))) {
168       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_IDENTIFIER");
169     }
170   }
171 
172   if (sample_type & PERF_SAMPLE_IP) {
173     if (PERFETTO_UNLIKELY(!reader.ReadOptional(ip))) {
174       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_IP");
175     }
176   }
177 
178   if (sample_type & PERF_SAMPLE_TID) {
179     if (PERFETTO_UNLIKELY(!reader.ReadOptional(pid_tid))) {
180       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_TID");
181     }
182   }
183 
184   if (sample_type & PERF_SAMPLE_TIME) {
185     if (PERFETTO_UNLIKELY(!reader.ReadOptional(time))) {
186       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_TIME");
187     }
188   }
189 
190   if (sample_type & PERF_SAMPLE_ADDR) {
191     if (PERFETTO_UNLIKELY(!reader.ReadOptional(addr))) {
192       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_ADDR");
193     }
194   }
195 
196   if (sample_type & PERF_SAMPLE_ID) {
197     if (PERFETTO_UNLIKELY(!reader.ReadOptional(id))) {
198       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_ID");
199     }
200   }
201 
202   if (identifier.has_value()) {
203     if (!id.has_value()) {
204       id = identifier;
205     } else if (PERFETTO_UNLIKELY(*identifier != *id)) {
206       return base::ErrStatus("ID and IDENTIFIER mismatch");
207     }
208   }
209 
210   if (sample_type & PERF_SAMPLE_STREAM_ID) {
211     if (PERFETTO_UNLIKELY(!reader.ReadOptional(stream_id))) {
212       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_STREAM_ID");
213     }
214   }
215 
216   if (sample_type & PERF_SAMPLE_CPU) {
217     struct {
218       int32_t cpu;
219       int32_t unused;
220     } tmp;
221     if (PERFETTO_UNLIKELY(!reader.Read(tmp))) {
222       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_CPU");
223     }
224     cpu = tmp.cpu;
225   }
226 
227   if (sample_type & PERF_SAMPLE_PERIOD) {
228     if (PERFETTO_UNLIKELY(!reader.ReadOptional(period))) {
229       return base ::ErrStatus("Not enough data to read PERF_SAMPLE_PERIOD");
230     }
231   }
232 
233   if (sample_type & PERF_SAMPLE_READ) {
234     if (PERFETTO_UNLIKELY(
235             !ParseSampleRead(reader, attr->read_format(), read_groups))) {
236       return base::ErrStatus("Failed to read PERF_SAMPLE_READ field");
237     }
238     if (read_groups.empty()) {
239       return base::ErrStatus("No data in PERF_SAMPLE_READ field");
240     }
241   }
242 
243   if (sample_type & PERF_SAMPLE_CALLCHAIN) {
244     if (PERFETTO_UNLIKELY(!ParseSampleCallchain(reader, cpu_mode, callchain))) {
245       return base::ErrStatus("Failed to read PERF_SAMPLE_CALLCHAIN field");
246     }
247   }
248 
249   return base::OkStatus();
250 }
251 
252 }  // namespace perfetto::trace_processor::perf_importer
253