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:
187
kernel/extended/perf/pmu/perf_hw_pmu.c
Normal file
187
kernel/extended/perf/pmu/perf_hw_pmu.c
Normal 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;
|
||||
}
|
||||
169
kernel/extended/perf/pmu/perf_sw_pmu.c
Normal file
169
kernel/extended/perf/pmu/perf_sw_pmu.c
Normal 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(®s, 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(®s);
|
||||
OsPerfHandleOverFlow(event, ®s);
|
||||
}
|
||||
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);
|
||||
}
|
||||
177
kernel/extended/perf/pmu/perf_timed_pmu.c
Normal file
177
kernel/extended/perf/pmu/perf_timed_pmu.c
Normal 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(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs));
|
||||
OsPerfFetchIrqRegs(®s);
|
||||
|
||||
for (index = 0; index < eventNum; index++) {
|
||||
Event *event = &(events->per[index]);
|
||||
OsPerfUpdateEventCount(event, 1); /* eventCount += 1 every once */
|
||||
OsPerfHandleOverFlow(event, ®s);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user