chore: Added RAM to firmware_size_history.py script (#1830)

## Summary

Added RAM delta output to firmware_size_history.py output, e.g.:

```
 % scripts/firmware_size_history.py --commits adcd796 8e18472
[info] Will restore to 'sd-card-fonts' when finished.
[info] Stashing uncommitted changes...
[info] Building 2 commits...

[1/2] adcd7961c9 feat: self-heal from transient WiFi loss, add dBm indicator during WebServerActivity  (#1780)
  Building (env: default)...
  Flash: 5,751,209, RAM: 97,996 bytes

[2/2] 8e184724d1 Merge branch 'master' into sd-card-fonts
  Building (env: default)...
  Flash: 5,762,395, RAM: 97,988 bytes

[info] Restoring 'sd-card-fonts'...
[info] Restoring stashed changes...

Commit            Flash    Delta          RAM    Delta  Title
──────────  ───────────  ───────  ───────────  ───────  ────────────────────────────────────────
adcd7961c9    5,751,209                97,996           feat: self-heal from transient WiFi loss, add dBm indicator during WebServerActivity  (#1780)
8e184724d1    5,762,395  +11,186       97,988       -8  Merge branch 'master' into sd-card-fonts
```

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**YES**_
This commit is contained in:
Zach Nelson
2026-05-04 22:11:40 -05:00
committed by GitHub
parent adcd7961c9
commit 78625afe76
+57 -30
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Build firmware at selected commits and report flash usage.
Build firmware at selected commits and report flash and RAM usage.
Two modes (mutually exclusive, one required):
@@ -33,6 +33,9 @@ import re
import subprocess
import sys
RAM_RE = re.compile(
r"RAM:.*?(\d+)\s+bytes\s+from\s+(\d+)\s+bytes"
)
FLASH_RE = re.compile(
r"Flash:.*?(\d+)\s+bytes\s+from\s+(\d+)\s+bytes"
)
@@ -93,9 +96,9 @@ def build_firmware(env):
return result.returncode, result.stdout + "\n" + result.stderr
def parse_flash_used(output):
"""Extract used-bytes integer from PlatformIO output, or None."""
m = FLASH_RE.search(output)
def parse_size_line(regex, output):
"""Extract used-bytes integer matching *regex* from PlatformIO output, or None."""
m = regex.search(output)
if m:
return int(m.group(1))
return None
@@ -111,10 +114,10 @@ def write_csv(out, rows, fieldnames):
def format_table(rows):
"""Print rows as an aligned human-readable table to stdout."""
COL_COMMIT = 10
COL_FLASH = 11
COL_SIZE = 11
COL_DELTA = 7
def fmt_flash(val):
def fmt_size(val):
if val == "FAILED":
return "FAILED"
return f"{val:,}"
@@ -126,25 +129,33 @@ def format_table(rows):
header = (
f"{'Commit':<{COL_COMMIT}} "
f"{'Flash':>{COL_FLASH}} "
f"{'Flash':>{COL_SIZE}} "
f"{'Delta':>{COL_DELTA}} "
f"{'RAM':>{COL_SIZE}} "
f"{'Delta':>{COL_DELTA}} "
f"Title"
)
sep = (
f"{BOX_CHAR * COL_COMMIT} "
f"{BOX_CHAR * COL_FLASH} "
f"{BOX_CHAR * COL_SIZE} "
f"{BOX_CHAR * COL_DELTA} "
f"{BOX_CHAR * COL_SIZE} "
f"{BOX_CHAR * COL_DELTA} "
f"{BOX_CHAR * 40}"
)
print(header)
print(sep)
for row in rows:
flash_str = fmt_flash(row["flash_bytes"])
delta_str = fmt_delta(row["delta"])
flash_str = fmt_size(row["flash_bytes"])
flash_d = fmt_delta(row["flash_delta"])
ram_str = fmt_size(row["ram_bytes"])
ram_d = fmt_delta(row["ram_delta"])
print(
f"{row['commit']:<{COL_COMMIT}} "
f"{flash_str:>{COL_FLASH}} "
f"{delta_str:>{COL_DELTA}} "
f"{flash_str:>{COL_SIZE}} "
f"{flash_d:>{COL_DELTA}} "
f"{ram_str:>{COL_SIZE}} "
f"{ram_d:>{COL_DELTA}} "
f"{row['title']}"
)
@@ -173,7 +184,7 @@ def build_commits_from_list(refs):
def main():
parser = argparse.ArgumentParser(
description="Measure firmware flash size across git commits.",
description="Measure firmware flash and RAM size across git commits.",
epilog=(
"Range mode walks every commit between START and END (one branch). "
"List mode builds specific refs that may come from different branches."
@@ -233,19 +244,22 @@ def main():
print(f" Building (env: {args.env})...", file=sys.stderr)
rc, output = build_firmware(args.env)
if rc != 0:
build_failed = rc != 0
if build_failed:
print(f" BUILD FAILED (exit {rc}) -- skipping", file=sys.stderr)
results.append((sha, title, None))
results.append((sha, title, None, None, True))
continue
used = parse_flash_used(output)
if used is None:
flash_used = parse_size_line(FLASH_RE, output)
ram_used = parse_size_line(RAM_RE, output)
if flash_used is None:
print(" Could not parse flash size from output -- skipping", file=sys.stderr)
results.append((sha, title, None))
results.append((sha, title, None, None, True))
continue
print(f" Flash used: {used:,} bytes", file=sys.stderr)
results.append((sha, title, used))
ram_str = f", RAM: {ram_used:,}" if ram_used is not None else ""
print(f" Flash: {flash_used:,}{ram_str} bytes", file=sys.stderr)
results.append((sha, title, flash_used, ram_used, False))
except KeyboardInterrupt:
print("\n[info] Interrupted -- writing partial results.", file=sys.stderr)
@@ -258,22 +272,35 @@ def main():
# Build result rows with deltas
rows = []
prev_size = None
for sha, title, used in results:
if used is not None and prev_size is not None:
delta = used - prev_size
prev_flash = None
prev_ram = None
for sha, title, flash_used, ram_used, build_failed in results:
flash_delta = ""
ram_delta = ""
if flash_used is not None and prev_flash is not None:
flash_delta = flash_used - prev_flash
if ram_used is not None and prev_ram is not None:
ram_delta = ram_used - prev_ram
if build_failed:
flash_bytes = "FAILED"
ram_bytes = "FAILED"
else:
delta = ""
flash_bytes = flash_used if flash_used is not None else "N/A"
ram_bytes = ram_used if ram_used is not None else "N/A"
rows.append({
"commit": sha[:10],
"title": title,
"flash_bytes": used if used is not None else "FAILED",
"delta": delta,
"flash_bytes": flash_bytes,
"flash_delta": flash_delta,
"ram_bytes": ram_bytes,
"ram_delta": ram_delta,
})
if used is not None:
prev_size = used
if flash_used is not None:
prev_flash = flash_used
if ram_used is not None:
prev_ram = ram_used
fieldnames = ["commit", "title", "flash_bytes", "delta"]
fieldnames = ["commit", "title", "flash_bytes", "flash_delta", "ram_bytes", "ram_delta"]
if args.csv is not None:
if args.csv == "-":