feat: L0-L1 支持Perf
1.【需求描述】:
L0-L1 支持Perf,提供2种模式的配置, 及3大类型的事件配置:
2种模式:计数模式(仅统计事件发生次数)、采样模式(收集上下文如任务ID、pc、backtrace等)。
3种事件类型:CPU硬件事件(cycle、branch、icache、dcache等)、OS软件事件(task switch、mux pend、irq等)、高精度周期事件(cpu clock)。
2.【方案描述】:
L0:
基于事件采样原理,以性能事件为基础,当事件发生时,相应的事件计数器溢出发生中断,在中断处理函数中记录事件信息,包括当前的pc、当前运 行的任务ID以及调用栈等信息。
L1:
新增perf字符设备,位于“dev/perf”,通过对设备节点的read\ioctl,实现用户态perf
BREAKING CHANGE:
1.新增一系列perf的对外API,位于los_perf.h中.
LOS_PerfInit配置采样数据缓冲区
LOS_PerfStart开启Perf采样
LOS_PerfStop停止Perf采样
LOS_PerfConfig配置Perf采样事件
LOS_PerfDataRead读取采样数据
LOS_PerfNotifyHookReg 注册采样数据缓冲区的钩子函数
LOS_PerfFlushHookReg 注册缓冲区刷cache的钩子
2. 用户态新增perf命令
【Usage】:
./perf [start] /[start id] Start perf.
./perf [stop] Stop perf.
./perf [read nBytes] Read nBytes raw data from perf buffer and print out.
./perf [list] List events to be used in -e.
./perf [stat] or [record] <option> <command>
-e, event selector. use './perf list' to list available events.
-p, event period.
-o, perf data output filename.
-t, taskId filter(whiltelist), if not set perf will sample all tasks.
-s, type of data to sample defined in PerfSampleType los_perf.h.
-P, processId filter(whiltelist), if not set perf will sample all processes.
-d, whether to prescaler (once every 64 counts), which only take effect on cpu cycle hardware event.
Close #I47I9A
Signed-off-by: LiteOS2021 <dinglu@huawei.com>
Change-Id: Ieb9b7483c85d1495df7c55bc0027f4309dff9814
This commit is contained in:
@@ -46,10 +46,10 @@ kernel_module(module_name) {
|
||||
"src/los_hw_runstop.S",
|
||||
"src/los_hw_tick.c",
|
||||
"src/los_hwi.c",
|
||||
"src/smp.c",
|
||||
"src/strncpy_from_user.c",
|
||||
"src/strnlen_user.c",
|
||||
"src/user_copy.c",
|
||||
"src/smp.c",
|
||||
]
|
||||
|
||||
if (LOSCFG_ARCH_ARM_VER == "armv7-a") {
|
||||
@@ -64,6 +64,10 @@ kernel_module(module_name) {
|
||||
|
||||
include_dirs = [ "src/include" ]
|
||||
|
||||
if (defined(LOSCFG_PERF_HW_PMU)) {
|
||||
sources += [ "src/pmu/armv7_pmu.c" ]
|
||||
}
|
||||
|
||||
if (defined(LOSCFG_GDB)) {
|
||||
configs += [ ":as_objs_libc_flags" ]
|
||||
}
|
||||
@@ -82,9 +86,11 @@ config("as_objs_libc_flags") {
|
||||
defines = [ "__ASSEMBLY__" ]
|
||||
|
||||
# linux style macros
|
||||
if (defined(LOSCFG_ARCH_ARM_V7A) || defined(LOSCFG_ARCH_ARM_V7R) || defined(LOSCFG_ARCH_ARM_V7M)) {
|
||||
if (defined(LOSCFG_ARCH_ARM_V7A) || defined(LOSCFG_ARCH_ARM_V7R) ||
|
||||
defined(LOSCFG_ARCH_ARM_V7M)) {
|
||||
defines += [ "__LINUX_ARM_ARCH__=7" ]
|
||||
} else if (defined(LOSCFG_ARCH_ARM_V8A) || defined(LOSCFG_ARCH_ARM_V8R) || defined(LOSCFG_ARCH_ARM_V8M)) {
|
||||
} else if (defined(LOSCFG_ARCH_ARM_V8A) || defined(LOSCFG_ARCH_ARM_V8R) ||
|
||||
defined(LOSCFG_ARCH_ARM_V8M)) {
|
||||
defines += [ "__LINUX_ARM_ARCH__=8" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ else
|
||||
LOCAL_SRCS += src/startup/reset_vector_up.S
|
||||
endif
|
||||
|
||||
ifeq ($(LOSCFG_PERF_HW_PMU), y)
|
||||
LOCAL_SRCS += src/pmu/armv7_pmu.c
|
||||
endif
|
||||
|
||||
LOCAL_FLAGS := $(LOCAL_INCLUDE)
|
||||
|
||||
AS_OBJS_LIBC_FLAGS = -D__ASSEMBLY__
|
||||
# linux style macros
|
||||
LINUX_ARCH_$(LOSCFG_ARCH_ARM_V7A) = -D__LINUX_ARM_ARCH__=7
|
||||
@@ -54,6 +60,6 @@ LINUX_ARCH_$(LOSCFG_ARCH_ARM_V8M) = -D__LINUX_ARM_ARCH__=8
|
||||
AS_OBJS_LIBC_FLAGS += $(LINUX_ARCH_y)
|
||||
|
||||
ifeq ($(LOSCFG_GDB), y)
|
||||
LOCAL_FLAGS := $(AS_OBJS_LIBC_FLAGS)
|
||||
LOCAL_FLAGS += $(AS_OBJS_LIBC_FLAGS)
|
||||
endif
|
||||
include $(MODULE)
|
||||
|
||||
108
arch/arm/arm/src/include/armv7_pmu_pri.h
Normal file
108
arch/arm/arm/src/include/armv7_pmu_pri.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
||||
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _ARMV7_PMU_PRI_H
|
||||
#define _ARMV7_PMU_PRI_H
|
||||
|
||||
#include "los_typedef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* counters overflow flag status reg */
|
||||
#define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */
|
||||
#define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK /* Mask for pmu overflowed */
|
||||
|
||||
/* pmnc config reg */
|
||||
#define ARMV7_PMNC_E (1U << 0) /* Enable all counters */
|
||||
#define ARMV7_PMNC_P (1U << 1) /* Reset all counters */
|
||||
#define ARMV7_PMNC_C (1U << 2) /* Cycle counter reset */
|
||||
#define ARMV7_PMNC_D (1U << 3) /* CCNT counts every 64th cpu cycle */
|
||||
#define ARMV7_PMNC_X (1U << 4) /* Export to ETM */
|
||||
#define ARMV7_PMNC_DP (1U << 5) /* Disable CCNT if non-invasive debug */
|
||||
#define ARMV7_PMNC_MASK 0x3f /* Mask for writable bits */
|
||||
|
||||
/* pmxevtyper event selection reg */
|
||||
#define ARMV7_EVTYPE_MASK 0xc80000ff /* Mask for writable bits */
|
||||
|
||||
/* armv7 counters index */
|
||||
#define ARMV7_IDX_COUNTER0 1
|
||||
#define ARMV7_IDX_CYCLE_COUNTER 0
|
||||
#define ARMV7_IDX_MAX_COUNTER 9
|
||||
|
||||
#define ARMV7_MAX_COUNTERS 32
|
||||
#define ARMV7_IDX_COUNTER_LAST (ARMV7_IDX_CYCLE_COUNTER + ARMV7_MAX_COUNTERS - 1)
|
||||
#define ARMV7_COUNTER_MASK (ARMV7_MAX_COUNTERS - 1)
|
||||
|
||||
/* armv7 event counter index mapping */
|
||||
#define ARMV7_CNT2BIT(x) (1UL << (x))
|
||||
#define ARMV7_IDX2CNT(x) (((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK)
|
||||
|
||||
enum PmuEventType {
|
||||
ARMV7_PERF_HW_CYCLES = 0xFF, /* cycles */
|
||||
ARMV7_PERF_HW_INSTRUCTIONS = 0x08, /* instructions */
|
||||
ARMV7_PERF_HW_DCACHES = 0x04, /* dcache */
|
||||
ARMV7_PERF_HW_DCACHE_MISSES = 0x03, /* dcache-misses */
|
||||
ARMV7_PERF_HW_ICACHES = 0x14, /* icache */
|
||||
ARMV7_PERF_HW_ICACHE_MISSES = 0x01, /* icache-misses */
|
||||
ARMV7_PERF_HW_BRANCHES = 0x0C, /* software change of pc */
|
||||
ARMV7_PERF_HW_BRANCE_MISSES = 0x10, /* branch-misses */
|
||||
ARMV7_PERF_HW_PRED_BRANCH = 0x12, /* predictable branches */
|
||||
ARMV7_PERF_HW_NUM_CYC_IRQ = 0x50, /* number of cycles Irqs are interrupted */
|
||||
ARMV7_PERF_HW_EXC_TAKEN = 0x09, /* exception_taken */
|
||||
ARMV7_PERF_HW_DATA_READ = 0x06, /* data read */
|
||||
ARMV7_PERF_HW_DATA_WRITE = 0x07, /* data write */
|
||||
ARMV7_PERF_HW_STREX_PASSED = 0x80, /* strex passed */
|
||||
ARMV7_PERF_HW_STREX_FAILED = 0x81, /* strex failed */
|
||||
ARMV7_PERF_HW_LP_IN_TCM = 0x82, /* literal pool in TCM region */
|
||||
ARMV7_PERF_HW_DMB_STALL = 0x90, /* DMB stall */
|
||||
ARMV7_PERF_HW_ITCM_ACCESS = 0x91, /* ITCM access */
|
||||
ARMV7_PERF_HW_DTCM_ACCESS = 0x92, /* DTCM access */
|
||||
ARMV7_PERF_HW_DATA_EVICTION = 0x93, /* data eviction */
|
||||
ARMV7_PERF_HW_SCU = 0x94, /* SCU coherency operation */
|
||||
ARMV7_PERF_HW_INSCACHE_DEP_DW = 0x95, /* instruction cache dependent stall */
|
||||
ARMV7_PERF_HW_DATA_CACHE_DEP_STALL = 0x96, /* data cache dependent stall */
|
||||
ARMV7_PERF_HW_NOCACHE_NO_PER_DEP_STALL = 0x97, /* non-cacheable no peripheral dependent stall */
|
||||
ARMV7_PERF_HW_NOCACHE_PER_DEP_STALL = 0x98, /* non-Cacheable peripheral dependent stall */
|
||||
ARMV7_PERF_HW_DATA_CACHE_HP_DEP_STALL = 0x99, /* data cache high priority dependent stall */
|
||||
ARMV7_PERF_HW_AXI_FAST_PERIPHERAL = 0x9A, /* Accesses_to_AXI_fast_peripheral_port(reads_and_writes) */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* _ARMV7_PMU_PRI_H */
|
||||
@@ -44,10 +44,24 @@ extern "C" {
|
||||
#define OS_SYSTEM_EXC_CURR_CPU 1
|
||||
#define OS_SYSTEM_EXC_OTHER_CPU 2
|
||||
|
||||
#define REGION_PATH_MAX 32
|
||||
|
||||
typedef struct {
|
||||
#ifdef LOSCFG_KERNEL_VM
|
||||
UINTPTR ip;
|
||||
UINT32 len; /* f_path length */
|
||||
CHAR f_path[REGION_PATH_MAX];
|
||||
#else
|
||||
UINTPTR ip;
|
||||
#endif
|
||||
} IpInfo;
|
||||
|
||||
extern UINT32 OsGetSystemStatus(VOID);
|
||||
extern VOID BackTraceSub(UINTPTR regFP);
|
||||
extern VOID OsExcInit(VOID);
|
||||
extern BOOL OsSystemExcIsReset(VOID);
|
||||
extern UINT32 BackTraceGet(UINTPTR regFP, IpInfo *callChain, UINT32 maxDepth);
|
||||
extern BOOL OsGetUsrIpInfo(UINTPTR ip, IpInfo *info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus
|
||||
|
||||
@@ -147,6 +147,14 @@ OsIrqHandler:
|
||||
/* disable irq, switch to svc mode */
|
||||
CPSID i, #0x13
|
||||
|
||||
#ifdef LOSCFG_KERNEL_PERF
|
||||
PUSH {R0-R3, R12, LR}
|
||||
MOV R0, LR
|
||||
MOV R1, FP
|
||||
BL OsPerfSetIrqRegs
|
||||
POP {R0-R3, R12, LR}
|
||||
#endif
|
||||
|
||||
STMFD SP!, {R0-R3, R12, LR}
|
||||
STMFD SP, {R13, R14}^
|
||||
SUB SP, SP, #(4 * 4)
|
||||
|
||||
@@ -690,20 +690,66 @@ FOUND:
|
||||
return found;
|
||||
}
|
||||
|
||||
VOID BackTraceSub(UINTPTR regFP)
|
||||
BOOL OsGetUsrIpInfo(UINTPTR ip, IpInfo *info)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
#ifdef LOSCFG_KERNEL_VM
|
||||
BOOL ret = FALSE;
|
||||
const CHAR *name = NULL;
|
||||
LosVmMapRegion *region = NULL;
|
||||
LosProcessCB *runProcess = OsCurrProcessGet();
|
||||
|
||||
if (LOS_IsUserAddress((VADDR_T)ip) == FALSE) {
|
||||
info->ip = ip;
|
||||
name = "kernel";
|
||||
ret = FALSE;
|
||||
goto END;
|
||||
}
|
||||
|
||||
region = LOS_RegionFind(runProcess->vmSpace, (VADDR_T)ip);
|
||||
if (region == NULL) {
|
||||
info->ip = ip;
|
||||
name = "invalid";
|
||||
ret = FALSE;
|
||||
goto END;
|
||||
}
|
||||
|
||||
info->ip = ip - OsGetTextRegionBase(region, runProcess);
|
||||
name = OsGetRegionNameOrFilePath(region);
|
||||
ret = TRUE;
|
||||
if (strcmp(name, "/lib/libc.so") != 0) {
|
||||
PRINT_ERR("ip = 0x%x, %s\n", info->ip, name);
|
||||
}
|
||||
END:
|
||||
info->len = strlen(name);
|
||||
if (strncpy_s(info->f_path, REGION_PATH_MAX, name, REGION_PATH_MAX - 1) != EOK) {
|
||||
info->f_path[0] = '\0';
|
||||
info->len = 0;
|
||||
PRINT_ERR("copy f_path failed, %s\n", name);
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
info->ip = ip;
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
UINT32 BackTraceGet(UINTPTR regFP, IpInfo *callChain, UINT32 maxDepth)
|
||||
{
|
||||
UINTPTR tmpFP, backLR;
|
||||
UINTPTR stackStart, stackEnd;
|
||||
UINTPTR backFP = regFP;
|
||||
UINT32 count = 0;
|
||||
BOOL ret;
|
||||
VADDR_T kvaddr;
|
||||
#ifdef LOSCFG_KERNEL_VM
|
||||
LosProcessCB *runProcess = OsCurrProcessGet();
|
||||
#endif
|
||||
|
||||
if (FindSuitableStack(regFP, &stackStart, &stackEnd, &kvaddr) == FALSE) {
|
||||
PrintExcInfo("traceback error fp = 0x%x\n", regFP);
|
||||
return;
|
||||
if (callChain == NULL) {
|
||||
PrintExcInfo("traceback error fp = 0x%x\n", regFP);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -715,7 +761,9 @@ VOID BackTraceSub(UINTPTR regFP)
|
||||
tmpFP = *(UINTPTR *)(UINTPTR)kvaddr;
|
||||
if (IsValidFP(tmpFP, stackStart, stackEnd, NULL) == TRUE) {
|
||||
backFP = tmpFP;
|
||||
PrintExcInfo("traceback fp fixed, trace using fp = 0x%x\n", backFP);
|
||||
if (callChain == NULL) {
|
||||
PrintExcInfo("traceback fp fixed, trace using fp = 0x%x\n", backFP);
|
||||
}
|
||||
}
|
||||
|
||||
while (IsValidFP(backFP, stackStart, stackEnd, &kvaddr) == TRUE) {
|
||||
@@ -723,38 +771,49 @@ VOID BackTraceSub(UINTPTR regFP)
|
||||
#ifdef LOSCFG_COMPILER_CLANG_LLVM
|
||||
backFP = *(UINTPTR *)(UINTPTR)kvaddr;
|
||||
if (IsValidFP(tmpFP + POINTER_SIZE, stackStart, stackEnd, &kvaddr) == FALSE) {
|
||||
PrintExcInfo("traceback backLR check failed, backLP: 0x%x\n", tmpFP + POINTER_SIZE);
|
||||
return;
|
||||
if (callChain == NULL) {
|
||||
PrintExcInfo("traceback backLR check failed, backLP: 0x%x\n", tmpFP + POINTER_SIZE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
backLR = *(UINTPTR *)(UINTPTR)kvaddr;
|
||||
#else
|
||||
backLR = *(UINTPTR *)(UINTPTR)kvaddr;
|
||||
if (IsValidFP(tmpFP - POINTER_SIZE, stackStart, stackEnd, &kvaddr) == FALSE) {
|
||||
PrintExcInfo("traceback backFP check failed, backFP: 0x%x\n", tmpFP - POINTER_SIZE);
|
||||
return;
|
||||
if (callChain == NULL) {
|
||||
PrintExcInfo("traceback backFP check failed, backFP: 0x%x\n", tmpFP - POINTER_SIZE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
backFP = *(UINTPTR *)(UINTPTR)kvaddr;
|
||||
#endif
|
||||
IpInfo info = {0};
|
||||
ret = OsGetUsrIpInfo((VADDR_T)backLR, &info);
|
||||
if (callChain == NULL) {
|
||||
PrintExcInfo("traceback %u -- lr = 0x%x fp = 0x%x ", count, backLR, backFP);
|
||||
if (ret) {
|
||||
#ifdef LOSCFG_KERNEL_VM
|
||||
LosVmMapRegion *region = NULL;
|
||||
if (LOS_IsUserAddress((VADDR_T)backLR) == TRUE) {
|
||||
region = LOS_RegionFind(runProcess->vmSpace, (VADDR_T)backLR);
|
||||
}
|
||||
if (region != NULL) {
|
||||
PrintExcInfo("traceback %u -- lr = 0x%x fp = 0x%x lr in %s --> 0x%x\n", count, backLR, backFP,
|
||||
OsGetRegionNameOrFilePath(region),
|
||||
backLR - OsGetTextRegionBase(region, runProcess));
|
||||
region = NULL;
|
||||
} else
|
||||
PrintExcInfo("lr in %s --> 0x%x\n", info.f_path, info.ip);
|
||||
#else
|
||||
PrintExcInfo("\n");
|
||||
#endif
|
||||
{
|
||||
PrintExcInfo("traceback %u -- lr = 0x%x fp = 0x%x\n", count, backLR, backFP);
|
||||
} else {
|
||||
PrintExcInfo("\n");
|
||||
}
|
||||
} else {
|
||||
(VOID)memcpy_s(&callChain[count], sizeof(IpInfo), &info, sizeof(IpInfo));
|
||||
}
|
||||
count++;
|
||||
if ((count == OS_MAX_BACKTRACE) || (backFP == tmpFP)) {
|
||||
if ((count == maxDepth) || (backFP == tmpFP)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
VOID BackTraceSub(UINTPTR regFP)
|
||||
{
|
||||
(VOID)BackTraceGet(regFP, NULL, OS_MAX_BACKTRACE);
|
||||
}
|
||||
|
||||
VOID BackTrace(UINT32 regFP)
|
||||
|
||||
373
arch/arm/arm/src/pmu/armv7_pmu.c
Normal file
373
arch/arm/arm/src/pmu/armv7_pmu.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
||||
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "armv7_pmu_pri.h"
|
||||
#include "perf_pmu_pri.h"
|
||||
#include "los_hw_cpu.h"
|
||||
#include "asm/platform.h"
|
||||
|
||||
OS_PMU_INTS(LOSCFG_KERNEL_CORE_NUM, g_pmuIrqNr);
|
||||
STATIC HwPmu g_armv7Pmu;
|
||||
|
||||
STATIC INLINE UINT32 Armv7PmncRead(VOID)
|
||||
{
|
||||
UINT32 value = 0;
|
||||
__asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7PmncWrite(UINT32 value)
|
||||
{
|
||||
value &= ARMV7_PMNC_MASK;
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(value));
|
||||
ISB;
|
||||
}
|
||||
|
||||
STATIC INLINE UINT32 Armv7PmuOverflowed(UINT32 pmnc)
|
||||
{
|
||||
return pmnc & ARMV7_OVERFLOWED_MASK;
|
||||
}
|
||||
|
||||
STATIC INLINE UINT32 Armv7PmuCntOverflowed(UINT32 pmnc, UINT32 index)
|
||||
{
|
||||
return pmnc & ARMV7_CNT2BIT(ARMV7_IDX2CNT(index));
|
||||
}
|
||||
|
||||
STATIC INLINE UINT32 Armv7CntValid(UINT32 index)
|
||||
{
|
||||
return index <= ARMV7_IDX_COUNTER_LAST;
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7PmuSelCnt(UINT32 index)
|
||||
{
|
||||
UINT32 counter = ARMV7_IDX2CNT(index);
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
|
||||
ISB;
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7PmuSetCntPeriod(UINT32 index, UINT32 period)
|
||||
{
|
||||
if (!Armv7CntValid(index)) {
|
||||
PRINT_ERR("CPU writing wrong counter %u\n", index);
|
||||
} else if (index == ARMV7_IDX_CYCLE_COUNTER) {
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (period));
|
||||
} else {
|
||||
Armv7PmuSelCnt(index);
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (period));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7BindEvt2Cnt(UINT32 index, UINT32 value)
|
||||
{
|
||||
PRINT_DEBUG("bind event: %u to counter: %u\n", value, index);
|
||||
Armv7PmuSelCnt(index);
|
||||
value &= ARMV7_EVTYPE_MASK;
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (value));
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7EnableCnt(UINT32 index)
|
||||
{
|
||||
UINT32 counter = ARMV7_IDX2CNT(index);
|
||||
PRINT_DEBUG("index : %u, counter: %u\n", index, counter);
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (ARMV7_CNT2BIT(counter)));
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7DisableCnt(UINT32 index)
|
||||
{
|
||||
UINT32 counter = ARMV7_IDX2CNT(index);
|
||||
PRINT_DEBUG("index : %u, counter: %u\n", index, counter);
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (ARMV7_CNT2BIT(counter)));
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7EnableCntInterrupt(UINT32 index)
|
||||
{
|
||||
UINT32 counter = ARMV7_IDX2CNT(index);
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (ARMV7_CNT2BIT(counter)));
|
||||
ISB;
|
||||
}
|
||||
|
||||
STATIC INLINE VOID Armv7DisableCntInterrupt(UINT32 index)
|
||||
{
|
||||
UINT32 counter = ARMV7_IDX2CNT(index);
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (ARMV7_CNT2BIT(counter)));
|
||||
/* Clear the overflow flag in case an interrupt is pending. */
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (ARMV7_CNT2BIT(counter)));
|
||||
ISB;
|
||||
}
|
||||
|
||||
STATIC INLINE UINT32 Armv7PmuGetOverflowStatus(VOID)
|
||||
{
|
||||
UINT32 value;
|
||||
|
||||
__asm__ volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (value));
|
||||
value &= ARMV7_FLAG_MASK;
|
||||
__asm__ volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (value));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
STATIC VOID Armv7EnableEvent(Event *event)
|
||||
{
|
||||
UINT32 cnt = event->counter;
|
||||
|
||||
if (!Armv7CntValid(cnt)) {
|
||||
PRINT_ERR("CPU enabling wrong PMNC counter IRQ enable %u\n", cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->period == 0) {
|
||||
PRINT_INFO("event period value not valid, counter: %u\n", cnt);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Enable counter and interrupt, and set the counter to count
|
||||
* the event that we're interested in.
|
||||
*/
|
||||
UINT32 intSave = LOS_IntLock();
|
||||
|
||||
Armv7DisableCnt(cnt);
|
||||
|
||||
/*
|
||||
* Set event (if destined for PMNx counters)
|
||||
* We only need to set the event for the cycle counter if we
|
||||
* have the ability to perform event filtering.
|
||||
*/
|
||||
if (cnt != ARMV7_IDX_CYCLE_COUNTER) {
|
||||
Armv7BindEvt2Cnt(cnt, event->eventId);
|
||||
}
|
||||
|
||||
/* Enable interrupt for this counter */
|
||||
Armv7EnableCntInterrupt(cnt);
|
||||
Armv7EnableCnt(cnt);
|
||||
LOS_IntRestore(intSave);
|
||||
|
||||
PRINT_DEBUG("enabled event: %u cnt: %u\n", event->eventId, cnt);
|
||||
}
|
||||
|
||||
STATIC VOID Armv7DisableEvent(Event *event)
|
||||
{
|
||||
UINT32 cnt = event->counter;
|
||||
|
||||
if (!Armv7CntValid(cnt)) {
|
||||
PRINT_ERR("CPU enabling wrong PMNC counter IRQ enable %u\n", cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
UINT32 intSave = LOS_IntLock();
|
||||
Armv7DisableCnt(cnt);
|
||||
Armv7DisableCntInterrupt(cnt);
|
||||
LOS_IntRestore(intSave);
|
||||
}
|
||||
|
||||
|
||||
STATIC VOID Armv7StartAllCnt(VOID)
|
||||
{
|
||||
PRINT_DEBUG("starting pmu...\n");
|
||||
|
||||
/* Enable all counters */
|
||||
UINT32 reg = Armv7PmncRead() | ARMV7_PMNC_E;
|
||||
if (g_armv7Pmu.cntDivided) {
|
||||
reg |= ARMV7_PMNC_D;
|
||||
} else {
|
||||
reg &= ~ARMV7_PMNC_D;
|
||||
}
|
||||
|
||||
Armv7PmncWrite(reg);
|
||||
HalIrqUnmask(g_pmuIrqNr[ArchCurrCpuid()]);
|
||||
}
|
||||
|
||||
STATIC VOID Armv7StopAllCnt(VOID)
|
||||
{
|
||||
PRINT_DEBUG("stopping pmu...\n");
|
||||
/* Disable all counters */
|
||||
Armv7PmncWrite(Armv7PmncRead() & ~ARMV7_PMNC_E);
|
||||
|
||||
HalIrqMask(g_pmuIrqNr[ArchCurrCpuid()]);
|
||||
}
|
||||
|
||||
STATIC VOID Armv7ResetAllCnt(VOID)
|
||||
{
|
||||
UINT32 index;
|
||||
|
||||
/* The counter and interrupt enable registers are unknown at reset. */
|
||||
for (index = ARMV7_IDX_CYCLE_COUNTER; index < ARMV7_IDX_MAX_COUNTER; index++) {
|
||||
Armv7DisableCnt(index);
|
||||
Armv7DisableCntInterrupt(index);
|
||||
}
|
||||
|
||||
/* Initialize & Reset PMNC: C and P bits and D bits */
|
||||
UINT32 reg = ARMV7_PMNC_P | ARMV7_PMNC_C | (g_armv7Pmu.cntDivided ? ARMV7_PMNC_D : 0);
|
||||
Armv7PmncWrite(reg);
|
||||
}
|
||||
|
||||
STATIC VOID Armv7SetEventPeriod(Event *event)
|
||||
{
|
||||
if (event->period != 0) {
|
||||
PRINT_INFO("counter: %u, period: 0x%x\n", event->counter, event->period);
|
||||
Armv7PmuSetCntPeriod(event->counter, PERIOD_CALC(event->period));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC UINTPTR Armv7ReadEventCnt(Event *event)
|
||||
{
|
||||
UINT32 value = 0;
|
||||
UINT32 index = event->counter;
|
||||
|
||||
if (!Armv7CntValid(index)) {
|
||||
PRINT_ERR("CPU reading wrong counter %u\n", index);
|
||||
} else if (index == ARMV7_IDX_CYCLE_COUNTER) {
|
||||
__asm__ volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
|
||||
} else {
|
||||
Armv7PmuSelCnt(index);
|
||||
__asm__ volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value));
|
||||
}
|
||||
|
||||
if (value < PERIOD_CALC(event->period)) {
|
||||
if (Armv7PmuCntOverflowed(Armv7PmuGetOverflowStatus(), event->counter)) {
|
||||
value += event->period;
|
||||
}
|
||||
} else {
|
||||
value -= PERIOD_CALC(event->period);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
STATIC const UINT32 g_armv7Map[] = {
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERF_HW_CYCLES,
|
||||
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERF_HW_INSTRUCTIONS,
|
||||
[PERF_COUNT_HW_DCACHE_REFERENCES] = ARMV7_PERF_HW_DCACHES,
|
||||
[PERF_COUNT_HW_DCACHE_MISSES] = ARMV7_PERF_HW_DCACHE_MISSES,
|
||||
[PERF_COUNT_HW_ICACHE_REFERENCES] = ARMV7_PERF_HW_ICACHES,
|
||||
[PERF_COUNT_HW_ICACHE_MISSES] = ARMV7_PERF_HW_ICACHE_MISSES,
|
||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERF_HW_BRANCHES,
|
||||
[PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERF_HW_BRANCE_MISSES,
|
||||
};
|
||||
|
||||
UINT32 Armv7PmuMapEvent(UINT32 eventType, BOOL reverse)
|
||||
{
|
||||
if (!reverse) { /* Common event to armv7 real event */
|
||||
if (eventType < ARRAY_SIZE(g_armv7Map)) {
|
||||
return g_armv7Map[eventType];
|
||||
}
|
||||
return eventType;
|
||||
} else { /* Armv7 real event to common event */
|
||||
UINT32 i;
|
||||
for (i = 0; i < ARRAY_SIZE(g_armv7Map); i++) {
|
||||
if (g_armv7Map[i] == eventType) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return PERF_HW_INVALID_EVENT_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC VOID Armv7PmuIrqHandler(VOID)
|
||||
{
|
||||
UINT32 index;
|
||||
PerfRegs regs;
|
||||
|
||||
PerfEvent *events = &(g_armv7Pmu.pmu.events);
|
||||
UINT32 eventNum = events->nr;
|
||||
|
||||
/* Get and reset the IRQ flags */
|
||||
UINT32 pmnc = Armv7PmuGetOverflowStatus();
|
||||
if (!Armv7PmuOverflowed(pmnc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(VOID)memset_s(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs));
|
||||
OsPerfFetchIrqRegs(®s);
|
||||
|
||||
Armv7StopAllCnt();
|
||||
|
||||
for (index = 0; index < eventNum; index++) {
|
||||
Event *event = &(events->per[index]);
|
||||
/*
|
||||
* We have a single interrupt for all counters. Check that
|
||||
* each counter has overflowed before we process it.
|
||||
*/
|
||||
if (!Armv7PmuCntOverflowed(pmnc, event->counter) || (event->period == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Armv7PmuSetCntPeriod(event->counter, PERIOD_CALC(event->period));
|
||||
|
||||
OsPerfUpdateEventCount(event, event->period);
|
||||
OsPerfHandleOverFlow(event, ®s);
|
||||
}
|
||||
Armv7StartAllCnt();
|
||||
}
|
||||
|
||||
UINT32 OsGetPmuMaxCounter(VOID)
|
||||
{
|
||||
return ARMV7_IDX_MAX_COUNTER;
|
||||
}
|
||||
|
||||
UINT32 OsGetPmuCycleCounter(VOID)
|
||||
{
|
||||
return ARMV7_IDX_CYCLE_COUNTER;
|
||||
}
|
||||
|
||||
UINT32 OsGetPmuCounter0(VOID)
|
||||
{
|
||||
return ARMV7_IDX_COUNTER0;
|
||||
}
|
||||
|
||||
STATIC HwPmu g_armv7Pmu = {
|
||||
.canDivided = TRUE,
|
||||
.enable = Armv7EnableEvent,
|
||||
.disable = Armv7DisableEvent,
|
||||
.start = Armv7StartAllCnt,
|
||||
.stop = Armv7StopAllCnt,
|
||||
.clear = Armv7ResetAllCnt,
|
||||
.setPeriod = Armv7SetEventPeriod,
|
||||
.readCnt = Armv7ReadEventCnt,
|
||||
.mapEvent = Armv7PmuMapEvent,
|
||||
};
|
||||
|
||||
UINT32 OsHwPmuInit(VOID)
|
||||
{
|
||||
UINT32 ret;
|
||||
UINT32 index;
|
||||
|
||||
for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
|
||||
ret = LOS_HwiCreate(g_pmuIrqNr[index], 0, 0, Armv7PmuIrqHandler, 0);
|
||||
if (ret != LOS_OK) {
|
||||
PRINT_ERR("pmu %u irq handler register failed\n", g_pmuIrqNr[index]);
|
||||
return ret;
|
||||
}
|
||||
#ifdef LOSCFG_KERNEL_SMP
|
||||
HalIrqSetAffinity(g_pmuIrqNr[index], CPUID_TO_AFFI_MASK(index));
|
||||
#endif
|
||||
}
|
||||
ret = OsPerfHwInit(&g_armv7Pmu);
|
||||
return ret;
|
||||
}
|
||||
@@ -149,6 +149,9 @@ VOID HalIrqInit(VOID)
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0);
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0);
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpHaltHandler, 0);
|
||||
#ifdef LOSCFG_KERNEL_SMP_CALL
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_FUNC_CALL, 0xa0, 0, OsMpFuncCallHandler, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -401,9 +401,12 @@ VOID HalIrqInit(VOID)
|
||||
|
||||
#ifdef LOSCFG_KERNEL_SMP
|
||||
/* register inter-processor interrupt */
|
||||
LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0);
|
||||
LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0);
|
||||
LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0);
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0);
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0);
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0);
|
||||
#ifdef LOSCFG_KERNEL_SMP_CALL
|
||||
(VOID)LOS_HwiCreate(LOS_MP_IPI_FUNC_CALL, 0xa0, 0, OsMpFuncCallHandler, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
61
arch/arm/include/perf.h
Normal file
61
arch/arm/include/perf.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
||||
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PERF_H
|
||||
#define _PERF_H
|
||||
|
||||
#include "los_typedef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define OsPerfArchFetchCallerRegs(regs) \
|
||||
do { \
|
||||
(regs)->pc = (UINTPTR)__builtin_return_address(0); \
|
||||
(regs)->fp = (UINTPTR)__builtin_frame_address(0); \
|
||||
} while (0)
|
||||
|
||||
#define OsPerfArchFetchIrqRegs(regs, tcb) \
|
||||
do { \
|
||||
(regs)->pc = (tcb)->pc; \
|
||||
(regs)->fp = (tcb)->fp; \
|
||||
} while (0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* _PERF_H */
|
||||
Reference in New Issue
Block a user