Static Stack Usage Analysis
Overview
tools/stackusage.py performs static stack usage analysis by reading
DWARF .debug_frame data from an ELF file. It extracts per-function
stack sizes from CFA (Canonical Frame Address) offsets and optionally
builds a call graph via disassembly to compute worst-case total stack
depth.
Self – stack bytes used by the function itself (max CFA offset).
Total – worst-case stack depth through the deepest call chain (self + callees). A marker prefix flags uncertain values.
Dependencies
The tool invokes standard toolchain binaries:
readelf – symbol table and DWARF frame info
objdump – disassembly for call graph analysis
addr2line – source file and line resolution
Both GNU and LLVM toolchains are supported. Use -p to set the
toolchain prefix (e.g. -p arm-none-eabi- for GCC,
-p llvm- for LLVM).
The ELF must contain DWARF debug info (-g or -gdwarf).
No special Kconfig option is needed.
Usage
Analyze a native ELF (no prefix needed):
python3 tools/stackusage.py nuttx
Cross-compiled ELF with GCC toolchain:
python3 tools/stackusage.py -p arm-none-eabi- nuttx
Cross-compiled ELF with LLVM toolchain:
python3 tools/stackusage.py -p llvm- nuttx
Show top 20 functions:
python3 tools/stackusage.py -p arm-none-eabi- -n 20 nuttx
Estimate recursion depth of 10:
python3 tools/stackusage.py -p arm-none-eabi- -r 10 nuttx
Command Line Options
positional arguments:
elf path to ELF file with DWARF debug info
options:
-p, --prefix PREFIX toolchain prefix (e.g. arm-none-eabi- or llvm-)
-n, --rank N show top N functions (default: 0 = all)
-r, --recursion-depth N
assumed recursion depth (default: 0)
Text Output
The default output is an aligned table. Each function’s deepest
backtrace is shown with one frame per row. The Self column shows
each frame’s own stack cost. The Backtrace column shows the
function name followed by its code size in parentheses (when available
from the symbol table), e.g. main(128). The entry point of each
call chain is suffixed with ~.
Example (nucleo-f429zi:trace, -n 3):
Total Self Backtrace File:Line
----- ---- --------------------------- -------------------------------------------
@2344 56 telnetd_main(236)~ apps/system/telnetd/telnetd.c:42
^24 nsh_telnetmain(128) apps/nshlib/nsh_telnetd.c:48
^48 nsh_session(400) apps/nshlib/nsh_session.c:73
...
@224 nsh_parse_cmdparm(1024) apps/nshlib/nsh_parse.c:2362
@96 nsh_execute(512) apps/nshlib/nsh_parse.c:510
^56 nsh_builtin(320) apps/nshlib/nsh_builtin.c:76
88 exec_builtin(256) apps/builtin/exec_builtin.c:61
...
^64 file_vopen(192) nuttx/fs/vfs/fs_open.c:124
...
@2328 16 sh_main(64)~ apps/system/nsh/sh_main.c:40
16 nsh_system_ctty(96) apps/nshlib/nsh_system.c:105
^32 nsh_system_(160) apps/nshlib/nsh_system.c:41
^48 nsh_session(400) apps/nshlib/nsh_session.c:73
...
@2312 24 nsh_main(80)~ apps/system/nsh/nsh_main.c:54
^24 nsh_consolemain(48) apps/nshlib/nsh_consolemain.c:65
^48 nsh_session(400) apps/nshlib/nsh_session.c:73
...
Uncertainty markers on both Total and Self columns indicate the most significant reason:
Marker |
Meaning |
|---|---|
|
entry point of the call chain (suffix) |
|
no DWARF data (self counted as zero) |
|
dynamic stack (alloca or VLA) |
|
recursion detected |
|
indirect call (function pointer) |
Uncertainty Reasons
Reason |
Description |
|---|---|
recursion: A->B->…->A |
Recursive cycle detected. Use |
indirect call (function pointer) |
Callee unknown at compile time. |
no DWARF data |
No |
dynamic stack (alloca/VLA) |
Function uses |
Uncertainty propagates upward: if any callee in the deepest path is uncertain the caller is also marked uncertain.
Recursion Depth Estimation
By default (-r 0) recursive back-edges contribute zero stack.
With -r N (N > 0) the tool estimates:
cycle_body_cost × N
For example A(64) -> B(32) -> A:
cycle_body_cost = 64 + 32 = 96
-r 10 → 96 × 10 = 960 bytes
The result is still marked uncertain.
Supported Architectures
Any architecture supported by the toolchain’s readelf,
objdump, and addr2line is supported. This includes
ARM, AArch64, x86, x86_64, MIPS, RISC-V, Xtensa, PowerPC, SPARC,
TriCore, SuperH, and others.