How to reset vTaskGetRunTimeStats?

thomask wrote on Monday, May 20, 2019:

Hi!

I’m using vTaskGetRunTimeStats() to see the relative CPU load of my tasks.

I’m using a microsecond timer for portGET_RUN_TIME_COUNTER_VALUE, so overflows happen every hour or so, making the output unusable.

Also I’m usually interested in the short-time behavior instead of load-since-system-boot. So extending ulRunTimeCounter to 64 bit would only solve part of the problem.

Ideally I’d like to have a vTaskInfoReset() function or a “reset” flag to vTaskGetRunTimeStats() to reset the ulRunTimeCounters of all tasks.

What would be the best way to go ahead?
(If I do it myself - would a patch be accepted?)

best regards,
Thomas

rtel wrote on Monday, May 20, 2019:

I have a feeling that at some point there was experimentation with such
a function, but it is evidently not in the code. For now what you can
do is add in your own function without modifying the source files by
using the freertos_tasks_c_additions.h header file. If you create that
header file and set configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H to 1 in
FreeRTOSConfig.h the header file will be included at the bottom of
tasks.c, where you will then have access to the private data within that
file.

richarddamon wrote on Monday, May 20, 2019:

I work a freertos_tacks_c_additions.h header file that does that shown below:

Call vTaskClearUsage() to reset all the execution time stats.

Hasn’t been thoughouly tested, but seems to work for me.
(FreeRTOSAdditions.h is a header file to provide the prototype for vTaskClearUsage() so other files can call this.)


#ifndef FREERTOS_FREERTOSPP_FREERTOS_TASKS_C_ADDITIONS_H
#define FREERTOS_FREERTOSPP_FREERTOS_TASKS_C_ADDITIONS_H

#include <FreeRTOSAdditions.h>

void vTaskClearUsageSingleList(List_t *pxList)
{
	configLIST_VOLATILE TCB_t *pxNextTCB, *pxFirstTCB;

	if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) {
		listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList );

		/* Clear Usage for each task that is referenced from
		pxList.  See the definition of TaskStatus_t in task.h for the
		meaning of each TaskStatus_t structure member. */
		do {
			listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList );
			pxNextTCB->ulRunTimeCounter = 0;
		} while( pxNextTCB != pxFirstTCB );
	}
}

void vTaskClearUsage() {
	UBaseType_t uxQueue = configMAX_PRIORITIES;
	vTaskSuspendAll();
	/* Clear runtime counter for each task in the Ready State */
	do {
		uxQueue--;
		vTaskClearUsageSingleList(&( pxReadyTasksLists[ uxQueue ] ));
	} while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

	/* Clear runtime counter for each task in the Blocked state. */
	vTaskClearUsageSingleList(( List_t * ) pxDelayedTaskList);
	vTaskClearUsageSingleList(( List_t * ) pxOverflowDelayedTaskList);

#if( INCLUDE_vTaskDelete == 1 )
	/* Clear runtime counter for each task that has been deleted but not yet cleaned up. */
	vTaskClearUsageSingleList(&xTasksWaitingTermination);
#endif

#if ( INCLUDE_vTaskSuspend == 1 )
	/* Clear runtime counter for each task in the Suspended state. */
	vTaskClearUsageSingleList(&xSuspendedTaskList);
#endif
	ulTaskSwitchedInTime = 0;
	ulTotalRunTime = 0;
	vConfigureTimerForRunTimeStats();
	xTaskResumeAll();
}

#endif /* FREERTOS_FREERTOSPP_FREERTOS_TASKS_C_ADDITIONS_H_ */

simon-w wrote on Tuesday, July 02, 2019:

I’m also interested to have this included in the standard package.
In the mean time I have an alternative approch - based on Richard Damon’s suggestion. This is an additional function ‘vTaskGetCombinedRunTimeStats()’ - below - in ‘freertos_tacks_c_additions.h’ that combines the output of ‘vTaskGetRunTimeStats()’ and ‘vTaskList()’ with the option to clear the counters on every call.

The size of ‘pcWriteBuffer’ passed in needs to increase from 40 to 60 bytes per task.

It basically works, but there are 2 issues. 1) when counters are not cleared, the total percentage adds up to 110-111% - not sure why. 2) when counters are cleared, the bulk of the execution time is recorded aganist the task calling this function instead of IDLE task (using hardware output this function takes 1% of CPU time - which is what is displayed when the counters are not cleared). Can anyone explain this?

The ESC[ codes to format the output need more work. I was trying to use cursor save and restore - maybe not supported properly by my terminal application.

void vTaskGetCombinedRunTimeStats( char* pcWriteBuffer, UBaseType_t uxClear)
{
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize, x;
uint32_t ulTotalTime, ulStatsAsPercentage;
char cStatus;

pcWriteBuffer += sprintf( pcWriteBuffer, “\x1b[1;1HTsk# TaskName\t State\tPrio\t HWM CPUtime(us) CPUload\r\n”);

// Snapshot number of tasks
// Allocate memory for the binary task data structure
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));

if( pxTaskStatusArray != NULL )
{
/* Generate the (binary) data - returns the number of tasks */
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalTime);

/* For percentage calculations. */
ulTotalTime /= 100UL;

/* Create a human readable table from the binary data. */
for( x = 0; x < uxArraySize; x++ )
{
  pcWriteBuffer += sprintf( pcWriteBuffer, "%3u  ", pxTaskStatusArray[ x ].xTaskNumber );
  sprintf(pcWriteBuffer, "%s            ", pxTaskStatusArray[ x ].pcTaskName);
  pcWriteBuffer += 14; // Assumes task name is less than 12 characters and more than 2
  switch(pxTaskStatusArray[ x ].eCurrentState )
  {
    case eRunning:
      cStatus = 'X';
      break;
    case eReady:
      cStatus = 'R';
      break;
    case eBlocked:
      cStatus = 'B';
      break;
    case eSuspended:
      cStatus = 'S';
      break;
    case eDeleted:
      cStatus = 'D';
      break;
    default:        /* Should not get here, but it is included to prevent static checking errors. */
      cStatus = 'I';
      break;
  }
  pcWriteBuffer += sprintf( pcWriteBuffer, " %c    %2i    %6i", cStatus, pxTaskStatusArray[ x ].uxCurrentPriority, pxTaskStatusArray[ x ].usStackHighWaterMark);

  if(ulTotalTime > 0UL)
  {
    ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime;

    if( ulStatsAsPercentage > 0UL )
    {
      pcWriteBuffer += sprintf( pcWriteBuffer, "%13u    %3u%%", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
    }
    else
    {
      pcWriteBuffer += sprintf( pcWriteBuffer, "%13u     <1%%", pxTaskStatusArray[ x ].ulRunTimeCounter );
    }
    
    if(uxClear)
    {
      pxTaskStatusArray[ x ].xHandle->ulRunTimeCounter = 0;
    }
  }
  pcWriteBuffer += sprintf(pcWriteBuffer, "\r\n\x1b[0K");
}

if(uxClear)
{
  ulTaskSwitchedInTime = 0;
  ulTotalRunTime = 0;
}

vPortFree( pxTaskStatusArray );

}
else
{
mtCOVERAGE_TEST_MARKER();
}
}