xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/etm/etm_v4_stream_demultiplexer.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/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::SafeAdd(cpu_header.cpu, 0, &cpu)) {
134     return base::ErrStatus("Integer overflow in ETM info header");
135   }
136 
137   uint32_t size;
138   if (!perf_importer::SafeMultiply(cpu_header.trace_parameter_count, 8,
139                                    &size)) {
140     return base::ErrStatus("Integer overflow in ETM info header");
141   }
142 
143   TraceBlobView blob;
144   if (!reader.ReadBlob(blob, size)) {
145     return base::ErrStatus(
146         "Not enough data in ETM info. trace_parameter_count=%" PRIu64,
147         cpu_header.trace_parameter_count);
148   }
149 
150   std::unique_ptr<Configuration> config;
151 
152   switch (cpu_header.magic) {
153     case kEtmV4Magic: {
154       ASSIGN_OR_RETURN(config, ParseEtmV4(std::move(blob)));
155       break;
156     }
157 
158     case kEteMagic: {
159       ASSIGN_OR_RETURN(config, ParseEte(std::move(blob)));
160       break;
161     }
162 
163     default:
164       return base::ErrStatus("Unknown magic: 0x%" PRIX64, cpu_header.magic);
165   }
166   return std::make_pair(cpu, std::move(config));
167 }
168 
ParseAuxtraceInfo(perf_importer::AuxtraceInfoRecord info)169 base::StatusOr<PerCpuConfiguration> ParseAuxtraceInfo(
170     perf_importer::AuxtraceInfoRecord info) {
171   PERFETTO_CHECK(info.type == PERF_AUXTRACE_CS_ETM);
172   perf_importer::Reader reader(std::move(info.payload));
173 
174   RawHeader header;
175   if (!reader.Read(header)) {
176     return base::ErrStatus("Failed to read ETM info header");
177   }
178 
179   if (header.version < 1) {
180     return base::ErrStatus("Unsupported version in EtmConfiguration: %" PRIu64,
181                            header.version);
182   }
183 
184   PerCpuConfiguration per_cpu_configuration;
185   base::FlatSet<uint8_t> seen_trace_ids;
186   for (; header.cpu_count != 0; --header.cpu_count) {
187     ASSIGN_OR_RETURN(auto cpu_config, ReadCpuConfig(reader));
188     uint32_t cpu = cpu_config.first;
189     std::unique_ptr<Configuration> config = std::move(cpu_config.second);
190 
191     // TODO(carlscab): support VMID
192     if (!config->etm_v4_config().enabledCID()) {
193       return base::ErrStatus(
194           "ETM Stream without context ID not supported (yet?)");
195     }
196 
197     const auto trace_id = config->etm_v4_config().getTraceID();
198     if (!OCSD_IS_VALID_CS_SRC_ID(trace_id)) {
199       return base::ErrStatus("Invalid trace id: %" PRIu8, trace_id);
200     }
201     if (seen_trace_ids.count(trace_id)) {
202       return base::ErrStatus("Duplicate configuration for trace Id: %" PRIu8,
203                              trace_id);
204     }
205 
206     bool success = per_cpu_configuration.Insert(cpu, std::move(config)).second;
207 
208     if (!success) {
209       return base::ErrStatus("Duplicate configuration for CPU Id: %" PRIu32,
210                              cpu);
211     }
212   }
213 
214   return std::move(per_cpu_configuration);
215 }
216 
217 // ETM data is embedded in the AUX buffers.
218 // Data can be stored in two different formats depending on whether ETR or TRBE
219 // is used to collect the data.
220 //
221 // In the former all CPUs write their data to the ETR and once trace is stopped
222 // on all CPUs it is written to system memory. Thus data for all CPUs arrives in
223 // one AUX record for the CPU that collected the data. The actual trace data
224 // will be in frame formatted form and needs to be passed to a decoder to
225 // extract the various streams. AUX data is passed by the perf importer to the
226 // CPU specific `AuxDataStream`, but as we just said we need to first decode
227 // this data to extract the real per CPU streams, so the `EtmV4Stream` classes
228 // (`AuxDataStream` subclasses) forward such data tho this class, that will
229 // decode the streams and finally forward them back to the CPU specific
230 // `EtmV4Stream` where it can now be handled.
231 //
232 // For the TRBE the data that arrives in the AUX record is unformatted and is
233 // the data for that given CPU so it can be directly processed by the
234 // `EtmV4Stream` class without needing to decode it first.
235 class EtmV4StreamDemultiplexer : public perf_importer::AuxDataTokenizer {
236  public:
EtmV4StreamDemultiplexer(TraceProcessorContext * context)237   explicit EtmV4StreamDemultiplexer(TraceProcessorContext* context)
238       : context_(context) {}
239   ~EtmV4StreamDemultiplexer() override = default;
240 
InitializeAuxDataStream(perf_importer::AuxStream * stream)241   base::StatusOr<perf_importer::AuxDataStream*> InitializeAuxDataStream(
242       perf_importer::AuxStream* stream) override {
243     if (stream->type() != perf_importer::AuxStream::Type::kCpuBound) {
244       return base::ErrStatus("ETM only supports CPU bound AUX streams");
245     }
246 
247     auto it = streams_.Find(stream->cpu());
248     if (!it) {
249       return base::ErrStatus("No EtmV4Stream for CPU: %" PRIu32, stream->cpu());
250     }
251 
252     return it->get();
253   }
254 
Init(perf_importer::AuxtraceInfoRecord info)255   base::Status Init(perf_importer::AuxtraceInfoRecord info) {
256     RETURN_IF_ERROR(decoder_.Init());
257     ASSIGN_OR_RETURN(PerCpuConfiguration per_cpu_configuration,
258                      ParseAuxtraceInfo(std::move(info)));
259 
260     for (auto id : EtmTracker::GetOrCreate(context_)->InsertEtmV4Config(
261              std::move(per_cpu_configuration))) {
262       RETURN_IF_ERROR(InitCpu(id));
263     }
264     return base::OkStatus();
265   }
266 
267  private:
InitCpu(tables::EtmV4ConfigurationTable::Id config_id)268   base::Status InitCpu(tables::EtmV4ConfigurationTable::Id config_id) {
269     auto config =
270         *context_->storage->etm_v4_configuration_table().FindById(config_id);
271 
272     auto stream = std::make_unique<EtmV4Stream>(context_, &decoder_, config_id);
273 
274     RETURN_IF_ERROR(decoder_.Attach(static_cast<uint8_t>(config.cs_trace_id()),
275                                     stream.get()));
276     PERFETTO_CHECK(streams_.Insert(config.cpu(), std::move(stream)).second);
277     return base::OkStatus();
278   }
279 
280   TraceProcessorContext* const context_;
281   FrameDecoder decoder_;
282   base::FlatHashMap<uint32_t, std::unique_ptr<EtmV4Stream>> streams_;
283 };
284 
285 }  // namespace
286 
287 // static
288 base::StatusOr<std::unique_ptr<perf_importer::AuxDataTokenizer>>
CreateEtmV4StreamDemultiplexer(TraceProcessorContext * context,perf_importer::AuxtraceInfoRecord info)289 CreateEtmV4StreamDemultiplexer(TraceProcessorContext* context,
290                                perf_importer::AuxtraceInfoRecord info) {
291   std::unique_ptr<EtmV4StreamDemultiplexer> tokenizer(
292       new EtmV4StreamDemultiplexer(context));
293 
294   RETURN_IF_ERROR(tokenizer->Init(std::move(info)));
295 
296   return std::unique_ptr<perf_importer::AuxDataTokenizer>(std::move(tokenizer));
297 }
298 
299 }  // namespace perfetto::trace_processor::etm
300