I'm working on a trace module which has to monitor FreeRTOS tasks' heap in order to detect stack overflows. I am wondering whether it is possible to get a task stack size after its creation. Can I get access to this information through the API? Or, is it stored in some internal structure? How do I get access to it?
I am wondering whether it is possible to get a task stack size after its creation.
Can I get access to this information through the API?
Or, is it stored in some internal structure? How do I get access to it?
Great question! Yes, yes you can get this information.
As of FreeRTOS V10.0.0, you'll see two really important additions in the History.txt
file here:
Changes between FreeRTOS V9.0.1 and FreeRTOS V10.0.0:
...
Introduced
configRECORD_STACK_HIGH_ADDRESS
. When set to1
the stack start address is saved into each task's TCB (assuming stack grows down).Introduced
configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
to allow user defined functionality, and user defined initialisation, to be added to FreeRTOS'stasks.c
source file. WhenconfigINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
is set to1
a user provided header file calledfreertos_task_c_additions.h
will be included at the bottom oftasks.c
. Functions defined in that header file can callfreertos_tasks_c_additions_init()
, which in turn calls a macro calledFREERTOS_TASKS_C_ADDITIONS_INIT()
, if it is defined.FREERTOS_TASKS_C_ADDITIONS_INIT()
can be defined inFreeRTOSConfig.h
.
1. To look for stack overflow risk, get the high stack watermark
See usStackHighWaterMark
in the TaskStatus_t
struct in task.h
. Even better: use these two functions:
uxTaskGetStackHighWaterMark();
uxTaskGetStackHighWaterMark2();
See: https://www.freertos.org/uxTaskGetStackHighWaterMark.html
There is also a really nice convenience function called vTaskList()
and vTaskListTasks()
from task.h
that prints out a table of all tasks, including their stack high water mark.
See here: https://www.freertos.org/a00021.html#vTaskList
2. Write your own taskGetStackSizeWords()
and taskGetStackSizeBytes()
functions to get stack size/depth in words and bytes
So, to get the stack depth (size) in words or bytes, you can do the following steps:
In your custom
FreeRTOSConfig.h
file, add:#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1 #define configRECORD_STACK_HIGH_ADDRESS 1
Create a file called
freertos_task_c_additions.h
. Since you definedconfigINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
as1
above, it will be automatically included by FreeRTOS directly into the very bottom of theirtasks.c
file, giving your file direct access to all of the private members andstatic
content intasks.c
. DefiningconfigRECORD_STACK_HIGH_ADDRESS
as1
above addspxEndOfStack
, which we need in the stack size calculation below, to the task control block (TCB_t
) struct intasks.c
. FreeRTOS tells us intasks.c
thatpxEndOfStack
"points to the highest valid address for the stack", meaning: the end of the stack.pxStack
, on the other hand, points to the beginning of the stack. So, the stack size ispxEndOfStack - pxStack + 1
.In your
freertos_task_c_additions.h
file, add the following:// NB: THIS IS TYPICALLY NOT A HEADER FILE YOU SHOULD INCLUDE IN YOUR CODE. // - Treat this like a .c source file. FreeRTOS includes this file instead. // - The file you should include to access these public functions below is // `freertos_task_c_additions_include.h`. #pragma once #include "FreeRTOS.h" #if ((configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1) \ && (configRECORD_STACK_HIGH_ADDRESS == 1)) // Get the stack size in words, not bytes, first passed to `xTaskCreate()` // when the task was created. This is the size of the stack allocated for // the task. // - Note: due to the alignment that FreeRTOS enforces to the top (end) of // the stack via `portBYTE_ALIGNMENT_MASK` in tasks.c, the available stack // size is actually one or two bytes or so less than what you input at // task creation. FreeRTOS adjusts `pxTopOfStack`, which gets assigned to // `pxEndOfStack`, for stack alignment. So, if you passed in 200 words to // `xTaskCreate()` (see: https://www.freertos.org/a00125.html) as the // `usStackDepth` value, this function may return 199 instead, which is // the actual, correct, stack size available. // - Partially learned from: // https://www.freertos.org/FreeRTOS_Support_Forum_Archive/January_2019/freertos_Retrieve_the_size_and_maximum_usage_of_the_stack_per_task_7ab5c6eb05j.html uint32_t taskGetStackSizeWords(TaskHandle_t taskHandle) { uint32_t stackSizeBytes; TCB_t * tcb = prvGetTCBFromHandle(taskHandle); // critical section to access protected TCB (Task Control Block) // members from this task's `struct tskTaskControlBlock` taskENTER_CRITICAL(); { // +1 to count both the end and start in the calculation stackSizeBytes = tcb->pxEndOfStack - tcb->pxStack + 1; } taskEXIT_CRITICAL(); // Optionally [but not recommended], you may add +1 again to give // us the same value that we input when we created the task; // technically, `portBYTE_ALIGNMENT_MASK` removed one of our bytes // due to alignment, so actually our stack is 1 byte less // than our input at creation. // stackSizeBytes += 1; return stackSizeBytes; } // Get the stack size in bytes. // - See details in the `taskGetStackSizeWords()` function above. uint32_t taskGetStackSizeBytes(TaskHandle_t taskHandle) { uint32_t stackSizeWords = taskGetStackSizeBytes(taskHandle)*sizeof(StackType_t); return stackSizeWords; } #endif // ((configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1) // && (configRECORD_STACK_HIGH_ADDRESS == 1))
Use the functions above.
In the source file where you want to use the functions above, you can get access to them by forward declaring them as follows, then using them afterwards:
#include "FreeRTOS.h" #include "task.h" // Note: `extern` here is optional for functions. // See my answer here: https://mcmap.net/q/74543/-how-to-call-a-static-function-in-one-c-source-code-file-from-another-c-source-code-file extern uint32_t taskGetStackSizeWords(TaskHandle_t taskHandle); extern uint32_t taskGetStackSizeBytes(TaskHandle_t taskHandle); void in_some_func() { uint32_t stackSizeBytes = taskGetStackSizeBytes(xTaskGetCurrentTaskHandle()); uint32_t stackSizeWords = taskGetStackSizeWords(xTaskGetCurrentTaskHandle()); // etc. }
Or, even better, you can create a new header file with the forward declarations inside it. Ex:
freertos_task_c_additions_include.h
:# pragma once #ifdef __cplusplus extern "C" { #endif #include "FreeRTOS.h" #include "task.h" #if ((configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1) \ && (configRECORD_STACK_HIGH_ADDRESS == 1)) uint32_t taskGetStackSizeWords(TaskHandle_t taskHandle); uint32_t taskGetStackSizeBytes(TaskHandle_t taskHandle); #endif // ((configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1) // && (configRECORD_STACK_HIGH_ADDRESS == 1)) #ifdef __cplusplus } #endif
Then use it:
#include "freertos_task_c_additions_include.h" void in_some_func() { uint32_t stackSizeBytes = taskGetStackSizeBytes(xTaskGetCurrentTaskHandle()); uint32_t stackSizeWords = taskGetStackSizeWords(xTaskGetCurrentTaskHandle()); // etc. }
Explanation
Since you defined configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
to 1
in your config file, FreeRTOS automatically includes this custom freertos_task_c_additions.h
file at the bottom of tasks.c
.
Treat this "header" file like a source code file that gets included in another source code file.
This way, your code in freertos_task_c_additions.h
can access all of the private members in tasks.c
. All of your customizations to FreeRTOS should be contained within this file, not inserted into the original FreeRTOS source code.
For more information:
- Search the code for
configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
. - See also this forum discussion: https://www.freertos.org/FreeRTOS_Support_Forum_Archive/January_2019/freertos_Retrieve_the_size_and_maximum_usage_of_the_stack_per_task_7ab5c6eb05j.html
References
- I relied heavily upon my detailed answer here: How to call a static function in one C source code file from another C source code file?
- I also studied the forum post in the link just above, especially the part "posted by mastupristi on January 31, 2019".
- The FreeRTOS
History.txt
file I quoted above - Reminder on how to use
extern "C"
to prevent name-mangling of C-compiled object files linked into C++ programs: What is the effect ofextern "C"
in C++? - https://www.freertos.org/uxTaskGetStackHighWaterMark.html
vTaskList()
andvTaskListTasks()
: https://www.freertos.org/a00021.html#vTaskList- This other answer. It had the link to here: https://www.freertos.org/uxTaskGetStackHighWaterMark.html
The task stack size is not stored, but can be calculated from the pxStack
and pxEndOfStack
members of the task control block. I would imagine the easiest way of detecting a stack overflow would be to use the built-in stack overflow checking though.
tskTaskControlBlock
which contains all the info about the beginning and the end of the stack is a private type which means it can't be accessed outside this file. I'm currently working on a project where I'm not allowed to modify FreeRTOS code, so I was looking for a way to get the information using the external API (accessible through the header files) –
Stinky There should be a call to taskGetStackSizeWords
instead of taskGetStackSizeBytes
in the answer above:
uint32_t taskGetStackSizeBytes(TaskHandle_t taskHandle)
{
uint32_t stackSizeBytes =
taskGetStackSizeWords(taskHandle)*sizeof(StackType_t);
return stackSizeBytes ;
}
Pay attention that the flag is configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
(singular) but the task.c requires freertos_tasks_c_additions.h
(plural).
#include "freertos_tasks_c_additions.h"
© 2022 - 2025 — McMap. All rights reserved.