!599 feat: L0~L1 支持Perf

Merge pull request !599 from LiteOS/perf
This commit is contained in:
openharmony_ci
2021-09-29 06:15:02 +00:00
committed by Gitee
54 changed files with 4690 additions and 35 deletions

View File

@@ -26,6 +26,13 @@ config KERNEL_SMP_TASK_SYNC
help
This option will enable task synchronized operate task across cores.
config KERNEL_SMP_CALL
bool "Enable Function call cross Multi-core"
default n
depends on KERNEL_SMP
help
This option will enable function call on multi-core.
config KERNEL_SCHED_STATISTICS
bool "Enable Scheduler statistics"
default n

View File

@@ -69,6 +69,9 @@ typedef struct {
UINT32 schedFlag; /* pending scheduler flag */
#ifdef LOSCFG_KERNEL_SMP
UINT32 excFlag; /* cpu halt or exc flag */
#ifdef LOSCFG_KERNEL_SMP_CALL
LOS_DL_LIST funcLink; /* mp function call link */
#endif
#endif
} Percpu;

View File

@@ -371,6 +371,10 @@ typedef struct {
LOS_DL_LIST msgListHead;
BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];
#endif
#ifdef LOSCFG_KERNEL_PERF
UINTPTR pc;
UINTPTR fp;
#endif
} LosTaskCB;
typedef struct {

View File

@@ -38,6 +38,12 @@
#ifdef LOSCFG_KERNEL_SMP
#ifdef LOSCFG_KERNEL_SMP_CALL
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_mpCallSpin);
#define MP_CALL_LOCK(state) LOS_SpinLockSave(&g_mpCallSpin, &(state))
#define MP_CALL_UNLOCK(state) LOS_SpinUnlockRestore(&g_mpCallSpin, (state))
#endif
VOID LOS_MpSchedule(UINT32 target)
{
UINT32 cpuid = ArchCurrCpuid();
@@ -94,6 +100,70 @@ VOID OsMpCollectTasks(VOID)
}
}
#ifdef LOSCFG_KERNEL_SMP_CALL
VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args)
{
UINT32 index;
UINT32 intSave;
if (func == NULL) {
return;
}
if (!(target & OS_MP_CPU_ALL)) {
return;
}
for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
if (CPUID_TO_AFFI_MASK(index) & target) {
MpCallFunc *mpCallFunc = (MpCallFunc *)LOS_MemAlloc(m_aucSysMem0, sizeof(MpCallFunc));
if (mpCallFunc == NULL) {
PRINT_ERR("smp func call malloc failed\n");
return;
}
mpCallFunc->func = func;
mpCallFunc->args = args;
MP_CALL_LOCK(intSave);
LOS_ListAdd(&g_percpu[index].funcLink, &(mpCallFunc->node));
MP_CALL_UNLOCK(intSave);
}
}
HalIrqSendIpi(target, LOS_MP_IPI_FUNC_CALL);
}
VOID OsMpFuncCallHandler(VOID)
{
UINT32 intSave;
UINT32 cpuid = ArchCurrCpuid();
LOS_DL_LIST *list = NULL;
MpCallFunc *mpCallFunc = NULL;
MP_CALL_LOCK(intSave);
while (!LOS_ListEmpty(&g_percpu[cpuid].funcLink)) {
list = LOS_DL_LIST_FIRST(&g_percpu[cpuid].funcLink);
LOS_ListDelete(list);
MP_CALL_UNLOCK(intSave);
mpCallFunc = LOS_DL_LIST_ENTRY(list, MpCallFunc, node);
mpCallFunc->func(mpCallFunc->args);
(VOID)LOS_MemFree(m_aucSysMem0, mpCallFunc);
MP_CALL_LOCK(intSave);
}
MP_CALL_UNLOCK(intSave);
}
VOID OsMpFuncCallInit(VOID)
{
UINT32 index;
/* init funclink for each core */
for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
LOS_ListInit(&g_percpu[index].funcLink);
}
}
#endif /* LOSCFG_KERNEL_SMP_CALL */
UINT32 OsMpInit(VOID)
{
UINT16 swtmrId;
@@ -101,7 +171,9 @@ UINT32 OsMpInit(VOID)
(VOID)LOS_SwtmrCreate(OS_MP_GC_PERIOD, LOS_SWTMR_MODE_PERIOD,
(SWTMR_PROC_FUNC)OsMpCollectTasks, &swtmrId, 0);
(VOID)LOS_SwtmrStart(swtmrId);
#ifdef LOSCFG_KERNEL_SMP_CALL
OsMpFuncCallInit();
#endif
return LOS_OK;
}

View File

@@ -39,6 +39,7 @@ group("extended") {
"hilog",
"hook",
"liteipc",
"perf",
"pipes",
"power",
"trace",
@@ -57,5 +58,6 @@ config("public") {
"liteipc:public",
"pipes:public",
"vdso:public",
"perf:public",
]
}

View File

@@ -96,3 +96,6 @@ source "kernel/extended/blackbox/Kconfig"
######################### config options of hidumper #########################
source "kernel/extended/hidumper/Kconfig"
######################### config options of perf #########################
source "kernel/extended/perf/Kconfig"

View File

@@ -0,0 +1,56 @@
# 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.
import("//kernel/liteos_a/liteos.gni")
module_switch = defined(LOSCFG_KERNEL_PERF)
module_name = get_path_info(rebase_path("."), "name")
kernel_module(module_name) {
sources = [
"los_perf.c",
"perf_output.c",
"perf_pmu.c",
]
if (defined(LOSCFG_PERF_HW_PMU)) {
sources += [ "pmu/perf_hw_pmu.c" ]
}
if (defined(LOSCFG_PERF_TIMED_PMU)) {
sources += [ "pmu/perf_timed_pmu.c" ]
}
if (defined(LOSCFG_PERF_SW_PMU)) {
sources += [ "pmu/perf_sw_pmu.c" ]
}
}
config("public") {
include_dirs = [ "." ]
}

View File

@@ -0,0 +1,38 @@
config KERNEL_PERF
bool "Enable Perf Feature"
default n
depends on KERNEL_EXTKERNEL
select KERNEL_SMP_CALL if KERNEL_SMP
help
If you wish to build LiteOS with support for perf.
choice
prompt "Time-consuming Calc Methods"
depends on KERNEL_PERF
config PERF_CALC_TIME_BY_TICK
bool "By Tick"
config PERF_CALC_TIME_BY_CYCLE
bool "By Cpu Cycle"
endchoice
config PERF_BUFFER_SIZE
int "Perf Sampling Buffer Size"
default 20480
depends on KERNEL_PERF
config PERF_HW_PMU
bool "Enable Hardware Pmu Events for Sampling"
default n
depends on KERNEL_PERF
config PERF_TIMED_PMU
bool "Enable Hrtimer Period Events for Sampling"
default n
depends on KERNEL_PERF && HRTIMER_ENABLE
config PERF_SW_PMU
bool "Enable Software Events for Sampling"
default y
depends on KERNEL_PERF && KERNEL_HOOK

View File

@@ -0,0 +1,22 @@
include $(LITEOSTOPDIR)/config.mk
MODULE_NAME := $(notdir $(shell pwd))
LOCAL_SRCS := $(wildcard *.c)
ifeq ($(LOSCFG_PERF_HW_PMU), y)
LOCAL_SRCS += $(wildcard pmu/perf_hw_pmu.c)
endif
ifeq ($(LOSCFG_PERF_TIMED_PMU), y)
LOCAL_SRCS += $(wildcard pmu/perf_timed_pmu.c)
endif
ifeq ($(LOSCFG_PERF_SW_PMU), y)
LOCAL_SRCS += $(wildcard pmu/perf_sw_pmu.c)
endif
LOCAL_FLAGS := $(LOCAL_INCLUDE)
include $(MODULE)

View File

@@ -0,0 +1,544 @@
/*
* 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 "los_perf_pri.h"
#include "perf_pmu_pri.h"
#include "perf_output_pri.h"
#include "los_init.h"
#include "los_process.h"
#include "los_tick.h"
#include "los_sys.h"
#include "los_spinlock.h"
STATIC Pmu *g_pmu = NULL;
STATIC PerfCB g_perfCb = {0};
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_perfSpin);
#define PERF_LOCK(state) LOS_SpinLockSave(&g_perfSpin, &(state))
#define PERF_UNLOCK(state) LOS_SpinUnlockRestore(&g_perfSpin, (state))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
STATIC INLINE UINT64 OsPerfGetCurrTime(VOID)
{
#ifdef LOSCFG_PERF_CALC_TIME_BY_TICK
return LOS_TickCountGet();
#else
return HalClockGetCycles();
#endif
}
STATIC UINT32 OsPmuInit(VOID)
{
#ifdef LOSCFG_PERF_HW_PMU
if (OsHwPmuInit() != LOS_OK) {
return LOS_ERRNO_PERF_HW_INIT_ERROR;
}
#endif
#ifdef LOSCFG_PERF_TIMED_PMU
if (OsTimedPmuInit() != LOS_OK) {
return LOS_ERRNO_PERF_TIMED_INIT_ERROR;
}
#endif
#ifdef LOSCFG_PERF_SW_PMU
if (OsSwPmuInit() != LOS_OK) {
return LOS_ERRNO_PERF_SW_INIT_ERROR;
}
#endif
return LOS_OK;
}
STATIC UINT32 OsPerfConfig(PerfEventConfig *eventsCfg)
{
UINT32 i;
UINT32 ret;
g_pmu = OsPerfPmuGet(eventsCfg->type);
if (g_pmu == NULL) {
PRINT_ERR("perf config type error %u!\n", eventsCfg->type);
return LOS_ERRNO_PERF_INVALID_PMU;
}
UINT32 eventNum = MIN(eventsCfg->eventsNr, PERF_MAX_EVENT);
(VOID)memset_s(&g_pmu->events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
for (i = 0; i < eventNum; i++) {
g_pmu->events.per[i].eventId = eventsCfg->events[i].eventId;
g_pmu->events.per[i].period = eventsCfg->events[i].period;
}
g_pmu->events.nr = i;
g_pmu->events.cntDivided = eventsCfg->predivided;
g_pmu->type = eventsCfg->type;
ret = g_pmu->config();
if (ret != LOS_OK) {
PRINT_ERR("perf config failed!\n");
(VOID)memset_s(&g_pmu->events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
return LOS_ERRNO_PERF_PMU_CONFIG_ERROR;
}
return LOS_OK;
}
STATIC VOID OsPerfPrintCount(VOID)
{
UINT32 index;
UINT32 intSave;
UINT32 cpuid = ArchCurrCpuid();
PerfEvent *events = &g_pmu->events;
UINT32 eventNum = events->nr;
PERF_LOCK(intSave);
for (index = 0; index < eventNum; index++) {
Event *event = &(events->per[index]);
/* filter out event counter with no event binded. */
if (event->period == 0) {
continue;
}
PRINT_EMG("[%s] eventType: 0x%x [core %u]: %llu\n", g_pmu->getName(event), event->eventId, cpuid,
event->count[cpuid]);
}
PERF_UNLOCK(intSave);
}
STATIC INLINE VOID OsPerfPrintTs(VOID)
{
#ifdef LOSCFG_PERF_CALC_TIME_BY_TICK
DOUBLE time = (g_perfCb.endTime - g_perfCb.startTime) * 1.0 / LOSCFG_BASE_CORE_TICK_PER_SECOND;
#else
DOUBLE time = (g_perfCb.endTime - g_perfCb.startTime) * 1.0 / OS_SYS_CLOCK;
#endif
PRINT_EMG("time used: %.6f(s)\n", time);
}
STATIC VOID OsPerfStart(VOID)
{
UINT32 cpuid = ArchCurrCpuid();
if (g_pmu == NULL) {
PRINT_ERR("pmu not registered!\n");
return;
}
if (g_perfCb.pmuStatusPerCpu[cpuid] != PERF_PMU_STARTED) {
UINT32 ret = g_pmu->start();
if (ret != LOS_OK) {
PRINT_ERR("perf start on core:%u failed, ret = 0x%x\n", cpuid, ret);
return;
}
g_perfCb.pmuStatusPerCpu[cpuid] = PERF_PMU_STARTED;
} else {
PRINT_ERR("percpu status err %d\n", g_perfCb.pmuStatusPerCpu[cpuid]);
}
}
STATIC VOID OsPerfStop(VOID)
{
UINT32 cpuid = ArchCurrCpuid();
if (g_pmu == NULL) {
PRINT_ERR("pmu not registered!\n");
return;
}
if (g_perfCb.pmuStatusPerCpu[cpuid] != PERF_PMU_STOPED) {
UINT32 ret = g_pmu->stop();
if (ret != LOS_OK) {
PRINT_ERR("perf stop on core:%u failed, ret = 0x%x\n", cpuid, ret);
return;
}
if (!g_perfCb.needSample) {
OsPerfPrintCount();
}
g_perfCb.pmuStatusPerCpu[cpuid] = PERF_PMU_STOPED;
} else {
PRINT_ERR("percpu status err %d\n", g_perfCb.pmuStatusPerCpu[cpuid]);
}
}
STATIC INLINE UINT32 OsPerfSaveIpInfo(CHAR *buf, IpInfo *info)
{
UINT32 size = 0;
#ifdef LOSCFG_KERNEL_VM
UINT32 len = ALIGN(info->len, sizeof(size_t));
*(UINTPTR *)buf = info->ip; /* save ip */
size += sizeof(UINTPTR);
*(UINT32 *)(buf + size) = len; /* save f_path length */
size += sizeof(UINT32);
if (strncpy_s(buf + size, REGION_PATH_MAX, info->f_path, info->len) != EOK) { /* save f_path */
PRINT_ERR("copy f_path failed, %s\n", info->f_path);
}
size += len;
#else
*(UINTPTR *)buf = info->ip; /* save ip */
size += sizeof(UINTPTR);
#endif
return size;
}
STATIC UINT32 OsPerfBackTrace(PerfBackTrace *callChain, UINT32 maxDepth, PerfRegs *regs)
{
UINT32 count = BackTraceGet(regs->fp, (IpInfo *)(callChain->ip), maxDepth);
PRINT_DEBUG("backtrace depth = %u, fp = 0x%x\n", count, regs->fp);
return count;
}
STATIC INLINE UINT32 OsPerfSaveBackTrace(CHAR *buf, PerfBackTrace *callChain, UINT32 count)
{
UINT32 i;
*(UINT32 *)buf = count;
UINT32 size = sizeof(UINT32);
for (i = 0; i < count; i++) {
size += OsPerfSaveIpInfo(buf + size, &(callChain->ip[i]));
}
return size;
}
STATIC UINT32 OsPerfCollectData(Event *event, PerfSampleData *data, PerfRegs *regs)
{
UINT32 size = 0;
UINT32 depth;
IpInfo pc = {0};
PerfBackTrace callChain = {0};
UINT32 sampleType = g_perfCb.sampleType;
CHAR *p = (CHAR *)data;
if (sampleType & PERF_RECORD_CPU) {
*(UINT32 *)(p + size) = ArchCurrCpuid();
size += sizeof(data->cpuid);
}
if (sampleType & PERF_RECORD_TID) {
*(UINT32 *)(p + size) = LOS_CurTaskIDGet();
size += sizeof(data->taskId);
}
if (sampleType & PERF_RECORD_PID) {
*(UINT32 *)(p + size) = LOS_GetCurrProcessID();
size += sizeof(data->processId);
}
if (sampleType & PERF_RECORD_TYPE) {
*(UINT32 *)(p + size) = event->eventId;
size += sizeof(data->eventId);
}
if (sampleType & PERF_RECORD_PERIOD) {
*(UINT32 *)(p + size) = event->period;
size += sizeof(data->period);
}
if (sampleType & PERF_RECORD_TIMESTAMP) {
*(UINT64 *)(p + size) = OsPerfGetCurrTime();
size += sizeof(data->time);
}
if (sampleType & PERF_RECORD_IP) {
OsGetUsrIpInfo(regs->pc, &pc);
size += OsPerfSaveIpInfo(p + size, &pc);
}
if (sampleType & PERF_RECORD_CALLCHAIN) {
depth = OsPerfBackTrace(&callChain, PERF_MAX_CALLCHAIN_DEPTH, regs);
size += OsPerfSaveBackTrace(p + size, &callChain, depth);
}
return size;
}
/*
* return TRUE if the taskId in the task filter list, return FALSE otherwise;
* return TRUE if user haven't specified any taskId(which is supposed
* to instrument the whole system)
*/
STATIC INLINE BOOL OsFilterId(UINT32 id, UINT32 *ids, UINT8 idsNr)
{
UINT32 i;
if (!idsNr) {
return TRUE;
}
for (i = 0; i < idsNr; i++) {
if (ids[i] == id) {
return TRUE;
}
}
return FALSE;
}
STATIC INLINE BOOL OsPerfFilter(UINT32 taskId, UINT32 processId)
{
return OsFilterId(taskId, g_perfCb.taskIds, g_perfCb.taskIdsNr) &&
OsFilterId(processId, g_perfCb.processIds, g_perfCb.processIdsNr);
}
STATIC INLINE UINT32 OsPerfParamValid(VOID)
{
UINT32 index;
UINT32 res = 0;
if (g_pmu == NULL) {
return 0;
}
PerfEvent *events = &g_pmu->events;
UINT32 eventNum = events->nr;
for (index = 0; index < eventNum; index++) {
res |= events->per[index].period;
}
return res;
}
STATIC UINT32 OsPerfHdrInit(UINT32 id)
{
PerfDataHdr head = {
.magic = PERF_DATA_MAGIC_WORD,
.sampleType = g_perfCb.sampleType,
.sectionId = id,
.eventType = g_pmu->type,
.len = sizeof(PerfDataHdr),
};
return OsPerfOutputWrite((CHAR *)&head, head.len);
}
VOID OsPerfUpdateEventCount(Event *event, UINT32 value)
{
if (event == NULL) {
return;
}
event->count[ArchCurrCpuid()] += (value & 0xFFFFFFFF); /* event->count is UINT64 */
}
VOID OsPerfHandleOverFlow(Event *event, PerfRegs *regs)
{
PerfSampleData data;
UINT32 len;
(VOID)memset_s(&data, sizeof(PerfSampleData), 0, sizeof(PerfSampleData));
if ((g_perfCb.needSample) && OsPerfFilter(LOS_CurTaskIDGet(), LOS_GetCurrProcessID())) {
len = OsPerfCollectData(event, &data, regs);
OsPerfOutputWrite((CHAR *)&data, len);
}
}
STATIC UINT32 OsPerfInit(VOID)
{
UINT32 ret;
if (g_perfCb.status != PERF_UNINIT) {
ret = LOS_ERRNO_PERF_STATUS_INVALID;
goto PERF_INIT_ERROR;
}
ret = OsPmuInit();
if (ret != LOS_OK) {
goto PERF_INIT_ERROR;
}
ret = OsPerfOutputInit(NULL, LOSCFG_PERF_BUFFER_SIZE);
if (ret != LOS_OK) {
ret = LOS_ERRNO_PERF_BUF_ERROR;
goto PERF_INIT_ERROR;
}
g_perfCb.status = PERF_STOPPED;
PERF_INIT_ERROR:
return ret;
}
STATIC VOID PerfInfoDump(VOID)
{
UINT32 i;
if (g_pmu != NULL) {
PRINTK("type: %d\n", g_pmu->type);
for (i = 0; i < g_pmu->events.nr; i++) {
PRINTK("events[%d]: %d, 0x%x\n", i, g_pmu->events.per[i].eventId, g_pmu->events.per[i].period);
}
PRINTK("predivided: %d\n", g_pmu->events.cntDivided);
} else {
PRINTK("pmu is NULL\n");
}
PRINTK("sampleType: 0x%x\n", g_perfCb.sampleType);
for (i = 0; i < g_perfCb.taskIdsNr; i++) {
PRINTK("filter taskIds[%d]: %d\n", i, g_perfCb.taskIds[i]);
}
for (i = 0; i < g_perfCb.processIdsNr; i++) {
PRINTK("filter processIds[%d]: %d\n", i, g_perfCb.processIds[i]);
}
PRINTK("needSample: %d\n", g_perfCb.needSample);
}
STATIC INLINE VOID OsPerfSetFilterIds(UINT32 *dstIds, UINT8 *dstIdsNr, UINT32 *ids, UINT32 idsNr)
{
errno_t ret;
if (idsNr) {
ret = memcpy_s(dstIds, PERF_MAX_FILTER_TSKS * sizeof(UINT32), ids, idsNr * sizeof(UINT32));
if (ret != EOK) {
PRINT_ERR("In %s At line:%d execute memcpy_s error\n", __FUNCTION__, __LINE__);
*dstIdsNr = 0;
return;
}
*dstIdsNr = MIN(idsNr, PERF_MAX_FILTER_TSKS);
} else {
*dstIdsNr = 0;
}
}
UINT32 LOS_PerfConfig(PerfConfigAttr *attr)
{
UINT32 ret;
UINT32 intSave;
if (attr == NULL) {
return LOS_ERRNO_PERF_CONFIG_NULL;
}
PERF_LOCK(intSave);
if (g_perfCb.status != PERF_STOPPED) {
ret = LOS_ERRNO_PERF_STATUS_INVALID;
PRINT_ERR("perf config status error : 0x%x\n", g_perfCb.status);
goto PERF_CONFIG_ERROR;
}
g_pmu = NULL;
g_perfCb.needSample = attr->needSample;
g_perfCb.sampleType = attr->sampleType;
OsPerfSetFilterIds(g_perfCb.taskIds, &g_perfCb.taskIdsNr, attr->taskIds, attr->taskIdsNr);
OsPerfSetFilterIds(g_perfCb.processIds, &g_perfCb.processIdsNr, attr->processIds, attr->processIdsNr);
ret = OsPerfConfig(&attr->eventsCfg);
PerfInfoDump();
PERF_CONFIG_ERROR:
PERF_UNLOCK(intSave);
return ret;
}
VOID LOS_PerfStart(UINT32 sectionId)
{
UINT32 intSave;
UINT32 ret;
PERF_LOCK(intSave);
if (g_perfCb.status != PERF_STOPPED) {
PRINT_ERR("perf start status error : 0x%x\n", g_perfCb.status);
goto PERF_START_ERROR;
}
if (!OsPerfParamValid()) {
PRINT_ERR("forgot call `LOS_PerfConfig(...)` before perf start?\n");
goto PERF_START_ERROR;
}
if (g_perfCb.needSample) {
ret = OsPerfHdrInit(sectionId); /* section header init */
if (ret != LOS_OK) {
PRINT_ERR("perf hdr init error 0x%x\n", ret);
goto PERF_START_ERROR;
}
}
SMP_CALL_PERF_FUNC(OsPerfStart); /* send to all cpu to start pmu */
g_perfCb.status = PERF_STARTED;
g_perfCb.startTime = OsPerfGetCurrTime();
PERF_START_ERROR:
PERF_UNLOCK(intSave);
return;
}
VOID LOS_PerfStop(VOID)
{
UINT32 intSave;
PERF_LOCK(intSave);
if (g_perfCb.status != PERF_STARTED) {
PRINT_ERR("perf stop status error : 0x%x\n", g_perfCb.status);
goto PERF_STOP_ERROR;
}
SMP_CALL_PERF_FUNC(OsPerfStop); /* send to all cpu to stop pmu */
OsPerfOutputFlush();
if (g_perfCb.needSample) {
OsPerfOutputInfo();
}
g_perfCb.status = PERF_STOPPED;
g_perfCb.endTime = OsPerfGetCurrTime();
OsPerfPrintTs();
PERF_STOP_ERROR:
PERF_UNLOCK(intSave);
return;
}
UINT32 LOS_PerfDataRead(CHAR *dest, UINT32 size)
{
return OsPerfOutputRead(dest, size);
}
VOID LOS_PerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func)
{
UINT32 intSave;
PERF_LOCK(intSave);
OsPerfNotifyHookReg(func);
PERF_UNLOCK(intSave);
}
VOID LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func)
{
UINT32 intSave;
PERF_LOCK(intSave);
OsPerfFlushHookReg(func);
PERF_UNLOCK(intSave);
}
VOID OsPerfSetIrqRegs(UINTPTR pc, UINTPTR fp)
{
LosTaskCB *runTask = (LosTaskCB *)ArchCurrTaskGet();
runTask->pc = pc;
runTask->fp = fp;
}
LOS_MODULE_INIT(OsPerfInit, LOS_INIT_LEVEL_KMOD_EXTENDED);

View File

@@ -0,0 +1,158 @@
/*
* 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 _LOS_PERF_PRI_H
#define _LOS_PERF_PRI_H
#include "los_perf.h"
#include "perf.h"
#include "los_mp.h"
#include "los_task_pri.h"
#include "los_exc_pri.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define PERF_EVENT_TO_CODE 0
#define PERF_CODE_TO_EVENT 1
#define PERF_DATA_MAGIC_WORD 0xEFEFEF00
#define SMP_CALL_PERF_FUNC(func) OsMpFuncCall(OS_MP_CPU_ALL, (SMP_FUNC_CALL)func, NULL)
enum PmuStatus {
PERF_PMU_STOPED,
PERF_PMU_STARTED,
};
typedef struct {
UINTPTR pc;
UINTPTR fp;
} PerfRegs;
typedef struct {
UINT32 ipNr;
IpInfo ip[PERF_MAX_CALLCHAIN_DEPTH];
} PerfBackTrace;
typedef struct {
UINT32 cpuid; /* cpu id */
UINT32 taskId; /* task id */
UINT32 processId; /* process id */
UINT32 eventId; /* record type */
UINT32 period; /* record period */
UINT64 time; /* record time */
IpInfo pc; /* instruction pointer */
PerfBackTrace callChain; /* number of callChain ips */
} PerfSampleData;
typedef struct {
UINT32 magic; /* magic number */
UINT32 eventType; /* enum PerfEventType */
UINT32 len; /* sample data file length */
UINT32 sampleType; /* IP | TID | TIMESTAMP... */
UINT32 sectionId; /* section id */
} PerfDataHdr;
typedef struct {
UINT32 counter;
UINT32 eventId;
UINT32 period;
UINT64 count[LOSCFG_KERNEL_CORE_NUM];
} Event;
typedef struct {
Event per[PERF_MAX_EVENT];
UINT8 nr;
UINT8 cntDivided;
} PerfEvent;
typedef struct {
enum PerfEventType type;
PerfEvent events;
UINT32 (*config)(VOID);
UINT32 (*start)(VOID);
UINT32 (*stop)(VOID);
CHAR *(*getName)(Event *event);
} Pmu;
typedef struct {
/* time info */
UINT64 startTime;
UINT64 endTime;
/* instrumentation status */
enum PerfStatus status;
enum PmuStatus pmuStatusPerCpu[LOSCFG_KERNEL_CORE_NUM];
/* configuration info */
UINT32 sampleType;
UINT32 taskIds[PERF_MAX_FILTER_TSKS];
UINT8 taskIdsNr;
UINT32 processIds[PERF_MAX_FILTER_TSKS];
UINT8 processIdsNr;
UINT8 needSample;
} PerfCB;
#ifndef OsPerfArchFetchIrqRegs
STATIC INLINE VOID OsPerfArchFetchIrqRegs(PerfRegs *regs, LosTaskCB *curTask) {}
#endif
STATIC INLINE VOID OsPerfFetchIrqRegs(PerfRegs *regs)
{
LosTaskCB *curTask = OsCurrTaskGet();
OsPerfArchFetchIrqRegs(regs, curTask);
PRINT_DEBUG("pc = 0x%x, fp = 0x%x\n", regs->pc, regs->fp);
}
#ifndef OsPerfArchFetchCallerRegs
STATIC INLINE VOID OsPerfArchFetchCallerRegs(PerfRegs *regs) {}
#endif
STATIC INLINE VOID OsPerfFetchCallerRegs(PerfRegs *regs)
{
OsPerfArchFetchCallerRegs(regs);
PRINT_DEBUG("pc = 0x%x, fp = 0x%x\n", regs->pc, regs->fp);
}
extern VOID OsPerfSetIrqRegs(UINTPTR pc, UINTPTR fp);
extern VOID OsPerfUpdateEventCount(Event *event, UINT32 value);
extern VOID OsPerfHandleOverFlow(Event *event, PerfRegs *regs);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif /* _LOS_PERF_PRI_H */

View File

@@ -0,0 +1,126 @@
/*
* 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 "perf_output_pri.h"
STATIC PERF_BUF_NOTIFY_HOOK g_perfBufNotifyHook = NULL;
STATIC PERF_BUF_FLUSH_HOOK g_perfBufFlushHook = NULL;
STATIC PerfOutputCB g_perfOutputCb;
STATIC VOID OsPerfDefaultNotify(VOID)
{
PRINT_INFO("perf buf waterline notify!\n");
}
UINT32 OsPerfOutputInit(VOID *buf, UINT32 size)
{
UINT32 ret;
BOOL releaseFlag = FALSE;
if (buf == NULL) {
buf = LOS_MemAlloc(m_aucSysMem1, size);
if (buf == NULL) {
return LOS_NOK;
} else {
releaseFlag = TRUE;
}
}
ret = LOS_CirBufInit(&g_perfOutputCb.ringbuf, buf, size);
if (ret != LOS_OK) {
goto RELEASE;
}
g_perfOutputCb.waterMark = size / PERF_BUFFER_WATERMARK_ONE_N;
g_perfBufNotifyHook = OsPerfDefaultNotify;
return ret;
RELEASE:
if (releaseFlag) {
(VOID)LOS_MemFree(m_aucSysMem1, buf);
}
return ret;
}
VOID OsPerfOutputFlush(VOID)
{
if (g_perfBufFlushHook != NULL) {
g_perfBufFlushHook(g_perfOutputCb.ringbuf.fifo, g_perfOutputCb.ringbuf.size);
}
}
UINT32 OsPerfOutputRead(CHAR *dest, UINT32 size)
{
OsPerfOutputFlush();
return LOS_CirBufRead(&g_perfOutputCb.ringbuf, dest, size);
}
STATIC BOOL OsPerfOutputBegin(UINT32 size)
{
if (g_perfOutputCb.ringbuf.remain < size) {
PRINT_INFO("perf buf has no enough space for 0x%x\n", size);
return FALSE;
}
return TRUE;
}
STATIC VOID OsPerfOutputEnd(VOID)
{
OsPerfOutputFlush();
if (LOS_CirBufUsedSize(&g_perfOutputCb.ringbuf) >= g_perfOutputCb.waterMark) {
if (g_perfBufNotifyHook != NULL) {
g_perfBufNotifyHook();
}
}
}
UINT32 OsPerfOutputWrite(CHAR *data, UINT32 size)
{
if (!OsPerfOutputBegin(size)) {
return LOS_NOK;
}
LOS_CirBufWrite(&g_perfOutputCb.ringbuf, data, size);
OsPerfOutputEnd();
return LOS_OK;
}
VOID OsPerfOutputInfo(VOID)
{
PRINT_EMG("dump perf data, addr: %p length: %#x\n", g_perfOutputCb.ringbuf.fifo, g_perfOutputCb.ringbuf.size);
}
VOID OsPerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func)
{
g_perfBufNotifyHook = func;
}
VOID OsPerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func)
{
g_perfBufFlushHook = func;
}

View File

@@ -0,0 +1,63 @@
/*
* 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_OUTPUT_PRI_H
#define _PERF_OUTPUT_PRI_H
#include "los_perf_pri.h"
#include "los_cir_buf.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
typedef struct {
CirBuf ringbuf; /* ring buffer */
UINT32 waterMark; /* notify water mark */
} PerfOutputCB;
extern UINT32 OsPerfOutputInit(VOID *buf, UINT32 size);
extern UINT32 OsPerfOutputRead(CHAR *dest, UINT32 size);
extern UINT32 OsPerfOutputWrite(CHAR *data, UINT32 size);
extern VOID OsPerfOutputInfo(VOID);
extern VOID OsPerfOutputFlush(VOID);
extern VOID OsPerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func);
extern VOID OsPerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif /* _PERF_OUTPUT_PRI_H */

View File

@@ -0,0 +1,70 @@
/*
* 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 "perf_pmu_pri.h"
STATIC Pmu *g_pmuMgr[PERF_EVENT_TYPE_MAX] = { NULL };
UINT32 OsPerfPmuRegister(Pmu *pmu)
{
UINT32 type;
if ((pmu == NULL) || (pmu->type >= PERF_EVENT_TYPE_MAX)) {
return LOS_NOK;
}
type = pmu->type;
if (g_pmuMgr[type] == NULL) {
g_pmuMgr[type] = pmu;
return LOS_OK;
}
return LOS_NOK;
}
Pmu *OsPerfPmuGet(UINT32 type)
{
if (type >= PERF_EVENT_TYPE_MAX) {
return NULL;
}
if (type == PERF_EVENT_TYPE_RAW) { /* process hardware raw events with hard pmu */
type = PERF_EVENT_TYPE_HW;
}
return g_pmuMgr[type];
}
VOID OsPerfPmuRm(UINT32 type)
{
if (type >= PERF_EVENT_TYPE_MAX) {
return;
}
g_pmuMgr[type] = NULL;
}

View File

@@ -0,0 +1,130 @@
/*
* 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_PMU_PRI_H
#define _PERF_PMU_PRI_H
#include "los_perf_pri.h"
#ifdef LOSCFG_HRTIMER_ENABLE
#include "linux/hrtimer.h"
#endif
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
typedef struct {
Pmu pmu;
BOOL canDivided;
UINT32 cntDivided;
VOID (*enable)(Event *event);
VOID (*disable)(Event *event);
VOID (*start)(VOID);
VOID (*stop)(VOID);
VOID (*clear)(VOID);
VOID (*setPeriod)(Event *event);
UINTPTR (*readCnt)(Event *event);
UINT32 (*mapEvent)(UINT32 eventType, BOOL reverse);
} HwPmu;
typedef struct {
Pmu pmu;
union {
struct { /* trace event */
BOOL enable;
};
#ifdef LOSCFG_HRTIMER_ENABLE
struct { /* timer event */
struct hrtimer hrtimer;
union ktime time;
union ktime cfgTime;
};
#endif
};
} SwPmu;
#define GET_HW_PMU(item) LOS_DL_LIST_ENTRY(item, HwPmu, pmu)
#define TIMER_PERIOD_LOWER_BOUND_US 100
#define CCNT_FULL 0xFFFFFFFF
#define CCNT_PERIOD_LOWER_BOUND 0x00000000
#define CCNT_PERIOD_UPPER_BOUND 0xFFFFFF00
#define PERIOD_CALC(p) (CCNT_FULL - (p))
#define VALID_PERIOD(p) ((PERIOD_CALC(p) > CCNT_PERIOD_LOWER_BOUND) \
&& (PERIOD_CALC(p) < CCNT_PERIOD_UPPER_BOUND))
#define PERF_HW_INVALID_EVENT_TYPE 0xFFFFFFFF
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#define PMU_LABEL_INT_1 \
NUM_HAL_INTERRUPT_PMU_0,
#define PMU_LABEL_INT_2 \
PMU_LABEL_INT_1 \
NUM_HAL_INTERRUPT_PMU_1,
#define PMU_LABEL_INT_3 \
PMU_LABEL_INT_2 \
NUM_HAL_INTERRUPT_PMU_2,
#define PMU_LABEL_INT_4 \
PMU_LABEL_INT_3 \
NUM_HAL_INTERRUPT_PMU_3,
#define PMU_INT(_num) PMU_LABEL_INT_##_num
#define OS_PMU_INTS(_num, _list) \
STATIC UINT32 _list [_num] = { \
PMU_INT(_num) \
}
extern UINT32 OsPerfPmuRegister(Pmu *pmu);
extern VOID OsPerfPmuRm(UINT32 type);
extern Pmu *OsPerfPmuGet(UINT32 type);
extern UINT32 OsHwPmuInit(VOID);
extern UINT32 OsSwPmuInit(VOID);
extern UINT32 OsTimedPmuInit(VOID);
extern UINT32 OsGetPmuCounter0(VOID);
extern UINT32 OsGetPmuMaxCounter(VOID);
extern UINT32 OsGetPmuCycleCounter(VOID);
extern UINT32 OsPerfHwInit(HwPmu *hwPmu);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif /* _PERF_PMU_PRI_H */

View File

@@ -0,0 +1,187 @@
/*
* 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 "perf_pmu_pri.h"
STATIC Pmu *g_perfHw = NULL;
STATIC CHAR *g_eventName[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = "cycles",
[PERF_COUNT_HW_INSTRUCTIONS] = "instructions",
[PERF_COUNT_HW_ICACHE_REFERENCES] = "icache",
[PERF_COUNT_HW_ICACHE_MISSES] = "icache-misses",
[PERF_COUNT_HW_DCACHE_REFERENCES] = "dcache",
[PERF_COUNT_HW_DCACHE_MISSES] = "dcache-misses",
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = "branches",
[PERF_COUNT_HW_BRANCH_MISSES] = "branches-misses",
};
/**
* 1.If config event is PERF_EVENT_TYPE_HW, then map it to the real eventId first, otherwise use the configured
* eventId directly.
* 2.Find available counter for each event.
* 3.Decide whether this hardware pmu need prescaler (once every 64 cycle counts).
*/
STATIC UINT32 OsPerfHwConfig(VOID)
{
UINT32 i;
HwPmu *armPmu = GET_HW_PMU(g_perfHw);
UINT32 maxCounter = OsGetPmuMaxCounter();
UINT32 counter = OsGetPmuCounter0();
UINT32 cycleCounter = OsGetPmuCycleCounter();
UINT32 cycleCode = armPmu->mapEvent(PERF_COUNT_HW_CPU_CYCLES, PERF_EVENT_TO_CODE);
if (cycleCode == PERF_HW_INVALID_EVENT_TYPE) {
return LOS_NOK;
}
PerfEvent *events = &g_perfHw->events;
UINT32 eventNum = events->nr;
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
if (!VALID_PERIOD(event->period)) {
PRINT_ERR("Config period: 0x%x invalid, should be in (%#x, %#x)\n", event->period,
PERIOD_CALC(CCNT_PERIOD_UPPER_BOUND), PERIOD_CALC(CCNT_PERIOD_LOWER_BOUND));
return LOS_NOK;
}
if (g_perfHw->type == PERF_EVENT_TYPE_HW) { /* do map */
UINT32 eventId = armPmu->mapEvent(event->eventId, PERF_EVENT_TO_CODE);
if (eventId == PERF_HW_INVALID_EVENT_TYPE) {
return LOS_NOK;
}
event->eventId = eventId;
}
if (event->eventId == cycleCode) {
event->counter = cycleCounter;
} else {
event->counter = counter;
counter++;
}
if (counter >= maxCounter) {
PRINT_ERR("max events: %u excluding cycle event\n", maxCounter - 1);
return LOS_NOK;
}
PRINT_DEBUG("Perf Config %u eventId = 0x%x, counter = 0x%x, period = 0x%x\n", i, event->eventId, event->counter,
event->period);
}
armPmu->cntDivided = events->cntDivided & armPmu->canDivided;
return LOS_OK;
}
STATIC UINT32 OsPerfHwStart(VOID)
{
UINT32 i;
UINT32 cpuid = ArchCurrCpuid();
HwPmu *armPmu = GET_HW_PMU(g_perfHw);
PerfEvent *events = &g_perfHw->events;
UINT32 eventNum = events->nr;
armPmu->clear();
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
armPmu->setPeriod(event);
armPmu->enable(event);
event->count[cpuid] = 0;
}
armPmu->start();
return LOS_OK;
}
STATIC UINT32 OsPerfHwStop(VOID)
{
UINT32 i;
UINT32 cpuid = ArchCurrCpuid();
HwPmu *armPmu = GET_HW_PMU(g_perfHw);
PerfEvent *events = &g_perfHw->events;
UINT32 eventNum = events->nr;
armPmu->stop();
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
UINTPTR value = armPmu->readCnt(event);
PRINT_DEBUG("perf stop readCnt value = 0x%x\n", value);
event->count[cpuid] += value;
/* multiplier of cycle counter */
UINT32 eventId = armPmu->mapEvent(event->eventId, PERF_CODE_TO_EVENT);
if ((eventId == PERF_COUNT_HW_CPU_CYCLES) && (armPmu->cntDivided != 0)) {
PRINT_DEBUG("perf stop is cycle\n");
event->count[cpuid] = event->count[cpuid] << 6; /* CCNT counts every 64th cpu cycle */
}
PRINT_DEBUG("perf stop eventCount[0x%x] : [%s] = %llu\n", event->eventId, g_eventName[eventId],
event->count[cpuid]);
}
return LOS_OK;
}
STATIC CHAR *OsPerfGetEventName(Event *event)
{
UINT32 eventId;
HwPmu *armPmu = GET_HW_PMU(g_perfHw);
eventId = armPmu->mapEvent(event->eventId, PERF_CODE_TO_EVENT);
if (eventId < PERF_COUNT_HW_MAX) {
return g_eventName[eventId];
} else {
return "unknown";
}
}
UINT32 OsPerfHwInit(HwPmu *hwPmu)
{
UINT32 ret;
if (hwPmu == NULL) {
return LOS_NOK;
}
hwPmu->pmu.type = PERF_EVENT_TYPE_HW;
hwPmu->pmu.config = OsPerfHwConfig;
hwPmu->pmu.start = OsPerfHwStart;
hwPmu->pmu.stop = OsPerfHwStop;
hwPmu->pmu.getName = OsPerfGetEventName;
(VOID)memset_s(&hwPmu->pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
ret = OsPerfPmuRegister(&hwPmu->pmu);
g_perfHw = OsPerfPmuGet(PERF_EVENT_TYPE_HW);
return ret;
}

View File

@@ -0,0 +1,169 @@
/*
* 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 "perf_pmu_pri.h"
#include "los_hook.h"
STATIC SwPmu g_perfSw;
STATIC CHAR *g_eventName[PERF_COUNT_SW_MAX] = {
[PERF_COUNT_SW_TASK_SWITCH] = "task switch",
[PERF_COUNT_SW_IRQ_RESPONSE] = "irq response",
[PERF_COUNT_SW_MEM_ALLOC] = "mem alloc",
[PERF_COUNT_SW_MUX_PEND] = "mux pend",
};
STATIC UINT32 g_traceEventMap[PERF_COUNT_SW_MAX] = {
[PERF_COUNT_SW_TASK_SWITCH] = LOS_HOOK_TYPE_TASK_SWITCHEDIN,
[PERF_COUNT_SW_IRQ_RESPONSE] = LOS_HOOK_TYPE_ISR_ENTER,
[PERF_COUNT_SW_MEM_ALLOC] = LOS_HOOK_TYPE_MEM_ALLOC,
[PERF_COUNT_SW_MUX_PEND] = LOS_HOOK_TYPE_MUX_PEND,
};
VOID OsPerfHook(UINT32 eventType)
{
if (!g_perfSw.enable) {
return;
}
PerfEvent *events = &g_perfSw.pmu.events;
UINT32 eventNum = events->nr;
UINT32 i;
PerfRegs regs;
(VOID)memset_s(&regs, sizeof(PerfRegs), 0, sizeof(PerfRegs));
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
if (event->counter == eventType) {
OsPerfUpdateEventCount(event, 1);
if (event->count[ArchCurrCpuid()] % event->period == 0) {
OsPerfFetchCallerRegs(&regs);
OsPerfHandleOverFlow(event, &regs);
}
return;
}
}
}
STATIC VOID LOS_PerfMemAlloc(VOID *pool, VOID *ptr, UINT32 size)
{
OsPerfHook(LOS_HOOK_TYPE_MEM_ALLOC);
}
STATIC VOID LOS_PerfMuxPend(const LosMux *muxCB, UINT32 timeout)
{
OsPerfHook(LOS_HOOK_TYPE_MUX_PEND);
}
STATIC VOID LOS_PerfIsrEnter(UINT32 hwiNum)
{
OsPerfHook(LOS_HOOK_TYPE_ISR_ENTER);
}
STATIC VOID LOS_PerfTaskSwitchedIn(const LosTaskCB *newTask, const LosTaskCB *runTask)
{
OsPerfHook(LOS_HOOK_TYPE_TASK_SWITCHEDIN);
}
STATIC VOID OsPerfCnvInit(VOID)
{
LOS_HookReg(LOS_HOOK_TYPE_MEM_ALLOC, LOS_PerfMemAlloc);
LOS_HookReg(LOS_HOOK_TYPE_MUX_PEND, LOS_PerfMuxPend);
LOS_HookReg(LOS_HOOK_TYPE_ISR_ENTER, LOS_PerfIsrEnter);
LOS_HookReg(LOS_HOOK_TYPE_TASK_SWITCHEDIN, LOS_PerfTaskSwitchedIn);
}
STATIC UINT32 OsPerfSwConfig(VOID)
{
UINT32 i;
PerfEvent *events = &g_perfSw.pmu.events;
UINT32 eventNum = events->nr;
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
if ((event->eventId < PERF_COUNT_SW_TASK_SWITCH) || (event->eventId >= PERF_COUNT_SW_MAX) ||
(event->period == 0)) {
return LOS_NOK;
}
event->counter = g_traceEventMap[event->eventId];
}
return LOS_OK;
}
STATIC UINT32 OsPerfSwStart(VOID)
{
UINT32 i;
UINT32 cpuid = ArchCurrCpuid();
PerfEvent *events = &g_perfSw.pmu.events;
UINT32 eventNum = events->nr;
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
event->count[cpuid] = 0;
}
g_perfSw.enable = TRUE;
return LOS_OK;
}
STATIC UINT32 OsPerfSwStop(VOID)
{
g_perfSw.enable = FALSE;
return LOS_OK;
}
STATIC CHAR *OsPerfGetEventName(Event *event)
{
UINT32 eventId = event->eventId;
if (eventId < PERF_COUNT_SW_MAX) {
return g_eventName[eventId];
}
return "unknown";
}
UINT32 OsSwPmuInit(VOID)
{
g_perfSw.pmu = (Pmu) {
.type = PERF_EVENT_TYPE_SW,
.config = OsPerfSwConfig,
.start = OsPerfSwStart,
.stop = OsPerfSwStop,
.getName = OsPerfGetEventName,
};
g_perfSw.enable = FALSE;
OsPerfCnvInit();
(VOID)memset_s(&g_perfSw.pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
return OsPerfPmuRegister(&g_perfSw.pmu);
}

View File

@@ -0,0 +1,177 @@
/*
* 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 "perf_pmu_pri.h"
#define US_PER_SECOND 1000000
#define HRTIMER_DEFAULT_PERIOD_US 1000
STATIC SwPmu g_perfTimed;
STATIC BOOL OsPerfTimedPeriodValid(UINT32 period)
{
return period >= TIMER_PERIOD_LOWER_BOUND_US;
}
STATIC UINT32 OsPerfTimedStart(VOID)
{
UINT32 i;
UINT32 cpuid = ArchCurrCpuid();
PerfEvent *events = &g_perfTimed.pmu.events;
UINT32 eventNum = events->nr;
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
event->count[cpuid] = 0;
}
if (cpuid != 0) { /* only need start on one core */
return LOS_OK;
}
if (hrtimer_start(&g_perfTimed.hrtimer, g_perfTimed.time, HRTIMER_MODE_REL) != 0) {
PRINT_ERR("Hrtimer start failed\n");
return LOS_NOK;
}
if (hrtimer_forward(&g_perfTimed.hrtimer, g_perfTimed.cfgTime) == 0) {
PRINT_ERR("Hrtimer forward failed\n");
return LOS_NOK;
}
g_perfTimed.time = g_perfTimed.cfgTime;
return LOS_OK;
}
STATIC UINT32 OsPerfTimedConfig(VOID)
{
UINT32 i;
PerfEvent *events = &g_perfTimed.pmu.events;
UINT32 eventNum = events->nr;
for (i = 0; i < eventNum; i++) {
Event *event = &(events->per[i]);
UINT32 period = event->period;
if (event->eventId == PERF_COUNT_CPU_CLOCK) {
if (!OsPerfTimedPeriodValid(period)) {
period = TIMER_PERIOD_LOWER_BOUND_US;
PRINT_ERR("config period invalid, should be >= 100, use default period:%u us\n", period);
}
g_perfTimed.cfgTime = (union ktime) {
.tv.sec = period / US_PER_SECOND,
.tv.usec = period % US_PER_SECOND
};
PRINT_INFO("hrtimer config period - sec:%d, usec:%d\n", g_perfTimed.cfgTime.tv.sec,
g_perfTimed.cfgTime.tv.usec);
return LOS_OK;
}
}
return LOS_NOK;
}
STATIC UINT32 OsPerfTimedStop(VOID)
{
UINT32 ret;
if (ArchCurrCpuid() != 0) { /* only need stop on one core */
return LOS_OK;
}
ret = hrtimer_cancel(&g_perfTimed.hrtimer);
if (ret != 1) {
PRINT_ERR("Hrtimer stop failed!, 0x%x\n", ret);
return LOS_NOK;
}
return LOS_OK;
}
STATIC VOID OsPerfTimedHandle(VOID)
{
UINT32 index;
PerfRegs regs;
PerfEvent *events = &g_perfTimed.pmu.events;
UINT32 eventNum = events->nr;
(VOID)memset_s(&regs, sizeof(PerfRegs), 0, sizeof(PerfRegs));
OsPerfFetchIrqRegs(&regs);
for (index = 0; index < eventNum; index++) {
Event *event = &(events->per[index]);
OsPerfUpdateEventCount(event, 1); /* eventCount += 1 every once */
OsPerfHandleOverFlow(event, &regs);
}
}
STATIC enum hrtimer_restart OsPerfHrtimer(struct hrtimer *hrtimer)
{
SMP_CALL_PERF_FUNC(OsPerfTimedHandle); /* send to all cpu to collect data */
return HRTIMER_RESTART;
}
STATIC CHAR *OsPerfGetEventName(Event *event)
{
if (event->eventId == PERF_COUNT_CPU_CLOCK) {
return "timed";
} else {
return "unknown";
}
}
UINT32 OsTimedPmuInit(VOID)
{
UINT32 ret;
g_perfTimed.time = (union ktime) {
.tv.sec = 0,
.tv.usec = HRTIMER_DEFAULT_PERIOD_US,
};
hrtimer_init(&g_perfTimed.hrtimer, 1, HRTIMER_MODE_REL);
ret = hrtimer_create(&g_perfTimed.hrtimer, g_perfTimed.time, OsPerfHrtimer);
if (ret != LOS_OK) {
return ret;
}
g_perfTimed.pmu = (Pmu) {
.type = PERF_EVENT_TYPE_TIMED,
.config = OsPerfTimedConfig,
.start = OsPerfTimedStart,
.stop = OsPerfTimedStop,
.getName = OsPerfGetEventName,
};
(VOID)memset_s(&g_perfTimed.pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
ret = OsPerfPmuRegister(&g_perfTimed.pmu);
return ret;
}

View File

@@ -143,6 +143,7 @@ enum LOS_MOUDLE_ID {
LOS_MOD_MUX = 0X1d,
LOS_MOD_CPUP = 0x1e,
LOS_MOD_HOOK = 0x1f,
LOS_MOD_PERF = 0x20,
LOS_MOD_SHELL = 0x31,
LOS_MOD_DRIVER = 0x41,
LOS_MOD_BUTT

View File

@@ -33,6 +33,7 @@
#define _LOS_MP_H
#include "los_config.h"
#include "los_list.h"
#ifdef __cplusplus
#if __cplusplus
@@ -48,8 +49,13 @@ typedef enum {
LOS_MP_IPI_WAKEUP,
LOS_MP_IPI_SCHEDULE,
LOS_MP_IPI_HALT,
#ifdef LOSCFG_KERNEL_SMP_CALL
LOS_MP_IPI_FUNC_CALL,
#endif
} MP_IPI_TYPE;
typedef VOID (*SMP_FUNC_CALL)(VOID *args);
#ifdef LOSCFG_KERNEL_SMP
extern VOID LOS_MpSchedule(UINT32 target);
extern VOID OsMpWakeHandler(VOID);
@@ -63,6 +69,28 @@ STATIC INLINE VOID LOS_MpSchedule(UINT32 target)
}
#endif
#ifdef LOSCFG_KERNEL_SMP_CALL
typedef struct {
LOS_DL_LIST node;
SMP_FUNC_CALL func;
VOID *args;
} MpCallFunc;
/**
* It is used to call function on target cpus by sending ipi, and the first param is target cpu mask value.
*/
extern VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args);
extern VOID OsMpFuncCallHandler(VOID);
#else
INLINE VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args)
{
(VOID)target;
if (func != NULL) {
func(args);
}
}
#endif /* LOSCFG_KERNEL_SMP_CALL */
#ifdef __cplusplus
#if __cplusplus
}

439
kernel/include/los_perf.h Normal file
View File

@@ -0,0 +1,439 @@
/*
* 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.
*/
/**
* @defgroup los_perf Perf
* @ingroup kernel
*/
#ifndef _LOS_PERF_H
#define _LOS_PERF_H
#include "los_typedef.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/**
* @ingroup los_perf
* Perf max sample filter task number.
*/
#define PERF_MAX_FILTER_TSKS 32
/**
* @ingroup los_perf
* Perf max sample event counter's number.
*/
#define PERF_MAX_EVENT 7
/**
* @ingroup los_perf
* Perf max backtrace depth.
*/
#define PERF_MAX_CALLCHAIN_DEPTH 10
/**
* @ingroup los_perf
* Perf sample data buffer's water mark 1/N.
*/
#define PERF_BUFFER_WATERMARK_ONE_N 2
/**
* @ingroup los_perf
* Perf status.
*/
enum PerfStatus {
PERF_UNINIT, /* perf isn't inited */
PERF_STARTED, /* perf is started */
PERF_STOPPED, /* perf is stopped */
};
/**
* @ingroup los_perf
* Define the type of the perf sample data buffer water mark hook function.
*
*/
typedef VOID (*PERF_BUF_NOTIFY_HOOK)(VOID);
/**
* @ingroup los_perf
* Define the type of the perf sample data buffer flush hook function.
*
*/
typedef VOID (*PERF_BUF_FLUSH_HOOK)(VOID *addr, UINT32 size);
/**
* @ingroup los_perf
* Perf error code: Bad status.
*
* Value: 0x02002000
*
* Solution: Follow the perf state machine.
*/
#define LOS_ERRNO_PERF_STATUS_INVALID LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x00)
/**
* @ingroup los_perf
* Perf error code: Hardware pmu init failed.
*
* Value: 0x02002001
*
* Solution: Check the pmu hwi irq.
*/
#define LOS_ERRNO_PERF_HW_INIT_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x01)
/**
* @ingroup los_perf
* Perf error code: Hrtimer init failed for hrtimer timed pmu init.
*
* Value: 0x02002002
*
* Solution: Check the Hrtimer init.
*/
#define LOS_ERRNO_PERF_TIMED_INIT_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x02)
/**
* @ingroup los_perf
* Perf error code: Software pmu init failed.
*
* Value: 0x02002003
*
* Solution: Check the Perf software events init.
*/
#define LOS_ERRNO_PERF_SW_INIT_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x03)
/**
* @ingroup los_perf
* Perf error code: Perf buffer init failed.
*
* Value: 0x02002004
*
* Solution: Check the buffer init size.
*/
#define LOS_ERRNO_PERF_BUF_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x04)
/**
* @ingroup los_perf
* Perf error code: Perf pmu type error.
*
* Value: 0x02002005
*
* Solution: Check whether the corresponding pmu is enabled in the menuconfig.
*/
#define LOS_ERRNO_PERF_INVALID_PMU LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x05)
/**
* @ingroup los_perf
* Perf error code: Perf pmu config error.
*
* Value: 0x02002006
*
* Solution: Check the config attr of event id and event period.
*/
#define LOS_ERRNO_PERF_PMU_CONFIG_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x06)
/**
* @ingroup los_perf
* Perf error code: Perf pmu config attr is NULL.
*
* Value: 0x02002007
*
* Solution: Check if the input params of attr is NULL.
*/
#define LOS_ERRNO_PERF_CONFIG_NULL LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x07)
/**
* @ingroup los_perf
* Perf types
*/
enum PerfEventType {
PERF_EVENT_TYPE_HW, /* boards common hw events */
PERF_EVENT_TYPE_TIMED, /* hrtimer timed events */
PERF_EVENT_TYPE_SW, /* software trace events */
PERF_EVENT_TYPE_RAW, /* boards special hw events, see enum PmuEventType in corresponding arch headfile */
PERF_EVENT_TYPE_MAX
};
/**
* @ingroup los_perf
* Common hardware pmu events
*/
enum PmuHwId {
PERF_COUNT_HW_CPU_CYCLES = 0, /* cpu cycle event */
PERF_COUNT_HW_INSTRUCTIONS, /* instruction event */
PERF_COUNT_HW_DCACHE_REFERENCES, /* dcache access event */
PERF_COUNT_HW_DCACHE_MISSES, /* dcache miss event */
PERF_COUNT_HW_ICACHE_REFERENCES, /* icache access event */
PERF_COUNT_HW_ICACHE_MISSES, /* icache miss event */
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, /* software change of pc event */
PERF_COUNT_HW_BRANCH_MISSES, /* branch miss event */
PERF_COUNT_HW_MAX,
};
/**
* @ingroup los_perf
* Common hrtimer timed events
*/
enum PmuTimedId {
PERF_COUNT_CPU_CLOCK = 0, /* hrtimer timed event */
};
/**
* @ingroup los_perf
* Common software pmu events
*/
enum PmuSwId {
PERF_COUNT_SW_TASK_SWITCH = 1, /* task switch event */
PERF_COUNT_SW_IRQ_RESPONSE, /* irq response event */
PERF_COUNT_SW_MEM_ALLOC, /* memory alloc event */
PERF_COUNT_SW_MUX_PEND, /* mutex pend event */
PERF_COUNT_SW_MAX,
};
/**
* @ingroup los_perf
* perf sample data types
* Config it through PerfConfigAttr->sampleType.
*/
enum PerfSampleType {
PERF_RECORD_CPU = 1U << 0, /* record current cpuid */
PERF_RECORD_TID = 1U << 1, /* record current task id */
PERF_RECORD_TYPE = 1U << 2, /* record event type */
PERF_RECORD_PERIOD = 1U << 3, /* record event period */
PERF_RECORD_TIMESTAMP = 1U << 4, /* record timestamp */
PERF_RECORD_IP = 1U << 5, /* record instruction pointer */
PERF_RECORD_CALLCHAIN = 1U << 6, /* record backtrace */
PERF_RECORD_PID = 1U << 7, /* record current process id */
};
/**
* @ingroup los_perf
* perf configuration sub event information
*
* This structure is used to config specific events attributes.
*/
typedef struct {
UINT32 type; /* enum PerfEventType */
struct {
UINT32 eventId; /* the specific event corresponds to the PerfEventType */
UINT32 period; /* event period, for every "period"th occurrence of the event a
sample will be recorded */
} events[PERF_MAX_EVENT]; /* perf event list */
UINT32 eventsNr; /* total perf event number */
BOOL predivided; /* whether to prescaler (once every 64 counts),
which only take effect on cpu cycle hardware event */
} PerfEventConfig;
/**
* @ingroup los_perf
* perf configuration main information
*
* This structure is used to set perf sampling attributes, including events, tasks and other information.
*/
typedef struct {
PerfEventConfig eventsCfg; /* perf event config */
UINT32 taskIds[PERF_MAX_FILTER_TSKS]; /* perf task filter list (allowlist) */
UINT32 taskIdsNr; /* task numbers of task filter allowlist,
if set 0 perf will sample all tasks */
UINT32 processIds[PERF_MAX_FILTER_TSKS]; /* perf process filter list (allowlist) */
UINT32 processIdsNr; /* process numbers of process filter allowlist,
if set 0 perf will sample all processes */
UINT32 sampleType; /* type of data to sample defined in PerfSampleType */
BOOL needSample; /* whether to sample data */
} PerfConfigAttr;
/**
* @ingroup los_perf
* @brief Init perf.
*
* @par Description:
* <ul>
* <li>Used to initialize the perf module, including initializing the PMU, allocating memory,
* etc.,which is called during the phase of system initialization.</li>
* </ul>
* @attention
* <ul>
* <li>If buf is not NULL, user must ensure size is not bigger than buf's length.</li>
* </ul>
*
* @param buf [IN] Pointer of sample data buffer;Use the dynamically allocated memory if the pointer is NULL.
* @param size [IN] Length of sample data buffer.
*
* @retval #LOS_ERRNO_PERF_STATUS_INVALID Perf in a wrong status.
* @retval #LOS_ERRNO_PERF_HW_INIT_ERROR Perf hardware pmu init fail.
* @retval #LOS_ERRNO_PERF_TIMED_INIT_ERROR Perf timed pmu init fail.
* @retval #LOS_ERRNO_PERF_SW_INIT_ERROR Perf software pmu init fail.
* @retval #LOS_ERRNO_PERF_BUF_ERROR Perf buffer init fail.
* @retval #LOS_OK Perf init success.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
UINT32 LOS_PerfInit(VOID *buf, UINT32 size);
/**
* @ingroup los_perf
* @brief Start perf sampling.
*
* @par Description
* Start perf sampling.
* @attention
* None.
*
* @param sectionId [IN] Set the section id for marking this piece of data in the perf sample data buffer.
* @retval None.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
VOID LOS_PerfStart(UINT32 sectionId);
/**
* @ingroup los_perf
* @brief Stop perf sampling.
*
* @par Description
* Stop perf sampling.
* @attention
* None.
*
* @param None.
*
* @retval None.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
VOID LOS_PerfStop(VOID);
/**
* @ingroup los_perf
* @brief Config perf parameters.
*
* @par Description
* Config perf parameters before sample, for example, sample event, sample task, etc. This interface need to be called
* before LOS_PerfStart.
* @attention
* None.
*
* @param attr [IN] Address of a perf event attr struct.
*
* @retval #LOS_ERRNO_PERF_STATUS_INVALID Perf in a wrong status.
* @retval #LOS_ERRNO_PERF_CONFIG_NULL Attr is NULL.
* @retval #LOS_ERRNO_PERF_INVALID_PMU Config perf pmu with error type.
* @retval #LOS_ERRNO_PERF_PMU_CONFIG_ERROR Config perf events fail with invalid event id or event period.
* @retval #LOS_OK Config success.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
UINT32 LOS_PerfConfig(PerfConfigAttr *attr);
/**
* @ingroup los_perf
* @brief Read data from perf sample data buffer.
*
* @par Description
* Because perf sample data buffer is a ringbuffer, the data may be covered after user read ringbuffer.
* @attention
* None.
*
* @param dest [IN] The destination address.
* @param size [IN] Read size.
* @retval #UINT32 The really read bytes.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
UINT32 LOS_PerfDataRead(CHAR *dest, UINT32 size);
/**
* @ingroup los_perf
* @brief Register perf sample data buffer water mark hook function.
*
* @par Description
* <ul>
* <li> Register perf sample data buffer water mark hook function.</li>
* <li> The registered hook will be called when buffer reaches the water mark./li>
* </ul>
* @attention
* None.
*
* @param func [IN] Buffer water mark hook function.
*
* @retval None.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
VOID LOS_PerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func);
/**
* @ingroup los_perf
* @brief Register perf sample data buffer flush hook function.
*
* @par Description
* <ul>
* <li> Register perf sample data buffer flush hook function.</li>
* <li> The flush hook will be called when the buffer be read or written.</li>
* </ul>
* @attention
* None.
*
* @param func [IN] Buffer flush hook function.
*
* @retval None.
* @par Dependency:
* <ul>
* <li>los_perf.h: the header file that contains the API declaration.</li>
* </ul>
*/
VOID LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif /* _LOS_PERF_H */