In the last post, we discussed how a thread is a semi-independent program and saw how every thread has a Thread Control Block (TCB) associated with it. The TCB allows a developer to configure how a thread will behave and set the stack size that will be associated with the thread. Don’t forget that every thread will have its own private stack in addition to the stack that is used during initialization. So how do we make sure that we have a properly sized stack? That is the exact question that we will be discussing in today’s post.
Many developers select their stack size by sheer luck. They look at the thread, shrug their shoulders and pick a value out of thin air while crossing their fingers and hoping that they have chosen wisely. If at any point their application exhibits strange behavior, developers will bump up their stack size with great optimism and hope that resolves the problem. Readers may think that I am picking on developers but the fact is, figuring out how to properly size the stack is not trivial and developers just don’t understand and deal with the stack enough to confidently select the right value.
There are a couple ways that developers can go about selecting the correct stack size. These include:
Manually calculating the worst-case stack analysis can be tricky business. Developers need to track local variables that are being allocated to the stack, parameters that are being passed into functions and possibly even interrupt and context switch frames (being able to store all the CPU registers). To properly track stack usage, developers need to understand their toolchain and determine when the stack will be used versus the CPU registers. The manual process can be time consuming and error prone.
Alternatively, developers may be able to use a static code analyzer instead. The static code analyzer will perform the calculations on its own in just a few seconds depending on the code size. Using a static code analyzer can provide a developer with a rough idea for how much stack space is required but the tools will often not consider interrupts or functions that are being accessed through a function pointer. Developers wanting to get an accurate result will have to create conditional compilation sections that replace the function pointer accesses with direct function calls.
Finally, there is an experimental method that developers can use that is built into the feature set for the e2 Studio. While running the code in debug mode, developers can access a special view called the RTOS Resources view which is accessible through the Partner OS menu. As shown in the image below, developers can use the Stack tab within the RTOS Resources view to observe the stack watermark in memory.
Every thread stack is initialized with a known pattern when the thread is created. As the stack is used, the watermark values become overwritten with data which allows a developer to see how deep into the stack the thread is going. The goal would be for a developer to run their code for a suitable time period and provide the worst-case inputs into the system in order to load the system threads to their maximum. The MaxStackUsage can then become a guideline for determining how to size the stack.
In the image, the default thread stack size being used is 1024 bytes; However, after stressing the system the maximum stack usage for the threads is found to be 152 bytes or less. The thread stack is oversized by nearly a factor of ten! Developers shouldn’t trust the watermark values blindly. These values are determined from running the application but what if the worst-case was never really achieved? Using these values could result in a stack overflow. A good general rule of thumb is to set the stack size to at least one and a half times the worst case. Doing so provides a safety buffer in case testing or the calculation was off slightly.
If Renesas Synergy™ Platform developers size their stacks improperly, they could end up with a stack overflow. When a stack overflow occurs, the most common result is that the developer will find himself in the NMI_handler. Developers then need to figure out which thread was the culprit in producing the stack overflow. In the debugger perspective, developers can examine the I/O Registers view in the upper right hand corner. Doing a search for STMON will reveal the register that will provide the necessary information to identify the violation.
In a best-case scenario, developers will use multiple methods to determine what their worst-case thread stack size should be. Generally using a static code analyzer along with the experimental method will provide developers with accurate results, based on engineering principles and not a lucky guess. As you can see, improperly sizing the stack can lead to memory being wasted or corrupted.
In the next post, we will discuss how to properly set thread priorities. Until next time,
Live long and profit!
Hot Tip of the Week
While analyzing the stack size of your application it may be helpful to also use the RTOS Resource Viewer available in e2 studio to also observe related RTOS elements such as semaphores, mutexes, and memory block pools. Watch this 3 min video to get a quick overview of what the RTOS Resource Viewer can do to speed your analysis tasks. https://www.youtube.com/watch?v=h-VJyRqfzxw&index=22&list=PLgUXqPkOStPu_uZCwn_1tM2QZIRDhcbCR