xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/perf/spe.h (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 // Collection of constant and utilities to parse SPE data.
18 // SPE packet spec can be found here:
19 // Arm Architecture Reference Manual for A-profile architecture
20 // https://developer.arm.com/documentation/ddi0487/latest/
21 
22 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PERF_SPE_H_
23 #define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_SPE_H_
24 
25 #include <cstddef>
26 #include <cstdint>
27 #include "perfetto/base/logging.h"
28 #include "perfetto/public/compiler.h"
29 
30 namespace perfetto::trace_processor::perf_importer::spe {
31 
32 // Test whether a given bit is set. e.g.
33 // IsBitSet<1>(0b0010) == true
34 // IsBitSet<0>(0b0010) == false
35 template <int bit, typename T>
IsBitSet(T value)36 inline constexpr bool IsBitSet(T value) {
37   static_assert(std::is_unsigned_v<T>);
38   static_assert(bit < sizeof(T) * 8);
39   return value & (T(1) << bit);
40 }
41 
42 // Index value in Address packets
43 enum class AddressIndex : uint8_t {
44   kInstruction,
45   kBranchTarget,
46   kDataVirtual,
47   kDataPhysical,
48   kPrevBranchTarget,
49   kUnknown,
50   kMax = kUnknown,
51 };
52 
53 // Index value in Counter packets
54 enum class CounterIndex : uint8_t {
55   kTotalLatency,
56   kIssueLatency,
57   kTranslationLatency,
58   kUnknown,
59   kMax = kUnknown,
60 };
61 
62 enum class ContextIndex : uint8_t {
63   kEl1,
64   kEl2,
65   kUnknown,
66   kMax = kUnknown,
67 };
68 
69 // Operation class for OperationType packets
70 enum class OperationClass : uint8_t {
71   kOther,
72   kLoadOrStoreOrAtomic,
73   kBranchOrExceptionReturn,
74   kUnknown,
75   kMax = kUnknown,
76 };
77 
78 // Data source types for a payload of a DataSource packet
79 enum class DataSource : uint8_t {
80   kL1D,
81   kL2,
82   kPeerCore,
83   kLocalCluster,
84   kSysCache,
85   kPeerCluster,
86   kRemote,
87   kDram,
88   kUnknown,
89   kMax = kUnknown,
90 };
91 
92 // Exception levels instructions can execute in.
93 enum class ExceptionLevel { kEl0, kEl1, kEl2, kEl3, kMax = kEl3 };
94 
95 // Common constants to both short and extended headers
96 constexpr uint8_t COMMON_HEADER_MASK = 0b1111'1000;
97 constexpr uint8_t COMMON_HEADER_ADDRESS_PACKET = 0b1011'0000;
98 constexpr uint8_t COMMON_HEADER_COUNTER_PACKET = 0b1001'1000;
99 
100 constexpr uint8_t COMMON_HEADER_SIZE_MASK = 0b0011'0000;
101 constexpr uint8_t COMMON_HEADER_SIZE_MASK_RSHIFT = 4;
102 
103 constexpr uint8_t COMMON_HEADER_NO_PAYLOAD_MASK = 0b1110'0000;
104 constexpr uint8_t COMMON_HEADER_NO_PAYLOAD = 0b0000'0000;
105 
106 // Constants for short headers
107 constexpr uint8_t SHORT_HEADER_PADDING = 0b0000'0000;
108 constexpr uint8_t SHORT_HEADER_END_PACKET = 0b0000'0001;
109 constexpr uint8_t SHORT_HEADER_TIMESTAMP_PACKET = 0b0111'0001;
110 
111 constexpr uint8_t SHORT_HEADER_MASK_1 = 0b1100'1111;
112 constexpr uint8_t SHORT_HEADER_EVENTS_PACKET = 0b0100'0010;
113 constexpr uint8_t SHORT_HEADER_DATA_SOURCE_PACKET = 0b0100'0011;
114 
115 constexpr uint8_t SHORT_HEADER_MASK_2 = 0b1111'1100;
116 constexpr uint8_t SHORT_HEADER_CONTEXT_PACKET = 0b0110'0100;
117 constexpr uint8_t SHORT_HEADER_OPERATION_TYPE_PACKET = 0b0100'1000;
118 
119 constexpr uint8_t SHORT_HEADER_INDEX_MASK = 0b0000'0111;
120 
121 // Constants for extended headers
122 constexpr uint8_t EXTENDED_HEADER_MASK = 0b1110'0000;
123 constexpr uint8_t EXTENDED_HEADER = 0b0010'0000;
124 
125 constexpr uint8_t EXTENDED_HEADER_INDEX_MASK = 0b0000'0011;
126 constexpr uint8_t EXTENDED_HEADER_INDEX_LSHIFT = 3;
127 
128 // OperationType packet constants
129 constexpr uint8_t PKT_OP_TYPE_HEADER_CLASS_MASK = 0b0000'0011;
130 constexpr uint8_t PKT_OP_TYPE_HEADER_CLASS_OTHER = 0b0000'0000;
131 constexpr uint8_t PKT_OP_TYPE_HEADER_CLASS_LD_ST_ATOMIC = 0b0000'0001;
132 constexpr uint8_t PKT_OP_TYPE_HEADER_CLASS_BR_ERET = 0b0000'0010;
133 
134 constexpr uint8_t PKT_OP_TYPE_PAYLOAD_SUBCLASS_OTHER_MASK = 0b1111'1110;
135 constexpr uint8_t PKT_OP_TYPE_PAYLOAD_SUBCLASS_OTHER = 0b0000'0000;
136 
137 constexpr uint8_t PKT_OP_TYPE_PAYLOAD_SUBCLASS_SVE_OTHER_MASK = 0b1000'1001;
138 constexpr uint8_t PKT_OP_TYPE_PAYLOAD_SUBCLASS_SVE_OTHER = 0b0000'1000;
139 
140 // DataSource packet constants
141 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_L1D = 0b0000'0000;
142 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_L2 = 0b0000'1000;
143 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_PEER_CORE = 0b0000'1001;
144 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_LOCAL_CLUSTER = 0b0000'1010;
145 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_SYS_CACHE = 0b0000'1011;
146 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_PEER_CLUSTER = 0b0000'1100;
147 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_REMOTE = 0b0000'1101;
148 constexpr uint16_t PKT_DATA_SOURCE_PAYLOAD_DRAM = 0b0000'1110;
149 
150 // Helper to cast a value into a typed enum. Takes care of invalid inputs by
151 // returning the `kUnknown` value.
152 template <typename T>
ToEnum(uint8_t val)153 T ToEnum(uint8_t val) {
154   if (PERFETTO_LIKELY(val < static_cast<uint8_t>(T::kMax))) {
155     return static_cast<T>(val);
156   }
157   return T::kUnknown;
158 }
159 
160 // An SPE record is a collection of packets. An End or Timestamp packet signals
161 // the end of a record. Each record consists of a 1 or 2 byte header followed by
162 // 0 - 4 bytes of payload. The `ShortHeader`, and `ExtendedHeader` hide all the
163 // low level bit fiddling details of handling packets. When parsing a stream of
164 // SPE records you can just check the first byte in the stream to determine if
165 // it belongs to a short or extended header and then use the appropiate class to
166 // determine packet type, payload length and packet details. There are other
167 // helper classes to parse payloads for the different packets.
168 
169 // Checks if a header bytes is a padding packet. (no payload)
170 inline bool IsPadding(uint8_t byte) {
171   return byte == SHORT_HEADER_PADDING;
172 }
173 
174 // Checks if a header byte corresponds to an extended header.
175 inline bool IsExtendedHeader(uint8_t byte) {
176   return (byte & EXTENDED_HEADER_MASK) == EXTENDED_HEADER;
177 }
178 
179 class ShortHeader {
180  public:
181   explicit ShortHeader(uint8_t byte) : byte_0_(byte) {
182     PERFETTO_DCHECK(!IsExtendedHeader(byte));
183   }
184 
185   inline bool IsPadding() { return byte_0_ == SHORT_HEADER_PADDING; }
186 
187   inline bool IsEndPacket() { return byte_0_ == SHORT_HEADER_END_PACKET; }
188 
189   inline bool IsTimestampPacket() {
190     return byte_0_ == SHORT_HEADER_TIMESTAMP_PACKET;
191   }
192 
193   bool IsAddressPacket() const {
194     return (byte_0_ & COMMON_HEADER_MASK) == COMMON_HEADER_ADDRESS_PACKET;
195   }
196 
197   AddressIndex GetAddressIndex() const {
198     PERFETTO_DCHECK(IsAddressPacket());
199     return ToEnum<AddressIndex>(index());
200   }
201 
202   bool IsCounterPacket() const {
203     return (byte_0_ & COMMON_HEADER_MASK) == COMMON_HEADER_COUNTER_PACKET;
204   }
205 
206   CounterIndex GetCounterIndex() const {
207     PERFETTO_DCHECK(IsCounterPacket());
208     return ToEnum<CounterIndex>(index());
209   }
210 
211   bool IsEventsPacket() const {
212     return (byte_0_ & SHORT_HEADER_MASK_1) == SHORT_HEADER_EVENTS_PACKET;
213   }
214 
215   bool IsContextPacket() const {
216     return (byte_0_ & SHORT_HEADER_MASK_2) == SHORT_HEADER_CONTEXT_PACKET;
217   }
218 
219   ContextIndex GetContextIndex() const { return ToEnum<ContextIndex>(index()); }
220 
221   bool IsDataSourcePacket() const {
222     return (byte_0_ & SHORT_HEADER_MASK_1) == SHORT_HEADER_DATA_SOURCE_PACKET;
223   }
224 
225   DataSource GetDataSource(uint64_t payload) {
226     PERFETTO_DCHECK(IsDataSourcePacket());
227     switch (payload) {
228       case PKT_DATA_SOURCE_PAYLOAD_L1D:
229         return DataSource::kL1D;
230       case PKT_DATA_SOURCE_PAYLOAD_L2:
231         return DataSource::kL2;
232       case PKT_DATA_SOURCE_PAYLOAD_PEER_CORE:
233         return DataSource::kPeerCore;
234       case PKT_DATA_SOURCE_PAYLOAD_LOCAL_CLUSTER:
235         return DataSource::kLocalCluster;
236       case PKT_DATA_SOURCE_PAYLOAD_SYS_CACHE:
237         return DataSource::kSysCache;
238       case PKT_DATA_SOURCE_PAYLOAD_PEER_CLUSTER:
239         return DataSource::kPeerCluster;
240       case PKT_DATA_SOURCE_PAYLOAD_REMOTE:
241         return DataSource::kRemote;
242       case PKT_DATA_SOURCE_PAYLOAD_DRAM:
243         return DataSource::kDram;
244       default:
245         break;
246     }
247     return DataSource::kUnknown;
248   }
249 
250   bool IsOperationTypePacket() const {
251     return (byte_0_ & SHORT_HEADER_MASK_2) ==
252            SHORT_HEADER_OPERATION_TYPE_PACKET;
253   }
254 
255   OperationClass GetOperationClass() const {
256     PERFETTO_DCHECK(IsOperationTypePacket());
257     switch (byte_0_ & PKT_OP_TYPE_HEADER_CLASS_MASK) {
258       case PKT_OP_TYPE_HEADER_CLASS_OTHER:
259         return OperationClass::kOther;
260 
261       case PKT_OP_TYPE_HEADER_CLASS_LD_ST_ATOMIC:
262         return OperationClass::kLoadOrStoreOrAtomic;
263 
264       case PKT_OP_TYPE_HEADER_CLASS_BR_ERET:
265         return OperationClass::kBranchOrExceptionReturn;
266 
267       default:
268         break;
269     }
270     return OperationClass::kUnknown;
271   }
272 
273   bool HasPayload() const {
274     return (byte_0_ & COMMON_HEADER_NO_PAYLOAD_MASK) !=
275            COMMON_HEADER_NO_PAYLOAD;
276   }
277 
278   uint8_t GetPayloadSize() const {
279     PERFETTO_DCHECK(!IsExtendedHeader(byte_0_));
280     if (!HasPayload()) {
281       return 0;
282     }
283     return static_cast<uint8_t>(1 << ((byte_0_ & COMMON_HEADER_SIZE_MASK) >>
284                                       COMMON_HEADER_SIZE_MASK_RSHIFT));
285   }
286 
287  private:
288   friend class ExtendedHeader;
289 
290   uint8_t index() const { return byte_0_ & SHORT_HEADER_INDEX_MASK; }
291 
292   uint8_t byte_0_;
293 };
294 
295 class ExtendedHeader {
296  public:
297   ExtendedHeader(uint8_t byte_0, uint8_t byte_1)
298       : byte_0_(byte_0), short_header_(byte_1) {
299     PERFETTO_DCHECK(IsExtendedHeader(byte_0));
300   }
301 
302   bool IsAddressPacket() const { return short_header_.IsAddressPacket(); }
303 
304   AddressIndex GetAddressIndex() const { return ToEnum<AddressIndex>(index()); }
305 
306   bool IsCounterPacket() const { return short_header_.IsCounterPacket(); }
307 
308   CounterIndex GetCounterIndex() const { return ToEnum<CounterIndex>(index()); }
309 
310   inline uint8_t GetPayloadSize() { return short_header_.GetPayloadSize(); }
311 
312  private:
313   uint8_t byte_1() const { return short_header_.byte_0_; }
314 
315   uint8_t index() const {
316     return static_cast<uint8_t>((byte_0_ & EXTENDED_HEADER_INDEX_MASK)
317                                 << EXTENDED_HEADER_INDEX_LSHIFT) +
318            short_header_.index();
319   }
320 
321   uint8_t byte_0_;
322   ShortHeader short_header_;
323 };
324 
325 enum class OperationOtherSubclass : uint8_t {
326   kOther,
327   kSveVecOp,
328   kUnknown,
329   kMax = kUnknown
330 };
331 class OperationTypeOtherPayload {
332  public:
333   explicit OperationTypeOtherPayload(uint8_t payload) : payload_(payload) {}
334 
335   OperationOtherSubclass subclass() const {
336     if ((payload_ & PKT_OP_TYPE_PAYLOAD_SUBCLASS_OTHER_MASK) ==
337         PKT_OP_TYPE_PAYLOAD_SUBCLASS_OTHER) {
338       return OperationOtherSubclass::kOther;
339     }
340     if ((payload_ & PKT_OP_TYPE_PAYLOAD_SUBCLASS_SVE_OTHER_MASK) ==
341         PKT_OP_TYPE_PAYLOAD_SUBCLASS_SVE_OTHER) {
342       return OperationOtherSubclass::kSveVecOp;
343     }
344     return OperationOtherSubclass::kUnknown;
345   }
346 
347  private:
348   uint8_t payload_;
349 };
350 
351 class OperationTypeLdStAtPayload {
352  public:
353   explicit OperationTypeLdStAtPayload(uint8_t payload) : payload_(payload) {}
354 
355   bool IsStore() const { return IsBitSet<0>(payload_); }
356 
357  private:
358   uint8_t payload_;
359 };
360 
361 namespace internal {
362 inline uint64_t GetPacketAddressAddress(uint64_t payload) {
363   return payload & 0x0FFFFFFFFFFFFFFF;
364 }
365 
366 inline bool GetPacketAddressNs(uint64_t payload) {
367   return IsBitSet<63>(payload);
368 }
369 
370 inline ExceptionLevel GetPacketAddressEl(uint64_t payload) {
371   return static_cast<ExceptionLevel>((payload >> 61) & 0x03);
372 }
373 
374 inline bool GetPacketAddressNse(uint64_t payload) {
375   return IsBitSet<60>(payload);
376 }
377 }  // namespace internal
378 
379 struct InstructionVirtualAddress {
380   explicit InstructionVirtualAddress(uint64_t payload)
381       : address(internal::GetPacketAddressAddress(payload)),
382         el(internal::GetPacketAddressEl(payload)),
383         ns(internal::GetPacketAddressNs(payload)),
384         nse(internal::GetPacketAddressNse(payload)) {}
385   uint64_t address;
386   ExceptionLevel el;
387   bool ns;
388   bool nse;
389 };
390 
391 struct DataVirtualAddress {
392   explicit DataVirtualAddress(uint64_t payload)
393       : address(internal::GetPacketAddressAddress(payload)) {}
394   uint64_t address;
395 };
396 
397 struct DataPhysicalAddress {
398   explicit DataPhysicalAddress(uint64_t payload)
399       : address(internal::GetPacketAddressAddress(payload)) {}
400   uint64_t address;
401 };
402 
403 }  // namespace perfetto::trace_processor::perf_importer::spe
404 
405 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_SPE_H_
406