Task Utilities
Note
This document is automatically translated using AI. Please excuse any detailed errors. The official English version is still in progress.
In FreeRTOS, task utilities are used to obtain task status, task handles, stack usage, and other information to assist in task debugging and system monitoring.
This document only covers some commonly used APIs in task utilities. For more information, please refer to the FreeRTOS official documentation.
Task Information API
The task information API is mainly used to obtain the basic identification information of the task, such as the current task handle or task name, which is convenient for locating the task source in debugging or logs.
xTaskGetCurrentTaskHandle()
xTaskGetCurrentTaskHandle()
is used to get the handle of the task currently running. It is commonly used in scenarios where you need to reference your own task, such as task self-suspension, self-deletion, and other operations.
API Prototype
TaskHandle_t xTaskGetCurrentTaskHandle( void );
Return Value |
Description |
---|---|
Returns the handle of the task currently running (i.e., calling this API) |
The return value is valid in the task context. |
Example 1: Get the handle of your own task and modify the priority
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Task 1 function
void vTask1 (void *pvParameters)
{
// Get the current task handle
TaskHandle_t xHandleTask1 = xTaskGetCurrentTaskHandle();
while (1) {
// Get task priority
UBaseType_t xPriorityTask1 = uxTaskPriorityGet(xHandleTask1);
printf("Priority of Task 1 is %d\n", xPriorityTask1);
// Raise the priority of Task 1 (if less than Task 2)
if (xPriorityTask1 < 2) {
printf("Raising Task 1 priority to %d\n", xPriorityTask1+1);
vTaskPrioritySet(xHandleTask1, xPriorityTask1+1);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void app_main(void)
{
xTaskCreate(vTask1, "Task1", 2048, NULL, 1, NULL);
}
The execution result of the above code is as follows:
Priority of Task 1 is 1
Raising Task 1 priority to 2
Priority of Task 1 is 2
Priority of Task 1 is 2
Priority of Task 1 is 2
Priority of Task 1 is 2
This example demonstrates the basic method of obtaining the handle of the task itself and its application in task self-management.
After the task is started, it obtains its own handle through xTaskGetCurrentTaskHandle()
, and combines uxTaskPriorityGet()
and vTaskPrioritySet()
to dynamically adjust its own priority.
Compared with directly passing in NULL
to represent the current task, using the task handle has stronger universality and flexibility. It can be used in combination with APIs that only accept handles such as pcTaskGetName()
, eTaskGetState()
, and it is also convenient for debugging information output and cross-module transmission of task control information.
Example 2: Pass out its own handle from within the task
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Define a global variable to save the task handle
TaskHandle_t xHandleTask = NULL;
// Task 1 function
void vTask1 (void *pvParameters)
{
while (1) {
if (xHandleTask != NULL){
vTaskSuspend(xHandleTask);
printf("Task 2 is suspended\n");
xHandleTask = NULL;
}
else {
printf("Task 1 is running\n");
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void vTask2 (void *pvParameters)
{
// Get the current task handle
xHandleTask = xTaskGetCurrentTaskHandle();
while (1) {
printf("Task 2 is running\n");
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void app_main(void)
{
xTaskCreate(vTask1, "Task1", 2048, NULL, 1, NULL);
xTaskCreate(vTask2, "Task2", 2048, NULL, 1, NULL);
}
The execution result of the above code is as follows:
Task 1 is running
Task 2 is running
Task 2 is suspended
Task 1 is running
Task 1 is running
Task 1 is running
Task 1 is running
This example demonstrates that the task actively obtains its own task handle through xTaskGetCurrentTaskHandle()
during its operation, and saves it to a global variable for other tasks to use, achieving direct control between tasks.
Compared with the method of passing in and saving the handle from the outside when the task is created, this method is managed by the task itself, so it has higher decoupling. This method is particularly suitable for scenarios such as task internal initialization, self-registration or dynamic binding after startup, and helps to build a clear, flexible and controllable task system.
Note
Decoupling refers to the low degree of mutual dependence between modules or components, which can be independently developed, used and modified, and the operation of another module will not be affected by the change of one module.
pcTaskGetName()
pcTaskGetName()
is used to get the name string of the specified task, which is commonly used for debugging, log output or task identification.
API prototype
char * pcTaskGetName( TaskHandle_t xTaskToQuery );
Input Parameter |
Parameter Function |
Parameter Description |
---|---|---|
|
The task handle whose name needs to be queried. |
If a non-empty handle is passed in, the name of the corresponding task is queried; if |
Return Value |
Description |
---|---|
Task name string pointer |
Returns a pointer to the queried task name, which is a standard string ending with |
Note
The string pointed to by the return value is statically allocated and should not be modified or released in user code. It can be directly passed to standard string functions for use, suitable for printing logs, debugging task status, and other scenarios.
Example 1: Query the task name and print
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Define a global variable to save the task handle
TaskHandle_t xHandleTask = NULL;
void vTask1 (void *pvParameters)
{
// Get the task name
const char *NameTask2 = NULL;
const char *NameTask1 = pcTaskGetName(NULL);
while (1) {
if (xHandleTask != NULL){
NameTask2 = pcTaskGetName(xHandleTask);
printf("Task 2 name: %s\n", NameTask2);
}
printf("Task 1 name: %s\n", NameTask1);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void vTask2 (void *pvParameters)
{
// Get the current task handle
xHandleTask = xTaskGetCurrentTaskHandle();
while (1) {
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void app_main(void)
{
xTaskCreate(vTask1, "Task1", 2048, NULL, 1, NULL);
xTaskCreate(vTask2, "Task2", 2048, NULL, 1, NULL);
}
The execution result of the above code is as follows:
Task 1 name: Task1
Task 2 name: Task2
Task 1 name: Task1
Task 2 name: Task2
Task 1 name: Task1
Task 2 name: Task2
This example demonstrates how to use pcTaskGetName()
to get the name of a specified task and periodically print it at runtime. This example gets the name of the current task by passing in NULL
, and also passes in the valid handle of other tasks to query the name of the corresponding task.
Although the task name is usually specified by the developer when creating the task, pcTaskGetName()
still has practical value when accessing across modules, outputting logs, creating dynamic tasks, or using it in conjunction with other APIs that only accept task handles, such as eTaskGetState()
.
It provides a unified way of information acquisition, avoiding manual maintenance of task name strings, and improving the decoupling and maintainability of the code.
State and Scheduling API
The state and scheduling APIs are used to query the current status of tasks or the running status of the system scheduler, which is convenient for judging task behavior and system scheduling.
eTaskGetState()
eTaskGetState()
is used to get the current status of a specified task, such as running, ready, blocked, suspended, or deleted. It is commonly used for task status monitoring and debugging.
API Prototype
eTaskState eTaskGetState( TaskHandle_t xTask );
Input Parameter |
Parameter Function |
Parameter Description |
---|---|---|
|
The task handle for which the status needs to be obtained. |
If a non-empty handle is passed in, the name of the corresponding task is queried; if |
Return Value |
Corresponding Status |
Status Description |
---|---|---|
|
Ready State |
The task is ready and waiting to be scheduled for execution. |
|
Running State |
The task is currently running (only applicable to the current task). |
|
Blocked State |
The task is waiting for an event or timeout. |
|
Suspended State |
The task is suspended and will not be scheduled. |
|
Deleted State |
The task has been deleted and is waiting for resource release. |
Note
The return value is an enumeration of type eTaskState
, returning the current status of the task.
Example 1: Get Task Status
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
TaskHandle_t task1Handle = NULL;
void vtask1(void *pvParameters) {
while (1) {
printf("Task 1 is running\n");
vTaskDelay(pdMS_TO_TICKS(2000)); // Enter blocked state
}
}
void vtask2(void *pvParameters) {
xTaskCreate(vtask1, "Task1", 2048, NULL, 2, &task1Handle);
eTaskState state;
int counter = 0;
while (1) {
if (eTaskGetState(xTaskGetCurrentTaskHandle()) == eRunning) {
printf("Task 2 state: RUNNING\n");
}
counter++;
if (task1Handle != NULL) {
state = eTaskGetState(task1Handle);
switch (state) {
case eRunning: printf("Task 1 state: RUNNING\n"); break;
case eReady: printf("Task 1 state: READY\n"); break;
case eBlocked: printf("Task 1 state: BLOCKED\n"); break;
case eSuspended: printf("Task 1 state: SUSPENDED\n"); break;
case eDeleted: printf("Task 1 state: DELETED\n"); break;
default: printf("Task 1 state: UNKNOWN\n"); break;
}
// Control Task 1 state changes
if (counter == 2) {
vTaskSuspend(task1Handle); // Task 1 suspended
printf("Task 1 suspended\n");
}
if (counter == 4) {
vTaskDelete(task1Handle); // Task 1 deleted
printf("Task 1 deleted\n");
}
}
vTaskDelay(pdMS_TO_TICKS(1000)); // Main task runs periodically
}
}
void app_main() {
xTaskCreate(vtask2, "Task2", 2048, NULL, 3, NULL); // Higher priority, convenient for obtaining Task 1 status
}
The execution result of the above code is as follows:
Task 2 state: RUNNING
Task 1 is running
Task 1 state: READY
Task 2 state: RUNNING
Task 1 state: BLOCKED
Task 1 suspended
Task 2 state: RUNNING
Task 1 state: SUSPENDED
Task 2 state: RUNNING
Task 1 state: SUSPENDED
Task 1 deleted
Task 2 state: RUNNING
Task 1 state: DELETED
This example demonstrates the use of eTaskGetState()
to obtain and observe the state changes of other tasks.
Two tasks are created in the system, where Task 1 has a lower priority, prints information during operation and periodically delays into a blocked state; the main control task Task 2 has a higher priority, immediately creates Task 1 after startup, and periodically performs the following operations:
Print its own status (verify that the current task is in the running state)
Obtain Task 1 status and print description through
switch
Suspend and delete Task 1 at specific periods, simulating state switching
Delay 1 second for each loop
According to the example output, the status and execution of Task 1 and Task 2 are as follows:
Loop number |
Execution result |
Task 1 |
Task 2 |
---|---|---|---|
1 |
Task 2 state: RUNNING |
Task 1 has been created, but has not yet run. |
Task 2 is executing, judging the current state as |
1 |
Task 1 is running |
Task 1 starts executing |
Task 2 yields the CPU after executing |
1 |
Task 1 state: READY |
Task 1 may have finished executing |
The scheduler switches back to Task 2, and Task 2 uses |
2 |
Task 2 state: RUNNING |
Task 1 is in a blocked state. |
Again, use |
2 |
Task 1 state: BLOCKED |
Task 1 has finished executing |
Task 2 queries the state of Task 1 and prints it as |
2 |
Task 1 suspended |
Task 1 is suspended, and the task enters the |
At this time |
3 |
Task 2 state: RUNNING |
Task 1 is in a suspended state. |
Again, use |
3 |
Task 1 state: SUSPENDED |
Task 1 is in a suspended state. |
Task 2 queries the state of Task 1, confirming that it is in a suspended state. |
4 |
Task 2 state: RUNNING |
Task 1 is in a suspended state. |
Again, use |
4 |
Task 1 state: SUSPENDED |
Task 1 is in a suspended state. |
Task 2 queries the state of Task 1, confirming that it is in a suspended state. |
4 |
Task 1 deleted |
The delete operation takes effect immediately, and Task 1 is marked for deletion. |
At this time |
5 |
Task 2 state: RUNNING |
Task 1 is in a deleted state. |
Again, use |
5 |
Task 1 state: DELETED |
Task 1 is in a deleted state. |
Query the state of Task 1, the system returns |
Although obtaining the task state provides an effective way to debug tasks, due to the very rapid state switching process and dependence on scheduler behavior, the following points should be noted when using:
After a task is created, it is immediately in a ready state, but whether it actually runs immediately depends on the scheduler’s scheduling decision (including priority and whether the current task yields the CPU).
Common triggers for the blocked state include: calling
vTaskDelay()
, waiting for a semaphore, queue, or event group.After a task is suspended, it will no longer be scheduled by the scheduler unless
vTaskResume()
is explicitly called to resume it.The status of a deleted task is
eDeleted
, but this status is only temporarily retained after the task is deleted. After that, its task handle may become invalid, and access will lead to unpredictable behavior. To avoid unpredictable behavior caused by accessing an invalid handle, it is recommended to set the corresponding task handle toNULL
after deleting the task.
printf()
is a blocking operation, which may cause task switching and affect the timing of state observation.The switching of task status (especially
eReady
andeBlocked
) is extremely brief, and it is easy to encounter boundary states in actual observation, which may cause misreading.The status information is maintained by the scheduler and updated at the appropriate time, so the status obtained through
eTaskGetState()
cannot be completely equivalent to the instantaneous execution situation of the task, and is only for reference.
Understanding the task status mechanism and its limitations can help make reasonable judgments in debugging and task scheduling design, and avoid misuse of status values.
xTaskGetSchedulerState()
xTaskGetSchedulerState()
is used to get the current state of the scheduler, to judge whether the scheduler is running, suspended or not started, and to help understand whether the system scheduling is normal.
API Prototype
BaseType_t xTaskGetSchedulerState( void );
Return value |
Description |
---|---|
|
The scheduler has not started yet, at this time all tasks will not be scheduled for execution, even if the task has been created. |
|
The scheduler has started and is running normally, tasks are scheduled according to priority. |
|
The scheduler is suspended, task scheduling is temporarily stopped, all tasks stop switching, but the task status remains unchanged, waiting for the scheduler to resume and continue scheduling. |
Example 1: Print scheduler status
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
void exampleTask(void *param)
{
int counter = 0;
bool print_resume = false;
BaseType_t state;
while (1) {
if (counter == 2) {
printf(">>> Suspended scheduler from task\n");
vTaskSuspendAll();
// Quietly busy wait for 3 seconds, unable to perform printf or delay
int64_t start = esp_timer_get_time();
while (esp_timer_get_time() - start < 3000000) {}
state = xTaskGetSchedulerState(); // Save the current scheduler status
xTaskResumeAll(); // Resume scheduler
print_resume = true;
}
if (print_resume) {
if (state == taskSCHEDULER_SUSPENDED) {
printf("Scheduler state: SUSPENDED\n");
}
printf(">>> Resumed scheduler from task\n");
print_resume = false;
}
state = xTaskGetSchedulerState();
if (state == taskSCHEDULER_RUNNING) {
printf("Scheduler state: RUNNING\n");
}
counter++;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
xTaskCreate(exampleTask, "exampleTask", 2048, NULL, 5, NULL);
}
The execution result of the above code is as follows:
Scheduler state: RUNNING
Scheduler state: RUNNING
>>> Suspended scheduler from task
Scheduler state: SUSPENDED
>>> Resumed scheduler from task
Scheduler state: RUNNING
Scheduler state: RUNNING
This example demonstrates how to use xTaskGetSchedulerState()
to print the scheduler state in a task, and the process of suspending and resuming the scheduler during execution.
The task periodically outputs the current state of the scheduler, showing that the scheduler still allows the current task to continue executing after suspension, and simulates the delay during suspension by busy waiting based on esp_timer_get_time()
.
After the scheduler is resumed, the task continues to print the running state normally.
It should be noted that:
Suspending the scheduler only prevents task switching, it does not pause the current task execution.
During the suspension of the scheduler, any blocking function, such as
vTaskDelay()
orprintf()
that uses a mutex internally, cannot be called, otherwise it will cause the system to assert failure.In the ESP-IDF environment, the scheduler startup is automatically completed by the system, and the
taskSCHEDULER_NOT_STARTED
state cannot be observed.
The operations of suspending and resuming the scheduler must be used in pairs to ensure the stable operation of the system.
Among them, vTaskSuspendAll()
is used to temporarily suspend the FreeRTOS task scheduler to prevent task switching; xTaskResumeAll()
resumes task scheduling and allows task switching to continue.
The two are used together to temporarily prohibit task switching in a section of code, ensuring the continuity and consistency of execution.
Note
Calling xTaskResumeAll()
only resumes the scheduler suspended by vTaskSuspendAll()
, but it will not resume the task suspended by vTaskSuspend()
.
In the native FreeRTOS environment, vTaskSuspendAll()
only suspends the task scheduler and does not disable interrupts, so interrupts can still be executed normally, which is suitable for scenarios where the critical area is large and interrupt response is allowed.
After calling xTaskResumeAll()
, the scheduler resumes operation and may trigger a task switch.
In the ESP-IDF environment, the principles of the two functions are similar, but the implementation is more complex. The suspension of scheduling generally only affects the calling core itself and will not suspend the scheduling of other cores. Due to the multi-core characteristics, it is usually necessary to combine hardware characteristics and multi-core synchronization mechanisms (such as spin locks) for flexible application when protecting shared resources.
For further information about suspending and resuming the scheduler in the ESP-IDF environment, please refer to the section on FreeRTOS Scheduler Suspension in the official ESP-IDF documentation.
Runtime API
The Runtime Class API is used to obtain system runtime information and task runtime statistics, assisting in performance analysis and system monitoring.
xTaskGetTickCount()
xTaskGetTickCount()
is used to obtain the tick count since system startup, commonly used for implementing delays or calculating time intervals.
API Prototype
TickType_t xTaskGetTickCount( void );
Return Value |
Explanation |
---|---|
Returns system tick count (ticks) |
The system tick count (ticks) that has passed since |
Example 1: Loop to read the current tick
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Task function
void vDelayTask (void *pvParameters)
{
while (1) {
TickType_t now = xTaskGetTickCount(); // Read the current tick
printf("Tick: %lu \n", now);
vTaskDelay(10); // Delay 10 ticks
}
}
void app_main(void)
{
xTaskCreate(vDelayTask, "Task1", 2048, NULL, 1, NULL);
}
The execution result of the above code is as follows:
Tick: 2
Tick: 12
Tick: 22
Tick: 32
Tick: 42
Tick: 52
This example creates a periodic task that calls xTaskGetTickCount()
in a loop to get the current system tick count and prints it out.
The task runs every 10 ticks through vTaskDelay(10)
, forming a fixed rhythm.
From the output results, it can be seen that the tick count starts to increase after the scheduler starts, and the task outputs the tick value stably according to the set cycle.
This API is also often used to calculate the time difference after the task starts.
You can call xTaskGetTickCount()
at the beginning of the task to record the initial tick value, call it again at a later moment and subtract it to calculate the number of ticks passed during the task execution. It is suitable for running time statistics or logic interval judgment.
In addition, when using vTaskDelayUntil()
to implement periodic tasks, a tick base time needs to be maintained in the task. This time base is initialized by xTaskGetTickCount()
to record the cycle start point, ensuring that each delay aligns with the tick to avoid cycle drift caused by task execution time fluctuations.
vTaskGetRunTimeStats()
vTaskGetRunTimeStats()
is used to obtain the runtime proportion information of each task, output in string form, commonly used for performance analysis and task execution time monitoring (requires enabling related configuration).
API Prototype
void vTaskGetRunTimeStats( char *pcWriteBuffer );
Input Parameter |
Parameter Function |
Parameter Explanation |
---|---|---|
|
A pointer to a character array allocated by the user. |
Used to receive runtime statistics information (in ASCII text format) from various tasks. |
Note
This buffer does not perform boundary checks, users need to ensure its capacity is large enough. It is recommended to reserve about 40 bytes for each task to prevent the output content from being truncated.
API Configuration
The relevant configuration needs to be enabled before calling this API:
Target Macro |
Function |
---|---|
|
Generate runtime statistics. After enabling, it can collect CPU time usage information for each task. |
|
Enable formatting output functions such as |
|
Support dynamic memory allocation. Enable dynamic creation of tasks/queues/timers and other APIs. |
|
Configure the runtime statistics timer. Users need to provide an implementation to initialize the hardware timer to collect time information. |
|
Get the current runtime for sampling by statistical functions. |
The native FreeRTOS configuration steps are as follows:
Open the
FreeRTOSConfig.h
file in the FreeRTOS project.Check if the macros in the table are defined (they may be commented out or set to 0).
If set to 0, change it to 1. If not, manually add the following content:
#define configGENERATE_RUN_TIME_STATS 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 // Can be changed to other self-implemented timer reading functions, returning an increasing time base (units can be microseconds, milliseconds, etc.). #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() // If SysTick has been initialized by FreeRTOS, leave it blank #define portGET_RUN_TIME_COUNTER_VALUE() (xTaskGetTickCount() * portTICK_PERIOD_MS)
Save the file and recompile.
The configuration steps based on the ESP-IDF environment are as follows:
Open the ESP-IDF terminal.
Enter the following command to open the
menuconfig
configuration interface:idf.py menuconfig
Follow the path: Component config > FreeRTOS > Kernel, then check
configGENERATE_RUN_TIME_STATS
,configUSE_TRACE_FACILITY
andconfigUSE_STATS_FORMATTING_FUNCTIONS
.If these two macros are not under this path, you can press
/
and enter the macro name to search.Press
s
to save the changes.Define macros in the project code:
// Take esp_timer as an example #include "esp_timer.h" #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() // esp_timer is automatically initialized, no implementation required #endif #ifndef portGET_RUN_TIME_COUNTER_VALUE #define portGET_RUN_TIME_COUNTER_VALUE() (esp_timer_get_time()) #endif
Note
In ESP-IDF, FreeRTOSConfig.h
is automatically managed by the system component. Direct modifications by the user will be overwritten during compilation, so it is not recommended to directly modify this file. The correct approach is to configure related options through menuconfig
, and define necessary macros in the project code to implement custom functions.
Example 1: Obtaining the proportion of task runtime information
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
// Bind esp_timer as the runtime statistics timer
#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() // esp_timer is automatically initialized, no implementation required
#endif
#ifndef portGET_RUN_TIME_COUNTER_VALUE
#define portGET_RUN_TIME_COUNTER_VALUE() (esp_timer_get_time()) // Returns microseconds
#endif
void vExampleTask(void *pvParameters)
{
while (1) {
printf("Example Task running...\n");
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
void app_main(void)
{
// Create example task
xTaskCreate(vExampleTask, "ExampleTask", 2048, NULL, 5, NULL);
// Allocate buffer to store runtime statistics string
char *stats_buffer = pvPortMalloc(512);
if (stats_buffer == NULL) {
printf("无法分配内存\n");
return;
}
// After enabling the runtime statistics function, continuously print task runtime information
while (1) {
vTaskGetRunTimeStats(stats_buffer);
printf("\nTask Run Time Stats (microseconds):\n%s\n", stats_buffer);
vTaskDelay(pdMS_TO_TICKS(2000));
}
// This point will never be reached
vPortFree(stats_buffer);
}
The execution result of the above code is as follows:
Example Task running...
Task Run Time Stats (microseconds):
main 20518 38%
IDLE1 19828 37%
IDLE0 0 <1%
ExampleTask 111 <1%
ipc1 32304 60%
ipc0 32149 60%
Example Task running...
Task Run Time Stats (microseconds):
main 32851 1%
IDLE1 2019828 98%
IDLE0 1991926 96%
ExampleTask 208 <1%
ipc0 32149 1%
ipc1 32304 1%
This example demonstrates how to use vTaskGetRunTimeStats()
in ESP-IDF to obtain the CPU usage information of tasks.
The example achieves runtime statistics by creating a simple task and combining it with esp_timer_get_time()
, periodically printing the cumulative runtime and percentage of all tasks in the system.
The statistical results include user tasks, the default main
task, and system tasks (such as IDLE
and IPC
tasks), which are used to observe the running proportion of each task.
This method is suitable for system performance analysis and task behavior monitoring. Before using, you need to enable the runtime statistics function in the configuration and provide a string buffer to store the output content.
Stack and Statistics API
The stack and statistics class API is used to obtain the task stack usage and the system task quantity and status list, helping to detect the risk of stack overflow and system task management.
uxTaskGetStackHighWaterMark()
uxTaskGetStackHighWaterMark()
is used to obtain the minimum remaining stack space since the task has been running, which is used to evaluate the peak of stack usage and assist in judging whether there is a risk of stack overflow.
API Prototype
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
Input Parameter |
Parameter Function |
Parameter Description |
---|---|---|
|
The task handle to be queried. |
If a non-empty handle is passed in, the stack space of the corresponding task is queried; if |
Return Value |
Description |
---|---|
Returns the stack high water mark in words. |
Used to monitor the safety margin of the task stack, helping to adjust the stack size in time to avoid overflow. |
Note
For example, in a 32-bit system:
If the return value is 1, it means that there are 4 bytes of stack that have not been used.
If the return value is close to 0, it means that the task stack is close to the risk of overflow.
If the return value is 0, it means that the task stack may have overflowed.
Example 1: Query the remaining stack space of the task
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Define a global variable to save the handle of Task 2
TaskHandle_t xHandleTask2 = NULL;
void vTask1(void *pvParameters)
{
while (1) {
// Simulate some operations
vTaskDelay(pdMS_TO_TICKS(1000));
// Get the minimum remaining stack space of the task (unit: word)
UBaseType_t uxHighWaterMarkForTask1 = uxTaskGetStackHighWaterMark(NULL); // Current task
UBaseType_t uxHighWaterMarkForTask2 = uxTaskGetStackHighWaterMark(xHandleTask2); // Task 2
printf("Minimum remaining stack for Task 1: %u \n", uxHighWaterMarkForTask1);
printf("Minimum remaining stack for Task 2: %u \n", uxHighWaterMarkForTask2);
}
}
void vTask2(void *pvParameters)
{
// Get the current task handle
xHandleTask2 = xTaskGetCurrentTaskHandle();
while (1) {
// Simulate some operations
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
xTaskCreate(vTask1, "Task 1", 2048, NULL, 2, NULL);
xTaskCreate(vTask2, "Task 2", 2048, NULL, 2, NULL);
}
The execution result of the above code is as follows:
Minimum remaining stack for Task 1: 1344
Minimum remaining stack for Task 2: 1352
Minimum remaining stack for Task 1: 128
Minimum remaining stack for Task 2: 1352
Minimum remaining stack for Task 1: 128
Minimum remaining stack for Task 2: 1352
This example demonstrates the typical usage of uxTaskGetStackHighWaterMark()
, by passing in different task handles, you can get the smallest remaining stack space since the task has been running.
The API returns the historical minimum remaining value, not the current remaining stack, which is used to evaluate the peak stack usage during task execution and determine whether there is a risk of approaching overflow. The return value is in units of word, which can be converted to bytes as needed.
By regularly printing this value, you can dynamically monitor the task stack usage during the debugging stage, ensure that the minimum remaining stack is always maintained within a reasonable range, thereby optimizing the stack configuration and improving the stability and reliability of the system.
uxTaskGetNumberOfTasks()
uxTaskGetNumberOfTasks()
is used to get the total number of tasks currently running in the system, which is convenient for monitoring the number of tasks and system load.
API Prototype
UBaseType_t uxTaskGetNumberOfTasks( void );
Return Value |
Explanation |
---|---|
Returns the number of tasks currently being managed by the RTOS kernel. |
Includes all ready, blocked and suspended tasks. Tasks that have been deleted but not yet released by the idle task are also included in the count. |
Example 1: Query the current number of tasks
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
TaskHandle_t xHandleTask2 = NULL;
void vTask1(void *pvParameters)
{
int count = 0;
while (1) {
UBaseType_t taskCount = uxTaskGetNumberOfTasks();
printf("Current number of system tasks: %u\n", taskCount);
if (count == 2) {
printf(">>>>> Task 2 SUSPENDED\n");
vTaskSuspend(xHandleTask2);
}
else if (count == 4) {
printf(">>>>> Task 2 DELETED\n");
vTaskDelete(xHandleTask2);
}
count++;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vTask2(void *pvParameters)
{
// Get the current task handle
xHandleTask2 = xTaskGetCurrentTaskHandle();
while (1)
{
printf(">>>>> Task 2 is RUNNING\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
xTaskCreate(vTask1, "Task1", 2048, NULL, 5, NULL);
xTaskCreate(vTask2, "Task2", 2048, NULL, 5, NULL);
}
The execution result of the above code is as follows:
Current system task count: 6
>>>>> Task 2 is RUNNING
Current system task count: 6
>>>>> Task 2 is RUNNING
Current system task count: 6
>>>>> Task 2 SUSPENDED
Current system task count: 6
Current system task count: 6
>>>>> Task 2 DELETED
Current system task count: 5
Current system task count: 5
This example obtains the total number of tasks in the current system in real time by calling uxTaskGetNumberOfTasks()
, verifying that this API will count tasks including ready state, blocked state, suspended state, and tasks that have been deleted but not yet released by idle tasks.
Task 1 periodically prints the task count, and suspends and deletes Task 2 during operation. The output result shows that the task count remains unchanged in the suspended state, and the task count decreases after the task is deleted and released, indicating that this API can accurately reflect the actual number of valid tasks in the system.
This function is often used in system debugging, task lifecycle monitoring, and resource management scenarios, making it easier for developers to determine whether the task is growing abnormally, whether it is being correctly released, thereby enhancing the controllability and stability of the system.
vTaskList()
vTaskList()
is used to output the status information of all tasks in the system in text form, including task name, status, priority, remaining stack, and task number, often used for task monitoring and debugging (requires enabling related configuration).
API Prototype
void vTaskList( char *pcWriteBuffer );
Input Parameter |
Parameter Function |
Parameter Description |
---|---|---|
|
A pointer to a character array allocated by the user. |
Used to receive the runtime statistics of each task (in ASCII text format). |
In the ASCII table, the following letters are used to represent the status of the task:
X
- Running
B
- Blocked
R
- Ready
D
- Deleted (waiting for cleanup)
S
- Suspended or blocked, no timeout
API Configuration
Before calling this API, you need to enable the related configuration:
Target Macro |
Function |
---|---|
|
Enables task status tracking feature, must be set to 1 to use task statistics related functions. |
|
Enables formatting output functions such as |
The native FreeRTOS configuration steps are as follows:
Open the
FreeRTOSConfig.h
file in the FreeRTOS project.Check if these two macros are defined (may be commented out or set to 0).
If set to 0, change it to 1. If not, manually add the following content:
#define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1
Save the file and recompile.
The configuration steps based on the ESP-IDF environment are as follows:
Open the ESP-IDF terminal.
Enter the following command to open the
menuconfig
configuration interface:idf.py menuconfig
Follow the path: Component config > FreeRTOS > Kernel, then select
configUSE_TRACE_FACILITY
andconfigUSE_STATS_FORMATTING_FUNCTIONS
.If these two macros are not under this path, you can press
/
and enter the macro name to search.Press
s
to save the changes.
Note
In ESP-IDF, FreeRTOSConfig.h
is automatically managed by system components. Direct modifications by users will be overwritten during compilation, so it is not recommended to directly modify this file. The correct approach is to configure related options through menuconfig
and define necessary macros in the project code to implement custom functions.
Example 1: Query Task List
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
TaskHandle_t xHandleTask2 = NULL;
void vPrintTaskList(void)
{
// Allocate enough buffer to store task list information
// Note that the buffer size needs to be adjusted appropriately as the stack size and task number vary
char *taskListBuffer = pvPortMalloc(512);
if (taskListBuffer != NULL) {
vTaskList(taskListBuffer);
printf("Task list:\n%s\n", taskListBuffer);
vPortFree(taskListBuffer);
} else {
printf("Insufficient memory, unable to print task list\n");
}
void vTask1(void *pvParameters)
{
int count = 0;
while (1) {
UBaseType_t taskCount = uxTaskGetNumberOfTasks();
printf("Current system task count: %u\n", taskCount);
vPrintTaskList();
if (count == 2) {
printf(">>>>> Task 2 SUSPENDED\n");
vTaskSuspend(xHandleTask2);
}
else if (count == 3) {
printf(">>>>> Task 2 DELETED\n");
vTaskDelete(xHandleTask2);
}
count++;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vTask2(void *pvParameters)
{
xHandleTask2 = xTaskGetCurrentTaskHandle();
while (1)
{
printf(">>>>> Task 2 is RUNNING\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
xTaskCreate(vTask1, "Task1", 4096, NULL, 2, NULL);
vTaskDelay(pdMS_TO_TICKS(1000));
xTaskCreate(vTask2, "Task2", 4096, NULL, 2, NULL);
}
The execution result of the above code is as follows:
Current system task count: 6
Task list:
Task1 X 2 2176 6
main R 1 1920 3
IDLE1 R 0 800 5
IDLE0 R 0 1016 4
ipc1 S 24 536 2
ipc0 S 24 528 1
Current system task count: 6
Task list:
Task1 X 2 2144 6
main R 1 1920 3
IDLE1 R 0 800 5
IDLE0 R 0 808 4
ipc0 S 24 528 1
ipc1 S 24 536 2
>>>>> Task 2 is RUNNING
Current system task count: 6
Task list:
Task1 X 2 2144 6
IDLE1 R 0 800 5
IDLE0 R 0 808 4
Task2 B 2 952 7
ipc1 S 24 536 2
ipc0 S 24 528 1
>>>>> Task 2 SUSPENDED
Current system task count: 6
Task list:
Task1 X 2 2144 6
IDLE1 R 0 800 5
IDLE0 R 0 808 4
Task2 S 2 952 7
ipc0 S 24 528 1
ipc1 S 24 536 2
>>>>> Task 2 DELETED
Current system task count: 5
Task list:
Task1 X 2 2144 6
IDLE1 R 0 800 5
IDLE0 R 0 808 4
ipc1 S 24 536 2
ipc0 S 24 528 1
Current system task count: 5
Task list:
Task1 X 2 2144 6
IDLE1 R 0 800 5
IDLE0 R 0 808 4
ipc0 S 24 528 1
ipc1 S 24 536 2
In the previous uxTaskGetNumberOfTasks()
example, although only two tasks were explicitly created, the actual printed task count was 6, indicating that there are other default tasks in the system.
This example extends on this basis, introducing vTaskList()
, which is used to output detailed information about all tasks in the current system, including task name, status, priority, remaining stack space, and task number.
From the running results, it can be seen that in addition to the user-created Task1 and Task2, the system also contains multiple internal tasks, such as idle tasks (IDLE0
and IDLE1
), the initial main
task (used to run app_main()
), and kernel scheduling related tasks (ipc0
and ipc1
).
Therefore, after Task1 is created, the total number of system tasks is 6; after creating Task2, although one task is added, the main
task exits and deletes itself after app_main()
is executed, so the total number of tasks is still 6, until Task2 is deleted and then reduced to 5.
When using vTaskList()
, note the following:
Properly delay before creating the second task (such as calling
vTaskDelay()
) to avoid stack overflow due to too fast task creation.The stack space required for each task should be allocated according to the function, and the size of the task information buffer (that is, the
buffer
passed intovTaskList()
) should also be reasonably allocated according to the number of tasks to prevent output truncation or insufficient memory.In ESP-IDF, the
main
task that executesapp_main()
will automatically delete itself after the function ends, and the number of tasks will decrease. But in native FreeRTOS,main
does not exist as a task and will not appear in the task list.The task number is an internal auto-increment identifier of the system, and it is not reused due to task deletion, so the number may jump.