Add uxTaskCallForEachTask, and refactor uxTaskGetSystemState to use it. (#1439)
---------
Co-authored-by: Anubhav Rawal <rawalexe@amazon.com>
Co-authored-by: Kody Stribrny <kstribrn@amazon.com>
diff --git a/include/task.h b/include/task.h
index 5353dc6..679b6fc 100644
--- a/include/task.h
+++ b/include/task.h
@@ -184,6 +184,13 @@
#endif
} TaskStatus_t;
+/* Callback type used by uxTaskCallForEachTask(). The callback receives one
+ * task handle and state at a time, plus an opaque caller-supplied context
+ * pointer. The callback may call vTaskGetInfo() if it needs a TaskStatus_t. */
+typedef void (* TaskStatusCallbackFunction_t)( TaskHandle_t xTask,
+ eTaskState eState,
+ void * pvCallbackContext );
+
/* Possible return values for eTaskConfirmSleepModeStatus(). */
typedef enum
{
@@ -2210,7 +2217,38 @@
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) PRIVILEGED_FUNCTION;
-#endif
+
+
+/**
+ * For each task, call pxCallbackFunction with the task's handle and state,
+ * and the provided context.
+ *
+ * NOTE: This function is intended for debugging use only as it suspends
+ * the scheduler for an extended period. The callback runs while the
+ * scheduler is suspended, so it must return quickly and must not perform
+ * blocking operations.
+ *
+ * NOTE: This API is privileged-only (it invokes a user callback from
+ * privileged context).
+ *
+ * @param pxCallbackFunction Callback to invoke once for each task (passing
+ * the task's handle, state, and the pvCallbackContext).
+ *
+ * @param pvCallbackContext Opaque caller-provided context passed through to
+ * each callback invocation.
+ *
+ * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in
+ * FreeRTOSConfig.h then *pulTotalRunTime is set to the total run time since
+ * boot. pulTotalRunTime can be set to NULL to omit the total run time
+ * information.
+ *
+ * @return The number tasks provided to the callback.
+ */
+ UBaseType_t uxTaskCallForEachTask( TaskStatusCallbackFunction_t pxCallbackFunction,
+ void * pvCallbackContext,
+ configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) PRIVILEGED_FUNCTION;
+
+#endif /* if ( configUSE_TRACE_FACILITY == 1 ) */
/**
* task. h
diff --git a/tasks.c b/tasks.c
index 3c97d3c..e117129 100644
--- a/tasks.c
+++ b/tasks.c
@@ -644,22 +644,6 @@
const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION;
/*
- * Fills an TaskStatus_t structure with information on each task that is
- * referenced from the pxList list (which may be a ready list, a delayed list,
- * a suspended list, etc.).
- *
- * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM
- * NORMAL APPLICATION CODE.
- */
-#if ( configUSE_TRACE_FACILITY == 1 )
-
- STATIC UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray,
- List_t * pxList,
- eTaskState eState ) PRIVILEGED_FUNCTION;
-
-#endif
-
-/*
* Searches pxList for a task with name pcNameToQuery - returning a handle to
* the task if it is found, or NULL if the task is not found.
*/
@@ -3695,7 +3679,7 @@
{
xIdleTaskHandles[ xCoreID ]->uxCoreAffinityMask = ( ( UBaseType_t ) 1U << ( UBaseType_t ) xCoreID );
}
- #endif
+ #endif /* #if ( ( configIDLE_AFFINITY == 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */
}
#endif /* if ( configNUMBER_OF_CORES == 1 ) */
}
@@ -4457,11 +4441,107 @@
#if ( configUSE_TRACE_FACILITY == 1 )
+ STATIC UBaseType_t prvForEachTaskInList( List_t * pxList,
+ eTaskState eState,
+ TaskStatusCallbackFunction_t pxCallbackFunction,
+ void * pvCallbackContext );
+
+/* for uxTaskGetSystemState callback context: current write position into TaskStatusArray */
+ typedef struct xTASK_STATUS_ARRAY_WRITER_CONTEXT
+ {
+ TaskStatus_t * pxTaskStatusArray;
+ UBaseType_t uxIndex;
+ } TaskStatusArrayWriterContext_t;
+
+/* callback for uxTaskGetSystemState: write one task's status into TaskStatusArray */
+ STATIC void prvTaskStatusArrayWriter( TaskHandle_t xTask,
+ eTaskState eState,
+ void * pvCallbackContext )
+ {
+ TaskStatusArrayWriterContext_t * pxContext = ( TaskStatusArrayWriterContext_t * ) pvCallbackContext;
+
+ vTaskGetInfo( xTask, &( pxContext->pxTaskStatusArray[ pxContext->uxIndex++ ] ), pdTRUE, eState );
+ }
+
+ STATIC void prvGetTotalRunTime( configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime )
+ {
+ if( pulTotalRunTime != NULL )
+ {
+ #if ( configGENERATE_RUN_TIME_STATS == 1 )
+ #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
+ portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) );
+ #else
+ *pulTotalRunTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE();
+ #endif
+ #else
+ *pulTotalRunTime = 0;
+ #endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */
+ }
+ }
+
+/* For each task, call the provided callback function (passing the provided context). */
+/* Caller must suspend the scheduler around use of this function. */
+ STATIC UBaseType_t prvCallForEachTask( TaskStatusCallbackFunction_t pxCallbackFunction,
+ void * pvCallbackContext )
+ {
+ UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES;
+
+ /* Visit each task in the Ready state. */
+ do
+ {
+ uxQueue--;
+ uxTask = ( UBaseType_t ) ( uxTask + prvForEachTaskInList( &( pxReadyTasksLists[ uxQueue ] ), eReady, pxCallbackFunction, pvCallbackContext ) );
+ } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY );
+
+ /* Visit each task in the Blocked state. */
+ uxTask = ( UBaseType_t ) ( uxTask + prvForEachTaskInList( ( List_t * ) pxDelayedTaskList, eBlocked, pxCallbackFunction, pvCallbackContext ) );
+ uxTask = ( UBaseType_t ) ( uxTask + prvForEachTaskInList( ( List_t * ) pxOverflowDelayedTaskList, eBlocked, pxCallbackFunction, pvCallbackContext ) );
+
+ #if ( INCLUDE_vTaskDelete == 1 )
+ {
+ /* Visit each task that has been deleted but not yet cleaned up. */
+ uxTask = ( UBaseType_t ) ( uxTask + prvForEachTaskInList( &xTasksWaitingTermination, eDeleted, pxCallbackFunction, pvCallbackContext ) );
+ }
+ #endif
+
+ #if ( INCLUDE_vTaskSuspend == 1 )
+ {
+ /* Visit each task in the Suspended state. */
+ uxTask = ( UBaseType_t ) ( uxTask + prvForEachTaskInList( &xSuspendedTaskList, eSuspended, pxCallbackFunction, pvCallbackContext ) );
+ }
+ #endif
+
+ return uxTask;
+ }
+
+ UBaseType_t uxTaskCallForEachTask( TaskStatusCallbackFunction_t pxCallbackFunction,
+ void * pvCallbackContext,
+ configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime )
+ {
+ UBaseType_t uxTask;
+
+ configASSERT( pxCallbackFunction != NULL );
+
+ if( pxCallbackFunction == NULL )
+ {
+ return 0;
+ }
+
+ vTaskSuspendAll();
+ {
+ uxTask = prvCallForEachTask( pxCallbackFunction, pvCallbackContext );
+ prvGetTotalRunTime( pulTotalRunTime );
+ }
+ ( void ) xTaskResumeAll();
+
+ return uxTask;
+ }
+
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime )
{
- UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES;
+ UBaseType_t uxTask = 0;
traceENTER_uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, pulTotalRunTime );
@@ -4470,54 +4550,11 @@
/* Is there a space in the array for each task in the system? */
if( uxArraySize >= uxCurrentNumberOfTasks )
{
- /* Fill in an TaskStatus_t structure with information on each
- * task in the Ready state. */
- do
- {
- uxQueue--;
- uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ) );
- } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY );
-
- /* Fill in an TaskStatus_t structure with information on each
- * task in the Blocked state. */
- uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ) );
- uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ) );
-
- #if ( INCLUDE_vTaskDelete == 1 )
- {
- /* Fill in an TaskStatus_t structure with information on
- * each task that has been deleted but not yet cleaned up. */
- uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ) );
- }
- #endif
-
- #if ( INCLUDE_vTaskSuspend == 1 )
- {
- /* Fill in an TaskStatus_t structure with information on
- * each task in the Suspended state. */
- uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ) );
- }
- #endif
-
- #if ( configGENERATE_RUN_TIME_STATS == 1 )
- {
- if( pulTotalRunTime != NULL )
- {
- #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
- portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) );
- #else
- *pulTotalRunTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE();
- #endif
- }
- }
- #else /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */
- {
- if( pulTotalRunTime != NULL )
- {
- *pulTotalRunTime = 0;
- }
- }
- #endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */
+ TaskStatusArrayWriterContext_t xContext;
+ xContext.pxTaskStatusArray = pxTaskStatusArray;
+ xContext.uxIndex = 0;
+ uxTask = prvCallForEachTask( prvTaskStatusArrayWriter, &xContext );
+ prvGetTotalRunTime( pulTotalRunTime );
}
else
{
@@ -6344,9 +6381,10 @@
#if ( configUSE_TRACE_FACILITY == 1 )
- STATIC UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray,
- List_t * pxList,
- eTaskState eState )
+ STATIC UBaseType_t prvForEachTaskInList( List_t * pxList,
+ eTaskState eState,
+ TaskStatusCallbackFunction_t pxCallbackFunction,
+ void * pvCallbackContext )
{
UBaseType_t uxTask = 0;
const ListItem_t * pxEndMarker = listGET_END_MARKER( pxList );
@@ -6355,10 +6393,7 @@
if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 )
{
- /* Populate an TaskStatus_t structure within the
- * pxTaskStatusArray array for each task that is referenced from
- * pxList. See the definition of TaskStatus_t in task.h for the
- * meaning of each TaskStatus_t structure member. */
+ /* Hand the callback each task handle referenced from pxList. */
for( pxIterator = listGET_HEAD_ENTRY( pxList ); pxIterator != pxEndMarker; pxIterator = listGET_NEXT( pxIterator ) )
{
/* MISRA Ref 11.5.3 [Void pointer assignment] */
@@ -6366,7 +6401,7 @@
/* coverity[misra_c_2012_rule_11_5_violation] */
pxTCB = listGET_LIST_ITEM_OWNER( pxIterator );
- vTaskGetInfo( ( TaskHandle_t ) pxTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState );
+ pxCallbackFunction( ( TaskHandle_t ) pxTCB, eState, pvCallbackContext );
uxTask++;
}
}