简介:
我们之前移植的 perf_counter (LVGL DMA2D/CPU搬运数据至framebuff性能比较)中使用它来评估一段代码的执行时间功能,除了可以用于评估某段代码的CPU占用cycel外,在freertos 环境下还对接了任务调度器的switch in 和switch out 的hook函数。该hook函数在任务切入的时候记录切人的tick值,在切出的时候记录tick值,计算切出和切入的差值即为本次任务所执行的时间t1,对每次任务运行时间做累加即任务运行总时间t_sum,perf_counter 自身可以获取系统运行的总时间t_total,用任务累计的运行时间t_sum/t_total 即为任务cpu 使用率。
perf_counter switch in/out 对应回调函数如下:
void __on_context_switch_in(uint32_t *pwStack)
{
struct __task_cycle_info_t *ptRootAgent = (struct __task_cycle_info_t *)pwStack;
int64_t lTimeStamp = get_system_ticks();
ptRootAgent->lLastTimeStamp = lTimeStamp;
ptRootAgent->tInfo.hwActiveCount++;
if (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord) {
// update all agents
task_cycle_info_agent_t *ptAgent = ptRootAgent->tList.ptNext;
while(NULL != ptAgent) {
if (NULL != ptAgent->ptInfo) {
if (ptAgent->ptInfo->bEnabled) {
ptAgent->ptInfo->hwActiveCount++;
}
}
ptAgent = ptAgent->ptNext;
}
}
}
void __on_context_switch_out(uint32_t *pwStack)
{
struct __task_cycle_info_t *ptRootAgent = (struct __task_cycle_info_t *)pwStack;
int64_t lCycleUsed = get_system_ticks() - ptRootAgent->lLastTimeStamp - g_nOffset;
ptRootAgent->tInfo.nUsedRecent = lCycleUsed;
ptRootAgent->tInfo.lUsedTotal += lCycleUsed;
if (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord) {
// update all agents
task_cycle_info_agent_t *ptAgent = ptRootAgent->tList.ptNext;
while(NULL != ptAgent) {
if (NULL != ptAgent->ptInfo) {
if (ptAgent->ptInfo->bEnabled) {
ptAgent->ptInfo->nUsedRecent = lCycleUsed;
ptAgent->ptInfo->lUsedTotal += lCycleUsed;
}
}
ptAgent = ptAgent->ptNext;
}
}
}从上述switch in/out 回调函数可以看出来,对应任务统计的cycel 信息保存在每个任务栈的栈底部,对应的数据结构如下:
struct __task_cycle_info_t {
task_cycle_info_t tInfo; //!< cycle information
int64_t lLastTimeStamp; //!< previous timestamp
task_cycle_info_agent_t tList; //!< the root of the agent list
uint32_t wMagicWord; //!< an magic word for validation
} ;其中的info 成员保存了对应的任务的cycel 信息。
typedef struct {
int64_t lStart;
int64_t lUsedTotal;
int32_t nUsedRecent;
uint16_t hwActiveCount;
uint16_t : 15;
uint16_t bEnabled : 1;
} task_cycle_info_t;有了以上的数据结构,该信息在何处保存,perf_counter 借用了任务栈底空间来保存该信息,对应的结构关系如下图。

在freertos 系统上我们实现task switchin/switchout/taskcreate hook 函数即可实现cpu 使用率的统计。
添加如下hook 函数的实现:
extern void __freertos_evr_on_task_switched_out (void *ptTCB);
extern void __freertos_evr_on_task_switched_in(void *ptTCB, unsigned int uxTopPriority) ;
extern void __freertos_evr_on_tick_update(void);
extern void __freertos_evr_on_create_task(void *ptTCB);
# define traceTASK_SWITCHED_OUT() \
__freertos_evr_on_task_switched_out(pxCurrentTCB)
# define traceTASK_SWITCHED_IN() \
__freertos_evr_on_task_switched_in(pxCurrentTCB, uxTopReadyPriority)
# define traceTASK_INCREMENT_TICK( xTickCount ) \
__freertos_evr_on_tick_update()
# define traceTASK_CREATE( pxNewTCB ) \
__freertos_evr_on_create_task((void*)pxNewTCB)
void __freertos_evr_on_task_switched_out (void *ptTCB) {
#if defined(RTE_Compiler_EventRecorder)
EventRecord2(EvtFreeRTOSTasks_TaskSwitchedOut, (uint32_t)ptTCB, 0U);
#else
(void)pxCurrentTCB;
#endif
__on_context_switch_out(((TCB_t *)ptTCB)->pxStack);
}
void __freertos_evr_on_task_switched_in(void *ptTCB, uint32_t uxTopPriority) {
#if defined(RTE_Compiler_EventRecorder)
EventRecord2(EvtFreeRTOSTasks_TaskSwitchedIn, (uint32_t)ptTCB, uxTopPriority);
#else
(void)pxCurrentTCB;
(void)uxTopPriority;
#endif
__on_context_switch_in(((TCB_t *)ptTCB)->pxStack);
}
void __freertos_evr_on_create_task(void * pxNewTCB){
tskTCBList * node;
/* malloc a node */
node = pvPortMalloc(sizeof(tskTCBList));
if(node != NULL)
{
rt_list_init(&node->list);
node->tcb = (TCB_t *)pxNewTCB;
node->cycle = &(((struct __task_cycle_info_t *)node->tcb->pxStack)->tInfo);
init_task_cycle_counter1((void *)node->cycle);
rt_list_insert_before(&tasklist,&node->list);
}
}添加如下测试命令:
unsigned int cpuusage(char argc,char ** argv)
{
/* Thread list */
int64_t ticks = get_system_ticks();
rt_list_t * pos;
tskTCBList * node;
int64_t other = ticks;
printf("%-16s %-16s %-16s %16s\r\n","name","total","ticks","cpuusage");
rt_list_for_each(pos,&tasklist)
{
node = rt_list_entry(pos,tskTCBList,list);
printf("%-16s %-16lld %-16lld %16f%%\r\n",node->tcb->pcTaskName,node->cycle->lUsedTotal,ticks,\
(double)(node->cycle->lUsedTotal*100)/(double)ticks);
other -= node->cycle->lUsedTotal;
}
if(other > 0)
printf("%-16s %-16lld %-16lld %16f%%\r\n","other",other,ticks,\
(double)(other*100)/(double)ticks);
return 1;
}
LTSH_FUNCTION_EXPORT(cpuusage,"show cpu usage");输入cpuusage 命令已经按照预期的输出了个任务的cpu 整体占用率

我要赚赏金
