From d1853dbbc06fcc3f163f7e9ece1af3007305c184 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 30 Oct 2017 16:03:56 +0800 Subject: [PATCH] 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 --- components/freertos/Kconfig | 62 ++++++++++- .../include/freertos/FreeRTOSConfig.h | 2 +- .../freertos/include/freertos/portmacro.h | 10 ++ components/freertos/include/freertos/queue.h | 17 +++ components/freertos/queue.c | 39 ++++++- components/freertos/tasks.c | 12 +-- .../test/test_freertos_debug_functions.c | 100 ++++++++++++++++++ tools/unit-test-app/sdkconfig.defaults | 1 + 8 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 components/freertos/test/test_freertos_debug_functions.c diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index e56cb1a01..ceb184180 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -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 diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index b45fef7fc..5f5cebc6b 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -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 diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 11d783e3a..5632cd562 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -80,6 +80,7 @@ extern "C" { #include /* required for XSHAL_CLIB */ #include #include "esp_crosscore_int.h" +#include "esp_timer.h" /* required for FreeRTOS run time stats */ #include @@ -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 ); diff --git a/components/freertos/include/freertos/queue.h b/components/freertos/include/freertos/queue.h index 876f1a1b3..9d95ad1bb 100644 --- a/components/freertos/include/freertos/queue.h +++ b/components/freertos/include/freertos/queue.h @@ -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 diff --git a/components/freertos/queue.c b/components/freertos/queue.c index b66b8065a..d74f37b2a 100644 --- a/components/freertos/queue.c +++ b/components/freertos/queue.c @@ -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. */ diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 4828dc710..bdfe1086c 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -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 diff --git a/components/freertos/test/test_freertos_debug_functions.c b/components/freertos/test/test_freertos_debug_functions.c new file mode 100644 index 000000000..16762c000 --- /dev/null +++ b/components/freertos/test/test_freertos_debug_functions.c @@ -0,0 +1,100 @@ +/* + * Test FreeRTOS debug functions and utilities. + * - Queue registry functions vQueueAddToRegistry(), vQueueUnregisterQueue(), + * and pcQueueGetName(backported) + * + * + */ + +#include +#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) diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index 5bad91322..6a3fc9dae 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -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