mirror of
https://gitee.com/openharmony/developtools_hiperf
synced 2025-02-17 06:39:21 +00:00
sync code
Signed-off-by: yuyanqinghw <yuyanqing539@huawei.com>
This commit is contained in:
parent
a15e8e9246
commit
7c9daa4bf7
1
BUILD.gn
1
BUILD.gn
@ -140,6 +140,7 @@ sources_platform_linux = [
|
||||
"./src/subcommand_stat.cpp",
|
||||
"./src/subcommand_record.cpp",
|
||||
"./src/subcommand_list.cpp",
|
||||
"./src/spe_decoder.cpp",
|
||||
]
|
||||
|
||||
common_deps = [
|
||||
|
@ -41,8 +41,10 @@ namespace HiPerf {
|
||||
using namespace OHOS::HiviewDFX;
|
||||
|
||||
static constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
|
||||
static constexpr uint32_t RECORD_SIZE_LIMIT_SPE = 524288;
|
||||
|
||||
enum perf_event_hiperf_ext_type {
|
||||
PERF_RECORD_AUXTRACE = 71,
|
||||
PERF_RECORD_HIPERF_CALLSTACK = UINT32_MAX / 2,
|
||||
};
|
||||
|
||||
@ -169,6 +171,21 @@ constexpr __u64 SAMPLE_TYPE = PERF_SAMPLE_IP | SAMPLE_ID | PERF_SAMPLE_PERIOD;
|
||||
constexpr __u32 MIN_SAMPLE_STACK_SIZE = 8;
|
||||
constexpr __u32 MAX_SAMPLE_STACK_SIZE = 65528;
|
||||
|
||||
class PerfRecordAuxtrace : public PerfEventRecord {
|
||||
public:
|
||||
PerfRecordAuxtraceData data_;
|
||||
u8* rawData_ = nullptr;
|
||||
explicit PerfRecordAuxtrace(uint8_t *p);
|
||||
PerfRecordAuxtrace(u64 size, u64 offset, u64 reference, u32 idx, u32 tid, u32 cpu, u32 pid);
|
||||
|
||||
bool GetBinary1(std::vector<uint8_t> &buf) const;
|
||||
bool GetBinary(std::vector<uint8_t> &buf) const override;
|
||||
void DumpData(int indent) const override;
|
||||
void DumpLog(const std::string &prefix) const override;
|
||||
|
||||
virtual size_t GetSize() const override;
|
||||
};
|
||||
|
||||
class PerfRecordMmap : public PerfEventRecord {
|
||||
public:
|
||||
PerfRecordMmapData data_;
|
||||
@ -350,6 +367,7 @@ public:
|
||||
*/
|
||||
class PerfRecordAux : public PerfEventRecord {
|
||||
public:
|
||||
uint64_t sampleType_ = SAMPLE_ID;
|
||||
PerfRecordAuxData data_;
|
||||
|
||||
explicit PerfRecordAux(uint8_t *p);
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "debug_logger.h"
|
||||
#include "perf_event_record.h"
|
||||
#include "perf_record_format.h"
|
||||
#include "ring_buffer.h"
|
||||
#include "tracked_command.h"
|
||||
#include "utilities.h"
|
||||
@ -44,6 +45,17 @@
|
||||
|
||||
// this for some performance debug
|
||||
#define HIDEBUG_SKIP_CALLBACK 0
|
||||
#define CALC_OFFSET(offset, size) ((offset) & ((size) - 1))
|
||||
#ifndef CLOCK_MONOTONIC_RAW
|
||||
#define CLOCK_MONOTONIC_RAW 4
|
||||
#endif
|
||||
#define PERF_AUXTRACE_RECORD_ALIGNMENT 8
|
||||
#define PTR_TO_VOID(ptr) ((void *)(uintptr_t)(ptr))
|
||||
#define PTR_ADD(b, o) ({ \
|
||||
uintptr_t _b = (uintptr_t)(b); \
|
||||
uintptr_t _p = (uintptr_t)&(((uint8_t *)(_b))[o]); \
|
||||
PTR_TO_VOID(_p); \
|
||||
})
|
||||
|
||||
namespace OHOS {
|
||||
namespace Developtools {
|
||||
@ -497,16 +509,23 @@ public:
|
||||
perf_event_mmap_page *mmapPage = nullptr;
|
||||
uint8_t *buf = nullptr;
|
||||
size_t bufSize = 0;
|
||||
size_t auxBufSize = 0;
|
||||
// for read and sort
|
||||
size_t dataSize = 0;
|
||||
perf_event_header header;
|
||||
uint64_t timestamp = 0;
|
||||
const perf_event_attr *attr = nullptr;
|
||||
size_t posCallChain = 0;
|
||||
int cpu = 0;
|
||||
void *auxBuf = nullptr;
|
||||
pid_t tid_ = 0;
|
||||
};
|
||||
|
||||
bool isHM_ = false;
|
||||
bool isSpe_ = false;
|
||||
|
||||
void SetHM(bool isHM);
|
||||
void SetConfig(std::map<const std::string, unsigned long long> &speOptMaps);
|
||||
private:
|
||||
size_t recordEventCount_ = 0; // only for debug time
|
||||
#ifdef HIPERF_DEBUG_TIME
|
||||
@ -530,9 +549,11 @@ private:
|
||||
void StatLoop();
|
||||
bool IsRecordInMmap(int timeout);
|
||||
void ReadRecordsFromMmaps();
|
||||
void ReadRecordsFromSpeMmaps(MmapFd& mmapFd, u64 auxSize, u32 pid, u32 tid);
|
||||
void SpeReadData(void *dataPage, u64 *dataTail, uint8_t *buf, u32 size);
|
||||
bool GetRecordFromMmap(MmapFd &mmap);
|
||||
void GetRecordFieldFromMmap(MmapFd &mmap, void *dest, size_t pos, size_t size);
|
||||
void MoveRecordToBuf(MmapFd &mmap);
|
||||
void MoveRecordToBuf(MmapFd &mmap, bool &isAuxEvent, u64 &auxSize, u32 &pid, u32 &tid);
|
||||
size_t GetCallChainPosInSampleRecord(const perf_event_attr &attr);
|
||||
size_t GetStackSizePosInSampleRecord(MmapFd &mmap);
|
||||
bool CutStackAndMove(MmapFd &mmap);
|
||||
@ -565,11 +586,16 @@ private:
|
||||
|
||||
unsigned int samplePeriod_ = 0;
|
||||
unsigned int sampleFreq_ = 0;
|
||||
unsigned long long config_ = 0;
|
||||
unsigned long long config1_ = 0;
|
||||
unsigned long long config2_ = 0;
|
||||
unsigned int speType_ = 0;
|
||||
|
||||
struct FdItem {
|
||||
OHOS::UniqueFd fd;
|
||||
int cpu;
|
||||
pid_t pid;
|
||||
pid_t tid;
|
||||
__u64 eventCount;
|
||||
mutable uint64_t perfId = 0;
|
||||
uint64_t GetPrefId() const
|
||||
@ -604,6 +630,7 @@ private:
|
||||
#endif
|
||||
const int pollTimeOut_ = 500; // ms
|
||||
size_t pageSize_ = 4096;
|
||||
size_t auxMmapPages_ = 128;
|
||||
bool systemTarget_ = false;
|
||||
bool excludeHiperf_ = false;
|
||||
pid_t selfPid_ = -1;
|
||||
@ -628,6 +655,7 @@ private:
|
||||
bool AddEvent(perf_type_id type, __u64 config, bool excludeUser = false,
|
||||
bool excludeKernel = false, bool followGroup = false);
|
||||
bool AddEvent(const std::string &eventString, bool followGroup = false);
|
||||
bool AddSpeEvent(u32 type, bool followGroup = false);
|
||||
bool IsEventSupport(perf_type_id type, __u64 config);
|
||||
bool IsEventAttrSupport(perf_event_attr &attr);
|
||||
|
||||
@ -642,6 +670,7 @@ private:
|
||||
bool CreateFdEvents();
|
||||
bool StatReport(const __u64 &durationInSec);
|
||||
bool CreateMmap(const FdItem &item, const perf_event_attr &attr);
|
||||
bool CreateSpeMmap(const FdItem &item, const perf_event_attr &attr);
|
||||
|
||||
const perf_event_attr *GetDefaultAttr()
|
||||
{
|
||||
|
@ -46,6 +46,16 @@ struct read_format {
|
||||
__u64 id; /* if PERF_FORMAT_ID */
|
||||
};
|
||||
|
||||
struct PerfRecordAuxtraceData {
|
||||
u64 size;
|
||||
u64 offset;
|
||||
u64 reference;
|
||||
u32 idx;
|
||||
u32 tid;
|
||||
u32 cpu;
|
||||
u32 reserved__;
|
||||
};
|
||||
|
||||
/*
|
||||
The MMAP events record the PROT_EXEC mappings so that
|
||||
we can correlate user-space IPs to code. They have
|
||||
@ -263,9 +273,7 @@ struct PerfRecordAuxData {
|
||||
u64 aux_offset;
|
||||
u64 aux_size;
|
||||
u64 flags;
|
||||
#if SAMPLE_ID_ALL
|
||||
struct sample_id sample_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
315
include/spe_decoder.h
Normal file
315
include/spe_decoder.h
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef SPE_DECODER_H_
|
||||
#define SPE_DECODER_H_
|
||||
|
||||
#include <linux/const.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "utilities.h"
|
||||
|
||||
#define BIT_ULL(x) (_BITULL(x))
|
||||
#define ULL(x) (_ULL(x))
|
||||
#define UL(x) (_UL(x))
|
||||
#define BIT(x) (UL(1) << (x))
|
||||
#define BITS_PER_LONG __BITS_PER_LONG
|
||||
#define BITS_PER_LONG_LONG 64
|
||||
|
||||
#define GENMASK_INPUT_CHECK(h, l) 0
|
||||
|
||||
#define PR_GENMASK(h, l) \
|
||||
(((~UL(0)) - (UL(1) << (l)) + 1) & \
|
||||
(~UL(0) >> (BITS_PER_LONG - 1 - (h))))
|
||||
#define GENMASK(h, l) \
|
||||
(GENMASK_INPUT_CHECK(h, l) + PR_GENMASK(h, l))
|
||||
|
||||
#define PR_GENMASK_ULL(h, l) \
|
||||
(((~ULL(0)) - (ULL(1) << (l)) + 1) & \
|
||||
(~ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h))))
|
||||
#define GENMASK_ULL(h, l) \
|
||||
(GENMASK_INPUT_CHECK(h, l) + PR_GENMASK_ULL(h, l))
|
||||
|
||||
#define PERF_SPE_PKT_DESC_MAX 256
|
||||
#define PERF_SPE_NEED_MORE_BYTES (-1)
|
||||
#define PERF_SPE_BAD_PACKET (-2)
|
||||
#define PERF_SPE_PKT_MAX_SZ 16
|
||||
|
||||
namespace OHOS {
|
||||
namespace Developtools {
|
||||
namespace HiPerf {
|
||||
enum SpePktType {
|
||||
PERF_SPE_BAD,
|
||||
PERF_SPE_PAD,
|
||||
PERF_SPE_END,
|
||||
PERF_SPE_TIMESTAMP,
|
||||
PERF_SPE_ADDRESS,
|
||||
PERF_SPE_COUNTER,
|
||||
PERF_SPE_CONTEXT,
|
||||
PERF_SPE_OP_TYPE,
|
||||
PERF_SPE_EVENTS,
|
||||
PERF_SPE_DATA_SOURCE,
|
||||
};
|
||||
|
||||
struct SpePkt {
|
||||
SpePktType type;
|
||||
unsigned char index;
|
||||
uint64_t payload;
|
||||
};
|
||||
|
||||
/* Short header (HEADER0) and extended header (HEADER1) */
|
||||
#define PERF_SPE_HEADER0_PAD 0x0
|
||||
#define PERF_SPE_HEADER0_END 0x1
|
||||
#define PERF_SPE_HEADER0_TIMESTAMP 0x71
|
||||
/* Mask for event & data source */
|
||||
#define PERF_SPE_HEADER0_MASK1 (GENMASK_ULL(7, 6) | GENMASK_ULL(3, 0))
|
||||
#define PERF_SPE_HEADER0_EVENTS 0x42
|
||||
#define PERF_SPE_HEADER0_SOURCE 0x43
|
||||
/* Mask for context & operation */
|
||||
#define PERF_SPE_HEADER0_MASK2 GENMASK_ULL(7, 2)
|
||||
#define PERF_SPE_HEADER0_CONTEXT 0x64
|
||||
#define PERF_SPE_HEADER0_OP_TYPE 0x48
|
||||
/* Mask for extended format */
|
||||
#define PERF_SPE_HEADER0_EXTENDED 0x20
|
||||
/* Mask for address & counter */
|
||||
#define PERF_SPE_HEADER0_MASK3 GENMASK_ULL(7, 3)
|
||||
#define PERF_SPE_HEADER0_ADDRESS 0xb0
|
||||
#define PERF_SPE_HEADER0_COUNTER 0x98
|
||||
#define PERF_SPE_HEADER1_ALIGNMENT 0x0
|
||||
|
||||
#define PERF_SPE_HDR_SHORT_INDEX(h) ((h) & GENMASK_ULL(2, 0))
|
||||
#define PERF_SPE_HDR_EXTENDED_INDEX(h0, h1) (((h0) & GENMASK_ULL(1, 0)) << 3 | \
|
||||
PERF_SPE_HDR_SHORT_INDEX(h1))
|
||||
|
||||
/* Address packet header */
|
||||
#define PERF_SPE_ADDR_PKT_HDR_INDEX_INS 0x0
|
||||
#define PERF_SPE_ADDR_PKT_HDR_INDEX_BRANCH 0x1
|
||||
#define PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT 0x2
|
||||
#define PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS 0x3
|
||||
#define PERF_SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH 0x4
|
||||
|
||||
/* Address packet payload */
|
||||
#define PERF_SPE_ADDR_PKT_ADDR_BYTE7_SHIFT 56
|
||||
#define PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(v) ((v) & GENMASK_ULL(55, 0))
|
||||
#define PERF_SPE_ADDR_PKT_ADDR_GET_BYTE_6(v) (((v) & GENMASK_ULL(55, 48)) >> 48)
|
||||
|
||||
#define PERF_SPE_ADDR_PKT_GET_NS(v) (((v) & BIT_ULL(63)) >> 63)
|
||||
#define PERF_SPE_ADDR_PKT_GET_EL(v) (((v) & GENMASK_ULL(62, 61)) >> 61)
|
||||
#define PERF_SPE_ADDR_PKT_GET_CH(v) (((v) & BIT_ULL(62)) >> 62)
|
||||
#define PERF_SPE_ADDR_PKT_GET_PAT(v) (((v) & GENMASK_ULL(59, 56)) >> 56)
|
||||
|
||||
#define PERF_SPE_ADDR_PKT_EL0 0
|
||||
#define PERF_SPE_ADDR_PKT_EL1 1
|
||||
#define PERF_SPE_ADDR_PKT_EL2 2
|
||||
#define PERF_SPE_ADDR_PKT_EL3 3
|
||||
|
||||
/* Context packet header */
|
||||
#define PERF_SPE_CTX_PKT_HDR_INDEX(h) ((h) & GENMASK_ULL(1, 0))
|
||||
|
||||
/* Counter packet header */
|
||||
#define PERF_SPE_CNT_PKT_HDR_INDEX_TOTAL_LAT 0x0
|
||||
#define PERF_SPE_CNT_PKT_HDR_INDEX_ISSUE_LAT 0x1
|
||||
#define PERF_SPE_CNT_PKT_HDR_INDEX_TRANS_LAT 0x2
|
||||
|
||||
/* Get bit 5th and 4th of the header */
|
||||
#define PERF_SPE_HDR_GET_BYTES_5_4(h) (((h) & (GENMASK_ULL(5, 4))) >> 4)
|
||||
|
||||
/* Event packet payload */
|
||||
enum SpeEvents {
|
||||
EVENT_EXCEPTION_GEN = 0,
|
||||
EVENT_RETIRED = 1,
|
||||
EVENT_L1D_ACCESS = 2,
|
||||
EVENT_L1D_REFILL = 3,
|
||||
EVENT_TLB_ACCESS = 4,
|
||||
EVENT_TLB_WALK = 5,
|
||||
EVENT_NOT_TAKEN = 6,
|
||||
EVENT_MISPRED = 7,
|
||||
EVENT_LLC_ACCESS = 8,
|
||||
EVENT_LLC_MISS = 9,
|
||||
EVENT_REMOTE_ACCESS = 10,
|
||||
EVENT_ALIGNMENT = 11,
|
||||
EVENT_PARTIAL_PREDICATE = 17,
|
||||
EVENT_EMPTY_PREDICATE = 18,
|
||||
};
|
||||
|
||||
|
||||
/* Operation packet header */
|
||||
#define PERF_SPE_OP_PKT_HDR_CLASS(h) ((h) & GENMASK_ULL(1, 0))
|
||||
#define PERF_SPE_OP_PKT_HDR_CLASS_OTHER 0x0
|
||||
#define PERF_SPE_OP_PKT_HDR_CLASS_LD_ST_ATOMIC 0x1
|
||||
#define PERF_SPE_OP_PKT_HDR_CLASS_BR_ERET 0x2
|
||||
|
||||
#define PERF_SPE_OP_PKT_IS_OTHER_SVE_OP(v) (((v) & (BIT(7) | BIT(3) | BIT(0))) == 0x8)
|
||||
|
||||
#define PERF_SPE_OP_PKT_COND BIT(0)
|
||||
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_GET(v) ((v) & GENMASK_ULL(7, 1))
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_GP_REG 0x0
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_SIMD_FP 0x4
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_UNSPEC_REG 0x10
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_NV_SYSREG 0x30
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_MTE_TAG 0x14
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_MEMCPY 0x20
|
||||
#define PERF_SPE_OP_PKT_LDST_SUBCLASS_MEMSET 0x25
|
||||
|
||||
#define PERF_SPE_OP_PKT_IS_LDST_ATOMIC(v) (((v) & (GENMASK_ULL(7, 5) | BIT(1))) == 0x2)
|
||||
|
||||
#define PERF_SPE_OP_PKT_AR BIT(4)
|
||||
#define PERF_SPE_OP_PKT_EXCL BIT(3)
|
||||
#define PERF_SPE_OP_PKT_AT BIT(2)
|
||||
#define PERF_SPE_OP_PKT_ST BIT(0)
|
||||
|
||||
#define PERF_SPE_OP_PKT_IS_LDST_SVE(v) (((v) & (BIT(3) | BIT(1))) == 0x8)
|
||||
|
||||
#define PERF_SPE_OP_PKT_SVE_SG BIT(7)
|
||||
/*
|
||||
* SVE effective vector length (EVL) is stored in byte 0 bits [6:4];
|
||||
* the length is rounded up to a power of two and use 32 as one step,
|
||||
* so EVL calculation is:
|
||||
*
|
||||
* 32 * (2 ^ bits [6:4]) = 32 << (bits [6:4])
|
||||
*/
|
||||
#define PERF_SPE_OP_PKG_SVE_EVL(v) (32 << (((v) & GENMASK_ULL(6, 4)) >> 4))
|
||||
#define PERF_SPE_OP_PKT_SVE_PRED BIT(2)
|
||||
#define PERF_SPE_OP_PKT_SVE_FP BIT(1)
|
||||
|
||||
#define PERF_SPE_OP_PKT_IS_INDIRECT_BRANCH(v) (((v) & GENMASK_ULL(7, 1)) == 0x2)
|
||||
|
||||
const int LEN_TYPE_BYTE = 1;
|
||||
const int LEN_TYPE_HLFWRD = 2;
|
||||
const int LEN_TYPE_WORD = 4;
|
||||
const int LEN_TYPE_DBLEWRD = 8;
|
||||
/*16 bytes of width*/
|
||||
const int BYTE_WIDTH = 16;
|
||||
|
||||
enum SpeSampleType {
|
||||
PERF_SPE_L1D_ACCESS = 1 << 0,
|
||||
PERF_SPE_L1D_MISS = 1 << 1,
|
||||
PERF_SPE_LLC_ACCESS = 1 << 2,
|
||||
PERF_SPE_LLC_MISS = 1 << 3,
|
||||
PERF_SPE_TLB_ACCESS = 1 << 4,
|
||||
PERF_SPE_TLB_MISS = 1 << 5,
|
||||
PERF_SPE_BRANCH_MISS = 1 << 6,
|
||||
PERF_SPE_REMOTE_ACCESS = 1 << 7,
|
||||
PERF_SPE_SVE_PARTIAL_PRED = 1 << 8,
|
||||
PERF_SPE_SVE_EMPTY_PRED = 1 << 9,
|
||||
};
|
||||
|
||||
enum SpeOpType {
|
||||
/* First level operation type */
|
||||
PERF_SPE_OP_OTHER = 1 << 0,
|
||||
PERF_SPE_OP_LDST = 1 << 1,
|
||||
PERF_SPE_OP_BRANCH_ERET = 1 << 2,
|
||||
|
||||
/* Second level operation type for OTHER */
|
||||
PERF_SPE_OP_SVE_OTHER = 1 << 16,
|
||||
PERF_SPE_OP_SVE_FP = 1 << 17,
|
||||
PERF_SPE_OP_SVE_PRED_OTHER = 1 << 18,
|
||||
|
||||
/* Second level operation type for LDST */
|
||||
PERF_SPE_OP_LD = 1 << 16,
|
||||
PERF_SPE_OP_ST = 1 << 17,
|
||||
PERF_SPE_OP_ATOMIC = 1 << 18,
|
||||
PERF_SPE_OP_EXCL = 1 << 19,
|
||||
PERF_SPE_OP_AR = 1 << 20,
|
||||
PERF_SPE_OP_SIMD_FP = 1 << 21,
|
||||
PERF_SPE_OP_GP_REG = 1 << 22,
|
||||
PERF_SPE_OP_UNSPEC_REG = 1 << 23,
|
||||
PERF_SPE_OP_NV_SYSREG = 1 << 24,
|
||||
PERF_SPE_OP_SVE_LDST = 1 << 25,
|
||||
PERF_SPE_OP_SVE_PRED_LDST = 1 << 26,
|
||||
PERF_SPE_OP_SVE_SG = 1 << 27,
|
||||
|
||||
/* Second level operation type for BRANCH_ERET */
|
||||
PERF_SPE_OP_BR_COND = 1 << 16,
|
||||
PERF_SPE_OP_BR_INDIRECT = 1 << 17,
|
||||
};
|
||||
|
||||
enum SpeNeoverseDataSource {
|
||||
PERF_SPE_NV_L1D = 0x0,
|
||||
PERF_SPE_NV_L2 = 0x8,
|
||||
PERF_SPE_NV_PEER_CORE = 0x9,
|
||||
PERF_SPE_NV_LOCAL_CLUSTER = 0xa,
|
||||
PERF_SPE_NV_SYS_CACHE = 0xb,
|
||||
PERF_SPE_NV_PEER_CLUSTER = 0xc,
|
||||
PERF_SPE_NV_REMOTE = 0xd,
|
||||
PERF_SPE_NV_DRAM = 0xe,
|
||||
};
|
||||
|
||||
struct SpeRecord {
|
||||
u32 type;
|
||||
int err;
|
||||
u32 op;
|
||||
u32 latency;
|
||||
u64 from_ip;
|
||||
u64 to_ip;
|
||||
u64 timestamp;
|
||||
u64 virt_addr;
|
||||
u64 phys_addr;
|
||||
u64 context_id;
|
||||
u16 source;
|
||||
};
|
||||
|
||||
struct SpeInsn;
|
||||
|
||||
struct SpeBuffer {
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
u64 offset;
|
||||
u64 trace_nr;
|
||||
};
|
||||
|
||||
struct SpeParams {
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct SpeDecoder {
|
||||
void *data;
|
||||
struct SpeRecord record;
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
|
||||
struct SpePkt packet;
|
||||
};
|
||||
|
||||
struct SpeDecoder *SpeDecoderNew(struct SpeParams *params);
|
||||
struct SpeDecoder *SpeDecoderDataNew(const unsigned char *speBuf, size_t speLen);
|
||||
void SpeDecoderFree(struct SpeDecoder *decoder);
|
||||
|
||||
int SpeDecode(struct SpeDecoder *decoder);
|
||||
|
||||
int SpePktDesc(const struct SpePkt *packet, char *buf, size_t len);
|
||||
void SpeDumpRawData(unsigned char *buf, size_t len, int indent, FILE *outputDump);
|
||||
|
||||
struct ReportItemAuxRawData {
|
||||
u32 type;
|
||||
float heating;
|
||||
u64 count;
|
||||
std::string comm;
|
||||
u64 pc;
|
||||
std::string SharedObject;
|
||||
std::string Symbol;
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
void AddReportItems(const std::vector<ReportItemAuxRawData>& auxRawData);
|
||||
void UpdateHeating();
|
||||
void DumpSpeReportData(int indent, FILE *outputDump);
|
||||
void DumpSpeReportHead(int indent, uint32_t type, uint64_t count);
|
||||
void GetSpeEventNameByType(uint32_t type, std::string& eventName);
|
||||
} // namespace HiPerf
|
||||
} // namespace Developtools
|
||||
} // namespace OHOS
|
||||
#endif // SPE_DECODER_H_
|
@ -115,6 +115,7 @@ private:
|
||||
void PrintHeaderInfo(const int &indent);
|
||||
void PrintSymbolFile(const int &indent, const SymbolFileStruct &symbolFileStruct);
|
||||
void PrintFeatureEventdesc(int indent, const PerfFileSectionEventDesc §ionEventdesc);
|
||||
void DumpSpeReport();
|
||||
VirtualRuntime vr_;
|
||||
|
||||
void SetHM();
|
||||
|
@ -194,6 +194,12 @@ public:
|
||||
void DumpOptions(void) const override;
|
||||
|
||||
static bool RegisterSubCommandRecord(void);
|
||||
std::map<const std::string, unsigned long long> speOptMap_ = {
|
||||
{"branch_filter", 0}, {"load_filter", 0},
|
||||
{"store_filter", 0}, {"ts_enable", 0},
|
||||
{"pa_enable", 0}, {"jitter", 0},
|
||||
{"min_latency", 0}, {"event_filter", 0},
|
||||
};
|
||||
|
||||
private:
|
||||
PerfEvents perfEvents_;
|
||||
@ -230,6 +236,7 @@ private:
|
||||
std::vector<pid_t> excludeTids_ = {};
|
||||
bool restart_ = false;
|
||||
std::vector<std::string> selectEvents_ = {};
|
||||
std::vector<std::string> speOptions_ = {};
|
||||
std::vector<std::vector<std::string>> selectGroups_ = {};
|
||||
std::vector<std::string> callStackType_ = {};
|
||||
std::vector<std::string> vecBranchFilters_ = {};
|
||||
@ -239,6 +246,7 @@ private:
|
||||
bool GetOptions(std::vector<std::string> &args);
|
||||
bool CheckArgsRange();
|
||||
bool CheckOptions();
|
||||
bool GetSpeOptions();
|
||||
bool CheckDataLimitOption();
|
||||
bool CheckSelectCpuPidOption();
|
||||
bool GetOptionFrequencyAndPeriod(std::vector<std::string> &args);
|
||||
@ -281,6 +289,7 @@ private:
|
||||
size_t recordNoSamples_ = 0;
|
||||
|
||||
bool isNeedSetPerfHarden_ = false;
|
||||
bool isSpe_ = false;
|
||||
|
||||
// callback to process record
|
||||
bool ProcessRecord(std::unique_ptr<PerfEventRecord>);
|
||||
|
@ -138,6 +138,7 @@ public:
|
||||
return userSpaceThreadMap_;
|
||||
}
|
||||
void SymbolicRecord(PerfRecordSample &recordSample);
|
||||
void SymbolSpeRecord(PerfRecordAuxtrace &recordAuxTrace);
|
||||
|
||||
// report use
|
||||
void UpdateFromPerfData(const std::vector<SymbolFileStruct> &);
|
||||
@ -201,6 +202,7 @@ private:
|
||||
void UpdateFromRecord(PerfRecordMmap2 &recordMmap2);
|
||||
void UpdateFromRecord(PerfRecordComm &recordComm);
|
||||
void DedupFromRecord(PerfRecordSample *recordSample);
|
||||
void UpdateFromRecord(PerfRecordAuxtrace &recordAuxTrace);
|
||||
// threads
|
||||
VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = "");
|
||||
VirtualThread &CreateThread(pid_t pid, pid_t tid, const std::string name = "");
|
||||
|
@ -33,7 +33,8 @@ def get_used_binaries(perf_data, report_file, local_lib_dir, html_template):
|
||||
time.sleep(2)
|
||||
with open('json.txt', 'r') as json_file:
|
||||
all_json = json_file.read()
|
||||
with open(html_template + '/report.html', 'r', encoding='utf-8') as html_file:
|
||||
template = os.path.join(html_template, 'report.html')
|
||||
with open(template, 'r', encoding='utf-8') as html_file:
|
||||
html_str = html_file.read()
|
||||
with open(report_file, 'w', encoding='utf-8') as report_html_file:
|
||||
report_html_file.write(html_str + all_json + '</script>'
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define HILOG_TAG "PerfRecord"
|
||||
|
||||
#include "perf_event_record.h"
|
||||
#include "spe_decoder.h"
|
||||
#include <cinttypes>
|
||||
|
||||
#include "utilities.h"
|
||||
@ -59,6 +60,8 @@ std::unique_ptr<PerfEventRecord> GetPerfEventRecord(const int type, uint8_t *p,
|
||||
return std::make_unique<PerfRecordRead>(data);
|
||||
case PERF_RECORD_AUX:
|
||||
return std::make_unique<PerfRecordAux>(data);
|
||||
case PERF_RECORD_AUXTRACE:
|
||||
return std::make_unique<PerfRecordAuxtrace>(data);
|
||||
case PERF_RECORD_ITRACE_START:
|
||||
return std::make_unique<PerfRecordItraceStart>(data);
|
||||
case PERF_RECORD_LOST_SAMPLES:
|
||||
@ -216,6 +219,77 @@ std::vector<u64> PerfRecordSample::ips_ = {};
|
||||
std::vector<DfxFrame> PerfRecordSample::callFrames_ = {};
|
||||
std::vector<pid_t> PerfRecordSample::serverPidMap_ = {};
|
||||
|
||||
PerfRecordAuxtrace::PerfRecordAuxtrace(uint8_t *p) : PerfEventRecord(p, "auxtrace")
|
||||
{
|
||||
size_t copySize = header.size - sizeof(header);
|
||||
if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
|
||||
HLOGE("memcpy_s return failed!!!");
|
||||
}
|
||||
rawData_ = p + header.size;
|
||||
}
|
||||
|
||||
PerfRecordAuxtrace::PerfRecordAuxtrace(u64 size, u64 offset, u64 reference, u32 idx, u32 tid, u32 cpu, u32 pid)
|
||||
: PerfEventRecord(PERF_RECORD_AUXTRACE, "auxtrace")
|
||||
{
|
||||
data_.size = size;
|
||||
data_.offset = offset;
|
||||
data_.reference = reference;
|
||||
data_.idx = idx;
|
||||
data_.tid = tid;
|
||||
data_.cpu = cpu;
|
||||
data_.reserved__ = pid;
|
||||
|
||||
header.size = sizeof(header) + sizeof(data_);
|
||||
}
|
||||
|
||||
bool PerfRecordAuxtrace::GetBinary1(std::vector<uint8_t> &buf) const
|
||||
{
|
||||
if (buf.size() < header.size) {
|
||||
buf.resize(header.size);
|
||||
}
|
||||
|
||||
GetHeaderBinary(buf);
|
||||
uint8_t *p = buf.data() + GetHeaderSize();
|
||||
|
||||
std::copy((uint8_t *)&data_, (uint8_t *)&data_ + header.size - GetHeaderSize(), p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerfRecordAuxtrace::GetBinary(std::vector<uint8_t> &buf) const
|
||||
{
|
||||
if (buf.size() < GetSize()) {
|
||||
buf.resize(GetSize());
|
||||
}
|
||||
|
||||
GetHeaderBinary(buf);
|
||||
uint8_t *p = buf.data() + GetHeaderSize();
|
||||
|
||||
std::copy((uint8_t *)&data_, (uint8_t *)&data_ + header.size - GetHeaderSize(), p);
|
||||
p += header.size - GetHeaderSize();
|
||||
std::copy((uint8_t *)rawData_, (uint8_t *)rawData_ + data_.size, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerfRecordAuxtrace::DumpData(int indent) const
|
||||
{
|
||||
PRINT_INDENT(indent, "size 0x%llx, offset 0x%llx, reference 0x%llx, idx %u, tid %u, cpu %u, pid %u\n",
|
||||
data_.size, data_.offset, data_.reference, data_.idx, data_.tid, data_.cpu, data_.reserved__);
|
||||
#if defined(is_ohos) && is_ohos
|
||||
SpeDumpRawData(rawData_, data_.size, indent, g_outputDump);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PerfRecordAuxtrace::DumpLog(const std::string &prefix) const
|
||||
{
|
||||
HLOGV("size %llu, offset 0x%llx, reference 0x%llx, idx %u, tid %u, cpu %u\n",
|
||||
data_.size, data_.offset, data_.reference, data_.idx, data_.tid, data_.cpu);
|
||||
}
|
||||
|
||||
size_t PerfRecordAuxtrace::GetSize() const
|
||||
{
|
||||
return header.size + data_.size;
|
||||
}
|
||||
|
||||
void PerfRecordSample::DumpLog(const std::string &prefix) const
|
||||
{
|
||||
HLOGV("%s: SAMPLE: id= %llu size %d pid %u tid %u ips %llu regs %llu, stacks %llu time %llu",
|
||||
@ -436,7 +510,7 @@ void PerfRecordSample::DumpData(int indent) const
|
||||
PRINT_INDENT(indent, "time %llu\n", data_.time);
|
||||
}
|
||||
if (sampleType_ & PERF_SAMPLE_ADDR) {
|
||||
PRINT_INDENT(indent, "addr hide\n");
|
||||
PRINT_INDENT(indent, "addr %p\n", reinterpret_cast<void *>(data_.addr));
|
||||
}
|
||||
if (sampleType_ & PERF_SAMPLE_STREAM_ID) {
|
||||
PRINT_INDENT(indent, "stream_id %" PRIu64 "\n", static_cast<uint64_t>(data_.stream_id));
|
||||
@ -895,15 +969,24 @@ bool PerfRecordAux::GetBinary(std::vector<uint8_t> &buf) const
|
||||
GetHeaderBinary(buf);
|
||||
uint8_t *p = buf.data() + GetHeaderSize();
|
||||
|
||||
auto pDest = reinterpret_cast<PerfRecordAuxData *>(p);
|
||||
*pDest = data_;
|
||||
PushToBinary(true, p, data_.aux_offset);
|
||||
PushToBinary(true, p, data_.aux_size);
|
||||
PushToBinary(true, p, data_.flags);
|
||||
PushToBinary2(sampleType_ & PERF_SAMPLE_TID, p, data_.sample_id.pid, data_.sample_id.tid);
|
||||
PushToBinary(sampleType_ & PERF_SAMPLE_TIME, p, data_.sample_id.time);
|
||||
PushToBinary(sampleType_ & PERF_SAMPLE_ID, p, data_.sample_id.id);
|
||||
PushToBinary(sampleType_ & PERF_SAMPLE_STREAM_ID, p, data_.sample_id.stream_id);
|
||||
|
||||
PushToBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.sample_id.cpu, data_.sample_id.res);
|
||||
PushToBinary(sampleType_ & PERF_SAMPLE_IDENTIFIER, p, data_.sample_id.id2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerfRecordAux::DumpData(int indent) const
|
||||
{
|
||||
PRINT_INDENT(indent, "aux_offset %llx, aux_size %llx, flags %llx\n", data_.aux_offset,
|
||||
data_.aux_size, data_.flags);
|
||||
PRINT_INDENT(indent, "aux_offset 0x%llx aux_size 0x%llx flags 0x%llx pid %u tid %u time %llu",
|
||||
data_.aux_offset, data_.aux_size, data_.flags, data_.sample_id.pid, data_.sample_id.tid,
|
||||
data_.sample_id.time);
|
||||
}
|
||||
|
||||
PerfRecordItraceStart::PerfRecordItraceStart(uint8_t *p) : PerfEventRecord(p, "itraceStart")
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <parameters.h>
|
||||
#endif
|
||||
|
||||
#include "spe_decoder.h"
|
||||
#include "debug_logger.h"
|
||||
#include "register.h"
|
||||
#include "subcommand_dump.h"
|
||||
@ -51,10 +52,114 @@ OHOS::UniqueFd PerfEvents::Open(perf_event_attr &attr, pid_t pid, int cpu, int g
|
||||
// dump when open failed.
|
||||
SubCommandDump::DumpPrintEventAttr(attr, std::numeric_limits<int>::min());
|
||||
}
|
||||
SubCommandDump::DumpPrintEventAttr(attr, std::numeric_limits<int>::min());
|
||||
HLOGV("perf_event_open: got fd %d for pid %d cpu %d group %d flags %lu", fd.Get(), pid, cpu, groupFd, flags);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void PerfEvents::SpeReadData(void *dataPage, u64 *dataTail, uint8_t *buf, u32 size)
|
||||
{
|
||||
void *src = nullptr;
|
||||
u32 left = 0;
|
||||
u32 offset = static_cast<u32>(*dataTail);
|
||||
u32 copySize;
|
||||
u32 traceSize = size;
|
||||
|
||||
while (traceSize > 0) {
|
||||
offset = CALC_OFFSET(offset, auxMmapPages_ * pageSize_);
|
||||
left = static_cast<u32>(auxMmapPages_ * pageSize_ - offset);
|
||||
copySize = min(traceSize, left);
|
||||
src = PTR_ADD(dataPage, offset);
|
||||
if (memcpy_s(buf, left, src, copySize) != 0) {
|
||||
HLOGV("SpeReadData memcpy_s failed.");
|
||||
}
|
||||
|
||||
traceSize -= copySize;
|
||||
offset += copySize;
|
||||
buf = reinterpret_cast<uint8_t *>(PTR_ADD(buf, copySize));
|
||||
}
|
||||
|
||||
*dataTail += size;
|
||||
}
|
||||
|
||||
static u64 arm_spe_reference()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
return static_cast<uint64_t>(ts.tv_sec) ^ static_cast<uint64_t>(ts.tv_nsec);
|
||||
}
|
||||
|
||||
void PerfEvents::ReadRecordsFromSpeMmaps(MmapFd& mmapFd, u64 auxSize, u32 pid, u32 tid)
|
||||
{
|
||||
if (mmapFd.mmapPage == nullptr || mmapFd.auxBuf == nullptr) {
|
||||
printf("ReadRecordsFromSpeMmaps mmapFd.mmapPage is nullptr, mmapFd.fd:%d", mmapFd.fd);
|
||||
return;
|
||||
}
|
||||
perf_event_mmap_page *userPage = reinterpret_cast<perf_event_mmap_page *>(mmapFd.mmapPage);
|
||||
void *auxPage = mmapFd.auxBuf;
|
||||
u64 auxHead = userPage->aux_head;
|
||||
u64 auxTail = userPage->aux_tail;
|
||||
HLOGD("mmap cpu %d, aux_head: %llu, aux_tail:%llu, auxSize:%llu",
|
||||
mmapFd.cpu, auxHead, auxTail, auxSize);
|
||||
if (auxHead <= auxTail) {
|
||||
return;
|
||||
}
|
||||
if (auxSize > auxMmapPages_ * pageSize_) {
|
||||
HLOGE("auxSize : %llu exceed max size:%zu", auxSize, auxMmapPages_ * pageSize_);
|
||||
userPage->aux_tail += auxSize;
|
||||
return;
|
||||
}
|
||||
int cpu = mmapFd.cpu;
|
||||
__sync_synchronize();
|
||||
PerfRecordAuxtrace auxtraceRecord = PerfRecordAuxtrace(auxSize, auxTail,
|
||||
arm_spe_reference(), cpu, tid, cpu, pid);
|
||||
static std::vector<u8> vbuf(RECORD_SIZE_LIMIT_SPE);
|
||||
uint8_t *buf;
|
||||
if ((buf = recordBuf_->AllocForWrite(auxtraceRecord.header.size + auxSize)) == nullptr) {
|
||||
HLOGD("alloc buffer failed: PerfRecordAuxtrace record, readSize: %llu", auxSize);
|
||||
return;
|
||||
}
|
||||
auxtraceRecord.GetBinary1(vbuf);
|
||||
std::copy(vbuf.begin(), vbuf.begin() + auxtraceRecord.header.size, buf);
|
||||
buf += auxtraceRecord.header.size;
|
||||
|
||||
while (auxSize > 0) {
|
||||
u64 readSize = pageSize_;
|
||||
if (auxSize < pageSize_) {
|
||||
readSize = auxSize;
|
||||
}
|
||||
__sync_synchronize();
|
||||
SpeReadData(auxPage, &auxTail, buf, readSize);
|
||||
__sync_synchronize();
|
||||
userPage->aux_tail += readSize;
|
||||
auxHead = userPage->aux_head;
|
||||
auxTail = userPage->aux_tail;
|
||||
buf += readSize;
|
||||
auxSize -= readSize;
|
||||
}
|
||||
recordBuf_->EndWrite();
|
||||
}
|
||||
|
||||
u32 GetSpeType()
|
||||
{
|
||||
FILE *fd;
|
||||
u32 speType;
|
||||
|
||||
fd = fopen("/sys/devices/arm_spe_0/type", "r");
|
||||
if (fd == nullptr) {
|
||||
HLOGV("open sysfs file failed");
|
||||
return -1;
|
||||
}
|
||||
if (fscanf_s(fd, "%u", &speType) <= 0) {
|
||||
HLOGV("fscanf_s file failed");
|
||||
return -1;
|
||||
}
|
||||
HLOGD("spetype %u", speType);
|
||||
|
||||
fclose(fd);
|
||||
return speType;
|
||||
}
|
||||
|
||||
PerfEvents::PerfEvents() : timeOut_(DEFAULT_TIMEOUT * THOUSANDS), timeReport_(0)
|
||||
{
|
||||
pageSize_ = sysconf(_SC_PAGESIZE);
|
||||
@ -66,7 +171,12 @@ PerfEvents::~PerfEvents()
|
||||
// close mmap
|
||||
for (auto it = cpuMmap_.begin(); it != cpuMmap_.end();) {
|
||||
const MmapFd &mmapItem = it->second;
|
||||
munmap(mmapItem.mmapPage, (1 + mmapPages_) * pageSize_);
|
||||
if (!isSpe_) {
|
||||
munmap(mmapItem.mmapPage, (1 + mmapPages_) * pageSize_);
|
||||
} else {
|
||||
munmap(mmapItem.mmapPage, (1 + auxMmapPages_) * pageSize_);
|
||||
munmap(mmapItem.auxBuf, auxMmapPages_ * pageSize_);
|
||||
}
|
||||
it = cpuMmap_.erase(it);
|
||||
}
|
||||
|
||||
@ -249,6 +359,10 @@ bool PerfEvents::AddEvent(const std::string &eventString, bool followGroup)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (eventName == "arm_spe_0") {
|
||||
u32 speType = GetSpeType();
|
||||
return AddSpeEvent(speType);
|
||||
}
|
||||
if (StringStartsWith(eventName, "0x")
|
||||
&& eventName.length() <= MAX_HEX_EVENT_NAME_LENGTH && IsHexDigits(eventName)) {
|
||||
return AddEvent(PERF_TYPE_RAW, std::stoull(eventName, nullptr, NUMBER_FORMAT_HEX_BASE),
|
||||
@ -265,6 +379,44 @@ bool PerfEvents::AddEvent(const std::string &eventString, bool followGroup)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PerfEvents::AddSpeEvent(u32 type, bool followGroup)
|
||||
{
|
||||
EventGroupItem &eventGroupItem = followGroup ? eventGroupItem_.back() :
|
||||
eventGroupItem_.emplace_back();
|
||||
EventItem &eventItem = eventGroupItem.eventItems.emplace_back();
|
||||
|
||||
if (memset_s(&eventItem.attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) {
|
||||
HLOGE("memset_s failed in PerfEvents::AddEvent");
|
||||
return false;
|
||||
}
|
||||
eventItem.attr.type = type;
|
||||
eventItem.attr.sample_period = MULTIPLE_SIZE;
|
||||
eventItem.attr.size = sizeof(perf_event_attr);
|
||||
eventItem.attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
|
||||
eventItem.attr.inherit = (inherit_ ? 1 : 0);
|
||||
eventItem.attr.sample_type = SAMPLE_ID;
|
||||
eventItem.attr.sample_id_all = 1;
|
||||
eventItem.attr.disabled = 1;
|
||||
eventItem.attr.config = 0x700010007; // temp type
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerfEvents::SetConfig(std::map<const std::string, unsigned long long> &speOptMaps)
|
||||
{
|
||||
int jitterOffset = 16;
|
||||
int branchOffset = 32;
|
||||
int loadOffset = 33;
|
||||
int storeOffset = 34;
|
||||
config_ |= (speOptMaps["ts_enable"] & 0x1) << 0;
|
||||
config_ |= (speOptMaps["pa_enable"] & 0x1) << 1;
|
||||
config_ |= (speOptMaps["jitter"] & 0x1) << jitterOffset;
|
||||
config_ |= (speOptMaps["branch_filter"] & 0x1) << branchOffset;
|
||||
config_ |= (speOptMaps["load_filter"] & 0x1) << loadOffset;
|
||||
config_ |= (speOptMaps["store_filter"] & 0x1) << storeOffset;
|
||||
config1_ |= speOptMaps["event_filter"];
|
||||
config2_ |= speOptMaps["min_latency"] & 0xfff;
|
||||
}
|
||||
|
||||
bool PerfEvents::AddEvent(perf_type_id type, __u64 config, bool excludeUser, bool excludeKernel,
|
||||
bool followGroup)
|
||||
{
|
||||
@ -1001,7 +1153,11 @@ bool PerfEvents::CreateFdEvents(void)
|
||||
|
||||
// if sampling, mmap ring buffer
|
||||
if (recordCallBack_) {
|
||||
CreateMmap(fdItem, eventItem.attr);
|
||||
if (isSpe_) {
|
||||
CreateSpeMmap(fdItem, eventItem.attr);
|
||||
} else {
|
||||
CreateMmap(fdItem, eventItem.attr);
|
||||
}
|
||||
}
|
||||
// update group leader
|
||||
int groupFdCacheNum = groupFdCache[icpu][ipid];
|
||||
@ -1085,6 +1241,41 @@ bool PerfEvents::StatReport(const __u64 &durationInSec)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerfEvents::CreateSpeMmap(const FdItem &item, const perf_event_attr &attr)
|
||||
{
|
||||
auto it = cpuMmap_.find(item.cpu);
|
||||
if (it == cpuMmap_.end()) {
|
||||
void *rbuf = mmap(nullptr, (1 + auxMmapPages_) * pageSize_, (PROT_READ | PROT_WRITE), MAP_SHARED,
|
||||
item.fd.Get(), 0);
|
||||
if (rbuf == MMAP_FAILED) {
|
||||
return false;
|
||||
}
|
||||
void *auxRbuf = mmap(nullptr, auxMmapPages_ * pageSize_, (PROT_READ | PROT_WRITE), MAP_SHARED,
|
||||
item.fd.Get(), 0);
|
||||
MmapFd mmapItem;
|
||||
mmapItem.fd = item.fd.Get();
|
||||
mmapItem.mmapPage = reinterpret_cast<perf_event_mmap_page *>(rbuf);
|
||||
mmapItem.buf = reinterpret_cast<uint8_t *>(rbuf) + pageSize_;
|
||||
mmapItem.auxBuf = auxRbuf;
|
||||
mmapItem.bufSize = auxMmapPages_ * pageSize_;
|
||||
mmapItem.auxBufSize = auxMmapPages_ * pageSize_;
|
||||
mmapItem.attr = &attr;
|
||||
mmapItem.tid_ = item.pid;
|
||||
mmapItem.cpu = item.cpu;
|
||||
cpuMmap_[item.cpu] = mmapItem;
|
||||
pollFds_.emplace_back(pollfd {mmapItem.fd, POLLIN, 0});
|
||||
} else {
|
||||
const MmapFd &mmapItem = it->second;
|
||||
int rc = ioctl(item.fd.Get(), PERF_EVENT_IOC_SET_OUTPUT, mmapItem.fd);
|
||||
if (rc != 0) {
|
||||
HLOGEP("ioctl PERF_EVENT_IOC_SET_OUTPUT (%d -> %d) ", item.fd.Get(), mmapItem.fd);
|
||||
perror("failed to share mapped buffer\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerfEvents::CreateMmap(const FdItem &item, const perf_event_attr &attr)
|
||||
{
|
||||
auto it = cpuMmap_.find(item.cpu);
|
||||
@ -1094,7 +1285,7 @@ bool PerfEvents::CreateMmap(const FdItem &item, const perf_event_attr &attr)
|
||||
if (rbuf == MMAP_FAILED) {
|
||||
char errInfo[ERRINFOLEN] = {0};
|
||||
strerror_r(errno, errInfo, ERRINFOLEN);
|
||||
perror("errno: %d, errstr: %s", errno, errInfo);
|
||||
perror("errno:%d, errstr:%s", errno, errInfo);
|
||||
perror("Fail to call mmap \n");
|
||||
return false;
|
||||
}
|
||||
@ -1169,12 +1360,14 @@ size_t PerfEvents::CalcBufferSize()
|
||||
|
||||
inline bool PerfEvents::IsRecordInMmap(int timeout)
|
||||
{
|
||||
HLOGV("enter")
|
||||
if (pollFds_.size() > 0) {
|
||||
if (poll(static_cast<struct pollfd*>(pollFds_.data()), pollFds_.size(), timeout) <= 0) {
|
||||
// time out try again
|
||||
return false;
|
||||
}
|
||||
}
|
||||
HLOGV("poll record from mmap")
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1201,7 +1394,7 @@ void PerfEvents::ReadRecordsFromMmaps()
|
||||
if (MmapRecordHeap_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool enableFlag = false;
|
||||
if (MmapRecordHeap_.size() > 1) {
|
||||
for (const auto &it : MmapRecordHeap_) {
|
||||
GetRecordFromMmap(*it);
|
||||
@ -1212,7 +1405,15 @@ void PerfEvents::ReadRecordsFromMmaps()
|
||||
while (heapSize > 1) {
|
||||
std::pop_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.begin() + heapSize,
|
||||
CompareRecordTime);
|
||||
MoveRecordToBuf(*MmapRecordHeap_[heapSize - 1]);
|
||||
bool auxEvent = false;
|
||||
u32 pid = 0;
|
||||
u32 tid = 0;
|
||||
u64 auxSize = 0;
|
||||
MoveRecordToBuf(*MmapRecordHeap_[heapSize - 1], auxEvent, auxSize, pid, tid);
|
||||
if (isSpe_ && auxEvent) {
|
||||
ReadRecordsFromSpeMmaps(*MmapRecordHeap_[heapSize - 1], auxSize, pid, tid);
|
||||
enableFlag = true;
|
||||
}
|
||||
if (GetRecordFromMmap(*MmapRecordHeap_[heapSize - 1])) {
|
||||
std::push_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.begin() + heapSize,
|
||||
CompareRecordTime);
|
||||
@ -1223,7 +1424,19 @@ void PerfEvents::ReadRecordsFromMmaps()
|
||||
}
|
||||
|
||||
while (GetRecordFromMmap(*MmapRecordHeap_.front())) {
|
||||
MoveRecordToBuf(*MmapRecordHeap_.front());
|
||||
bool auxEvent = false;
|
||||
u32 pid = 0;
|
||||
u32 tid = 0;
|
||||
u64 auxSize = 0;
|
||||
MoveRecordToBuf(*MmapRecordHeap_.front(), auxEvent, auxSize, pid, tid);
|
||||
if (isSpe_ && auxEvent) {
|
||||
ReadRecordsFromSpeMmaps(*MmapRecordHeap_.front(), auxSize, pid, tid);
|
||||
enableFlag = true;
|
||||
}
|
||||
}
|
||||
if (isSpe_ && enableFlag) {
|
||||
PerfEventsEnable(false);
|
||||
PerfEventsEnable(true);
|
||||
}
|
||||
MmapRecordHeap_.clear();
|
||||
{
|
||||
@ -1376,7 +1589,7 @@ bool PerfEvents::CutStackAndMove(MmapFd &mmap)
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerfEvents::MoveRecordToBuf(MmapFd &mmap)
|
||||
void PerfEvents::MoveRecordToBuf(MmapFd &mmap, bool &isAuxEvent, u64 &auxSize, u32 &pid, u32 &tid)
|
||||
{
|
||||
uint8_t *buf = nullptr;
|
||||
if (mmap.header.type == PERF_RECORD_SAMPLE) {
|
||||
@ -1397,6 +1610,16 @@ void PerfEvents::MoveRecordToBuf(MmapFd &mmap)
|
||||
HLOGD("PERF_RECORD_LOST: lost sample record");
|
||||
goto RETURN;
|
||||
}
|
||||
if (mmap.header.type == PERF_RECORD_AUX) {
|
||||
isAuxEvent = true;
|
||||
// in AUX : header + u64 aux_offset + u64 aux_size
|
||||
uint64_t auxSizePos = sizeof(perf_event_header) + sizeof(uint64_t);
|
||||
uint64_t pidPos = auxSizePos + sizeof(uint64_t) * 2; // 2 : offset
|
||||
uint64_t tidPos = pidPos + sizeof(uint32_t);
|
||||
GetRecordFieldFromMmap(mmap, &auxSize, mmap.mmapPage->data_tail + auxSizePos, sizeof(auxSize));
|
||||
GetRecordFieldFromMmap(mmap, &pid, mmap.mmapPage->data_tail + pidPos, sizeof(pid));
|
||||
GetRecordFieldFromMmap(mmap, &tid, mmap.mmapPage->data_tail + tidPos, sizeof(tid));
|
||||
}
|
||||
|
||||
if ((buf = recordBuf_->AllocForWrite(mmap.header.size)) == nullptr) {
|
||||
// this record type must be Non-Sample
|
||||
|
@ -259,7 +259,7 @@ bool PerfFileReader::ReadRecord(ProcessRecordCB &callback)
|
||||
const auto startReadTime = steady_clock::now();
|
||||
#endif
|
||||
// record size can not exceed 64K
|
||||
HIPERF_BUF_ALIGN uint8_t buf[RECORD_SIZE_LIMIT];
|
||||
HIPERF_BUF_ALIGN static uint8_t buf[RECORD_SIZE_LIMIT_SPE];
|
||||
// diff with reader
|
||||
uint64_t remainingSize = header_.data.size;
|
||||
size_t recordNumber = 0;
|
||||
@ -275,13 +275,22 @@ bool PerfFileReader::ReadRecord(ProcessRecordCB &callback)
|
||||
if (header == nullptr) {
|
||||
HLOGE("read record header is null");
|
||||
return false;
|
||||
} else if (header->size > sizeof(buf)) {
|
||||
} else if (header->size > RECORD_SIZE_LIMIT) {
|
||||
HLOGE("read record header size error %hu", header->size);
|
||||
return false;
|
||||
}
|
||||
if (remainingSize >= header->size) {
|
||||
size_t headerSize = sizeof(perf_event_header);
|
||||
if (Read(buf + headerSize, header->size - headerSize)) {
|
||||
size_t speSize = 0;
|
||||
if (header->type == PERF_RECORD_AUXTRACE) {
|
||||
struct PerfRecordAuxtraceData *auxtrace = reinterpret_cast<struct PerfRecordAuxtraceData *>
|
||||
(header + 1);
|
||||
speSize = auxtrace->size;
|
||||
if (speSize > 0) {
|
||||
Read(buf + header->size, auxtrace->size);
|
||||
}
|
||||
}
|
||||
uint8_t *data = buf;
|
||||
std::unique_ptr<PerfEventRecord> record = GetPerfEventRecord(
|
||||
static_cast<perf_event_type>(header->type), data, *GetDefaultAttr());
|
||||
@ -291,7 +300,7 @@ bool PerfFileReader::ReadRecord(ProcessRecordCB &callback)
|
||||
} else {
|
||||
HLOGV("record type %u", record->GetType());
|
||||
}
|
||||
remainingSize -= header->size;
|
||||
remainingSize = remainingSize - header->size - speSize;
|
||||
#ifdef HIPERF_DEBUG_TIME
|
||||
const auto startCallbackTime = steady_clock::now();
|
||||
#endif
|
||||
|
@ -123,14 +123,14 @@ bool PerfFileWriter::WriteRecord(const PerfEventRecord &record)
|
||||
return false;
|
||||
}
|
||||
|
||||
HLOGV("write '%s'", record.GetName().c_str());
|
||||
HLOGV("write '%s', size %zu", record.GetName().c_str(), record.GetSize());
|
||||
|
||||
if (record.GetSize() > RECORD_SIZE_LIMIT) {
|
||||
if (record.GetSize() > RECORD_SIZE_LIMIT_SPE) {
|
||||
HLOGD("%s record size exceed limit", record.GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
// signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xb64eb195
|
||||
static std::vector<u8> buf(RECORD_SIZE_LIMIT);
|
||||
static std::vector<u8> buf(RECORD_SIZE_LIMIT_SPE);
|
||||
|
||||
if (!record.GetBinary(buf)) {
|
||||
return false;
|
||||
@ -162,7 +162,7 @@ bool PerfFileWriter::ReadDataSection(ProcessRecordCB &callback)
|
||||
bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback)
|
||||
{
|
||||
// record size can not exceed 64K
|
||||
HIPERF_BUF_ALIGN uint8_t buf[RECORD_SIZE_LIMIT];
|
||||
HIPERF_BUF_ALIGN static uint8_t buf[RECORD_SIZE_LIMIT_SPE];
|
||||
// diff with reader
|
||||
uint64_t remainingSize = dataSection_.size;
|
||||
size_t recordNumber = 0;
|
||||
@ -175,10 +175,19 @@ bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback)
|
||||
return false;
|
||||
} else {
|
||||
perf_event_header *header = reinterpret_cast<perf_event_header *>(buf);
|
||||
HLOG_ASSERT(header->size < sizeof(buf));
|
||||
HLOG_ASSERT(header->size < RECORD_SIZE_LIMIT);
|
||||
if (remainingSize >= header->size) {
|
||||
size_t headerSize = sizeof(perf_event_header);
|
||||
if (Read(buf + headerSize, header->size - headerSize)) {
|
||||
size_t speSize = 0;
|
||||
if (header->type == PERF_RECORD_AUXTRACE) {
|
||||
struct PerfRecordAuxtraceData *auxtrace = reinterpret_cast<struct PerfRecordAuxtraceData *>
|
||||
(header + 1);
|
||||
speSize = auxtrace->size;
|
||||
if (speSize > 0) {
|
||||
Read(buf + header->size, auxtrace->size);
|
||||
}
|
||||
}
|
||||
uint8_t *data = buf;
|
||||
// the record is allowed from a cache memory, does not free memory after use
|
||||
auto record = GetPerfSampleFromCacheMain(static_cast<perf_event_type>(header->type),
|
||||
@ -187,7 +196,7 @@ bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback)
|
||||
if (record == nullptr) {
|
||||
return true;
|
||||
}
|
||||
remainingSize -= header->size;
|
||||
remainingSize = remainingSize - header->size - speSize;
|
||||
// call callback to process, do not destroy the record
|
||||
callback(std::move(record));
|
||||
recordNumber++;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
#include "ring_buffer.h"
|
||||
#include "perf_event_record.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace Developtools {
|
||||
@ -107,7 +108,13 @@ uint8_t *RingBuffer::GetReadData()
|
||||
if (header == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
readSize_ += header->size;
|
||||
|
||||
if (header->type == PERF_RECORD_AUXTRACE) {
|
||||
struct PerfRecordAuxtraceData *auxtrace = reinterpret_cast<struct PerfRecordAuxtraceData *>(header + 1);
|
||||
readSize_ += header->size + auxtrace->size;
|
||||
} else {
|
||||
readSize_ += header->size;
|
||||
}
|
||||
return buf_.get() + readPos;
|
||||
}
|
||||
|
||||
|
996
src/spe_decoder.cpp
Normal file
996
src/spe_decoder.cpp
Normal file
@ -0,0 +1,996 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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 char *SpePktName(enum SpePktType type)
|
||||
{
|
||||
const char* 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(unsigned char hdr)
|
||||
{
|
||||
return 1U << PERF_SPE_HDR_GET_BYTES_5_4(hdr);
|
||||
}
|
||||
|
||||
static int SpeGetPayload(const unsigned char *buf, size_t len,
|
||||
unsigned char extHdr, struct SpePkt *packet)
|
||||
{
|
||||
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 = *(uint8_t *)buf; break;
|
||||
case LEN_TYPE_HLFWRD: packet->payload = LE16_TO_CPU(*(uint16_t *)buf); break;
|
||||
case LEN_TYPE_WORD: packet->payload = LE32_TO_CPU(*(uint32_t *)buf); break;
|
||||
case LEN_TYPE_DBLEWRD: packet->payload = LE64_TO_CPU(*(uint64_t *)buf); break;
|
||||
default: return PERF_SPE_BAD_PACKET;
|
||||
}
|
||||
|
||||
return 1 + extHdr + payloadLen;
|
||||
}
|
||||
|
||||
static int SpeGetPad(struct SpePkt *packet)
|
||||
{
|
||||
packet->type = PERF_SPE_PAD;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int SpeGetAlignment(const unsigned char *buf, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
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)
|
||||
{
|
||||
packet->type = PERF_SPE_END;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int SpeGetTimestamp(const unsigned char *buf, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
packet->type = PERF_SPE_TIMESTAMP;
|
||||
return SpeGetPayload(buf, len, 0, packet);
|
||||
}
|
||||
|
||||
static int SpeGetEvents(const unsigned char *buf, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
packet->type = PERF_SPE_EVENTS;
|
||||
packet->index = SpePayloadLen(buf[0]);
|
||||
return SpeGetPayload(buf, len, 0, packet);
|
||||
}
|
||||
|
||||
static int SpeGetDataSource(const unsigned char *buf, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
packet->type = PERF_SPE_DATA_SOURCE;
|
||||
return SpeGetPayload(buf, len, 0, packet);
|
||||
}
|
||||
|
||||
static int SpeGetContext(const unsigned char *buf, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
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, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
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, size_t len,
|
||||
const unsigned char extHdr, struct SpePkt *packet)
|
||||
{
|
||||
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, size_t len,
|
||||
const unsigned char extHdr, struct SpePkt *packet)
|
||||
{
|
||||
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, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
unsigned int hdr;
|
||||
unsigned char extHdr = 0;
|
||||
|
||||
memset_s(packet, sizeof(struct SpePkt), 0, sizeof(struct SpePkt));
|
||||
|
||||
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) { /* no timestamp at end of record */
|
||||
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) {
|
||||
/* 16-bit extended format header */
|
||||
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, size_t len,
|
||||
struct SpePkt *packet)
|
||||
{
|
||||
int ret = SpeDoGetPacket(buf, len, packet);
|
||||
/* put multiple consecutive PADs on the same line, up to
|
||||
* 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, ...)
|
||||
{
|
||||
va_list args;
|
||||
int ret;
|
||||
|
||||
/* If any errors occur, exit */
|
||||
if (err && *err) {
|
||||
return *err;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = vsnprintf_s(*bufPtr, *bufLen, *bufLen - 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (ret < 0) {
|
||||
if (err && !*err) {
|
||||
*err = 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)
|
||||
{
|
||||
u64 payload = packet->payload;
|
||||
int err = 0;
|
||||
|
||||
SpePktOutString(&err, &buf, &bufLen, "EV");
|
||||
if (payload & BIT(EVENT_EXCEPTION_GEN)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " EXCEPTION-GEN");
|
||||
}
|
||||
if (payload & BIT(EVENT_RETIRED)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " RETIRED");
|
||||
}
|
||||
if (payload & BIT(EVENT_L1D_ACCESS)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " L1D-ACCESS");
|
||||
}
|
||||
if (payload & BIT(EVENT_L1D_REFILL)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " L1D-REFILL");
|
||||
}
|
||||
if (payload & BIT(EVENT_TLB_ACCESS)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " TLB-ACCESS");
|
||||
}
|
||||
if (payload & BIT(EVENT_TLB_WALK)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " TLB-REFILL");
|
||||
}
|
||||
if (payload & BIT(EVENT_NOT_TAKEN)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " NOT-TAKEN");
|
||||
}
|
||||
if (payload & BIT(EVENT_MISPRED)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " MISPRED");
|
||||
}
|
||||
if (payload & BIT(EVENT_LLC_ACCESS)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " LLC-ACCESS");
|
||||
}
|
||||
if (payload & BIT(EVENT_LLC_MISS)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " LLC-REFILL");
|
||||
}
|
||||
if (payload & BIT(EVENT_REMOTE_ACCESS)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " REMOTE-ACCESS");
|
||||
}
|
||||
if (payload & BIT(EVENT_ALIGNMENT)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " ALIGNMENT");
|
||||
}
|
||||
if (payload & BIT(EVENT_PARTIAL_PREDICATE)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " SVE-PARTIAL-PRED");
|
||||
}
|
||||
if (payload & BIT(EVENT_EMPTY_PREDICATE)) {
|
||||
SpePktOutString(&err, &buf, &bufLen, " SVE-EMPTY-PRED");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int SpePktDescOpType(const struct SpePkt *packet,
|
||||
char *buf, size_t bufLen)
|
||||
{
|
||||
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");
|
||||
|
||||
/* SVE effective vector length */
|
||||
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:
|
||||
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)) {
|
||||
/* SVE effective vector length */
|
||||
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");
|
||||
}
|
||||
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:
|
||||
/* Unknown packet index */
|
||||
err = UN_PRMT;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int SpePktDescAddr(const struct SpePkt *packet,
|
||||
char *buf, size_t bufLen)
|
||||
{
|
||||
int ns, el, idx = packet->index;
|
||||
int ch, 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:
|
||||
/* Unknown packet index */
|
||||
err = UN_PRMT;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int SpePktDesCont(const struct SpePkt *packet,
|
||||
char *buf, size_t bufLen)
|
||||
{
|
||||
u64 payload = packet->payload;
|
||||
const char *name = SpePktName(packet->type);
|
||||
int err = 0;
|
||||
|
||||
SpePktOutString(&err, &buf, &bufLen, "%s %d ", name,
|
||||
(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)
|
||||
{
|
||||
int idx = packet->index;
|
||||
unsigned long long payload = packet->payload;
|
||||
const char *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);
|
||||
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 %lld", name, payload);
|
||||
break;
|
||||
case PERF_SPE_ADDRESS:
|
||||
err = SpePktDescAddr(packet, buf, bufLen);
|
||||
break;
|
||||
case PERF_SPE_CONTEXT:
|
||||
SpePktOutString(&err, &buf, &blen, "%s 0x%lx el%d",
|
||||
name, (unsigned long)payload, idx + 1);
|
||||
break;
|
||||
case PERF_SPE_COUNTER:
|
||||
err = SpePktDesCont(packet, buf, bufLen);
|
||||
break;
|
||||
default:
|
||||
/* Unknown packet type */
|
||||
err = UN_PRMT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If any errors are detected, the raw data is output*/
|
||||
if (err) {
|
||||
err = 0;
|
||||
SpePktOutString(&err, &bufOrig, &bufLen, "%s 0x%llx (%d)",
|
||||
name, payload, packet->index);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static u64 SpeCalcIp(int index, u64 payload)
|
||||
{
|
||||
u64 ns, el, val;
|
||||
|
||||
/* Instruction virtual address or Branch target address */
|
||||
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);
|
||||
|
||||
/* Clean highest byte */
|
||||
payload = PERF_SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
|
||||
|
||||
/* Fill highest byte for EL1 or EL2 (VHE) mode */
|
||||
if (ns && (el == PERF_SPE_ADDR_PKT_EL1 || el == PERF_SPE_ADDR_PKT_EL2)) {
|
||||
payload |= 0xffULL << PERF_SPE_ADDR_PKT_ADDR_BYTE7_SHIFT;
|
||||
}
|
||||
|
||||
/* Data access virtual address */
|
||||
} else if (index == PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT) {
|
||||
/* Clean tags */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Data access physical address */
|
||||
} else if (index == PERF_SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS) {
|
||||
/* Clean highest byte */
|
||||
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;
|
||||
}
|
||||
|
||||
struct SpeDecoder *SpeDecoderNew(struct SpeParams *params)
|
||||
{
|
||||
struct SpeDecoder *decoder;
|
||||
|
||||
decoder = (struct SpeDecoder*)malloc(sizeof(struct SpeDecoder));
|
||||
if (!decoder) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decoder->data = params->data;
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
void SpeDecoderFree(struct SpeDecoder *decoder)
|
||||
{
|
||||
free(decoder);
|
||||
}
|
||||
|
||||
static int SpeGetNextPacket(struct SpeDecoder *decoder)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
do {
|
||||
if (!decoder->len) {
|
||||
/* Failed to read out trace data */
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = SpeGetPacket(decoder->buf, decoder->len,
|
||||
&decoder->packet);
|
||||
if (ret <= 0) {
|
||||
/* Move forward for 1 byte */
|
||||
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 int SpeReadRecord(struct SpeDecoder *decoder)
|
||||
{
|
||||
int err;
|
||||
int idx;
|
||||
u64 payload, ip;
|
||||
|
||||
memset_s(&decoder->record, sizeof(decoder->record), 0, sizeof(decoder->record));
|
||||
decoder->record.context_id = (u64)-1;
|
||||
|
||||
while (1) {
|
||||
err = SpeGetNextPacket(decoder);
|
||||
if (err <= 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
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:
|
||||
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;
|
||||
}
|
||||
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:
|
||||
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!");
|
||||
}
|
||||
break;
|
||||
case PERF_SPE_EVENTS:
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
return SpeReadRecord(decoder);
|
||||
}
|
||||
|
||||
struct SpeDecoder *SpeDecoderDataNew(const unsigned char *speBuf, size_t speLen)
|
||||
{
|
||||
struct SpeDecoder *decoder;
|
||||
|
||||
decoder = reinterpret_cast<SpeDecoder *>(malloc(sizeof(struct SpeDecoder)));
|
||||
if (!decoder) {
|
||||
return NULL;
|
||||
}
|
||||
memset_s(decoder, sizeof(struct SpeDecoder), 0, sizeof(struct SpeDecoder));
|
||||
|
||||
decoder->buf = speBuf;
|
||||
decoder->len = speLen;
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
void SpeDumpRawData(unsigned char *buf, size_t len, int indent, FILE *outputDump)
|
||||
{
|
||||
if (outputDump != nullptr) {
|
||||
g_outputDump = outputDump;
|
||||
}
|
||||
struct SpePkt packet;
|
||||
size_t pos = 0;
|
||||
int ret, pktLen, i;
|
||||
char desc[PERF_SPE_PKT_DESC_MAX];
|
||||
|
||||
PRINT_INDENT(indent, ". ... ARM SPE data: size %#zx bytes\n",
|
||||
len);
|
||||
|
||||
while (len) {
|
||||
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++) { // 16 : space
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(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(int indent, uint32_t type, 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);
|
||||
|
||||
// head print
|
||||
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(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);
|
||||
for (auto& it2 : it->second) {
|
||||
PRINT_INDENT(indent + 1, "%*.2f%% ", FULL_PERCENTAGE_LEN, it2.second.heating);
|
||||
PRINT_INDENT(indent + 1, "%-*llu ", FULL_PERCENTAGE_LEN, it2.second.count);
|
||||
PRINT_INDENT(indent + 1, "%-*s ", SPE_PERCENTAGE_COMM_LEN, it2.second.comm.c_str());
|
||||
PRINT_INDENT(indent + 1, "0x%-*llx ", SPE_PERCENTAGE_PC_LEN, it2.second.pc);
|
||||
PRINT_INDENT(indent + 1, "%-*s ", SPE_PERCENTAGE_DSO_LEN, it2.second.SharedObject.c_str());
|
||||
PRINT_INDENT(indent + 1, "%-*s", SPE_PERCENTAGE_FUNC_LEN, it2.second.Symbol.c_str());
|
||||
PRINT_INDENT(indent + 1, "0x%llx\n", it2.second.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace HiPerf
|
||||
} // namespace Developtools
|
||||
} // namespace OHOS
|
@ -27,6 +27,7 @@
|
||||
#include "perf_event_record.h"
|
||||
#include "perf_events.h"
|
||||
#include "register.h"
|
||||
#include "spe_decoder.h"
|
||||
#include "symbols_file.h"
|
||||
#include "utilities.h"
|
||||
#include "virtual_runtime.h"
|
||||
@ -182,6 +183,7 @@ bool SubCommandDump::OnSubCommand(std::vector<std::string> &args)
|
||||
// before load data section
|
||||
SetHM();
|
||||
DumpDataPortion(indent_);
|
||||
DumpSpeReport();
|
||||
}
|
||||
|
||||
if (dumpFeatures_ || dumpAll_) {
|
||||
@ -412,7 +414,7 @@ void SubCommandDump::ExprotUserData(std::unique_ptr<PerfEventRecord> &record)
|
||||
recordSample->data_.tid, exportSampleIndex_, recordSample->data_.time);
|
||||
std::string resolvedPath = CanonicalizeSpecPath(userData.c_str());
|
||||
std::unique_ptr<FILE, decltype(&fclose)> fpUserData(fopen(resolvedPath.c_str(), "wb"), fclose);
|
||||
std::vector<u8> buf(RECORD_SIZE_LIMIT);
|
||||
static std::vector<u8> buf(RECORD_SIZE_LIMIT);
|
||||
if (!recordSample->GetBinary(buf)) {
|
||||
HLOGE("export user sample data failed");
|
||||
return;
|
||||
@ -601,6 +603,19 @@ void SubCommandDump::SetHM()
|
||||
vr_.SetDevhostPid(devhost);
|
||||
}
|
||||
}
|
||||
|
||||
void SubCommandDump::DumpSpeReport()
|
||||
{
|
||||
#if defined(is_ohos) && is_ohos
|
||||
std::string cmdline = reader_->GetFeatureString(FEATURE::CMDLINE);
|
||||
if (cmdline.find("-e arm_spe_0") != std::string::npos) {
|
||||
HLOGD("dump spe report data");
|
||||
UpdateHeating();
|
||||
DumpSpeReportData(indent_, g_outputDump);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace HiPerf
|
||||
} // namespace Developtools
|
||||
} // namespace OHOS
|
||||
|
@ -156,6 +156,45 @@ void SubCommandRecord::DumpOptions() const
|
||||
printf(" report_:\t%s\n", report_ ? "true" : "false");
|
||||
}
|
||||
|
||||
bool SubCommandRecord::GetSpeOptions()
|
||||
{
|
||||
std::string speName;
|
||||
|
||||
for (size_t i = 0; i < selectEvents_.size(); i++) {
|
||||
std::string optionValue = selectEvents_[i];
|
||||
|
||||
std::vector<std::string> valueExpressions = StringSplit(optionValue, "/");
|
||||
if (i == 0) {
|
||||
if (valueExpressions.size() > 1) {
|
||||
speName = valueExpressions[0];
|
||||
valueExpressions.erase(valueExpressions.begin());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto item: valueExpressions) {
|
||||
std::vector<std::string> expressions = StringSplit(item, "=");
|
||||
size_t itemNum = 2;
|
||||
if (expressions.size() == itemNum) {
|
||||
std::string name = expressions[0];
|
||||
unsigned long long num = std::stoull(expressions[1]);
|
||||
if (speOptMap_.find(name) != speOptMap_.end()) {
|
||||
speOptMap_[name] = num;
|
||||
}
|
||||
if (num != 0) {
|
||||
speOptions_.emplace_back(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (speName.size() > 0) {
|
||||
selectEvents_.clear();
|
||||
selectEvents_.emplace_back(speName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
|
||||
{
|
||||
if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
|
||||
@ -235,6 +274,9 @@ bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
|
||||
if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
|
||||
return false;
|
||||
}
|
||||
if (!GetSpeOptions()) {
|
||||
return false;
|
||||
}
|
||||
if (!Option::GetOptionValue(args, "-s", callStackType_)) {
|
||||
return false;
|
||||
}
|
||||
@ -779,6 +821,13 @@ bool SubCommandRecord::PreparePerfEvent()
|
||||
};
|
||||
perfEvents_.SetRecordCallBack(processRecord);
|
||||
|
||||
if (selectEvents_.size() > 0 && selectEvents_[0] == "arm_spe_0") {
|
||||
selectEvents_.insert(selectEvents_.begin(), "sw-dummy");
|
||||
perfEvents_.isSpe_ = true;
|
||||
perfEvents_.SetConfig(speOptMap_);
|
||||
isSpe_ = true;
|
||||
}
|
||||
|
||||
perfEvents_.SetCpu(selectCpus_);
|
||||
perfEvents_.SetPid(selectPids_); // Tids has insert Pids in CheckTargetProcessOptions()
|
||||
|
||||
@ -926,6 +975,13 @@ void SubCommandRecord::WriteCommEventBeforeSampling()
|
||||
virtualRuntime_.GetThread(it->first, tid);
|
||||
}
|
||||
}
|
||||
if (mapPids_.empty()) {
|
||||
if (!selectPids_.empty()) {
|
||||
for (auto pid : selectPids_) {
|
||||
virtualRuntime_.GetThread(pid, pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SubCommandRecord::ClientCommandResponse(bool OK)
|
||||
@ -1650,6 +1706,12 @@ bool SubCommandRecord::CollectionSymbol(std::unique_ptr<PerfEventRecord> record)
|
||||
// the record is allowed from a cache memory, does not free memory after use
|
||||
record.release();
|
||||
}
|
||||
|
||||
if (isSpe_ && record->GetType() == PERF_RECORD_AUXTRACE) {
|
||||
PerfRecordAuxtrace *sample = static_cast<PerfRecordAuxtrace *>(record.get());
|
||||
virtualRuntime_.SymbolSpeRecord(*sample);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -769,7 +769,7 @@ bool SubCommandStat::CheckOptions(const std::vector<pid_t> &pids)
|
||||
return false;
|
||||
}
|
||||
if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty()
|
||||
&& selectTids_.empty() ) {
|
||||
&& selectTids_.empty()) {
|
||||
printf("You need to set the -p option or --app option.\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "dfx_map.h"
|
||||
#include "register.h"
|
||||
#include "spe_decoder.h"
|
||||
#include "symbols_file.h"
|
||||
#include "utilities.h"
|
||||
|
||||
@ -460,6 +461,12 @@ void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record)
|
||||
UpdateFromRecord(*recordComm);
|
||||
#ifdef HIPERF_DEBUG_TIME
|
||||
processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
|
||||
#endif
|
||||
} else if (record.GetType() == PERF_RECORD_AUXTRACE) {
|
||||
auto recordAuxTrace = static_cast<PerfRecordAuxtrace *>(&record);
|
||||
UpdateFromRecord(*recordAuxTrace);
|
||||
#ifdef HIPERF_DEBUG_TIME
|
||||
processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
|
||||
#endif
|
||||
} else {
|
||||
HLOGW("skip record type %d", record.GetType());
|
||||
@ -793,6 +800,73 @@ void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
|
||||
UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
|
||||
}
|
||||
|
||||
void VirtualRuntime::UpdateFromRecord(PerfRecordAuxtrace &recordAuxTrace)
|
||||
{
|
||||
if (recordCallBack_ == nullptr) {
|
||||
#if defined(is_ohos) && is_ohos
|
||||
recordAuxTrace.DumpLog(__FUNCTION__);
|
||||
SpeDecoder *decoder = SpeDecoderDataNew(recordAuxTrace.rawData_, recordAuxTrace.data_.size);
|
||||
std::vector<SpeRecord> records;
|
||||
while (true) {
|
||||
int ret = SpeDecode(decoder);
|
||||
if (ret <= 0) {
|
||||
break;
|
||||
}
|
||||
struct SpeRecord record = SpeRecord(decoder->record);
|
||||
records.emplace_back(record);
|
||||
}
|
||||
std::vector<ReportItemAuxRawData> auxRawData;
|
||||
for (auto rec: records) {
|
||||
u64 pc = 0;
|
||||
if (rec.from_ip) {
|
||||
pc = rec.from_ip;
|
||||
} else if (rec.to_ip) {
|
||||
pc = rec.to_ip;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
DfxSymbol symbol = GetSymbol(pc, recordAuxTrace.data_.reserved__, recordAuxTrace.data_.tid);
|
||||
HLOGV("pc 0x%llx symbol %s", pc, symbol.ToDebugString().c_str());
|
||||
struct ReportItemAuxRawData reportItem = {rec.type, 0.0f, 1, symbol.comm_.data(), pc,
|
||||
symbol.module_.data(), symbol.GetName().data(),
|
||||
symbol.fileVaddr_};
|
||||
auxRawData.emplace_back(reportItem);
|
||||
HLOGV("type %u, from_ip: 0x%llx, to_ip: 0x%llx, timestamp: %llu, virt_addr: 0x%llx, phys_addr: 0x%llx",
|
||||
rec.type, rec.from_ip, rec.to_ip, rec.timestamp, rec.virt_addr, rec.phys_addr);
|
||||
}
|
||||
AddReportItems(auxRawData);
|
||||
SpeDecoderFree(decoder);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualRuntime::SymbolSpeRecord(PerfRecordAuxtrace &recordAuxTrace)
|
||||
{
|
||||
#if defined(is_ohos) && is_ohos
|
||||
recordAuxTrace.DumpLog(__FUNCTION__);
|
||||
SpeDecoder *decoder = SpeDecoderDataNew(recordAuxTrace.rawData_, recordAuxTrace.data_.size);
|
||||
while (true) {
|
||||
int ret = SpeDecode(decoder);
|
||||
if (ret <= 0) {
|
||||
break;
|
||||
}
|
||||
struct SpeRecord record = SpeRecord(decoder->record);
|
||||
u64 pc = 0;
|
||||
if (record.from_ip) {
|
||||
pc = record.from_ip;
|
||||
} else if (record.to_ip) {
|
||||
pc = record.to_ip;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
DfxSymbol symbol = GetSymbol(pc, recordAuxTrace.data_.reserved__, recordAuxTrace.data_.tid);
|
||||
HLOGV("pc 0x%llx symbol %s", pc, symbol.ToDebugString().c_str());
|
||||
}
|
||||
SpeDecoderFree(decoder);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
|
||||
{
|
||||
recordCallBack_ = recordCallBack;
|
||||
|
@ -49,7 +49,8 @@ static const std::string TEST_FILE_ELF_STRIPPED_NOEFHDR = "elf_test_stripped_noe
|
||||
#endif
|
||||
|
||||
// vmlinux
|
||||
static const std::string TEST_FILE_VMLINUX = "vmlinux";
|
||||
static const std::string
|
||||
TEST_FILE_VMLINUX = "vmlinux";
|
||||
static const std::string TEST_FILE_VMLINUX_STRIPPED = "vmlinux_stripped";
|
||||
static const std::string TEST_FILE_VMLINUX_STRIPPED_NOBUILDID = "vmlinux_stripped_nobuildid";
|
||||
static const std::string TEST_FILE_VMLINUX_STRIPPED_BROKEN = "vmlinux_stripped_broken";
|
||||
|
Loading…
x
Reference in New Issue
Block a user