# Hang / freeze diagnosis overlay. # # Build (stacks on top of debug.conf — order matters, this one must come last): # west build -b meshtracker_x1 zephcore --pristine -- \ # -DEXTRA_CONF_FILE="boards/common/debug.conf;boards/common/hangdebug.conf" # # Exists to answer one question before any others: when the node goes silent, # is the MCU dead, or is the radio wedged with the MCU still running? Those # have completely different fixes and the ordinary debug build cannot tell them # apart, because it produces no output either way. # ========== Logging mode: deliberately left DEFERRED ========== # CONFIG_LOG_MODE_IMMEDIATE was tried here and BRICKS THE BOOT on any board # whose console is a USB CDC ACM (meshtracker_x1, promicro_lr2021, t1000_e...). # Immediate logging writes synchronously from the calling context, and before # the USB host has enumerated, uart_poll_out() on a CDC ACM with no host blocks # forever — so the node hangs during early init and never comes up. This is the # stall debug.conf's own backend comment has always warned about; it is harmless # in deferred mode because the log thread absorbs it, and fatal in immediate # mode because the booting thread does. Do not re-add it. # # It is also unnecessary for catching a crash: Zephyr's fatal handler calls # LOG_PANIC(), which switches logging to synchronous and flushes whatever is # queued. A genuine fault therefore prints its dump even in deferred mode — # meaning a freeze that produces NO output is evidence of a hard hang or a # supply collapse rather than of a lost message. # ========== Claw back the RAM immediate mode would have saved ========== # debug.conf sizes the deferred log buffer at 8 KB for heavy DBG output. This # overlay is not trying to capture a firehose — it wants the thread table and a # fault dump — so a quarter of that is plenty and returns 6 KB to a build that # otherwise sits above 91 % on the X1. Headroom is the point: a production # build survives what a debug build does not, so the debug build's own footprint # is a suspect in its own right. CONFIG_LOG_BUFFER_SIZE=2048 # ========== Proof of life + stack headroom ========== # Periodic per-thread stack usage, printed from a thread of its own. Two jobs: # # 1. Proof of life. If these lines keep appearing after the radio goes quiet, # the MCU is healthy and the radio is wedged — look at the driver. If they # stop when the radio does, the hang is MCU-side (fault, deadlock, or a # starved scheduler) and the radio is a red herring. # # 2. Stack headroom. The X1 debug build sits near 92% RAM, so a stack overflow # is a live hypothesis rather than a formality. This rules it in or out by # showing each thread's high-water mark. CONFIG_THREAD_ANALYZER=y CONFIG_THREAD_ANALYZER_AUTO=y CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=10 CONFIG_THREAD_ANALYZER_RUN_UNLOCKED=y CONFIG_THREAD_NAME=y # ========== Turn silent corruption into a loud fault ========== # Without this an overflowing stack quietly scribbles on whatever is below it and # the failure surfaces somewhere unrelated. With it, the MPU traps the overrun # at the moment it happens and — thanks to IMMEDIATE logging above — the dump # actually reaches the console. CONFIG_HW_STACK_PROTECTION=y # Keep the fatal handler halting rather than rebooting (debug.conf already sets # this). With synchronous logging the dump is out before the halt, and halting # preserves the state for a debugger if one is ever attached. CONFIG_ZEPHCORE_RESET_ON_FATAL_ERROR=n