You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LiteOS-Reading/src/kernel/extended/perf/los_perf.c

487 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*----------------------------------------------------------------------------
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2020. All rights reserved.
* Description: LiteOS Performance Monitor Module Implementation
* Author: Huawei LiteOS Team
* Create: 2017-01-01
* 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"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#ifdef LOSCFG_KERNEL_PERF
STATIC Pmu *g_pmu = NULL;//用于保存当前系统中所使用的硬件性能计数器
STATIC PerfCB g_perfCb = {0};//保存了性能测量回调函数和一些测量结果数据
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_perfSpin);
//定义了一个自旋锁 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)\r\n", time);
}
STATIC VOID OsPerfStart(VOID)//启动及更新CPU和计数器状态
{
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 UINT32 OsPerfBackTrace(UINTPTR *callChain, UINT32 maxDepth, PerfRegs *regs)
//获取当前函数调用链的信息,并将结果存储在 callChain 数组中
{
UINT32 i;
UINT32 count = ArchBackTraceGet(regs->fp, callChain, maxDepth);//获取当前状态的帧指针值
PRINT_DEBUG("backtrace depth = %u, fp = 0x%x\n", count, regs->fp);
for (i = 0; i < count; i++) {
PRINT_DEBUG("ip[%u]: 0x%x\n", i, callChain[i]);//打印调用链中每个函数的指令地址
}
return count;//返回用链的深度
}
STATIC UINT32 OsPerfCollectData(Event *event, PerfSampleData *data, PerfRegs *regs)
//收集性能计数器的数据,并将其存储在 data 结构体中
{
UINT32 size = 0;
UINT32 depth;
UINT32 sampleType = g_perfCb.sampleType;
CHAR *p = (CHAR *)data;
if (sampleType & PERF_RECORD_CPU) {
*(UINT32 *)(p + size) = ArchCurrCpuid();//函数获取当前CPU的ID并存储在 data->cpuid 字段中
size += sizeof(data->cpuid);
}
if (sampleType & PERF_RECORD_TID) {
*(UINT32 *)(p + size) = LOS_CurTaskIDGet();// 函数获取当前任务的ID并存储在 data->taskId 字段中
size += sizeof(data->taskId);
}
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();//获取当前时间戳,并存储在 data->time 字段中
size += sizeof(data->time);
}
if (sampleType & PERF_RECORD_IP) {
*(UINTPTR *)(p + size) = regs->pc;
size += sizeof(data->pc);
}
if (sampleType & PERF_RECORD_CALLCHAIN) {
depth = OsPerfBackTrace((UINTPTR *)(p + size + sizeof(data->callChain.ipNr)), PERF_MAX_CALLCHAIN_DEPTH, regs);
*(UINT32 *)(p + size) = depth;
size += sizeof(data->callChain.ipNr) + depth * sizeof(data->callChain.ip[0]);
}
return size;
}
/*
* return TRUE if the taskId in the taskId list, return FALSE otherwise;
* return TRUE if user haven't specified any taskId(which is supposed
* to instrument the whole system)
*/
STATIC BOOL OsPerfTaskFilter(UINT32 taskId)//过滤任务ID判断一个给定的任务ID是否需要进行性能优化
{
UINT32 i;
if (!g_perfCb.taskIdsNr) {
return TRUE;
}
for (i = 0; i < g_perfCb.taskIdsNr; i++) { //储存允许过滤任务ID的列表
if (g_perfCb.taskIds[i] == taskId) {
return TRUE;
}
}
return FALSE;
}
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) && OsPerfTaskFilter(LOS_CurTaskIDGet())) {//判断是否优化
len = OsPerfCollectData(event, &data, regs);
OsPerfOutPutWrite((CHAR *)&data, len);
}
}
UINT32 LOS_PerfInit(VOID *buf, UINT32 size)//初始化性能统计模块
{
UINT32 ret;
UINT32 intSave;
PERF_LOCK(intSave);//保存中断状态
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(buf, size);//性能输出缓冲区进行初始化
if (ret != LOS_OK) {
ret = LOS_ERRNO_PERF_BUF_ERROR;
goto PERF_INIT_ERROR;
}
g_perfCb.status = PERF_STOPED;
PERF_INIT_ERROR:
PERF_UNLOCK(intSave);//解锁中断处理
return ret;
}
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_STOPED) {
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;//将全局标量PMU计数器置零
g_perfCb.needSample = attr->needSample;
g_perfCb.taskFilterEnable = attr->taskFilterEnable;
g_perfCb.sampleType = attr->sampleType;
if (attr->taskFilterEnable) {//开启任务过滤功能
ret = memcpy_s(g_perfCb.taskIds, PERF_MAX_FILTER_TSKS * sizeof(UINT32), attr->taskIds,
g_perfCb.taskIdsNr * sizeof(UINT32));
if (ret != EOK) {
PRINT_ERR("In %s At line:%d execute memcpy_s error\n", __FUNCTION__, __LINE__);
goto PERF_CONFIG_ERROR;
}
g_perfCb.taskIdsNr = MIN(attr->taskIdsNr, PERF_MAX_FILTER_TSKS);
}
ret = OsPerfConfig(&attr->eventsCfg);//对事件配置进行处理
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_STOPED) {//判断统计模块是否打开
PRINT_ERR("perf start status error : 0x%x\n", g_perfCb.status);
goto PERF_START_ERROR;
}
if (!OsPerfParamValid()) {//检车行呢个统计模块的参数是否有效
PRINT_ERR("forgot call `LOS_Config(...)` before instrumenting?\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); /* 所有CPU开始PMU计数器启动性能统计模块i奥 */
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_STOPED;
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)//注册型那个统计模块的通知hook函数
{
UINT32 intSave;
//保存中断状态,恢复中断状态
PERF_LOCK(intSave);
OsPerfNotifyHookReg(func);
PERF_UNLOCK(intSave);
}
VOID LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func)//刷新hook
{
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;
}
#endif /* LOSCFG_KERNEL_PERF == YES */
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */