]> granicus.if.org Git - esp-idf/commitdiff
FreeRTOS/make Queue Registry and Run Time Stats configurable
authorDarian Leung <darian@espressif.com>
Mon, 30 Oct 2017 08:03:56 +0000 (16:03 +0800)
committerDarian Leung <darian@espressif.com>
Tue, 14 Nov 2017 07:50:31 +0000 (15:50 +0800)
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
components/freertos/include/freertos/FreeRTOSConfig.h
components/freertos/include/freertos/portmacro.h
components/freertos/include/freertos/queue.h
components/freertos/queue.c
components/freertos/tasks.c
components/freertos/test/test_freertos_debug_functions.c [new file with mode: 0644]
tools/unit-test-app/sdkconfig.defaults

index e56cb1a01b53bed2ba82b84c615ece58c9f9fb94..ceb184180d509e3503d559695b8aba6715620a35 100644 (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
index b45fef7fc4d39d5730212c8a45114a2f07470be4..5f5cebc6babd2281f55ec6b5c82a3210e9b89f8e 100644 (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
index 11d783e3acad3e17d1534c1aaeea9519900de9dd..5632cd56209e9b0ee14f1b7cf3e3e62ce0334908 100644 (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 );
index 876f1a1b30b6fdbb8477f88aaf657e86b3519516..9d95ad1bb6b9bb8b3b6375aff5d1b250810fdc90 100644 (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
index b66b8065a496cc10c4dc542b2898bf4d4459b71c..d74f37b2a29aa62f03533784ec1a710b7d4fc6a0 100644 (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. */
 
index 4828dc710fed20ad46b999aaf89fe443d1d2f050..bdfe1086c94518eef94266168563554d66a62ad4 100644 (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
diff --git a/components/freertos/test/test_freertos_debug_functions.c b/components/freertos/test/test_freertos_debug_functions.c
new file mode 100644 (file)
index 0000000..16762c0
--- /dev/null
@@ -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)
index 5bad913229a19df4ab65c4ac8f7613aea8d1fc35..6a3fc9dae6867cd393aa0a026e069cc3993afe7d 100644 (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