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 )
#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;
/*

View file

@ -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 );

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. */
#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
}
/*-----------------------------------------------------------*/