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++;
             }
         }