0
0
Power-electronicsHow-ToBeginner · 3 min read

How to Check Stack Usage in Embedded C: Simple Methods

To check stack usage in embedded C, use a technique called stack painting by filling the stack with a known pattern before running your program, then inspect how much of that pattern remains after execution. Alternatively, use linker symbols to find stack boundaries and calculate usage during runtime.
📐

Syntax

Stack usage checking involves two main parts: initializing the stack memory with a known pattern and then checking how much of that pattern remains unused.

Example steps:

  • void stack_paint(void *start, size_t size, uint8_t pattern); - fills stack memory with a pattern.
  • size_t stack_check(void *start, size_t size, uint8_t pattern); - returns used stack size by scanning for overwritten pattern.

Also, linker symbols like _estack or _sstack define stack start/end addresses.

c
/* Fill stack with pattern */
void stack_paint(uint8_t *start, size_t size, uint8_t pattern) {
    for (size_t i = 0; i < size; i++) {
        start[i] = pattern;
    }
}

/* Check how much stack is used */
size_t stack_check(uint8_t *start, size_t size, uint8_t pattern) {
    size_t used = 0;
    for (size_t i = 0; i < size; i++) {
        if (start[i] != pattern) {
            used = size - i;
            break;
        }
    }
    return used;
}
💻

Example

This example shows how to paint the stack with 0xAA pattern before main runs and then check stack usage after some function calls.

c
#include <stdio.h>
#include <stdint.h>

#define STACK_SIZE 256

uint8_t stack_memory[STACK_SIZE];

void stack_paint(uint8_t *start, size_t size, uint8_t pattern) {
    for (size_t i = 0; i < size; i++) {
        start[i] = pattern;
    }
}

size_t stack_check(uint8_t *start, size_t size, uint8_t pattern) {
    size_t used = 0;
    for (size_t i = 0; i < size; i++) {
        if (start[i] != pattern) {
            used = size - i;
            break;
        }
    }
    return used;
}

void recursive_function(int depth) {
    uint8_t buffer[20]; // Use some stack
    for (int i = 0; i < 20; i++) {
        buffer[i] = depth;
    }
    if (depth > 0) {
        recursive_function(depth - 1);
    }
}

int main() {
    stack_paint(stack_memory, STACK_SIZE, 0xAA);

    recursive_function(5);

    size_t used = stack_check(stack_memory, STACK_SIZE, 0xAA);
    printf("Estimated stack used: %zu bytes\n", used);
    return 0;
}
Output
Estimated stack used: 20 bytes
⚠️

Common Pitfalls

  • Not initializing the stack pattern: Forgetting to fill the stack with a known pattern leads to incorrect usage results.
  • Stack grows in unexpected direction: Some systems have stack growing up or down; scanning must match direction.
  • Interrupts and other stacks: Interrupt stacks or other contexts may use stack memory, confusing results.
  • Optimizations: Compiler optimizations may remove unused variables, affecting stack usage measurement.
c
/* Wrong: Not painting stack before use */
// stack_check will always report zero usage or garbage

/* Right: Always paint stack before running code */
stack_paint(stack_start, stack_size, 0xAA);
📊

Quick Reference

Tips for checking stack usage in embedded C:

  • Fill stack memory with a unique pattern before running your program.
  • After execution, scan stack memory for overwritten bytes to estimate usage.
  • Use linker symbols to find stack boundaries for accurate measurement.
  • Consider stack growth direction when scanning memory.
  • Test with worst-case call depth and interrupts to find maximum stack usage.

Key Takeaways

Always fill stack memory with a known pattern before running your embedded program.
Scan the stack memory after execution to find how much has been overwritten to estimate usage.
Use linker symbols to get accurate stack start and end addresses.
Remember stack growth direction varies by system and affects scanning logic.
Test with worst-case scenarios to ensure stack size is sufficient.