简介:
我们之前移植的 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 整体占用率