FreeRTOS/make Queue Registry and Run Time Stats configurable

This commit makes the configQUEUE_REGISTRY_SIZE and
configGENERATE_RUN_TIME_STATS configurable in menuconfig.

- configQUEUE_REGISTRY_SIZE can now be set in menuconfig.
- The functions vQueueAddToRegistry() and vQueueUnregisterQueue() were made
  SMP compatbile
- pcQueueGetName() was backported from FreeRTOS v9.0.0
- Added test case for Queue Registry functions

- configGENERATE_RUN_TIME_STATS can now be enabled in menuconfig. CCOUNT or
  esp_timer can be selected as the FreeRTOS run time clock in menuconfig as
  well, although CCOUNT will overflow quickly.
- Run time stats collection (in vTaskSwitchContext) and generation (in
  uxTaskGetSystemState) have been made SMP compatible. Therefore
  vTaskGetRunTimeStats() now displays the run time usage of each task as a
  percentage of total runtime of both CPUs

Squash
This commit is contained in:
Darian Leung 2017-10-30 16:03:56 +08:00
parent 09d2791cfd
commit d1853dbbc0
8 changed files with 234 additions and 9 deletions

View file

@ -275,6 +275,16 @@ config TIMER_QUEUE_LENGTH
For most uses the default value of 10 is OK.
config FREERTOS_QUEUE_REGISTRY_SIZE
int "FreeRTOS queue registry size"
range 0 20
default 0
help
FreeRTOS uses the queue registry as a means for kernel aware debuggers to locate queues, semaphores,
and mutexes. The registry allows for a textual name to be associated with a queue for easy identification
within a debugging GUI. A value of 0 will disable queue registry functionality, and a value larger than 0
will specify the number of queues/semaphores/mutexes that the registry can hold.
config FREERTOS_USE_TRACE_FACILITY
bool "Enable FreeRTOS trace facility"
default n
@ -285,13 +295,63 @@ config FREERTOS_USE_TRACE_FACILITY
config FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
bool "Enable FreeRTOS stats formatting functions"
depends on FREERTOS_USE_TRACE_FACILITY
depends on FREERTOS_USE_TRACE_FACILITY || FREERTOS_GENERATE_RUN_TIME_STATS
default n
help
If enabled, configUSE_STATS_FORMATTING_FUNCTIONS will be defined as 1 in
FreeRTOS. This will allow the usage of stats formatting functions such
as vTaskList().
config FREERTOS_GENERATE_RUN_TIME_STATS
bool "Enable FreeRTOS to collect run time stats"
default n
help
If enabled, configGENERATE_RUN_TIME_STATS will be defined as 1 in
FreeRTOS. This will allow FreeRTOS to collect information regarding the
usage of processor time amongst FreeRTOS tasks. Run time stats are
generated using either the ESP Timer or the CPU Clock as the clock
source (Note that run time stats are only valid until the clock source
overflows). The function vTaskGetRunTimeStats() will also be available
if FREERTOS_USE_STATS_FORMATTING_FUNCTIONS and
FREERTOS_USE_TRACE_FACILITY are enabled. vTaskGetRunTimeStats() will
display the run time of each task as a % of the total run time of all
CPUs (task run time / no of CPUs) / (total run time / 100 )
choice FREERTOS_RUN_TIME_STATS_CLK
prompt "Choose the clock source for run time stats"
depends on FREERTOS_GENERATE_RUN_TIME_STATS
default FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
help
Choose the clock source for FreeRTOS run time stats. Options are CPU0's
CPU Clock or the ESP Timer. Both clock sources are 32 bits. The CPU
Clock can run at a higher frequency hence provide a finer resolution
but will overflow much quicker. Note that run time stats are only valid
until the clock source overflows.
config FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
bool "Use ESP TIMER for run time stats"
help
ESP Timer will be used as the clock source for FreeRTOS run time stats.
The ESP Timer runs at a frequency of 1MHz regardless of Dynamic
Frequency Scaling. Therefore the ESP Timer will overflow in
approximately 4290 seconds.
config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK
bool "Use CPU Clock for run time stats"
help
CPU Clock will be used as the clock source for the generation of run
time stats. The CPU Clock has a frequency dependent on
ESP32_DEFAULT_CPU_FREQ_MHZ and Dynamic Frequency Scaling (DFS).
Therefore the CPU Clock frequency can fluctuate between 80 to 240MHz.
Run time stats generated using the CPU Clock represents the number of
CPU cycles each task is allocated and DOES NOT reflect the amount of
time each task runs for (as CPU clock frequency can change). If the CPU
clock consistently runs at the maximum frequency of 240MHz, it will
overflow in approximately 17 seconds.
endchoice
menuconfig FREERTOS_DEBUG_INTERNALS
bool "Debug FreeRTOS internals"
default n

View file

@ -219,7 +219,7 @@ int xt_clock_freq(void) __attribute__((deprecated));
#define configBENCHMARK 0 /* Provided by Xtensa port patch */
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 0
#define configQUEUE_REGISTRY_SIZE 0
#define configQUEUE_REGISTRY_SIZE CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1

View file

@ -80,6 +80,7 @@ extern "C" {
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
#include <xtensa/xtruntime.h>
#include "esp_crosscore_int.h"
#include "esp_timer.h" /* required for FreeRTOS run time stats */
#include <esp_heap_caps.h>
@ -299,6 +300,15 @@ static inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, u
/* Fine resolution time */
#define portGET_RUN_TIME_COUNTER_VALUE() xthal_get_ccount()
//ccount or esp_timer are initialized elsewhere
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
/* Coarse resolution time (us) */
#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) x = (uint32_t)esp_timer_get_time()
#endif
/* Kernel utilities. */
void vPortYield( void );

View file

@ -1627,6 +1627,23 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION
void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
#endif
/*
* @note This function has been back ported from FreeRTOS v9.0.0
*
* The queue registry is provided as a means for kernel aware debuggers to
* locate queues, semaphores and mutexes. Call pcQueueGetName() to look
* up and return the name of a queue in the queue registry from the queue's
* handle.
*
* @param xQueue The handle of the queue the name of which will be returned.
* @return If the queue is in the registry then a pointer to the name of the
* queue is returned. If the queue is not in the registry then NULL is
* returned.
*/
#if( configQUEUE_REGISTRY_SIZE > 0 )
const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
#endif
/*
* Generic version of the function used to creaet a queue using dynamic memory
* allocation. This is called by other functions and macros that create other

View file

@ -205,6 +205,9 @@ _Static_assert(sizeof(StaticQueue_t) == sizeof(Queue_t), "StaticQueue_t != Queue
array position being vacant. */
QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ];
//Need to add queue registry mutex to protect against simultaneous access
static portMUX_TYPE queue_registry_spinlock = portMUX_INITIALIZER_UNLOCKED;
#endif /* configQUEUE_REGISTRY_SIZE */
@ -2316,7 +2319,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
{
UBaseType_t ux;
UNTESTED_FUNCTION();
portENTER_CRITICAL(&queue_registry_spinlock);
/* See if there is an empty space in the registry. A NULL name denotes
a free slot. */
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
@ -2335,6 +2338,38 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
mtCOVERAGE_TEST_MARKER();
}
}
portEXIT_CRITICAL(&queue_registry_spinlock);
}
#endif /* configQUEUE_REGISTRY_SIZE */
/*-----------------------------------------------------------*/
#if ( configQUEUE_REGISTRY_SIZE > 0 )
//This function is backported from FreeRTOS v9.0.0
const char *pcQueueGetName( QueueHandle_t xQueue ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
UBaseType_t ux;
const char *pcReturn = NULL; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
portENTER_CRITICAL(&queue_registry_spinlock);
/* Note there is nothing here to protect against another task adding or
removing entries from the registry while it is being searched. */
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
{
if( xQueueRegistry[ ux ].xHandle == xQueue )
{
pcReturn = xQueueRegistry[ ux ].pcQueueName;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
portEXIT_CRITICAL(&queue_registry_spinlock);
return pcReturn;
}
#endif /* configQUEUE_REGISTRY_SIZE */
@ -2346,6 +2381,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
{
UBaseType_t ux;
portENTER_CRITICAL(&queue_registry_spinlock);
/* See if the handle of the queue being unregistered in actually in the
registry. */
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
@ -2361,6 +2397,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
mtCOVERAGE_TEST_MARKER();
}
}
portEXIT_CRITICAL(&queue_registry_spinlock);
} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */

View file

@ -309,7 +309,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK
#if ( configGENERATE_RUN_TIME_STATS == 1 )
PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */
PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime[portNUM_PROCESSORS] = {0U}; /*< Holds the value of a timer/counter the last time a task was switched in on a particular core. */
PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */
#endif
@ -2729,16 +2729,16 @@ void vTaskSwitchContext( void )
against suspect run time stat counter implementations - which
are provided by the application, not the kernel. */
taskENTER_CRITICAL_ISR(&xTaskQueueMutex);
if( ulTotalRunTime > ulTaskSwitchedInTime )
if( ulTotalRunTime > ulTaskSwitchedInTime[ xPortGetCoreID() ] )
{
pxCurrentTCB[ xPortGetCoreID() ]->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
pxCurrentTCB[ xPortGetCoreID() ]->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime[ xPortGetCoreID() ] );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskEXIT_CRITICAL_ISR(&xTaskQueueMutex);
ulTaskSwitchedInTime = ulTotalRunTime;
ulTaskSwitchedInTime[ xPortGetCoreID() ] = ulTotalRunTime;
}
#endif /* configGENERATE_RUN_TIME_STATS */
@ -4372,7 +4372,6 @@ For ESP32 FreeRTOS, vTaskExitCritical implements both portEXIT_CRITICAL and port
volatile UBaseType_t uxArraySize, x;
uint32_t ulTotalTime, ulStatsAsPercentage;
UNTESTED_FUNCTION();
#if( configUSE_TRACE_FACILITY != 1 )
{
#error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats().
@ -4433,7 +4432,8 @@ For ESP32 FreeRTOS, vTaskExitCritical implements both portEXIT_CRITICAL and port
/* What percentage of the total run time has the task used?
This will always be rounded down to the nearest integer.
ulTotalRunTimeDiv100 has already been divided by 100. */
ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime;
/* Also need to consider total run time of all */
ulStatsAsPercentage = (pxTaskStatusArray[ x ].ulRunTimeCounter/portNUM_PROCESSORS)/ ulTotalTime;
/* Write the task name to the string, padding with
spaces so it can be printed in tabular form more

View file

@ -0,0 +1,100 @@
/*
* Test FreeRTOS debug functions and utilities.
* - Queue registry functions vQueueAddToRegistry(), vQueueUnregisterQueue(),
* and pcQueueGetName(backported)
*
*
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "unity.h"
#if (CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE > 0)
#define NO_OF_QUEUES_PER_CORE ((int)((CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE - 3)/portNUM_PROCESSORS)) //Save space for some preallocated tasks
#define NO_OF_QUEUES_TOTAL (NO_OF_QUEUES_PER_CORE * portNUM_PROCESSORS)
#define QUEUE_NAME_MAX_LENGTH 10
static SemaphoreHandle_t start_sem = NULL;
static SemaphoreHandle_t done_sem = NULL;
static char *names[NO_OF_QUEUES_TOTAL];
static QueueHandle_t handles[NO_OF_QUEUES_TOTAL];
void test_queue_registry_task(void *arg)
{
int core = xPortGetCoreID();
int offset = core * NO_OF_QUEUES_PER_CORE;
//Create queues and accompanying queue names
for(int i = 0; i < NO_OF_QUEUES_PER_CORE; i++){
handles[i + offset] = xQueueCreate(1,1); //Create queues
names[i + offset] = calloc(QUEUE_NAME_MAX_LENGTH, sizeof(char));
sprintf(names[i + offset], "Queue%d%d", core, i);
}
xSemaphoreTake(start_sem, portMAX_DELAY); //Wait for start vQueueAddToRegistry()
for(int i = 0; i < NO_OF_QUEUES_PER_CORE; i++){
vQueueAddToRegistry(handles[i + offset] , names[i + offset]); //Register queues to queue registry
}
xSemaphoreGive(done_sem); //Signal that vQueueAddToRegistry() has completed
vTaskDelay(1);
xSemaphoreTake(start_sem, portMAX_DELAY); //Wait to start vQueueUnregisterQueue()
for(int i = 0; i < NO_OF_QUEUES_PER_CORE; i++){
vQueueDelete(handles[i + offset]); //Internally calls vQueueUnregisterQueue
}
xSemaphoreGive(done_sem); //Signal done
vTaskDelete(NULL); //Delete self
}
TEST_CASE("Test FreeRTOS Queue Registry", "[freertos]")
{
//Create synchronization semaphores
start_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
done_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
for(int i = 0; i < portNUM_PROCESSORS; i++){ //Create tasks to test queue registry
xTaskCreatePinnedToCore(test_queue_registry_task, "testing task", 4096, NULL, UNITY_FREERTOS_PRIORITY+1, NULL, i);
}
portDISABLE_INTERRUPTS();
for(int i = 0; i < portNUM_PROCESSORS; i++){
xSemaphoreGive(start_sem); //Trigger start
}
portENABLE_INTERRUPTS();
for(int i = 0; i < portNUM_PROCESSORS; i++){
xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete vQueueAddToRegistry
}
for(int i = 0; i < NO_OF_QUEUES_TOTAL; i++){
const char *addr = pcQueueGetName(handles[i]);
TEST_ASSERT(addr == names[i]) //Check vQueueAddToRegistry was successful
}
portDISABLE_INTERRUPTS();
for(int i = 0; i < portNUM_PROCESSORS; i++){
xSemaphoreGive(start_sem); //Trigger start
}
portENABLE_INTERRUPTS();
for(int i = 0; i < portNUM_PROCESSORS; i++){
xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete vQueueUnregisterQueue
}
for(int i = 0; i < NO_OF_QUEUES_TOTAL; i++){
const char *addr = pcQueueGetName(handles[i]);
TEST_ASSERT(addr == NULL) //Check vQueueUnregisterQueue was successful
handles[i] = NULL;
}
//Cleanup
for(int i = 0; i < NO_OF_QUEUES_TOTAL; i++){
free(names[i]);
names[i] = NULL;
}
vSemaphoreDelete(start_sem);
start_sem = NULL;
vSemaphoreDelete(done_sem);
done_sem = NULL;
}
#endif //(CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE > 0)

View file

@ -20,3 +20,4 @@ CONFIG_SPI_FLASH_ENABLE_COUNTERS=y
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_TASK_WDT=n
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7