Skip to content

bash bloat / fbuild symbols: no arm-none-eabi path; can't measure LPC845 bloat from FastLED's wrapper #594

@zackees

Description

@zackees

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

  1. Edited platformio.ini to add -DFASTLED_LPC_RX_SCT_WS2812=1
  2. Edited ~/.fbuild/prod/cache/.../lpc845_flash.ld to bump FLASH from 64K → 128K
  3. Ran fbuild build -e lpc845brk — got a complete firmware.map even when the first attempt failed at link time
  4. 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)
  5. 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

  • bash bloat lpc845brk runs end-to-end against a fresh build and prints a per-symbol top-N table
  • When the build is over-budget, the bloat command surfaces the overage and still produces an ELF + report (degenerate-case workflow)
  • The arm-none-eabi-nm from fbuild's developer-gnu toolchain is auto-resolved (no PIO dependency)

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions