• 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/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