2

I'm writing an embedded program that uses a static limited stack area of a known size (in other words, I have X bytes for the stack, and there's no overlaying OS that can allocate more stack on demand for me). I want to avoid errors during runtime, and catch them in build time instead - to have some indication if I mistakenly declared too much variables in some function block that won't fit in the stack during the runtime.

Given that I don't use recursive calls in my program, can I somehow know during compilation time how much space on stack all my local variables will take on the deepest function call path? Or at least know how much space my variables will take in a single block (function) if the compiler is not smart enough to analyze it on all the nested calls?

user1483597
  • 540
  • 2
  • 4
  • 20
  • 1
    Does this answer your question?: [How to determine maximum stack usage in embedded system with gcc?](https://stackoverflow.com/questions/6387614/how-to-determine-maximum-stack-usage-in-embedded-system-with-gcc) – Emoun May 05 '21 at 08:53
  • The old school way is to set your whole stack area to a value like 0xAA upon reset from a debugger, then let the program run for a while, make sure to provoke all use-cases. Then halt and inspect how far down you still have 0xAA in memory. It isn't a 100% scientific, fool-proof method but works just fine in practice, in the vast majority of cases. – Lundin May 05 '21 at 09:17
  • @Emoun How can a compiler keep track of interrupts? Does it somehow download a dynamic analyser with the program or what? If not, then that link is not the correct answer. Seems like some naive PC programming solution. Though [this](https://stackoverflow.com/a/6390246/584518) answer proposes the very same method as I did in the comment above. – Lundin May 05 '21 at 09:17
  • @Lundin Nowhere in OP's question does he mention the need to keep track of interrupts, so that link answers the immediate question of how to get stack usage using the GCC. It is an embedded system, so I'm assuming from OP's description that their program has complete control. However, if you need to take interrupts into account, you could analyze the interrupt handler independently and just add the numbers to be sure. Haven't really thought this through though, so not saying it would work flawlessly. – Emoun May 05 '21 at 09:25
  • @Emoun It's kind of impossible to design embedded systems completely free of interrupts. We like to avoid them when possible, but at a bare minimum there will likely still be at least a cyclic timer interrupt and misc error state interrupts. – Lundin May 05 '21 at 09:27
  • "_can I somehow know during compilation..._" strictly, not during compilation, but during linking, yes. Though perhaps not with the GNU linker - not directly. The compiler can determine the stack frame size of individual functions using `-fstack-usage`, but you need to combine that information with the linker call-graph output of the linker. A methid of doing that is described in this answer https://stackoverflow.com/questions/52208523/how-is-stack-memory-consumption-calculated/52212033#52212033, which links to https://dzone.com/articles/gnu-static-stack-usage-analysis – Clifford May 05 '21 at 22:28

1 Answers1

4

Given that I don't use recursive calls in my program, can I somehow know during compilation time how much space on stack all my local variables will take on the deepest function call path?

Only if you don't use interrupts. Which is extremely likely in any embedded system. So you'll have to find out stack use with dynamic analysis.

The old school way is to set your whole stack area to a value like 0xAA upon reset from a debugger, then let the program run for a while, make sure to provoke all use-cases. Then halt and inspect how far down you still have 0xAA in memory. It isn't a 100% scientific, fool-proof method but works just fine in practice, in the vast majority of cases.

Other methods involve setting write breakpoints at certain stack locations where you don't expect the program to end up, sort of like a "hardware stack canary". Run the program and ensure that the breakpoint never triggers. If it does, then investigate from there, move the breakpoint further down the memory map to see exactly where.

Another good practice is to always memory map your stack so that it can only overflow into forbidden memory or at least into read-only flash etc - ideally you'd get a hardware exception for stack overflow. You definitely want to avoid the stack overflowing into other RAM sections like .data/.bss, as that will cause severe and extremely subtle error scenarios.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    *Only if you don't use interrupts.* ?? Just add the mainline worst stack to the interrupt worst stack. How is this not calculable? If you nest, you have to add all of them. Canaries work, but it is always a better case to combine static and dynamic techniques. – artless noise Jun 09 '22 at 21:50
  • @artlessnoise Yeah sure but this is very MCU-dependent. Some allow interrupts to be interrupted, some don't. Calculating the maximum call stack depth when all interrupts have fired and interrupted each other is easy, calculating the individual stack use of every ISR at that point, less so. – Lundin Jun 10 '22 at 06:23