K05 — FreeRTOS C++ heap bridge, heap models and HeapStats

K05 separates three questions that are often collapsed into one:

  1. Which FreeRTOS heap policy fits the deployment (heap_1heap_5)?
  2. How do all ordinary C++ allocation/deallocation forms enter one selected FreeRTOS heap domain?
  3. 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.

S
Description
No description provided
Readme 735 KiB
Languages
C++ 42.9%
C 25.6%
Makefile 14.6%
Shell 12.2%
Assembly 3.2%
Other 1.5%