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 #!/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): Two modes (mutually exclusive, one required):
@@ -33,6 +33,9 @@ import re
import subprocess import subprocess
import sys import sys
RAM_RE = re.compile(
r"RAM:.*?(\d+)\s+bytes\s+from\s+(\d+)\s+bytes"
)
FLASH_RE = re.compile( FLASH_RE = re.compile(
r"Flash:.*?(\d+)\s+bytes\s+from\s+(\d+)\s+bytes" 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 return result.returncode, result.stdout + "\n" + result.stderr
def parse_flash_used(output): def parse_size_line(regex, output):
"""Extract used-bytes integer from PlatformIO output, or None.""" """Extract used-bytes integer matching *regex* from PlatformIO output, or None."""
m = FLASH_RE.search(output) m = regex.search(output)
if m: if m:
return int(m.group(1)) return int(m.group(1))
return None return None
@@ -111,10 +114,10 @@ def write_csv(out, rows, fieldnames):
def format_table(rows): def format_table(rows):
"""Print rows as an aligned human-readable table to stdout.""" """Print rows as an aligned human-readable table to stdout."""
COL_COMMIT = 10 COL_COMMIT = 10
COL_FLASH = 11 COL_SIZE = 11
COL_DELTA = 7 COL_DELTA = 7
def fmt_flash(val): def fmt_size(val):
if val == "FAILED": if val == "FAILED":
return "FAILED" return "FAILED"
return f"{val:,}" return f"{val:,}"
@@ -126,25 +129,33 @@ def format_table(rows):
header = ( header = (
f"{'Commit':<{COL_COMMIT}} " 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"{'Delta':>{COL_DELTA}} "
f"Title" f"Title"
) )
sep = ( sep = (
f"{BOX_CHAR * COL_COMMIT} " 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 * COL_DELTA} "
f"{BOX_CHAR * 40}" f"{BOX_CHAR * 40}"
) )
print(header) print(header)
print(sep) print(sep)
for row in rows: for row in rows:
flash_str = fmt_flash(row["flash_bytes"]) flash_str = fmt_size(row["flash_bytes"])
delta_str = fmt_delta(row["delta"]) flash_d = fmt_delta(row["flash_delta"])
ram_str = fmt_size(row["ram_bytes"])
ram_d = fmt_delta(row["ram_delta"])
print( print(
f"{row['commit']:<{COL_COMMIT}} " f"{row['commit']:<{COL_COMMIT}} "
f"{flash_str:>{COL_FLASH}} " f"{flash_str:>{COL_SIZE}} "
f"{delta_str:>{COL_DELTA}} " f"{flash_d:>{COL_DELTA}} "
f"{ram_str:>{COL_SIZE}} "
f"{ram_d:>{COL_DELTA}} "
f"{row['title']}" f"{row['title']}"
) )
@@ -173,7 +184,7 @@ def build_commits_from_list(refs):
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Measure firmware flash size across git commits.", description="Measure firmware flash and RAM size across git commits.",
epilog=( epilog=(
"Range mode walks every commit between START and END (one branch). " "Range mode walks every commit between START and END (one branch). "
"List mode builds specific refs that may come from different branches." "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) print(f" Building (env: {args.env})...", file=sys.stderr)
rc, output = build_firmware(args.env) 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) print(f" BUILD FAILED (exit {rc}) -- skipping", file=sys.stderr)
results.append((sha, title, None)) results.append((sha, title, None, None, True))
continue continue
used = parse_flash_used(output) flash_used = parse_size_line(FLASH_RE, output)
if used is None: 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) 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 continue
print(f" Flash used: {used:,} bytes", file=sys.stderr) ram_str = f", RAM: {ram_used:,}" if ram_used is not None else ""
results.append((sha, title, used)) print(f" Flash: {flash_used:,}{ram_str} bytes", file=sys.stderr)
results.append((sha, title, flash_used, ram_used, False))
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n[info] Interrupted -- writing partial results.", file=sys.stderr) print("\n[info] Interrupted -- writing partial results.", file=sys.stderr)
@@ -258,22 +272,35 @@ def main():
# Build result rows with deltas # Build result rows with deltas
rows = [] rows = []
prev_size = None prev_flash = None
for sha, title, used in results: prev_ram = None
if used is not None and prev_size is not None: for sha, title, flash_used, ram_used, build_failed in results:
delta = used - prev_size 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: 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({ rows.append({
"commit": sha[:10], "commit": sha[:10],
"title": title, "title": title,
"flash_bytes": used if used is not None else "FAILED", "flash_bytes": flash_bytes,
"delta": delta, "flash_delta": flash_delta,
"ram_bytes": ram_bytes,
"ram_delta": ram_delta,
}) })
if used is not None: if flash_used is not None:
prev_size = used 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 is not None:
if args.csv == "-": if args.csv == "-":