* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "spe_decoder.h"
#include "hiperf_hilog.h"
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define LE16_TO_CPU bswap_16
#define LE32_TO_CPU bswap_32
#define LE64_TO_CPU bswap_64
#else
#define LE16_TO_CPU
#define LE32_TO_CPU
#define LE64_TO_CPU
#endif
namespace OHOS {
namespace Developtools {
namespace HiPerf {
constexpr const int UN_PRMT = -1;
const std::string SpePktName(enum SpePktType type)
{
std::string spePacketName = "";
switch (type) {
case PERF_SPE_PAD: spePacketName = "PAD"; break;
case PERF_SPE_END: spePacketName = "END"; break;
case PERF_SPE_TIMESTAMP: spePacketName = "TS"; break;
case PERF_SPE_ADDRESS: spePacketName = "ADDR"; break;
case PERF_SPE_COUNTER: spePacketName = "LAT"; break;
case PERF_SPE_CONTEXT: spePacketName = "CONTEXT"; break;
case PERF_SPE_OP_TYPE: spePacketName = "OP-TYPE"; break;
case PERF_SPE_EVENTS: spePacketName = "EVENTS"; break;
case PERF_SPE_DATA_SOURCE: spePacketName = "DATA-SOURCE"; break;
default: spePacketName = "INVALID"; break;
}
return spePacketName;
}
static unsigned int SpePayloadLen(const unsigned char hdr)
{
return 1U << PERF_SPE_HDR_GET_BYTES_5_4(hdr);
}
static int SpeGetPayload(const unsigned char *buf, const size_t len,
const unsigned char extHdr, struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
size_t payloadLen = SpePayloadLen(buf[extHdr]);
if (len < 1 + extHdr + payloadLen) {
return PERF_SPE_NEED_MORE_BYTES;
}
buf += 1 + extHdr;
switch (payloadLen) {
case LEN_TYPE_BYTE: packet->payload = *(reinterpret_cast<const uint8_t *>(buf)); break;
case LEN_TYPE_HLFWRD: packet->payload = LE16_TO_CPU(*reinterpret_cast<const uint16_t *>(buf)); break;
case LEN_TYPE_WORD: packet->payload = LE32_TO_CPU(*reinterpret_cast<const uint32_t *>(buf)); break;
case LEN_TYPE_DBLEWRD: packet->payload = LE64_TO_CPU(*reinterpret_cast<const uint64_t *>(buf)); break;
default: return PERF_SPE_BAD_PACKET;
}
return 1 + extHdr + payloadLen;
}
static int SpeGetPad(struct SpePkt *packet)
{
CHECK_TRUE(packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_PAD;
return 1;
}
static int SpeGetAlignment(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
unsigned int alignment = 1 << ((buf[0] & 0xf) + 1);
if (len < alignment)
return PERF_SPE_NEED_MORE_BYTES;
packet->type = PERF_SPE_PAD;
return alignment - (((uintptr_t)buf) & (alignment - 1));
}
static int SpeGetEnd(struct SpePkt *packet)
{
CHECK_TRUE(packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_END;
return 1;
}
static int SpeGetTimestamp(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_TIMESTAMP;
return SpeGetPayload(buf, len, 0, packet);
}
static int SpeGetEvents(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_EVENTS;
packet->index = SpePayloadLen(buf[0]);
return SpeGetPayload(buf, len, 0, packet);
}
static int SpeGetDataSource(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_DATA_SOURCE;
return SpeGetPayload(buf, len, 0, packet);
}
static int SpeGetContext(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_CONTEXT;
packet->index = PERF_SPE_CTX_PKT_HDR_INDEX(buf[0]);
return SpeGetPayload(buf, len, 0, packet);
}
static int SpeGetOpType(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_OP_TYPE;
packet->index = PERF_SPE_OP_PKT_HDR_CLASS(buf[0]);
return SpeGetPayload(buf, len, 0, packet);
}
static int SpeGetCounter(const unsigned char *buf, const size_t len,
const unsigned char extHdr, struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_COUNTER;
if (extHdr) {
packet->index = PERF_SPE_HDR_EXTENDED_INDEX(buf[0], buf[1]);
} else {
packet->index = PERF_SPE_HDR_SHORT_INDEX(buf[0]);
}
return SpeGetPayload(buf, len, extHdr, packet);
}
static int SpeGetAddr(const unsigned char *buf, const size_t len,
const unsigned char extHdr, struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
packet->type = PERF_SPE_ADDRESS;
if (extHdr) {
packet->index = PERF_SPE_HDR_EXTENDED_INDEX(buf[0], buf[1]);
} else {
packet->index = PERF_SPE_HDR_SHORT_INDEX(buf[0]);
}
return SpeGetPayload(buf, len, extHdr, packet);
}
static int SpeDoGetPacket(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
unsigned int hdr;
unsigned char extHdr = 0;
if (memset_s(packet, sizeof(struct SpePkt), 0, sizeof(struct SpePkt)) != EOK) {
HLOGE("memset_s failed in SpeDoGetPacket");
return -1;
}
if (!len) {
return PERF_SPE_NEED_MORE_BYTES;
}
hdr = buf[0];
if (hdr == PERF_SPE_HEADER0_PAD) {
return SpeGetPad(packet);
} else if (hdr == PERF_SPE_HEADER0_END) {
return SpeGetEnd(packet);
}
if (hdr == PERF_SPE_HEADER0_TIMESTAMP) {
return SpeGetTimestamp(buf, len, packet);
} else if ((hdr & PERF_SPE_HEADER0_MASK1) == PERF_SPE_HEADER0_EVENTS) {
return SpeGetEvents(buf, len, packet);
} else if ((hdr & PERF_SPE_HEADER0_MASK1) == PERF_SPE_HEADER0_SOURCE) {
return SpeGetDataSource(buf, len, packet);
} else if ((hdr & PERF_SPE_HEADER0_MASK2) == PERF_SPE_HEADER0_CONTEXT) {
return SpeGetContext(buf, len, packet);
} else if ((hdr & PERF_SPE_HEADER0_MASK2) == PERF_SPE_HEADER0_OP_TYPE) {
return SpeGetOpType(buf, len, packet);
}
if ((hdr & PERF_SPE_HEADER0_MASK2) == PERF_SPE_HEADER0_EXTENDED) {
if (len == 1) {
return PERF_SPE_BAD_PACKET;
}
extHdr = 1;
hdr = buf[1];
if (hdr == PERF_SPE_HEADER1_ALIGNMENT) {
return SpeGetAlignment(buf, len, packet);
}
}
* The short format header's byte 0 or the extended format header's
* byte 1 has been assigned to 'hdr', which uses the same encoding for
* address packet and counter packet, so don't need to distinguish if
* it's short format or extended format and handle in once.
*/
if ((hdr & PERF_SPE_HEADER0_MASK3) == PERF_SPE_HEADER0_ADDRESS) {
return SpeGetAddr(buf, len, extHdr, packet);
}
if ((hdr & PERF_SPE_HEADER0_MASK3) == PERF_SPE_HEADER0_COUNTER) {
return SpeGetCounter(buf, len, extHdr, packet);
}
return PERF_SPE_BAD_PACKET;
}
int SpeGetPacket(const unsigned char *buf, const size_t len,
struct SpePkt *packet)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
int ret = SpeDoGetPacket(buf, len, packet);
* the fixed-width output format of 16 bytes per line.
*/
if (ret > 0 && packet->type == PERF_SPE_PAD) {
while (ret < BYTE_WIDTH && len > (size_t)ret && !buf[ret]) {
ret += 1;
}
}
return ret;
}
static int SpePktOutString(int *err, char **bufPtr, size_t *bufLen,
const char *fmt, ...)
{
CHECK_TRUE(*bufPtr != nullptr && bufLen != nullptr && fmt != nullptr, -1, 1, "Invalid pointer!");
va_list args;
int ret = 0;
if (err && *err) {
return *err;
}
if (*bufLen - 1 < 0) {
HLOGW("SpePktOutString failed, bufLen: %d", static_cast<int>(*bufLen));
}
va_start(args, fmt);
ret = vsnprintf_s(*bufPtr, *bufLen, *bufLen - 1, fmt, args);
va_end(args);
if (ret < 0) {
if (err && !*err) {
*err = ret;
}
HLOGW("vsnprintf_s failed: %d\n", ret);
* If the return value is *bufLen or greater, the output was
* truncated and the buffer overflowed.
*/
} else if ((size_t)ret >= *bufLen) {
(*bufPtr)[*bufLen - 1] = '\0';
* Set *err to 'ret' to avoid overflow if tries to
* fill this buffer sequentially.
*/
if (err && !*err) {
*err = ret;
}
} else {
*bufPtr += ret;
*bufLen -= ret;
}
return ret;
}
static int SpePktDescEvent(const struct SpePkt *packet,
char *buf, size_t bufLen)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
u64 payload = packet->payload;
int err = 0;
if (bufLen > 0) {
SpePktOutString(&err, &buf, &bufLen, "EV");
}
std::vector<std::pair<enum SpeEvents, const char*>> events = {
{EVENT_EXCEPTION_GEN, " EXCEPTION-GEN"}, {EVENT_RETIRED, " RETIRED"},
{EVENT_L1D_ACCESS, " L1D-ACCESS"}, {EVENT_L1D_REFILL, " L1D-REFILL"},
{EVENT_TLB_ACCESS, " TLB-ACCESS"}, {EVENT_TLB_WALK, " TLB-REFILL"},
{EVENT_NOT_TAKEN, " NOT-TAKEN"}, {EVENT_MISPRED, " MISPRED"},
{EVENT_LLC_ACCESS, " LLC-ACCESS"}, {EVENT_LLC_MISS, " LLC-REFILL"},
{EVENT_REMOTE_ACCESS, " REMOTE-ACCESS"}, {EVENT_ALIGNMENT, " ALIGNMENT"},
{EVENT_PARTIAL_PREDICATE, " SVE-PARTIAL-PRED"}, {EVENT_EMPTY_PREDICATE, " SVE-EMPTY-PRED"}
};
for (auto it = events.begin(); it != events.end(); ++it) {
if ((payload & BIT(it->first)) && bufLen > 0) {
SpePktOutString(&err, &buf, &bufLen, it->second);
}
}
return err;
}
static int SpePktDescStatAtomicOpType(char *buf, size_t bufLen, u64 payload)
{
int err = 0;
SpePktOutString(&err, &buf, &bufLen,
payload & 0x1 ? "ST" : "LD");
if (PERF_SPE_OP_PKT_IS_LDST_ATOMIC(payload)) {
if (payload & PERF_SPE_OP_PKT_AT)
SpePktOutString(&err, &buf, &bufLen, " AT");
if (payload & PERF_SPE_OP_PKT_EXCL)
SpePktOutString(&err, &buf, &bufLen, " EXCL");
if (payload & PERF_SPE_OP_PKT_AR)
SpePktOutString(&err, &buf, &bufLen, " AR");
}
switch (PERF_SPE_OP_PKT_LDST_SUBCLASS_GET(payload)) {
case PERF_SPE_OP_PKT_LDST_SUBCLASS_SIMD_FP:
SpePktOutString(&err, &buf, &bufLen, " SIMD-FP");
break;
case PERF_SPE_OP_PKT_LDST_SUBCLASS_GP_REG:
SpePktOutString(&err, &buf, &bufLen, " GP-REG");
break;
case PERF_SPE_OP_PKT_LDST_SUBCLASS_UNSPEC_REG:
SpePktOutString(&err, &buf, &bufLen, " UNSPEC-REG");
break;
case PERF_SPE_OP_PKT_LDST_SUBCLASS_NV_SYSREG:
SpePktOutString(&err, &buf, &bufLen, " NV-SYSREG");
break;
case PERF_SPE_OP_PKT_LDST_SUBCLASS_MTE_TAG:
SpePktOutString(&err, &buf, &bufLen, " MTE-TAG");
break;
case PERF_SPE_OP_PKT_LDST_SUBCLASS_MEMCPY:
SpePktOutString(&err, &buf, &bufLen, " MEMCPY");
break;
case PERF_SPE_OP_PKT_LDST_SUBCLASS_MEMSET:
SpePktOutString(&err, &buf, &bufLen, " MEMSET");
break;
default:
break;
}
if (PERF_SPE_OP_PKT_IS_LDST_SVE(payload)) {
SpePktOutString(&err, &buf, &bufLen, " EVLEN %d",
PERF_SPE_OP_PKG_SVE_EVL(payload));
if (payload & PERF_SPE_OP_PKT_SVE_PRED)
SpePktOutString(&err, &buf, &bufLen, " PRED");
if (payload & PERF_SPE_OP_PKT_SVE_SG)
SpePktOutString(&err, &buf, &bufLen, " SG");
}
return err;
}
static int SpePktDescOpType(const struct SpePkt *packet,
char *buf, size_t bufLen)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
u64 payload = packet->payload;
int err = 0;
switch (packet->index) {
case PERF_SPE_OP_PKT_HDR_CLASS_OTHER:
if (PERF_SPE_OP_PKT_IS_OTHER_SVE_OP(payload)) {
SpePktOutString(&err, &buf, &bufLen, "SVE-OTHER");
SpePktOutString(&err, &buf, &bufLen, " EVLEN %d",
PERF_SPE_OP_PKG_SVE_EVL(payload));
if (payload & PERF_SPE_OP_PKT_SVE_FP)
SpePktOutString(&err, &buf, &bufLen, " FP");
if (payload & PERF_SPE_OP_PKT_SVE_PRED)
SpePktOutString(&err, &buf, &bufLen, " PRED");
} else {
SpePktOutString(&err, &buf, &bufLen, "OTHER");
SpePktOutString(&err, &buf, &bufLen, " %s",
payload & PERF_SPE_OP_PKT_COND ?
"COND-SELECT" : "INSN-OTHER");
}
break;
case PERF_SPE_OP_PKT_HDR_CLASS_LD_ST_ATOMIC:
err = SpePktDescStatAtomicOpType(buf, bufLen, payload);
break;
case PERF_SPE_OP_PKT_HDR_CLASS_BR_ERET:
SpePktOutString(&err, &buf, &bufLen, "B");
if (payload & PERF_SPE_OP_PKT_COND)
SpePktOutString(&err, &buf, &bufLen, " COND");
if (PERF_SPE_OP_PKT_IS_INDIRECT_BRANCH(payload))
SpePktOutString(&err, &buf, &bufLen, " IND");
break;
default:
err = UN_PRMT;
break;
}
return err;
}
static int SpePktDescAddr(const struct SpePkt *packet,
char *buf, size_t bufLen)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
int ns;
int el;
int idx = packet->index;
int ch;
int pat;
u64 payload = packet->payload;
int err = 0;
static const char *idxName[] = {"PC", "TGT", "VA", "PA", "PBT"};
switch (idx) {
case PERF_SPE_ADDR_PKT_HDR_INDEX_INS:
case PERF_SPE_ADDR_PKT_HDR_INDEX_BRANCH:
case PERF_SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH:
ns = !!PERF_SPE_ADDR_PKT_GET_NS(payload);
el = PERF_SPE_ADDR_PKT_GET_EL(payload);
payload = PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
SpePktOutString(&err, &buf, &bufLen,
"%s 0x%llx el%d ns=%d",
idxName[idx], payload, el, ns);
break;
case PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT:
SpePktOutString(&err, &buf, &bufLen,
"VA 0x%llx", payload);
break;
case PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS:
ns = !!PERF_SPE_ADDR_PKT_GET_NS(payload);
ch = !!PERF_SPE_ADDR_PKT_GET_CH(payload);
pat = PERF_SPE_ADDR_PKT_GET_PAT(payload);
payload = PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
SpePktOutString(&err, &buf, &bufLen,
"PA 0x%llx ns=%d ch=%d pat=%x",
payload, ns, ch, pat);
break;
default:
err = UN_PRMT;
break;
}
return err;
}
static int SpePktDesCont(const struct SpePkt *packet,
char *buf, size_t bufLen)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
u64 payload = packet->payload;
const std::string name = SpePktName(packet->type);
int err = 0;
SpePktOutString(&err, &buf, &bufLen, "%s %d ", name.c_str(),
(unsigned short)payload);
switch (packet->index) {
case PERF_SPE_CNT_PKT_HDR_INDEX_TOTAL_LAT:
SpePktOutString(&err, &buf, &bufLen, "TOT");
break;
case PERF_SPE_CNT_PKT_HDR_INDEX_ISSUE_LAT:
SpePktOutString(&err, &buf, &bufLen, "ISSUE");
break;
case PERF_SPE_CNT_PKT_HDR_INDEX_TRANS_LAT:
SpePktOutString(&err, &buf, &bufLen, "XLAT");
break;
default:
break;
}
return err;
}
int SpePktDesc(const struct SpePkt *packet, char *buf,
size_t bufLen)
{
CHECK_TRUE(buf != nullptr && packet != nullptr, -1, 1, "Invalid pointer!");
int idx = packet->index;
unsigned long long payload = packet->payload;
const std::string name = SpePktName(packet->type);
char *bufOrig = buf;
size_t blen = bufLen;
int err = 0;
switch (packet->type) {
case PERF_SPE_BAD:
case PERF_SPE_PAD:
case PERF_SPE_END:
SpePktOutString(&err, &buf, &blen, "%s", name.c_str());
break;
case PERF_SPE_EVENTS:
err = SpePktDescEvent(packet, buf, bufLen);
break;
case PERF_SPE_OP_TYPE:
err = SpePktDescOpType(packet, buf, bufLen);
break;
case PERF_SPE_DATA_SOURCE:
case PERF_SPE_TIMESTAMP:
SpePktOutString(&err, &buf, &blen, "%s %llu", name.c_str(), payload);
break;
case PERF_SPE_ADDRESS:
err = SpePktDescAddr(packet, buf, bufLen);
break;
case PERF_SPE_CONTEXT:
SpePktOutString(&err, &buf, &blen, "%s 0x%llx el%d",
name.c_str(), payload, idx + 1);
break;
case PERF_SPE_COUNTER:
err = SpePktDesCont(packet, buf, bufLen);
break;
default:
err = UN_PRMT;
break;
}
if (err) {
err = 0;
SpePktOutString(&err, &bufOrig, &bufLen, "%s 0x%llx (%d)",
name.c_str(), payload, packet->index);
}
return err;
}
static u64 SpeCalcIp(const int index, u64 payload)
{
u64 ns;
u64 el;
u64 val;
if (index == PERF_SPE_ADDR_PKT_HDR_INDEX_INS ||
index == PERF_SPE_ADDR_PKT_HDR_INDEX_BRANCH) {
ns = PERF_SPE_ADDR_PKT_GET_NS(payload);
el = PERF_SPE_ADDR_PKT_GET_EL(payload);
payload = PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
if (ns && (el == PERF_SPE_ADDR_PKT_EL1 || el == PERF_SPE_ADDR_PKT_EL2)) {
payload |= 0xffULL << PERF_SPE_ADDR_PKT_ADDR_BYTE7_SHIFT;
}
} else if (index == PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT) {
payload = PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
* Armv8 ARM (ARM DDI 0487F.c), chapter "D10.2.1 Address packet"
* defines the data virtual address payload format, the top byte
* (bits [63:56]) is assigned as top-byte tag; so we only can
* retrieve address value from bits [55:0].
*
* According to Documentation/arch/arm64/memory.rst, if detects the
* specific pattern in bits [55:52] of payload which falls in
* the kernel space, should fixup the top byte and this allows
* perf tool to parse DSO symbol for data address correctly.
*
* For this reason, if detects the bits [55:52] is 0xf, will
* fill 0xff into the top byte.
*/
val = PERF_SPE_ADDR_PKT_ADDR_GET_BYTE_6(payload);
if ((val & 0xf0ULL) == 0xf0ULL) {
payload |= 0xffULL << PERF_SPE_ADDR_PKT_ADDR_BYTE7_SHIFT;
}
} else if (index == PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS) {
payload = PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
} else {
static u32 seen_idx = 0;
if (!(seen_idx & BIT(index))) {
seen_idx |= BIT(index);
HLOGV("ignoring address packet index: 0x%x\n", index);
}
}
return payload;
}
void SpeDecoderFree(struct SpeDecoder *decoder)
{
CHECK_TRUE(decoder != nullptr, NO_RETVAL, 1, "Invalid pointer!");
free(decoder);
}
static int SpeGetNextPacket(struct SpeDecoder *decoder)
{
CHECK_TRUE(decoder != nullptr, -1, 1, "Invalid pointer!");
int ret = 1;
do {
if (!decoder->len) {
if (ret <= 0) {
return ret;
}
}
ret = SpeGetPacket(decoder->buf, decoder->len,
&decoder->packet);
if (ret <= 0) {
decoder->buf += 1;
decoder->len -= 1;
return -EBADMSG;
}
decoder->buf += ret;
decoder->len -= static_cast<size_t>(ret);
} while (decoder->packet.type == PERF_SPE_PAD);
return 1;
}
static void SpeReadRecordOpType(struct SpeDecoder *decoder, int idx, u64 payload)
{
switch (idx) {
case PERF_SPE_OP_PKT_HDR_CLASS_LD_ST_ATOMIC:
decoder->record.op |= PERF_SPE_OP_LDST;
if (payload & PERF_SPE_OP_PKT_ST) {
decoder->record.op |= PERF_SPE_OP_ST;
} else {
decoder->record.op |= PERF_SPE_OP_LD;
}
if (PERF_SPE_OP_PKT_IS_LDST_SVE(payload)) {
decoder->record.op |= PERF_SPE_OP_SVE_LDST;
}
break;
case PERF_SPE_OP_PKT_HDR_CLASS_OTHER:
decoder->record.op |= PERF_SPE_OP_OTHER;
if (PERF_SPE_OP_PKT_IS_OTHER_SVE_OP(payload)) {
decoder->record.op |= PERF_SPE_OP_SVE_OTHER;
}
break;
case PERF_SPE_OP_PKT_HDR_CLASS_BR_ERET:
decoder->record.op |= PERF_SPE_OP_BRANCH_ERET;
break;
default:
HLOGV("Get packet error!");
}
}
static void SpeReadRecordEvents(struct SpeDecoder *decoder, u64 payload)
{
if (payload & BIT(EVENT_L1D_REFILL)) {
decoder->record.type |= PERF_SPE_L1D_MISS;
}
if (payload & BIT(EVENT_L1D_ACCESS)) {
decoder->record.type |= PERF_SPE_L1D_ACCESS;
}
if (payload & BIT(EVENT_TLB_WALK)) {
decoder->record.type |= PERF_SPE_TLB_MISS;
}
if (payload & BIT(EVENT_TLB_ACCESS)) {
decoder->record.type |= PERF_SPE_TLB_ACCESS;
}
if (payload & BIT(EVENT_LLC_MISS)) {
decoder->record.type |= PERF_SPE_LLC_MISS;
}
if (payload & BIT(EVENT_LLC_ACCESS)) {
decoder->record.type |= PERF_SPE_LLC_ACCESS;
}
if (payload & BIT(EVENT_REMOTE_ACCESS)) {
decoder->record.type |= PERF_SPE_REMOTE_ACCESS;
}
if (payload & BIT(EVENT_MISPRED)) {
decoder->record.type |= PERF_SPE_BRANCH_MISS;
}
if (payload & BIT(EVENT_PARTIAL_PREDICATE)) {
decoder->record.type |= PERF_SPE_SVE_PARTIAL_PRED;
}
if (payload & BIT(EVENT_EMPTY_PREDICATE)) {
decoder->record.type |= PERF_SPE_SVE_EMPTY_PRED;
}
}
static void SpeReadRecordAddress(struct SpeDecoder *decoder, int idx, u64 payload)
{
u64 ip = SpeCalcIp(idx, payload);
if (idx == PERF_SPE_ADDR_PKT_HDR_INDEX_INS) {
decoder->record.from_ip = ip;
} else if (idx == PERF_SPE_ADDR_PKT_HDR_INDEX_BRANCH) {
decoder->record.to_ip = ip;
} else if (idx == PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT) {
decoder->record.virt_addr = ip;
} else if (idx == PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS) {
decoder->record.phys_addr = ip;
}
}
static int SpeReadRecord(struct SpeDecoder *decoder)
{
u64 payload;
CHECK_TRUE(decoder != nullptr, -1, 1, "Invalid pointer!");
if (memset_s(&decoder->record, sizeof(decoder->record), 0, sizeof(decoder->record)) != EOK) {
HLOGE("memset_s failed in SpeReadRecord.");
return -1;
}
decoder->record.context_id = (u64)-1;
while (true) {
int err = SpeGetNextPacket(decoder);
CHECK_TRUE(err > 0, err, 1, "SpeGetNextPacket failed!");
int idx = decoder->packet.index;
payload = decoder->packet.payload;
switch (decoder->packet.type) {
case PERF_SPE_TIMESTAMP:
decoder->record.timestamp = payload;
return 1;
case PERF_SPE_END:
return 1;
case PERF_SPE_ADDRESS:
SpeReadRecordAddress(decoder, idx, payload);
break;
case PERF_SPE_COUNTER:
if (idx == PERF_SPE_CNT_PKT_HDR_INDEX_TOTAL_LAT) {
decoder->record.latency = payload;
}
break;
case PERF_SPE_CONTEXT:
decoder->record.context_id = payload;
break;
case PERF_SPE_OP_TYPE:
SpeReadRecordOpType(decoder, idx, payload);
break;
case PERF_SPE_EVENTS:
SpeReadRecordEvents(decoder, payload);
break;
case PERF_SPE_DATA_SOURCE:
decoder->record.source = payload;
break;
case PERF_SPE_BAD:
break;
case PERF_SPE_PAD:
break;
default:
printf("Get packet error!\n");
return -1;
}
}
return 0;
}
int SpeDecode(struct SpeDecoder *decoder)
{
CHECK_TRUE(decoder != nullptr, -1, 1, "Invalid pointer!");
return SpeReadRecord(decoder);
}
struct SpeDecoder *SpeDecoderDataNew(const unsigned char *speBuf, const size_t speLen)
{
CHECK_TRUE(speBuf != nullptr, nullptr, 1, "Invalid pointer!");
struct SpeDecoder *decoder;
decoder = reinterpret_cast<SpeDecoder *>(malloc(sizeof(struct SpeDecoder)));
if (!decoder) {
return nullptr;
}
if (memset_s(decoder, sizeof(struct SpeDecoder), 0, sizeof(struct SpeDecoder)) != EOK) {
HLOGE("memset_s failed in SpeDecoderDataNew.");
free(decoder);
return nullptr;
}
decoder->buf = speBuf;
decoder->len = speLen;
return decoder;
}
bool SpeDumpRawData(unsigned char *buf, size_t len, const int indent, FILE *outputDump)
{
CHECK_TRUE(buf != nullptr, false, 1, "Invalid pointer!");
if (outputDump != nullptr) {
g_outputDump = outputDump;
}
struct SpePkt packet;
size_t pos = 0;
int pktLen;
int i;
char desc[PERF_SPE_PKT_DESC_MAX];
PRINT_INDENT(indent, ". ... ARM SPE data: size %#zx bytes\n", len);
while (len) {
int ret = SpeGetPacket(buf, len, &packet);
if (ret > 0) {
pktLen = ret;
} else {
pktLen = 1;
}
PRINT_INDENT(indent, ".");
PRINT_INDENT(indent, " %08zx: ", pos);
for (i = 0; i < pktLen; i++) {
PRINT_INDENT(indent, " %02x", buf[i]);
}
for (; i < 16; i++) {
PRINT_INDENT(indent, " ");
}
if (ret > 0) {
ret = SpePktDesc(&packet, desc,
PERF_SPE_PKT_DESC_MAX);
if (!ret) {
PRINT_INDENT(indent, " %s\n", desc);
}
} else {
PRINT_INDENT(indent, " Bad packet!\n");
}
pos += static_cast<size_t>(pktLen);
buf += pktLen;
if (len >= static_cast<size_t>(pktLen)) {
len -= static_cast<size_t>(pktLen);
} else {
break;
}
}
return true;
}
std::map<u32, std::map<u64, ReportItemAuxRawData>> AuxRawDataMap_;
std::map<u32, u64> typeCount;
const std::vector<u32> DEFAULT_SPE_EVENT_TYPE = {
PERF_SPE_L1D_ACCESS,
PERF_SPE_L1D_MISS,
PERF_SPE_LLC_ACCESS,
PERF_SPE_LLC_MISS,
PERF_SPE_TLB_ACCESS,
PERF_SPE_TLB_MISS,
PERF_SPE_BRANCH_MISS,
PERF_SPE_REMOTE_ACCESS,
PERF_SPE_SVE_PARTIAL_PRED,
PERF_SPE_SVE_EMPTY_PRED,
};
constexpr const int SPE_PERCENTAGE_COMM_LEN = 40;
constexpr const int SPE_PERCENTAGE_PC_LEN = 18;
constexpr const int SPE_PERCENTAGE_DSO_LEN = 50;
constexpr const int SPE_PERCENTAGE_FUNC_LEN = 60;
constexpr const int SPE_PERCENTAGE_OFFSET_LEN = 20;
void AddReportItems(const std::vector<ReportItemAuxRawData>& auxRawData)
{
for (const auto& data : auxRawData) {
for (auto type : DEFAULT_SPE_EVENT_TYPE) {
if (data.type & type) {
if (typeCount.count(type) == 0) {
typeCount[type] = 1;
} else {
typeCount[type]++;
}
std::map<u64, ReportItemAuxRawData>& map = AuxRawDataMap_[type];
auto data1 = map.find(data.pc);
if (data1 == map.end()) {
HLOGV("add %llx", data.pc);
map[data.pc] = {type, 0.0f, 1, data.comm, data.pc, data.SharedObject, data.Symbol, data.offset};
} else {
HLOGV("add pc: %llx", data.pc);
data1->second.count++;
HLOGV("add pc: %llx count: %llu", data.pc, data1->second.count);
}
}
}
}
}
void UpdateHeating()
{
for (auto it = AuxRawDataMap_.begin(); it != AuxRawDataMap_.end(); it++) {
u64 cc = typeCount[it->first];
for (auto& it2 : it->second) {
float heating = (float)it2.second.count / cc * FULL_PERCENTAGE;
HLOGV("heating %llu/%llu %f", it2.second.count, cc, heating);
it2.second.heating = heating;
}
}
}
void GetSpeEventNameByType(const uint32_t type, std::string& eventName)
{
switch (type) {
case PERF_SPE_L1D_ACCESS:
eventName = "l1d-access";
break;
case PERF_SPE_L1D_MISS:
eventName = "l1d-miss";
break;
case PERF_SPE_LLC_ACCESS:
eventName = "llc-access";
break;
case PERF_SPE_LLC_MISS:
eventName = "llc-miss";
break;
case PERF_SPE_TLB_ACCESS:
eventName = "tlb-access";
break;
case PERF_SPE_TLB_MISS:
eventName = "tlb-miss";
break;
case PERF_SPE_BRANCH_MISS:
eventName = "branch-miss";
break;
case PERF_SPE_REMOTE_ACCESS:
eventName = "remote-access";
break;
case PERF_SPE_SVE_PARTIAL_PRED:
eventName = "paritial_read";
break;
case PERF_SPE_SVE_EMPTY_PRED:
eventName = "empty_read";
break;
default:
eventName = "unknow";
return;
}
}
void DumpSpeReportHead(const int indent, const uint32_t type, const uint64_t count)
{
std::string eventName = "";
GetSpeEventNameByType(type, eventName);
PRINT_INDENT(indent, "\nEvent :%s\n", eventName.c_str());
PRINT_INDENT(indent, "Samples Count: %" PRIu64 "\n", count);
const std::string head = "Heating";
PRINT_INDENT(indent, "%-*s ", FULL_PERCENTAGE_LEN, head.c_str());
const std::string eventCount = " count";
PRINT_INDENT(indent, "%-*s ", FULL_PERCENTAGE_LEN, eventCount.c_str());
const std::string comm = " comm";
PRINT_INDENT(indent, "%-*s ", SPE_PERCENTAGE_COMM_LEN, comm.c_str());
const std::string pc = " PC";
PRINT_INDENT(indent, "%-*s ", SPE_PERCENTAGE_PC_LEN, pc.c_str());
const std::string dso = " dso";
PRINT_INDENT(indent, "%-*s ", SPE_PERCENTAGE_DSO_LEN, dso.c_str());
const std::string func = " func";
PRINT_INDENT(indent, "%-*s", SPE_PERCENTAGE_FUNC_LEN, func.c_str());
const std::string offset = " offset";
PRINT_INDENT(indent, "%-*s\n", SPE_PERCENTAGE_OFFSET_LEN, offset.c_str());
return;
}
void DumpSpeReportData(const int indent, FILE *outputDump)
{
if (outputDump != nullptr) {
g_outputDump = outputDump;
}
if (AuxRawDataMap_.empty()) {
return;
}
PRINT_INDENT(indent, "\n ==== Spe Report Data ====\n");
for (auto it = AuxRawDataMap_.begin(); it != AuxRawDataMap_.end(); it++) {
u64 count = typeCount[it->first];
DumpSpeReportHead(indent, it->first, count);
std::vector<ReportItemAuxRawData> auxRawData;
for (auto& it2 : it->second) {
struct ReportItemAuxRawData reportItem = {
it2.second.type,
it2.second.heating,
it2.second.count,
it2.second.comm,
it2.second.pc,
it2.second.SharedObject,
it2.second.Symbol,
it2.second.offset
};
auxRawData.emplace_back(reportItem);
}
std::sort(auxRawData.begin(), auxRawData.end(), [](const ReportItemAuxRawData& lhs,
const ReportItemAuxRawData& rhs) {
return lhs.count > rhs.count;
});
for (auto& it3 : auxRawData) {
PRINT_INDENT(indent + 1, "%*.2f%% ", FULL_PERCENTAGE_LEN, it3.heating);
PRINT_INDENT(indent + 1, "%-*llu ", FULL_PERCENTAGE_LEN, it3.count);
PRINT_INDENT(indent + 1, "%-*s ", SPE_PERCENTAGE_COMM_LEN, it3.comm.c_str());
PRINT_INDENT(indent + 1, "0x%-*llx ", SPE_PERCENTAGE_PC_LEN, it3.pc);
PRINT_INDENT(indent + 1, "%-*s ", SPE_PERCENTAGE_DSO_LEN, it3.SharedObject.c_str());
PRINT_INDENT(indent + 1, "%-*s", SPE_PERCENTAGE_FUNC_LEN, it3.Symbol.c_str());
PRINT_INDENT(indent + 1, "0x%llx\n", it3.offset);
}
}
}
}
}
}