1. 程式人生 > 其它 >freertos的視覺化追蹤和執行時間統計功能

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");
   
}

注意:上述程式碼只是核心程式碼,其餘程式碼沒有列出

執行結果

執行結果如下:

注意:上圖中顯示的堆疊資訊表示該任務的空閒堆疊歷史最小值,單位為word。

總結

本文介紹了freertos系統的視覺化追蹤功能和執行時間統計功能及其使用方法,並通過示例演示瞭如何實時監視rtos任務的狀態和系統的執行時間。