Embedded systems often include bugs developers have a hard time to find. One common cause of such tricky bugs are stacks that grow beyond their dedicated memory area, thus causing unpredictable behavior and a malfunctioning system.
This is because the stack can overwrite important variable values, that thus inadvertently get the wrong value. Alternatively, a variable overwrite the stack, as the stack has expanded into the memory area dedicated to that variable. Upon the next function call return, the software crashes as it returns to the wrong location (the return address is stored on the overwritten stack and execution continues on a random location). Due to these reasons, stack related bugs often appear to be completely random, in most cases making them incredibly difficult to find.
If you use an RTOS running parallel tasks, these problems can multiply. Each task has its own stack, and with more tasks, you get more stacks that can cause stack related bugs. The stack depth analyzer in TrueSTUDIO Pro can help resolve such problems and help develop more robust systems. Read this blog post to learn more on analyzing the stack usage on Cortex-M systems developed with the GNU GCC compiler in TrueSTUDIO!
Introduction to the Static Stack Usage Analyzer
The Static Stack Analyzer view in TrueSTUDIO Pro calculates the stack usage based on the compiled and linked program, and presents stack usage information in the view. It is thus a great tool for finding stack related bugs on ARM Cortex-M targets. Traditionally, this highly useful feature has not been available in embedded IDE's - in particular those based on the GNU GCC compiler.
Atollic TrueSTUDIO change this, with its powerful Static Stack Analyzer view. The view contains two tabs; The List tab and the Call Graph tab. Use them to get a deep and detailed understanding of how your embedded system uses the stack.
The List Tab
The List tab is populated with the stack usage for each function included in the program. There is one line per function and each line consist of Function name, Local cost, Type, Location and Info columns.
Normally there is a small icon to the left of the function name in the Function column. The icon is:
- A green dot when the function uses STATIC stack allocation (fixed stack usage)
- A blue square when the function uses DYNAMIC stack allocation (run-time dependent stack usage)
- An 010 icon if the stack information is not known. This can be the case for library functions or assembler functions.
- Three arrows in a circle are used in the Call Graph view when the function makes recursive calls
The Local cost column specifies how many bytes of stack the function will use. This column does not take into account any stack which may be needed by functions it may call.
The Type column specifies:
- STATIC (the function uses a fixed stack size)
- DYNAMIC (the function uses a run-time dependent stack size)
- Empty field (no stack usage information is available for the function)
The Call Graph Tab
The Call Graph tab contains an expandable list with the functions included in the program. Lines, representing the functions, can be expanded to see the function call hierarchy.
The icon in the Function column, and the Type column, behave the same as in the List tab.
The Depth column specifies the call stack depth for each function:
- 0 when function does not call any other functions
- Number >=1 when function calls other functions
- ? when function makes recursive calls or the depth could not be calculated
The Max cost column specifies how many bytes of stack afunction will use including stack needed for called functions.
The Local cost column specifies how many bytes of stack a function will use. This column does not take into account any stack which may be needed by functions it may call.
Not only can you use this feature to work out the maximum stack usage the main() function and its call-tree use, but you can also use it to work out how much memory you need to allocate to various RTOS tasks; for example "Task_BackGroundCalculation()", or "Task_TCPIPListener()", or whatever your tasks may be called.
Using the Static Stack Analyzer
The Static Stack Analyzer view will be populated when a project has been built and is selected in the Project Explorer. The program needs to be built with the Generate per function stack usage information option enabled. Otherwise the view will not be able to present any stack usage information.
If the top of the view displays the message No stack usage information found, please enable this feature in the compiler settings and rebuild the project.
To do this, open the properties for the project, for instance with a right-click on the project in the Project Explorer view. Select Properties and in the dialog, select C/C++ Build, Settings. Select the Tool Settings-tab, C Compiler, Debugging and enable Generate per function stack usage information.
Then save the settings and rebuild the program.
The main() function is normally called by the Reset_Handler. It might therefore not be visible in an unexpanded call-stack tree. In such case, main() can be displayed by expanding the Reset_Handler node.
Additionally, by double-clicking on a line which displays the file location and line number in a tab, the corresponding function implementation will be opened in the Editor view.
Understanding how the application uses the stack is a must for embedded developers who want to be in control of their design. In particular, the capability to analyze the maximum stack usage is a major bug-preventer. Every professional embedded developer ought to be on top of this, and TrueSTUDIO Pro makes this type of stack usage analysis ever so much easier.
To my knowledge, Atollic TrueSTUDIO Pro is the only GNU GCC-based ARM Cortex IDE on the market that offers this valuable capability. Use this feature to ensure your project do not suffer from stack overwrite bugs that are very hard to find and correct!
To learn more on ARM Cortex development and debugging, read this free whitepaper: