You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FastLED's ESP32-S3 Blink bloat gate reads fbuild symbolsreport.json.total_flash. That field is a sum of attributed symbol sizes, not the firmware image size. On the same FastLED commit (01caa0a89f, FastLED/FastLED#4468), CI reported 383,120 B and a local NixOS build reported 387,217 B, while fbuild's image-flash values differed by only 64 B (473,588 vs 473,524 B). This makes the local gate fail on source that CI passes.
The symbol diff in FastLED/FastLED#4468 identified the main mechanism: local nm contributes a sized _stext row (~10.9 KB) at the start of .flash.text; CI has no such row. _stext is a linker-script section-start marker, not a function or object. The local report also synthesized only 276 map-derived rows versus 1,026 in CI, so the marker's span suppresses real .literal.*/.text.* ownership and the net reported difference is ~4 KB, not 10.9 KB. Some framework archive totals differ too; the current evidence does not establish that filtering _stext alone closes the entire gap.
Reproduced with fbuild 2.5.26 on current FastLED source: a clean base worktree (only an unrelated Python import change) reports total_flash=387,289 B, image flash 473,588 B, _stext=10,912 B, 276 map-derived rows. A response-LUT feature worktree reports 387,363 B, image flash 473,680 B, _stext=10,916 B, and no response-LUT/pipeline symbols in Blink. The local feature delta is 74 B; the ~4 KB baseline failure is already present on the base worktree.
Source inspection points at fbuild-core/src/symbol_analysis/mod.rs: parse_nm_output accepts any nonzero sized symbol; build_fine_grained_map_with_synth inserts all nm ranges into nm_covered before map synthesis; retain_loaded_symbols filters by PT_LOAD containment, which cannot reject an in-range linker marker such as _stext. The total_flash rollup then sums these attribution rows.
Proposal
Treat linker-script boundary labels as non-owning markers before both symbol rollup and nm_covered construction. Keep per-symbol attribution useful, but expose or use a stable, explicitly named image/allocated-section flash metric for cross-machine regression gates; diagnostic attributed-symbol sums should not masquerade as physical flash usage. Preserve the per-input-section map synthesis when a marker overlaps real sections. Coordinate the FastLED gate's metric change under FastLED/FastLED#4468.
Acceptance criteria
Add a focused RED fixture with _stext at the start of a loaded .flash.text segment and live .literal.* sections under its inferred nm size; show that the current analyzer counts _stext and suppresses those map-derived owners. Make it GREEN with the fix.
Add a paired-fixture test for the same loadable bytes with/without a sized linker marker; the gate-facing flash metric agrees, and attribution does not double count or lose the literal owners.
Document the semantics of image flash versus attributed-symbol flash in report.json and CLI output; do not silently change an existing field without a migration path for consumers.
Re-run FastLED's ESP32-S3 Blink gate on one unchanged commit in CI and locally (or equivalent distinct toolchain environments) and show that it no longer produces a false ~4 KB regression. If framework objects actually differ, report that separately from attribution drift.
Decisions
P2 tooling correctness: this blocks reliable local preflight and can invite unjustified baseline bumps, but does not alter shipped firmware.
Do not raise FastLED's pinned baseline to the local number: the clean base already fails, and the firmware image sizes nearly agree.
Open questions
Which exact CI/local toolchain or linker-map difference makes _stext appear as a sized nm row only locally? The differing reports establish the failure mode; an archived CI firmware.map and nm output for the same commit would isolate the input difference.
Context
FastLED's ESP32-S3 Blink bloat gate reads
fbuild symbolsreport.json.total_flash. That field is a sum of attributed symbol sizes, not the firmware image size. On the same FastLED commit (01caa0a89f, FastLED/FastLED#4468), CI reported 383,120 B and a local NixOS build reported 387,217 B, while fbuild's image-flash values differed by only 64 B (473,588 vs 473,524 B). This makes the local gate fail on source that CI passes.The symbol diff in FastLED/FastLED#4468 identified the main mechanism: local
nmcontributes a sized_stextrow (~10.9 KB) at the start of.flash.text; CI has no such row._stextis a linker-script section-start marker, not a function or object. The local report also synthesized only 276 map-derived rows versus 1,026 in CI, so the marker's span suppresses real.literal.*/.text.*ownership and the net reported difference is ~4 KB, not 10.9 KB. Some framework archive totals differ too; the current evidence does not establish that filtering_stextalone closes the entire gap.Reproduced with fbuild 2.5.26 on current FastLED source: a clean base worktree (only an unrelated Python import change) reports
total_flash=387,289 B, image flash 473,588 B,_stext=10,912 B, 276 map-derived rows. A response-LUT feature worktree reports 387,363 B, image flash 473,680 B,_stext=10,916 B, and no response-LUT/pipeline symbols in Blink. The local feature delta is 74 B; the ~4 KB baseline failure is already present on the base worktree.Source inspection points at
fbuild-core/src/symbol_analysis/mod.rs:parse_nm_outputaccepts any nonzero sized symbol;build_fine_grained_map_with_synthinserts allnmranges intonm_coveredbefore map synthesis;retain_loaded_symbolsfilters by PT_LOAD containment, which cannot reject an in-range linker marker such as_stext. Thetotal_flashrollup then sums these attribution rows.Proposal
Treat linker-script boundary labels as non-owning markers before both symbol rollup and
nm_coveredconstruction. Keep per-symbol attribution useful, but expose or use a stable, explicitly named image/allocated-section flash metric for cross-machine regression gates; diagnostic attributed-symbol sums should not masquerade as physical flash usage. Preserve the per-input-section map synthesis when a marker overlaps real sections. Coordinate the FastLED gate's metric change under FastLED/FastLED#4468.Acceptance criteria
_stextat the start of a loaded.flash.textsegment and live.literal.*sections under its inferrednmsize; show that the current analyzer counts_stextand suppresses those map-derived owners. Make it GREEN with the fix.report.jsonand CLI output; do not silently change an existing field without a migration path for consumers.Decisions
Open questions
_stextappear as a sizednmrow only locally? The differing reports establish the failure mode; an archived CIfirmware.mapandnmoutput for the same commit would isolate the input difference.Related issues