Add timer system state snapshot APIs

This commit is contained in:
hanzhijian 2026-07-31 16:50:23 +08:00
parent 4269c69a16
commit 98dcb8f9d7
3 changed files with 214 additions and 1 deletions

View file

@ -1626,6 +1626,22 @@
#define traceRETURN_xTimerGetExpiryTime( xReturn ) #define traceRETURN_xTimerGetExpiryTime( xReturn )
#endif #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 #ifndef traceENTER_xTimerGetStaticBuffer
#define traceENTER_xTimerGetStaticBuffer( xTimer, ppxTimerBuffer ) #define traceENTER_xTimerGetStaticBuffer( xTimer, ppxTimerBuffer )
#endif #endif
@ -3319,8 +3335,9 @@ typedef struct xSTATIC_TIMER
TaskFunction_t pvDummy6; TaskFunction_t pvDummy6;
#if ( configUSE_TRACE_FACILITY == 1 ) #if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxDummy7; UBaseType_t uxDummy7;
void * pvDummy8;
#endif #endif
uint8_t ucDummy8; uint8_t ucDummy9;
} StaticTimer_t; } StaticTimer_t;
/* /*

View file

@ -77,6 +77,19 @@
struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */
typedef struct tmrTimerControl * TimerHandle_t; 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. * 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; 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, * BaseType_t xTimerGetStaticBuffer( TimerHandle_t xTimer,
* StaticTimer_t ** ppxTimerBuffer ); * StaticTimer_t ** ppxTimerBuffer );

139
timers.c
View file

@ -89,6 +89,7 @@
portTIMER_CALLBACK_ATTRIBUTE TimerCallbackFunction_t pxCallbackFunction; /**< The function that will be called when the timer expires. */ portTIMER_CALLBACK_ATTRIBUTE TimerCallbackFunction_t pxCallbackFunction; /**< The function that will be called when the timer expires. */
#if ( configUSE_TRACE_FACILITY == 1 ) #if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; /**< An ID assigned by trace tools such as FreeRTOS+Trace */ 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 #endif
uint8_t ucStatus; /**< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ uint8_t ucStatus; /**< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */
} xTIMER; } xTIMER;
@ -149,6 +150,11 @@
PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL;
PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = 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; 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 * The timer service task (daemon). Timer functionality is controlled by this
* task. Other tasks communicate with the timer service task using the * task. Other tasks communicate with the timer service task using the
@ -441,10 +452,56 @@
pxNewTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_AUTORELOAD; pxNewTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_AUTORELOAD;
} }
#if ( configUSE_TRACE_FACILITY == 1 )
{
prvAddTimerToRegistry( pxNewTimer );
}
#endif
traceTIMER_CREATE( pxNewTimer ); 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, BaseType_t xTimerGenericCommandFromTask( TimerHandle_t xTimer,
const BaseType_t xCommandID, const BaseType_t xCommandID,
const TickType_t xOptionalValue, const TickType_t xOptionalValue,
@ -1047,6 +1104,12 @@
break; break;
case tmrCOMMAND_DELETE: case tmrCOMMAND_DELETE:
#if ( configUSE_TRACE_FACILITY == 1 )
{
prvRemoveTimerFromRegistry( pxTimer );
}
#endif
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
{ {
/* The timer has already been removed from the active list, /* The timer has already been removed from the active list,
@ -1310,6 +1373,75 @@
#endif /* configUSE_TRACE_FACILITY */ #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 ) #if ( configUSE_TRACE_FACILITY == 1 )
void vTimerSetTimerNumber( TimerHandle_t xTimer, void vTimerSetTimerNumber( TimerHandle_t xTimer,
@ -1334,6 +1466,13 @@
{ {
xTimerQueue = NULL; xTimerQueue = NULL;
xTimerTaskHandle = NULL; xTimerTaskHandle = NULL;
#if ( configUSE_TRACE_FACILITY == 1 )
{
pxTimerRegistry = NULL;
uxCurrentNumberOfTimers = 0U;
}
#endif
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/