3.9 KiB
K05 — FreeRTOS C++ heap bridge, heap models and HeapStats
K05 separates three questions that are often collapsed into one:
- Which FreeRTOS heap policy fits the deployment (
heap_1…heap_5)? - How do all ordinary C++ allocation/deallocation forms enter one selected FreeRTOS heap domain?
- What can current free bytes, largest block and minimum-ever free bytes actually prove?
The Hazard3 executable uses the real upstream heap_4.c. It allocates three
C++ arrays, frees the first and third, and obtains two free regions separated
by the still-live middle array. A request one byte larger than the largest
region fails even though the sum of free bytes is larger than the request.
After the middle array is released, heap_4 coalesces adjacent blocks and the
same request succeeds.
Heap policy map
| Scheme | Release | Coalescence | Memory source / typical fit |
|---|---|---|---|
heap_1 |
no | not applicable | one monotonic region; allocate during startup and never free |
heap_2 |
yes | no | legacy/simple reuse with fragmentation risk |
heap_3 |
libc free |
delegated | compiler/linker malloc heap; requires that runtime contract |
heap_4 |
yes | adjacent blocks | one FreeRTOS region; general single-region teaching target |
heap_5 |
yes | within regions | multiple address-ordered, possibly non-contiguous regions |
Only one implementation supplies pvPortMalloc()/vPortFree() in an image.
The C++ bridge does not make the five policies run simultaneously.
C++ surface
HeapStats is a value snapshot of
HeapStats_t. HeapWatermark interprets
current and peak consumption relative to a captured baseline. The bridge in
cpp_heap.cpp supplies all six required paths:
- scalar and array
operator new; - scalar and array unsized
operator delete; - scalar and array sized
operator delete.
This no-exceptions profile gives ordinary new a fail-fast contract. The
controlled failure experiment therefore calls pvPortMalloc() directly with
the malloc-failed hook disabled; it is a deliberately fallible probe, not a
claim that ordinary new returns nullptr.
Seven checkpoints
| Point | Required evidence |
|---|---|
| 1 | baseline: one free block and current free bytes recorded |
| 2 | three C++ allocations; payload intact; current free decreases |
| 3 | first and third freed; two free blocks; total exceeds largest |
| 4 | request=largest+1 fails; successful allocation count unchanged |
| 5 | middle freed; one coalesced block; current free equals baseline |
| 6 | identical request now succeeds and lies in ucHeap |
| 7 | retry freed; baseline recovered; minimum-ever still records history |
On the checked RV32I build the core fragmentation evidence is:
available = 0x2fe0 (12256)
largest = 0x27d0 (10192)
request = 0x27d1 (10193)
blocks = 2
peak used = 0x27f0 (10224)
Build and verify
make check
Expected summary:
PASS host: HeapStats mapping and watermark history
PASS ABI: complete new/delete bridge and zero hosted C++ runtime
PASS task01: heap_4 fragmentation + coalescence + history
Debug
b heap_models_debug_checkpoint
p g_last_checkpoint
p g_baseline_stats
p g_after_alloc_stats
p g_fragmented_stats
p g_after_failure_stats
p g_coalesced_stats
p g_after_retry_stats
p g_final_stats
p g_failure_request
p g_cpp_allocation_addresses
p g_cpp_deallocation_addresses
p g_peak_bytes_used
p g_heap_models_pass
Scope boundary
available_bytes is a sum, not a promise of one contiguous payload. The
largest free block is allocator metadata-level evidence and an allocation also
needs its aligned header. K05 compares policy and measures one selected heap;
runtime-selected resources and typed allocators are deferred to K06.
Render the printable worksheet with make pdf.