Summary
bash bloat <board> (ci/bloat.py) is wired only for ESP32-family chips. Running it on lpc845brk fails immediately because there's no arm-none-eabi entry in BOARD_CHIP_MAP. The underlying fbuild symbols already supports any ELF + matching nm, so the gap is purely in the shim.
This blocks per-symbol bloat analysis on the LPC8xx low-memory bring-up — the very target where every byte matters and where the -DFASTLED_LPC_RX_SCT_WS2812=1 config currently overflows the 64 KB FLASH region by 11,112 bytes.
Reproduction
$ bash bloat lpc845brk
Bloat: board 'lpc845brk' has no nm mapping. Add it to BOARD_CHIP_MAP in
ci/bloat.py — entries are mechanical (arch + chip prefix). Supported
boards today: esp32, esp32c3, esp32c6, esp32h2, esp32s2, esp32s3.
ci/bloat.py:75 only registers Xtensa + RISC-V ESP32 chips:
BOARD_CHIP_MAP = {
"esp32": ("xtensa", "esp32"),
"esp32s2": ("xtensa", "esp32s2"),
"esp32s3": ("xtensa", "esp32s3"),
"esp32c3": ("riscv", "esp32c3"),
"esp32c6": ("riscv", "esp32c6"),
"esp32h2": ("riscv", "esp32h2"),
}
And PIO_NM_BY_ARCH (above) only knows about toolchain-xtensa-* and toolchain-riscv32-esp-elf PIO packages.
What's missing
Three independent gaps:
1. ci/bloat.py BOARD_CHIP_MAP — no arm-none-eabi entry
Need an "arm" arch family that points at the developer-gnu toolchain. The toolchain lives at:
~/.fbuild/prod/cache/toolchains/developer-gnu/<hash>/15.2.Rel1/bin/arm-none-eabi-nm.exe
That's a fbuild cache path, not a ~/.platformio/packages path, so pio_packages_dir() won't find it. The shim needs to either:
- Query fbuild for the resolved nm path (
fbuild symbols already supports --build-info for this — build_info.json carries nm_path), or
- Add an alternate "fbuild-managed" toolchain lookup branch alongside the existing PIO branch.
2. fbuild build has no "emit ELF on overflow" mode
When you want to measure over-budget bloat, the build has to succeed enough to produce an ELF. Today, an LD region overflowed by N bytes error is fatal — no ELF emitted, LTO temp objects deleted on exit. The only workaround is to hand-patch the LD script in the fbuild cache (~/.fbuild/prod/cache/platforms/.../lpc845_flash.ld → bump LENGTH = 64K to 128K), build, then revert. That is the workflow I had to use to gather this report.
Reasonable surfaces:
fbuild build --no-flash-check / --bloat-analysis that bypasses the LD region check (or auto-patches an INPUT(...) override)
fbuild symbols --over-budget <env> that orchestrates the build + analyze in one step
- Or simply: keep
.lto-tmp/*.ltrans*.ltrans.o after a failed link so external nm can be run on the LTO partitions directly
3. bash bloat doesn't speak fbuild-native builds
The shim looks for ELFs at:
build_root / "pio" / board / ".pio" / "build" / board / "firmware.elf"
build_root / board / "firmware.elf"
fbuild emits to .fbuild/build/<env>/release/firmware.elf — that path isn't in the candidate list, so even on green builds bash bloat lpc845brk would still fail to locate the ELF.
What I actually did to work around it
- Edited
platformio.ini to add -DFASTLED_LPC_RX_SCT_WS2812=1
- Edited
~/.fbuild/prod/cache/.../lpc845_flash.ld to bump FLASH from 64K → 128K
- Ran
fbuild build -e lpc845brk — got a complete firmware.map even when the first attempt failed at link time
- Parsed the linker map with a one-off Python script to compute per-symbol sizes via address deltas (LTO partitions merge into 2 giant
ltrans*.ltrans.o chunks, but the map's 0x... <demangled> lines are still per-symbol)
- Reverted everything
This worked but is the wrong shape — there should be a one-command path.
Findings the workaround surfaced (separate from the tooling gap)
For tracking, the symbol-level results of the workaround run:
- Total
.text with RX_SCT_WS2812=1: 76,272 bytes vs 65,232 budget → +11,040 bytes over (matches linker's +11,112 report within fixup math)
- Top single symbol:
fl::CRGB::computeAdjustment(unsigned char, fl::CRGB const&, fl::CRGB const&) = 9,516 bytes (12.5% of .text)
- libgcc double-precision soft-FP cascade pulled in:
__adddf3 2,088 + __muldf3 1,392 + __subsf3 1,172 ≈ 5 KB despite the "no soft-FP" invariant
fl::string::trim() const = 2,966 bytes
fl::variant<fl::function<…(fl::json const&)>::…> copy/move/destroy clones across the FreeFunction / InlinedLambda / NonConstMember / ConstMember alternatives ≈ 12 KB combined
These are separate FastLED-side bug-class issues and should be filed against FastLED, not fbuild. This issue is purely about the toolchain gap.
Acceptance criteria
Out of scope
- Fixing the actual FastLED-side bloat (file separate issues against FastLED for
CRGB::computeAdjustment, soft-FP cascade in low-memory mode, fl::string::trim, etc.)
- Adding more ESP32 chips / adding non-LPC ARM boards (this issue is about the arch family, LPC845 is the test case)
Summary
bash bloat <board>(ci/bloat.py) is wired only for ESP32-family chips. Running it onlpc845brkfails immediately because there's noarm-none-eabientry inBOARD_CHIP_MAP. The underlyingfbuild symbolsalready supports any ELF + matchingnm, so the gap is purely in the shim.This blocks per-symbol bloat analysis on the LPC8xx low-memory bring-up — the very target where every byte matters and where the
-DFASTLED_LPC_RX_SCT_WS2812=1config currently overflows the 64 KB FLASH region by 11,112 bytes.Reproduction
ci/bloat.py:75only registers Xtensa + RISC-V ESP32 chips:And
PIO_NM_BY_ARCH(above) only knows abouttoolchain-xtensa-*andtoolchain-riscv32-esp-elfPIO packages.What's missing
Three independent gaps:
1.
ci/bloat.pyBOARD_CHIP_MAP — noarm-none-eabientryNeed an
"arm"arch family that points at the developer-gnu toolchain. The toolchain lives at:That's a fbuild cache path, not a
~/.platformio/packagespath, sopio_packages_dir()won't find it. The shim needs to either:fbuild symbolsalready supports--build-infofor this —build_info.jsoncarriesnm_path), or2.
fbuild buildhas no "emit ELF on overflow" modeWhen you want to measure over-budget bloat, the build has to succeed enough to produce an ELF. Today, an LD
region overflowed by N byteserror is fatal — no ELF emitted, LTO temp objects deleted on exit. The only workaround is to hand-patch the LD script in the fbuild cache (~/.fbuild/prod/cache/platforms/.../lpc845_flash.ld→ bumpLENGTH = 64Kto128K), build, then revert. That is the workflow I had to use to gather this report.Reasonable surfaces:
fbuild build --no-flash-check/--bloat-analysisthat bypasses the LD region check (or auto-patches anINPUT(...)override)fbuild symbols --over-budget <env>that orchestrates the build + analyze in one step.lto-tmp/*.ltrans*.ltrans.oafter a failed link so externalnmcan be run on the LTO partitions directly3.
bash bloatdoesn't speak fbuild-native buildsThe shim looks for ELFs at:
fbuild emits to
.fbuild/build/<env>/release/firmware.elf— that path isn't in the candidate list, so even on green buildsbash bloat lpc845brkwould still fail to locate the ELF.What I actually did to work around it
platformio.inito add-DFASTLED_LPC_RX_SCT_WS2812=1~/.fbuild/prod/cache/.../lpc845_flash.ldto bump FLASH from 64K → 128Kfbuild build -e lpc845brk— got a completefirmware.mapeven when the first attempt failed at link timeltrans*.ltrans.ochunks, but the map's0x... <demangled>lines are still per-symbol)This worked but is the wrong shape — there should be a one-command path.
Findings the workaround surfaced (separate from the tooling gap)
For tracking, the symbol-level results of the workaround run:
.textwithRX_SCT_WS2812=1: 76,272 bytes vs 65,232 budget → +11,040 bytes over (matches linker's+11,112report within fixup math)fl::CRGB::computeAdjustment(unsigned char, fl::CRGB const&, fl::CRGB const&)= 9,516 bytes (12.5% of.text)__adddf32,088 +__muldf31,392 +__subsf31,172 ≈ 5 KB despite the "no soft-FP" invariantfl::string::trim() const= 2,966 bytesfl::variant<fl::function<…(fl::json const&)>::…>copy/move/destroy clones across the FreeFunction / InlinedLambda / NonConstMember / ConstMember alternatives ≈ 12 KB combinedThese are separate FastLED-side bug-class issues and should be filed against FastLED, not fbuild. This issue is purely about the toolchain gap.
Acceptance criteria
bash bloat lpc845brkruns end-to-end against a fresh build and prints a per-symbol top-N tablearm-none-eabi-nmfrom fbuild's developer-gnu toolchain is auto-resolved (no PIO dependency)Out of scope
CRGB::computeAdjustment, soft-FP cascade in low-memory mode,fl::string::trim, etc.)