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/etm/etm_v4_stream_demultiplexer.h"
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <memory>
23 #include <utility>
24
25 #include "perfetto/base/flat_set.h"
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/flat_hash_map.h"
28 #include "perfetto/ext/base/status_or.h"
29 #include "src/trace_processor/importers/etm/etm_tracker.h"
30 #include "src/trace_processor/importers/etm/etm_v4_stream.h"
31 #include "src/trace_processor/importers/etm/frame_decoder.h"
32 #include "src/trace_processor/importers/etm/opencsd.h"
33 #include "src/trace_processor/importers/etm/types.h"
34 #include "src/trace_processor/importers/perf/aux_data_tokenizer.h"
35 #include "src/trace_processor/importers/perf/aux_stream_manager.h"
36 #include "src/trace_processor/importers/perf/auxtrace_info_record.h"
37 #include "src/trace_processor/importers/perf/reader.h"
38 #include "src/trace_processor/importers/perf/util.h"
39 #include "src/trace_processor/tables/etm_tables_py.h"
40 #include "src/trace_processor/util/status_macros.h"
41
42 namespace perfetto::trace_processor::etm {
43 namespace {
44
45 static constexpr uint64_t kEtmV4Magic = 0x4040404040404040ULL;
46 static constexpr uint64_t kEteMagic = 0x5050505050505050ULL;
47
48 struct RawHeader {
49 uint64_t version;
50 uint32_t cpu_count;
51 uint32_t pmu_type;
52 uint64_t snapshot;
53 };
54
55 struct RawCpuHeader {
56 uint64_t magic;
57 uint64_t cpu;
58 uint64_t trace_parameter_count;
59 };
60
61 struct RawEtmV4Info {
62 uint64_t trcconfigr;
63 uint64_t trctraceidr;
64 uint64_t trcidr0;
65 uint64_t trcidr1;
66 uint64_t trcidr2;
67 uint64_t trcidr8;
68 uint64_t trcauthstatus;
69 };
70
71 struct RawEteInfo : public RawEtmV4Info {
72 uint64_t trcdevarch;
73 };
74
ParseEtmV4(TraceBlobView blob)75 base::StatusOr<std::unique_ptr<Configuration>> ParseEtmV4(TraceBlobView blob) {
76 perf_importer::Reader reader(std::move(blob));
77
78 RawEtmV4Info info;
79 if (!reader.Read(info)) {
80 return base::ErrStatus("Failed to read EtmV4Info");
81 }
82
83 ocsd_etmv4_cfg cfg;
84 memset(&cfg, 0, sizeof(cfg));
85 cfg.reg_idr0 = static_cast<uint32_t>(info.trcidr0);
86 cfg.reg_idr1 = static_cast<uint32_t>(info.trcidr1);
87 cfg.reg_idr2 = static_cast<uint32_t>(info.trcidr2);
88 cfg.reg_idr8 = static_cast<uint32_t>(info.trcidr8);
89 cfg.reg_idr9 = 0;
90 cfg.reg_idr10 = 0;
91 cfg.reg_idr11 = 0;
92 cfg.reg_idr12 = 0;
93 cfg.reg_idr13 = 0;
94 cfg.reg_configr = static_cast<uint32_t>(info.trcconfigr);
95 cfg.reg_traceidr = static_cast<uint32_t>(info.trctraceidr);
96 // For minor_version >= 4 we can assume ARCH_AA64
97 cfg.arch_ver = ((cfg.reg_idr0 >> 4) & 0x0F) >= 4 ? ARCH_AA64 : ARCH_V8;
98 cfg.core_prof = profile_CortexA;
99
100 return std::make_unique<Configuration>(cfg);
101 }
102
ParseEte(TraceBlobView blob)103 base::StatusOr<std::unique_ptr<Configuration>> ParseEte(TraceBlobView blob) {
104 perf_importer::Reader reader(std::move(blob));
105
106 RawEteInfo info;
107 if (!reader.Read(info)) {
108 return base::ErrStatus("Failed to read RawEteInfo");
109 }
110
111 ocsd_ete_cfg cfg;
112 cfg.reg_idr0 = static_cast<uint32_t>(info.trcidr0);
113 cfg.reg_idr1 = static_cast<uint32_t>(info.trcidr1);
114 cfg.reg_idr2 = static_cast<uint32_t>(info.trcidr2);
115 cfg.reg_idr8 = static_cast<uint32_t>(info.trcidr8);
116 cfg.reg_configr = static_cast<uint32_t>(info.trcconfigr);
117 cfg.reg_traceidr = static_cast<uint32_t>(info.trctraceidr);
118 cfg.reg_devarch = static_cast<uint32_t>(info.trcdevarch);
119 cfg.arch_ver = ARCH_AA64;
120 cfg.core_prof = profile_CortexA;
121
122 return std::make_unique<Configuration>(cfg);
123 }
124
125 base::StatusOr<std::pair<uint32_t, std::unique_ptr<Configuration>>>
ReadCpuConfig(perf_importer::Reader & reader)126 ReadCpuConfig(perf_importer::Reader& reader) {
127 RawCpuHeader cpu_header;
128 if (!reader.Read(cpu_header)) {
129 return base::ErrStatus("Failed to read ETM info header");
130 }
131
132 uint32_t cpu;
133 if (!perf_importer::SafeCast(cpu_header.cpu, &cpu)) {
134 return base::ErrStatus("Integer overflow in ETM info header");
135 }
136
137 uint32_t size;
138 if (cpu_header.trace_parameter_count >
139 std::numeric_limits<uint32_t>::max() / 8) {
140 return base::ErrStatus("Integer overflow in ETM info header");
141 }
142 size = static_cast<uint32_t>(cpu_header.trace_parameter_count * 8);
143
144 TraceBlobView blob;
145 if (!reader.ReadBlob(blob, size)) {
146 return base::ErrStatus(
147 "Not enough data in ETM info. trace_parameter_count=%" PRIu64,
148 cpu_header.trace_parameter_count);
149 }
150
151 std::unique_ptr<Configuration> config;
152
153 switch (cpu_header.magic) {
154 case kEtmV4Magic: {
155 ASSIGN_OR_RETURN(config, ParseEtmV4(std::move(blob)));
156 break;
157 }
158
159 case kEteMagic: {
160 ASSIGN_OR_RETURN(config, ParseEte(std::move(blob)));
161 break;
162 }
163
164 default:
165 return base::ErrStatus("Unknown magic: 0x%" PRIX64, cpu_header.magic);
166 }
167 return std::make_pair(cpu, std::move(config));
168 }
169
ParseAuxtraceInfo(perf_importer::AuxtraceInfoRecord info)170 base::StatusOr<PerCpuConfiguration> ParseAuxtraceInfo(
171 perf_importer::AuxtraceInfoRecord info) {
172 PERFETTO_CHECK(info.type == PERF_AUXTRACE_CS_ETM);
173 perf_importer::Reader reader(std::move(info.payload));
174
175 RawHeader header;
176 if (!reader.Read(header)) {
177 return base::ErrStatus("Failed to read ETM info header");
178 }
179
180 if (header.version < 1) {
181 return base::ErrStatus("Unsupported version in EtmConfiguration: %" PRIu64,
182 header.version);
183 }
184
185 PerCpuConfiguration per_cpu_configuration;
186 base::FlatSet<uint8_t> seen_trace_ids;
187 for (; header.cpu_count != 0; --header.cpu_count) {
188 ASSIGN_OR_RETURN(auto cpu_config, ReadCpuConfig(reader));
189 uint32_t cpu = cpu_config.first;
190 std::unique_ptr<Configuration> config = std::move(cpu_config.second);
191
192 // TODO(carlscab): support VMID
193 if (!config->etm_v4_config().enabledCID()) {
194 return base::ErrStatus(
195 "ETM Stream without context ID not supported (yet?)");
196 }
197
198 const auto trace_id = config->etm_v4_config().getTraceID();
199 if (!OCSD_IS_VALID_CS_SRC_ID(trace_id)) {
200 return base::ErrStatus("Invalid trace id: %" PRIu8, trace_id);
201 }
202 if (seen_trace_ids.count(trace_id)) {
203 return base::ErrStatus("Duplicate configuration for trace Id: %" PRIu8,
204 trace_id);
205 }
206
207 bool success = per_cpu_configuration.Insert(cpu, std::move(config)).second;
208
209 if (!success) {
210 return base::ErrStatus("Duplicate configuration for CPU Id: %" PRIu32,
211 cpu);
212 }
213 }
214
215 return std::move(per_cpu_configuration);
216 }
217
218 // ETM data is embedded in the AUX buffers.
219 // Data can be stored in two different formats depending on whether ETR or TRBE
220 // is used to collect the data.
221 //
222 // In the former all CPUs write their data to the ETR and once trace is stopped
223 // on all CPUs it is written to system memory. Thus data for all CPUs arrives in
224 // one AUX record for the CPU that collected the data. The actual trace data
225 // will be in frame formatted form and needs to be passed to a decoder to
226 // extract the various streams. AUX data is passed by the perf importer to the
227 // CPU specific `AuxDataStream`, but as we just said we need to first decode
228 // this data to extract the real per CPU streams, so the `EtmV4Stream` classes
229 // (`AuxDataStream` subclasses) forward such data to this class, that will
230 // decode the streams and finally forward them back to the CPU specific
231 // `EtmV4Stream` where it can now be handled.
232 //
233 // For the TRBE the data that arrives in the AUX record is unformatted and is
234 // the data for that given CPU so it can be directly processed by the
235 // `EtmV4Stream` class without needing to decode it first.
236 //
237 // Data flow for framed data (ETR):
238 // 1. `PerfDataTokenizer` parses `AuxData` for cpu x and forwards it to the
239 // `AuxDataStream` bound to that cpu
240 // 2. `EtmV4Stream` bound to cpu x determines AuxData is framed and forwards
241 // it to the `FrameDecoder` owned by `EtmV4StreamDemultiplexer`.
242 // 3. De-multiplexed ETM data is sent to its corresponding `EtmV4Stream` where
243 // it is stored in `TraceStorage`.
244 //
245 // Data flow for raw data (TRBE):
246 // 1. `PerfDataTokenizer` parses `AuxData` for cpu x and forwards it to the
247 // `AuxDataStream` bound to that cpu
248 // 2. `EtmV4Stream` bound to cpu x determines AuxData is raw and can directly
249 // store it in `TraceStorage`.
250 class EtmV4StreamDemultiplexer : public perf_importer::AuxDataTokenizer {
251 public:
EtmV4StreamDemultiplexer(TraceProcessorContext * context)252 explicit EtmV4StreamDemultiplexer(TraceProcessorContext* context)
253 : context_(context) {}
254 ~EtmV4StreamDemultiplexer() override = default;
255
InitializeAuxDataStream(perf_importer::AuxStream * stream)256 base::StatusOr<perf_importer::AuxDataStream*> InitializeAuxDataStream(
257 perf_importer::AuxStream* stream) override {
258 if (stream->type() != perf_importer::AuxStream::Type::kCpuBound) {
259 return base::ErrStatus("ETM only supports CPU bound AUX streams");
260 }
261
262 auto it = streams_.Find(stream->cpu());
263 if (!it) {
264 return base::ErrStatus("No EtmV4Stream for CPU: %" PRIu32, stream->cpu());
265 }
266
267 return it->get();
268 }
269
Init(perf_importer::AuxtraceInfoRecord info)270 base::Status Init(perf_importer::AuxtraceInfoRecord info) {
271 RETURN_IF_ERROR(decoder_.Init());
272 ASSIGN_OR_RETURN(PerCpuConfiguration per_cpu_configuration,
273 ParseAuxtraceInfo(std::move(info)));
274
275 for (auto id : EtmTracker::GetOrCreate(context_)->InsertEtmV4Config(
276 std::move(per_cpu_configuration))) {
277 RETURN_IF_ERROR(InitCpu(id));
278 }
279 return base::OkStatus();
280 }
281
282 private:
InitCpu(tables::EtmV4ConfigurationTable::Id config_id)283 base::Status InitCpu(tables::EtmV4ConfigurationTable::Id config_id) {
284 auto config =
285 *context_->storage->etm_v4_configuration_table().FindById(config_id);
286
287 auto stream = std::make_unique<EtmV4Stream>(context_, &decoder_, config_id);
288
289 RETURN_IF_ERROR(decoder_.Attach(static_cast<uint8_t>(config.cs_trace_id()),
290 stream.get()));
291 PERFETTO_CHECK(streams_.Insert(config.cpu(), std::move(stream)).second);
292 return base::OkStatus();
293 }
294
295 TraceProcessorContext* const context_;
296 FrameDecoder decoder_;
297 base::FlatHashMap<uint32_t, std::unique_ptr<EtmV4Stream>> streams_;
298 };
299
300 } // namespace
301
302 // static
303 base::StatusOr<std::unique_ptr<perf_importer::AuxDataTokenizer>>
CreateEtmV4StreamDemultiplexer(TraceProcessorContext * context,perf_importer::AuxtraceInfoRecord info)304 CreateEtmV4StreamDemultiplexer(TraceProcessorContext* context,
305 perf_importer::AuxtraceInfoRecord info) {
306 std::unique_ptr<EtmV4StreamDemultiplexer> tokenizer(
307 new EtmV4StreamDemultiplexer(context));
308
309 RETURN_IF_ERROR(tokenizer->Init(std::move(info)));
310
311 return std::unique_ptr<perf_importer::AuxDataTokenizer>(std::move(tokenizer));
312 }
313
314 } // namespace perfetto::trace_processor::etm
315