Files
nuttx/Documentation/debugging/stackusage.rst
T
Xiang Xiao 55e752ada8
Build Documentation / build-html (push) Has been cancelled
Documentation/debugging: add stackusage tool documentation
Add RST documentation for the stackusage.py static stack analysis tool,
covering usage, command-line options, output format, and markers.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-03-17 11:26:44 -03:00

160 lines
5.7 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
===========================
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.