From 98dcb8f9d7ba60e908d15f0206393a5e80515f3a Mon Sep 17 00:00:00 2001 From: hanzhijian Date: Fri, 31 Jul 2026 16:50:23 +0800 Subject: [PATCH] Add timer system state snapshot APIs --- include/FreeRTOS.h | 19 ++++++- include/timers.h | 57 +++++++++++++++++++ timers.c | 139 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 1 deletion(-) diff --git a/include/FreeRTOS.h b/include/FreeRTOS.h index 83fd430bdc7..2fe2ec719cb 100644 --- a/include/FreeRTOS.h +++ b/include/FreeRTOS.h @@ -1626,6 +1626,22 @@ #define traceRETURN_xTimerGetExpiryTime( xReturn ) #endif +#ifndef traceENTER_uxTimerGetNumberOfTimers + #define traceENTER_uxTimerGetNumberOfTimers() +#endif + +#ifndef traceRETURN_uxTimerGetNumberOfTimers + #define traceRETURN_uxTimerGetNumberOfTimers( uxNumberOfTimers ) +#endif + +#ifndef traceENTER_uxTimerGetSystemState + #define traceENTER_uxTimerGetSystemState( pxTimerStatusArray, uxArraySize ) +#endif + +#ifndef traceRETURN_uxTimerGetSystemState + #define traceRETURN_uxTimerGetSystemState( uxNumberOfTimers ) +#endif + #ifndef traceENTER_xTimerGetStaticBuffer #define traceENTER_xTimerGetStaticBuffer( xTimer, ppxTimerBuffer ) #endif @@ -3319,8 +3335,9 @@ typedef struct xSTATIC_TIMER TaskFunction_t pvDummy6; #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxDummy7; + void * pvDummy8; #endif - uint8_t ucDummy8; + uint8_t ucDummy9; } StaticTimer_t; /* diff --git a/include/timers.h b/include/timers.h index 191703928f2..73dea7be69c 100644 --- a/include/timers.h +++ b/include/timers.h @@ -77,6 +77,19 @@ struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ typedef struct tmrTimerControl * TimerHandle_t; +/* Used with uxTimerGetSystemState() to return the state of each timer in the + * timer registry. */ +typedef struct xTIMER_STATUS +{ + TimerHandle_t xHandle; /* The handle of the timer. This value will be invalid if the timer is deleted after the structure is populated. */ + const char * pcTimerName; /* A pointer to the name assigned when the timer was created. */ + TickType_t xTimerPeriodInTicks; /* The timer's period in ticks. */ + TickType_t xNextExpiryTime; /* The next expiry time, or 0 if the timer is inactive. */ + void * pvTimerID; /* The application-supplied timer ID. */ + BaseType_t xIsActive; /* pdTRUE if the timer is active, otherwise pdFALSE. */ + BaseType_t xAutoReload; /* pdTRUE for an auto-reload timer, otherwise pdFALSE. */ +} TimerStatus_t; + /* * Defines the prototype to which timer callback functions must conform. */ @@ -1333,6 +1346,50 @@ TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; */ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +/** + * configUSE_TRACE_FACILITY must be set to 1 for + * uxTimerGetNumberOfTimers() to be available. + * + * @return The number of timers currently stored in the timer registry. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTimerGetNumberOfTimers( void ) PRIVILEGED_FUNCTION; +#endif + +/** + * configUSE_TRACE_FACILITY must be set to 1 for + * uxTimerGetSystemState() to be available. + * + * uxTimerGetSystemState() populates one TimerStatus_t structure for each + * timer currently stored in the timer registry. Timers are automatically + * added to the registry when they are created and removed when they are + * deleted. + * + * This function is intended for debugging use and suspends the scheduler while + * the registry is copied. The returned values provide a best-effort snapshot + * of timer state. Timer commands still waiting in the timer command queue are + * not reflected. + * + * TimerStatus_t contains borrowed handles and pointers. A timer handle becomes + * invalid when that timer is deleted, and the application must ensure that the + * memory referenced by timer names and IDs remains valid while it is used. + * + * When MPU wrappers are enabled, this API is privileged-only. + * + * @param pxTimerStatusArray A pointer to an array of TimerStatus_t structures. + * The array must contain at least one entry for each timer in the registry. The + * number of timers can be obtained using uxTimerGetNumberOfTimers(). + * + * @param uxArraySize The number of TimerStatus_t entries in the array. + * + * @return The number of TimerStatus_t structures populated, or zero if the + * supplied array is too small or pxTimerStatusArray is NULL. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTimerGetSystemState( TimerStatus_t * const pxTimerStatusArray, + const UBaseType_t uxArraySize ) PRIVILEGED_FUNCTION; +#endif + /** * BaseType_t xTimerGetStaticBuffer( TimerHandle_t xTimer, * StaticTimer_t ** ppxTimerBuffer ); diff --git a/timers.c b/timers.c index 1bc40bc46f7..cfa2ee47567 100644 --- a/timers.c +++ b/timers.c @@ -89,6 +89,7 @@ portTIMER_CALLBACK_ATTRIBUTE TimerCallbackFunction_t pxCallbackFunction; /**< The function that will be called when the timer expires. */ #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTimerNumber; /**< An ID assigned by trace tools such as FreeRTOS+Trace */ + struct tmrTimerControl * pxNextTimer; /**< Points to the next timer in the timer registry. */ #endif uint8_t ucStatus; /**< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ } xTIMER; @@ -149,6 +150,11 @@ PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; + #if ( configUSE_TRACE_FACILITY == 1 ) + PRIVILEGED_DATA static Timer_t * pxTimerRegistry = NULL; + PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTimers = 0U; + #endif + /*-----------------------------------------------------------*/ /* @@ -157,6 +163,11 @@ */ static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; + #if ( configUSE_TRACE_FACILITY == 1 ) + static void prvAddTimerToRegistry( Timer_t * pxTimer ) PRIVILEGED_FUNCTION; + static void prvRemoveTimerFromRegistry( Timer_t * pxTimer ) PRIVILEGED_FUNCTION; + #endif + /* * The timer service task (daemon). Timer functionality is controlled by this * task. Other tasks communicate with the timer service task using the @@ -441,10 +452,56 @@ pxNewTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_AUTORELOAD; } + #if ( configUSE_TRACE_FACILITY == 1 ) + { + prvAddTimerToRegistry( pxNewTimer ); + } + #endif + traceTIMER_CREATE( pxNewTimer ); } /*-----------------------------------------------------------*/ + #if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvAddTimerToRegistry( Timer_t * pxTimer ) + { + taskENTER_CRITICAL(); + { + pxTimer->pxNextTimer = pxTimerRegistry; + pxTimerRegistry = pxTimer; + uxCurrentNumberOfTimers++; + } + taskEXIT_CRITICAL(); + } + + static void prvRemoveTimerFromRegistry( Timer_t * pxTimer ) + { + Timer_t ** ppxTimer = &pxTimerRegistry; + + taskENTER_CRITICAL(); + { + while( *ppxTimer != NULL ) + { + if( *ppxTimer == pxTimer ) + { + *ppxTimer = pxTimer->pxNextTimer; + pxTimer->pxNextTimer = NULL; + uxCurrentNumberOfTimers--; + break; + } + else + { + ppxTimer = &( ( *ppxTimer )->pxNextTimer ); + } + } + } + taskEXIT_CRITICAL(); + } + + #endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + BaseType_t xTimerGenericCommandFromTask( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, @@ -1047,6 +1104,12 @@ break; case tmrCOMMAND_DELETE: + #if ( configUSE_TRACE_FACILITY == 1 ) + { + prvRemoveTimerFromRegistry( pxTimer ); + } + #endif + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) { /* The timer has already been removed from the active list, @@ -1310,6 +1373,75 @@ #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ + #if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTimerGetNumberOfTimers( void ) + { + UBaseType_t uxNumberOfTimers; + + traceENTER_uxTimerGetNumberOfTimers(); + + /* A critical section is not required because the variable is of + * type UBaseType_t. */ + uxNumberOfTimers = uxCurrentNumberOfTimers; + + traceRETURN_uxTimerGetNumberOfTimers( uxNumberOfTimers ); + + return uxNumberOfTimers; + } + + UBaseType_t uxTimerGetSystemState( TimerStatus_t * const pxTimerStatusArray, + const UBaseType_t uxArraySize ) + { + UBaseType_t uxNumberOfTimers = 0U; + + traceENTER_uxTimerGetSystemState( pxTimerStatusArray, uxArraySize ); + + if( pxTimerStatusArray != NULL ) + { + vTaskSuspendAll(); + { + if( uxArraySize >= uxCurrentNumberOfTimers ) + { + Timer_t * pxTimer = pxTimerRegistry; + UBaseType_t uxTimer = 0U; + + while( pxTimer != NULL ) + { + pxTimerStatusArray[ uxTimer ].xHandle = pxTimer; + pxTimerStatusArray[ uxTimer ].pcTimerName = pxTimer->pcTimerName; + pxTimerStatusArray[ uxTimer ].xTimerPeriodInTicks = pxTimer->xTimerPeriodInTicks; + pxTimerStatusArray[ uxTimer ].pvTimerID = pxTimer->pvTimerID; + pxTimerStatusArray[ uxTimer ].xIsActive = ( ( pxTimer->ucStatus & tmrSTATUS_IS_ACTIVE ) != 0U ) ? pdTRUE : pdFALSE; + pxTimerStatusArray[ uxTimer ].xAutoReload = ( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0U ) ? pdTRUE : pdFALSE; + + if( pxTimerStatusArray[ uxTimer ].xIsActive != pdFALSE ) + { + pxTimerStatusArray[ uxTimer ].xNextExpiryTime = listGET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ) ); + } + else + { + pxTimerStatusArray[ uxTimer ].xNextExpiryTime = 0U; + } + + uxTimer++; + pxTimer = pxTimer->pxNextTimer; + } + + uxNumberOfTimers = uxTimer; + } + } + ( void ) xTaskResumeAll(); + } + + traceRETURN_uxTimerGetSystemState( uxNumberOfTimers ); + + return uxNumberOfTimers; + } + + #endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + #if ( configUSE_TRACE_FACILITY == 1 ) void vTimerSetTimerNumber( TimerHandle_t xTimer, @@ -1334,6 +1466,13 @@ { xTimerQueue = NULL; xTimerTaskHandle = NULL; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxTimerRegistry = NULL; + uxCurrentNumberOfTimers = 0U; + } + #endif } /*-----------------------------------------------------------*/