=========================== 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 ==================== .. code-block:: text 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 ``-r N`` to estimate. indirect call (function pointer) Callee unknown at compile time. no DWARF data No ``.debug_frame`` entry; self counted as zero. dynamic stack (alloca/VLA) Function uses ``alloca()`` or variable-length arrays; self is a minimum. ====================================== ========================================= 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.