mirror of
https://github.com/meshtastic/firmware.git
synced 2026-11-06 14:24:20 -05:00
* nRF52, RP2xx0: drain the USB console before an assert reset
Serial.flush() on both TinyUSB cores only moves the next packet into the
endpoint buffer and returns, so the reset that follows an assert discards
the rest of the CDC TX FIFO and the packet still waiting for the host.
Field logs end mid-line ("assert failed src/platfor"), losing the file
and line.
Wait, bounded to 250 ms, for the FIFO to drain, then 20 ms for the host
to collect the last packet. Skipped with no host connected and where the
USB task can't run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* nRF52: say more when an assert resets
The assert line alone can't tell an out-of-memory crash apart from heap
corruption or a stack overflow, and for a failed operator new it always
names alloc.cpp.
- After the assert line, log the FreeRTOS task (the log's [thread] tag is
the last OSThread to run), its stack high-water mark, free heap and the
largest free block, or that the free list failed a bounds-checked walk.
- A failed operator new logs the requested size and the caller's address
first, for addr2line.
- MemGet::getMaxAllocHeap() walks newlib-nano's free list on nRF52, so the
periodic heap line reports the largest block there. That line no longer
prints "min 0" where the minimum isn't tracked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep out-of-memory logging from allocating or recursing
RedirectablePrint::log() heap-allocated a copy of every format string to
append the newline, so with the heap exhausted the log calls in a failed
operator new and in the assert handler failed their own allocation and
recursed. Copy usual-length formats on the stack instead (96 B; some tasks
have well under 1 KB of stack free) and only use the heap for long ones.
Guard logAllocFailure() and __assert_func() against re-entry for the paths
that still allocate (long formats, BLE).
getMaxAllocHeap() on nRF52 now reports what malloc() can return (chunk
size less 8 B of header and alignment), and 0 if the free-list walk
fails rather than a partial figure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* nRF52: drain before a nested assert reset; bound the heap walk by heap size
A nested assert reset at once, dropping the outer assert's lines still
in the USB FIFO; drain first (the drain neither logs nor allocates).
The free-list walk treated more than 2048 chunks as corruption, which a
valid but badly fragmented heap could reach. Bound it by the most chunks
the heap can hold at the 8 B minimum, so passing the bound means a loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* nRF52: treat free-list chunks under newlib-nano's minimum as corruption
newlib-nano never frees a chunk smaller than MALLOC_MINCHUNK (12 B on
32-bit), so a smaller size in the list means it is damaged. Reject those,
and derive the walk's chunk bound from the same minimum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* nRF52: compare heap addresses as integers in the free-list walk
__HeapBase and __HeapLimit are distinct linker objects, so subtracting
them as pointers is undefined, and cppcheck's high-severity
subtractPointers check failed the static-analysis jobs. Do the bounds
arithmetic on uintptr_t, as the STM32 sbrk headroom code already does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>