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