freertos的可视化追踪和运行时间统计功能

简介

很多时候,我们想要知道rtos任务目前的运行情况,比如任务的状态、优先级、cpu的占用率等等,或者我们想要知道当前正系统在运行的是哪一个任务,又或者我们想要知道某一个任务运行了多长时间。这个时候,我们可以使用freertos的可视化追踪功能和运行时间统计功能来帮助我们监控系统的以上信息。

运行时间统计功能

我们可以通过一个基准定时器来统计rtos各个任务的运行时间,怎么统计呢?

首先我们要弄清楚什么是任务运行,简单来说,任务切入到任务切出之间的时间就是这个任务这一次的运行时间;当然一个任务经常会多次切入和多次切出,所以我们需要把每一次运行的时间记录下来,好消息是任务的TCB里面就有这么一个东西,如下:

  #if ( configGENERATE_RUN_TIME_STATS == 1 )
        configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
    #endif
void vTaskSwitchContext( void )
{
    if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    {
        /* The scheduler is currently suspended - do not allow a context
         * switch. */
        xYieldPending = pdTRUE;
    }
    else
    {
        xYieldPending = pdFALSE;
        traceTASK_SWITCHED_OUT();

        #if ( configGENERATE_RUN_TIME_STATS == 1 )
            {
                #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
                    portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
                #else
                    ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
                #endif

                /* Add the amount of time the task has been running to the
                 * accumulated time so far.  The time the task started running was
                 * stored in ulTaskSwitchedInTime.  Note that there is no overflow
                 * protection here so count values are only valid until the timer
                 * overflows.  The guard against negative values is to protect
                 * against suspect run time stat counter implementations - which
                 * are provided by the application, not the kernel. */
                if( ulTotalRunTime > ulTaskSwitchedInTime )
                {
                    pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                ulTaskSwitchedInTime = ulTotalRunTime;
            }
        #endif /* configGENERATE_RUN_TIME_STATS */
        
        /*** 省略无关代码 ***/
        ......
        ......
    }
}

如上代码所示,每一次任务切入或者切出的时候记录一下基准定时器的计数值,然后相减再乘以基准时钟周期就得到该任务这一次运行的时间,最后累加到该任务的TCB的ulRunTimeCounter中。

可视化追踪功能

每一个任务的所有状态信息都可以放到一个结构体保存起来,这个结构体是下面这个样子:

typedef struct xTASK_STATUS
{
    TaskHandle_t xHandle;                         /* The handle of the task to which the rest of the information in the structure relates. */
    const char * pcTaskName;                      /* A pointer to the task's name.  This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
    UBaseType_t xTaskNumber;                      /* A number unique to the task. */
    eTaskState eCurrentState;                     /* The state in which the task existed when the structure was populated. */
    UBaseType_t uxCurrentPriority;                /* The priority at which the task was running (may be inherited) when the structure was populated. */
    UBaseType_t uxBasePriority;                   /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex.  Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
    configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock.  See https://www.FreeRTOS.org/rtos-run-time-stats.html.  Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
    StackType_t * pxStackBase;                    /* Points to the lowest address of the task's stack area. */
    configSTACK_DEPTH_TYPE usStackHighWaterMark;  /* The minimum amount of stack space that has remained for the task since the task was created.  The closer this value is to zero the closer the task has come to overflowing its stack. */
} TaskStatus_t;

这些状态信息可以通过uxTaskGetSystemState()返回给应用程序,uxTaskGetSystemState()这个函数的主体如下:

#if ( configUSE_TRACE_FACILITY == 1 )

    UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
                                      const UBaseType_t uxArraySize,
                                      configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime )
    {
        UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES;

        vTaskSuspendAll();
        {
            /* Is there a space in the array for each task in the system? */
            if( uxArraySize >= uxCurrentNumberOfTasks )
            {
                /* Fill in an TaskStatus_t structure with information on each
                 * task in the Ready state. */
                do
                {
                    uxQueue--;
                    uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady );
                } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

                /* Fill in an TaskStatus_t structure with information on each
                 * task in the Blocked state. */
                uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked );
                uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked );

                #if ( INCLUDE_vTaskDelete == 1 )
                    {
                        /* Fill in an TaskStatus_t structure with information on
                         * each task that has been deleted but not yet cleaned up. */
                        uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted );
                    }
                #endif

                #if ( INCLUDE_vTaskSuspend == 1 )
                    {
                        /* Fill in an TaskStatus_t structure with information on
                         * each task in the Suspended state. */
                        uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended );
                    }
                #endif

                #if ( configGENERATE_RUN_TIME_STATS == 1 )
                    {
                        if( pulTotalRunTime != NULL )
                        {
                            #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
                                portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) );
                            #else
                                *pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
                            #endif
                        }
                    }
                #else /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */
                    {
                        if( pulTotalRunTime != NULL )
                        {
                            *pulTotalRunTime = 0;
                        }
                    }
                #endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        ( void ) xTaskResumeAll();

        return uxTask;
    }

#endif /* configUSE_TRACE_FACILITY */

它主要是遍历所有任务(就绪态任务、阻塞态任务、已删除未释放内存任务、挂起态任务),获取系统的每个任务的状态信息,然后把这些信息放到一个数组里面,返回给应用。

代码示例

以下代码展示了rtos任务每隔1秒钟打印出所有的任务的状态信息和系统任务的运行时间。

内核配置头文件关键部分如下:

FreeRTOSConfig.h

//===================使用可视化追踪功能和运行时间统计功能      =======/
#define configUSE_TRACE_FACILITY	1
#define configGENERATE_RUN_TIME_STATS 1

typedef unsigned short uint16_t;
extern void TIME4_Init(uint16_t arr,uint16_t psc);
#define TIMER4_TC         ( * ( ( volatile uint16_t * )0x40000824 ) )
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() TIME4_Init(65535, 36000)   //初始化基准定时器,基准时钟频率越高,统计的精确度越高
#define portGET_RUN_TIME_COUNTER_VALUE() TIMER4_TC
#define configRUN_TIME_COUNTER_TYPE uint16_t
//==========================================================//

这里我使用TIME4做基准定时器,时基是2000HZ(0.5ms),统计的运行时间最大为32.768s。

main.c

/*获取OS任务信息*/
void get_task_state(void)
{
    const char task_state[]={'r','R','B','S','D'};
    volatile UBaseType_t uxArraySize, x;
     unsigned portSHORT ulTotalRunTime,ulStatsAsPercentage;
 
    /* 获取任务总数目 */
    uxArraySize = uxTaskGetNumberOfTasks();
   if(uxArraySize > MAX_TASK_NUM)
    {
        printf("当前任务数量过多!\n");
    }
 
    /*获取每个任务的状态信息 */
    uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime );

    printf("任务名        状态       ID       优先级       堆栈        CPU使用率\n");
 
    /* 避免除零错误 */
    if( ulTotalRunTime > 0 )
    {
        /* 将获得的每一个任务状态信息部分的转化为程序员容易识别的字符串格式 */
        for( x = 0; x < uxArraySize; x++ )
        {
            char tmp[128];
           
            /* 计算任务运行时间与总运行时间的百分比。*/
            ulStatsAsPercentage =(uint16_t)(pxTaskStatusArray[ x ].ulRunTimeCounter)*100 / ulTotalRunTime;
 
            if( ulStatsAsPercentage > 0UL )
            {
 
               sprintf(tmp,"%-15s%-10c%-10lu%-12lu%-12d%d%%",pxTaskStatusArray[ x].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState],
                                                                       pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority,
                                                                       pxTaskStatusArray[ x ].usStackHighWaterMark,ulStatsAsPercentage);
            }
            else
            {
                /* 任务运行时间不足总运行时间的1%*/
                sprintf(tmp,"%-15s%-10c%-10lu%-12lu%-12dt<1%%",pxTaskStatusArray[x ].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState],
                                                                       pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority,
                                                                       pxTaskStatusArray[ x ].usStackHighWaterMark);               
            }
           printf("%s\n",tmp);
        }
    }
    printf("任务状态:   r-运行  R-就绪  B-阻塞  S-挂起  D-删除\n");
   
}

注意:上述代码只是核心代码,其余代码没有列出

运行结果

运行结果如下:
12345
注意:上图中显示的堆栈信息表示该任务的空闲堆栈历史最小值,单位为word。

总结

本文介绍了freertos系统的可视化追踪功能和运行时间统计功能及其使用方法,并通过示例演示了如何实时监视rtos任务的状态和系统的运行时间。

posted @ 2022-05-25 15:44  李星云姬如雪  阅读(566)  评论(0编辑  收藏  举报