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/spe_record_parser.h"
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <optional>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/trace_processor/trace_blob_view.h"
25 #include "src/trace_processor/importers/common/mapping_tracker.h"
26 #include "src/trace_processor/importers/common/process_tracker.h"
27 #include "src/trace_processor/importers/common/virtual_memory_mapping.h"
28 #include "src/trace_processor/importers/perf/reader.h"
29 #include "src/trace_processor/importers/perf/spe.h"
30 #include "src/trace_processor/storage/trace_storage.h"
31 #include "src/trace_processor/tables/metadata_tables_py.h"
32 #include "src/trace_processor/tables/perf_tables_py.h"
33 #include "src/trace_processor/types/trace_processor_context.h"
34
35 namespace perfetto::trace_processor::perf_importer {
36
37 // static
ToString(spe::DataSource ds)38 const char* SpeRecordParserImpl::ToString(spe::DataSource ds) {
39 switch (ds) {
40 case spe::DataSource::kUnknown:
41 return "UNKNOWN";
42 case spe::DataSource::kL1D:
43 return "L1D";
44 case spe::DataSource::kL2:
45 return "L2";
46 case spe::DataSource::kPeerCore:
47 return "PEER_CORE";
48 case spe::DataSource::kLocalCluster:
49 return "LOCAL_CLUSTER";
50 case spe::DataSource::kSysCache:
51 return "SYS_CACHE";
52 case spe::DataSource::kPeerCluster:
53 return "PEER_CLUSTER";
54 case spe::DataSource::kRemote:
55 return "REMOTE";
56 case spe::DataSource::kDram:
57 return "DRAM";
58 }
59 PERFETTO_FATAL("For GCC");
60 }
61
62 // static
ToString(spe::ExceptionLevel el)63 const char* SpeRecordParserImpl::ToString(spe::ExceptionLevel el) {
64 switch (el) {
65 case spe::ExceptionLevel::kEl0:
66 return "EL0";
67 case spe::ExceptionLevel::kEl1:
68 return "EL1";
69 case spe::ExceptionLevel::kEl2:
70 return "EL2";
71 case spe::ExceptionLevel::kEl3:
72 return "EL3";
73 }
74 PERFETTO_FATAL("For GCC");
75 }
76
77 // static
ToString(OperationName name)78 const char* SpeRecordParserImpl::ToString(OperationName name) {
79 switch (name) {
80 case OperationName::kOther:
81 return "OTHER";
82 case OperationName::kSveVecOp:
83 return "SVE_VEC_OP";
84 case OperationName::kLoad:
85 return "LOAD";
86 case OperationName::kStore:
87 return "STORE";
88 case OperationName::kBranch:
89 return "BRANCH";
90 case OperationName::kUnknown:
91 return "UNKNOWN";
92 }
93 PERFETTO_FATAL("For GCC");
94 }
95
ToStringId(OperationName name)96 StringId SpeRecordParserImpl::ToStringId(OperationName name) {
97 if (operation_name_strings_[name] == kNullStringId) {
98 operation_name_strings_[name] =
99 context_->storage->InternString(ToString(name));
100 }
101 return operation_name_strings_[name];
102 }
103
ToStringId(spe::ExceptionLevel el)104 StringId SpeRecordParserImpl::ToStringId(spe::ExceptionLevel el) {
105 if (exception_level_strings_[el] == kNullStringId) {
106 exception_level_strings_[el] =
107 context_->storage->InternString(ToString(el));
108 }
109 return exception_level_strings_[el];
110 }
111
ToStringId(spe::DataSource ds)112 StringId SpeRecordParserImpl::ToStringId(spe::DataSource ds) {
113 if (data_source_strings_[ds] == kNullStringId) {
114 data_source_strings_[ds] = context_->storage->InternString(ToString(ds));
115 }
116 return data_source_strings_[ds];
117 }
118
SpeRecordParserImpl(TraceProcessorContext * context)119 SpeRecordParserImpl::SpeRecordParserImpl(TraceProcessorContext* context)
120 : context_(context), reader_(TraceBlobView()) {}
121
ParseSpeRecord(int64_t ts,TraceBlobView data)122 void SpeRecordParserImpl::ParseSpeRecord(int64_t ts, TraceBlobView data) {
123 reader_ = Reader(std::move(data));
124 inflight_row_ = {};
125 inflight_row_.ts = ts;
126 inflight_record_ = {};
127
128 // No need to check that there is enough data as this has been validated by
129 // the tokenization step.
130 while (reader_.size_left() != 0) {
131 uint8_t byte_0;
132 reader_.Read(byte_0);
133
134 if (spe::IsExtendedHeader(byte_0)) {
135 uint8_t byte_1;
136 reader_.Read(byte_1);
137 spe::ExtendedHeader extended_header(byte_0, byte_1);
138 ReadExtendedPacket(extended_header);
139 } else {
140 ReadShortPacket(spe::ShortHeader(byte_0));
141 }
142 }
143 if (!inflight_record_.instruction_address) {
144 context_->storage->mutable_spe_record_table()->Insert(inflight_row_);
145 return;
146 }
147
148 const auto& inst = *inflight_record_.instruction_address;
149
150 inflight_row_.exception_level = ToStringId(inst.el);
151
152 if (inst.el == spe::ExceptionLevel::kEl0 && inflight_row_.utid) {
153 const auto upid =
154 *context_->storage->thread_table()
155 .FindById(tables::ThreadTable::Id(*inflight_row_.utid))
156 ->upid();
157
158 VirtualMemoryMapping* mapping =
159 context_->mapping_tracker->FindUserMappingForAddress(upid,
160 inst.address);
161 if (mapping) {
162 inflight_row_.instruction_frame_id =
163 mapping->InternFrame(mapping->ToRelativePc(inst.address), "");
164 }
165 } else if (inst.el == spe::ExceptionLevel::kEl1) {
166 VirtualMemoryMapping* mapping =
167 context_->mapping_tracker->FindKernelMappingForAddress(inst.address);
168 if (mapping) {
169 inflight_row_.instruction_frame_id =
170 mapping->InternFrame(mapping->ToRelativePc(inst.address), "");
171 }
172 }
173
174 if (!inflight_row_.instruction_frame_id.has_value()) {
175 inflight_row_.instruction_frame_id = GetDummyMapping()->InternFrame(
176 GetDummyMapping()->ToRelativePc(inst.address), "");
177 }
178
179 context_->storage->mutable_spe_record_table()->Insert(inflight_row_);
180 }
181
ReadShortPacket(spe::ShortHeader short_header)182 void SpeRecordParserImpl::ReadShortPacket(spe::ShortHeader short_header) {
183 if (short_header.IsAddressPacket()) {
184 ReadAddressPacket(short_header.GetAddressIndex());
185
186 } else if (short_header.IsCounterPacket()) {
187 ReadCounterPacket(short_header.GetCounterIndex());
188
189 } else if (short_header.IsEventsPacket()) {
190 ReadEventsPacket(short_header);
191
192 } else if (short_header.IsContextPacket()) {
193 ReadContextPacket(short_header);
194
195 } else if (short_header.IsOperationTypePacket()) {
196 ReadOperationTypePacket(short_header);
197
198 } else if (short_header.IsDataSourcePacket()) {
199 ReadDataSourcePacket(short_header);
200
201 } else {
202 reader_.Skip(short_header.GetPayloadSize());
203 }
204 }
205
ReadExtendedPacket(spe::ExtendedHeader extended_header)206 void SpeRecordParserImpl::ReadExtendedPacket(
207 spe::ExtendedHeader extended_header) {
208 if (extended_header.IsAddressPacket()) {
209 ReadAddressPacket(extended_header.GetAddressIndex());
210
211 } else if (extended_header.IsCounterPacket()) {
212 ReadCounterPacket(extended_header.GetCounterIndex());
213
214 } else {
215 reader_.Skip(extended_header.GetPayloadSize());
216 }
217 }
218
ReadAddressPacket(spe::AddressIndex index)219 void SpeRecordParserImpl::ReadAddressPacket(spe::AddressIndex index) {
220 uint64_t payload;
221 reader_.Read(payload);
222
223 switch (index) {
224 case spe::AddressIndex::kInstruction:
225 inflight_record_.instruction_address =
226 spe::InstructionVirtualAddress(payload);
227 break;
228
229 case spe::AddressIndex::kDataVirtual:
230 inflight_row_.data_virtual_address =
231 static_cast<int64_t>(spe::DataVirtualAddress(payload).address);
232 break;
233
234 case spe::AddressIndex::kDataPhysical:
235 inflight_row_.data_physical_address =
236 static_cast<int64_t>(spe::DataPhysicalAddress(payload).address);
237 break;
238
239 case spe::AddressIndex::kBranchTarget:
240 case spe::AddressIndex::kPrevBranchTarget:
241 case spe::AddressIndex::kUnknown:
242 break;
243 }
244 }
245
ReadCounterPacket(spe::CounterIndex index)246 void SpeRecordParserImpl::ReadCounterPacket(spe::CounterIndex index) {
247 uint16_t value;
248 reader_.Read(value);
249 switch (index) {
250 case spe::CounterIndex::kTotalLatency:
251 inflight_row_.total_latency = value;
252 break;
253
254 case spe::CounterIndex::kIssueLatency:
255 inflight_row_.issue_latency = value;
256 break;
257
258 case spe::CounterIndex::kTranslationLatency:
259 inflight_row_.translation_latency = value;
260 break;
261
262 case spe::CounterIndex::kUnknown:
263 break;
264 }
265 }
266
ReadEventsPacket(spe::ShortHeader short_header)267 void SpeRecordParserImpl::ReadEventsPacket(spe::ShortHeader short_header) {
268 inflight_row_.events_bitmask =
269 static_cast<int64_t>(ReadPayload(short_header));
270 }
271
ReadContextPacket(spe::ShortHeader short_header)272 void SpeRecordParserImpl::ReadContextPacket(spe::ShortHeader short_header) {
273 uint32_t tid;
274 reader_.Read(tid);
275 inflight_row_.utid = context_->process_tracker->GetOrCreateThread(tid);
276 switch (short_header.GetContextIndex()) {
277 case spe::ContextIndex::kEl1:
278 case spe::ContextIndex::kEl2:
279 case spe::ContextIndex::kUnknown:
280 break;
281 }
282 }
283
ReadOperationTypePacket(spe::ShortHeader short_header)284 void SpeRecordParserImpl::ReadOperationTypePacket(
285 spe::ShortHeader short_header) {
286 uint8_t payload;
287 reader_.Read(payload);
288 inflight_row_.operation = ToStringId(GetOperationName(short_header, payload));
289 }
290
GetOperationName(spe::ShortHeader short_header,uint8_t payload) const291 SpeRecordParserImpl::OperationName SpeRecordParserImpl::GetOperationName(
292 spe::ShortHeader short_header,
293 uint8_t payload) const {
294 switch (short_header.GetOperationClass()) {
295 case spe::OperationClass::kOther:
296 switch (spe::OperationTypeOtherPayload(payload).subclass()) {
297 case spe::OperationOtherSubclass::kOther:
298 return OperationName::kOther;
299 case spe::OperationOtherSubclass::kSveVecOp:
300 return OperationName::kSveVecOp;
301 case spe::OperationOtherSubclass::kUnknown:
302 return OperationName::kUnknown;
303 }
304 PERFETTO_FATAL("For GCC");
305
306 case spe::OperationClass::kLoadOrStoreOrAtomic:
307 if (spe::OperationTypeLdStAtPayload(payload).IsStore()) {
308 return OperationName::kStore;
309 }
310 return OperationName::kLoad;
311
312 case spe::OperationClass::kBranchOrExceptionReturn:
313 return OperationName::kBranch;
314
315 case spe::OperationClass::kUnknown:
316 return OperationName::kUnknown;
317 }
318 PERFETTO_FATAL("For GCC");
319 }
320
GetDummyMapping()321 VirtualMemoryMapping* SpeRecordParserImpl::GetDummyMapping() {
322 if (!dummy_mapping_) {
323 dummy_mapping_ =
324 &context_->mapping_tracker->CreateDummyMapping("spe_dummy");
325 }
326 return dummy_mapping_;
327 }
328
ReadDataSourcePacket(spe::ShortHeader short_header)329 void SpeRecordParserImpl::ReadDataSourcePacket(spe::ShortHeader short_header) {
330 inflight_row_.data_source =
331 ToStringId(short_header.GetDataSource(ReadPayload(short_header)));
332 }
333
ReadPayload(spe::ShortHeader short_header)334 uint64_t SpeRecordParserImpl::ReadPayload(spe::ShortHeader short_header) {
335 switch (short_header.GetPayloadSize()) {
336 case 1: {
337 uint8_t data;
338 reader_.Read(data);
339 return data;
340 }
341 case 2: {
342 uint16_t data;
343 reader_.Read(data);
344 return data;
345 }
346 case 4: {
347 uint32_t data;
348 reader_.Read(data);
349 return data;
350 }
351 case 8: {
352 uint64_t data;
353 reader_.Read(data);
354 return data;
355 }
356 default:
357 break;
358 }
359 PERFETTO_FATAL("Unreachable");
360 }
361
362 } // namespace perfetto::trace_processor::perf_importer
363