这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【换取逻辑分析仪】RA8使用perfcount计算freertos任务CPU使用

共1条 1/1 1 跳转至

【换取逻辑分析仪】RA8使用perfcount计算freertos任务CPU使用率

工程师
2024-10-14 12:06:33   被打赏 38 分(兑奖)     打赏

简介:

        我们之前移植的 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 借用了任务栈底空间来保存该信息,对应的结构关系如下图。

image.png


在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 整体占用率

image.png



共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]