summaryrefslogtreecommitdiff
path: root/build-aux/measurestack/app_output.py
diff options
context:
space:
mode:
Diffstat (limited to 'build-aux/measurestack/app_output.py')
-rw-r--r--build-aux/measurestack/app_output.py139
1 files changed, 139 insertions, 0 deletions
diff --git a/build-aux/measurestack/app_output.py b/build-aux/measurestack/app_output.py
new file mode 100644
index 0000000..5336b85
--- /dev/null
+++ b/build-aux/measurestack/app_output.py
@@ -0,0 +1,139 @@
+# build-aux/measurestack/app_output.py - Generate `*_stack.c` files
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+import typing
+
+from . import analyze
+from .analyze import QName
+
+# pylint: disable=unused-variable
+__all__ = [
+ "print_c",
+]
+
+
+def print_group(
+ result: analyze.AnalyzeResult,
+ location_xform: typing.Callable[[QName], str],
+ grp_name: str,
+) -> None:
+ grp = result.groups[grp_name]
+ if not grp.rows:
+ print(f"= {grp_name} (empty) =")
+ return
+
+ nsum = sum(v.nstatic * v.cnt for v in grp.rows.values())
+ nmax = max(v.nstatic for v in grp.rows.values())
+
+ # Figure sizes.
+ namelen = max(
+ [len(location_xform(k)) for k in grp.rows.keys()] + [len(grp_name) + 4]
+ )
+ numlen = len(str(nsum))
+ sep1 = ("=" * namelen) + " " + "=" * numlen
+ sep2 = ("-" * namelen) + " " + "-" * numlen
+
+ # Print.
+ print("= " + grp_name + " " + sep1[len(grp_name) + 3 :])
+ for qname, val in sorted(grp.rows.items()):
+ name = location_xform(qname)
+ if val.nstatic == 0:
+ continue
+ print(
+ f"{name:<{namelen}} {val.nstatic:>{numlen}}"
+ + (f" * {val.cnt}" if val.cnt != 1 else "")
+ )
+ print(sep2)
+ print(f"{'Total':<{namelen}} {nsum:>{numlen}}")
+ print(f"{'Maximum':<{namelen}} {nmax:>{numlen}}")
+ print(sep1)
+
+
+def next_power_of_2(x: int) -> int:
+ return 1 << (x.bit_length())
+
+
+def print_c(
+ result: analyze.AnalyzeResult, location_xform: typing.Callable[[QName], str]
+) -> None:
+ print("#include <stddef.h> /* for size_t */")
+ print()
+ print("/*")
+ print_group(result, location_xform, "Threads")
+ print_group(result, location_xform, "Interrupt handlers")
+ print("*/")
+ intrstack = max(
+ v.nstatic for v in result.groups["Interrupt handlers"].rows.values()
+ )
+ stack_guard_size = 16 * 2
+
+ class CrRow(typing.NamedTuple):
+ name: str
+ cnt: int
+ base: int
+ size: int
+
+ rows: list[CrRow] = []
+ mainrow: CrRow | None = None
+ for funcname, val in result.groups["Threads"].rows.items():
+ name = str(funcname.base())
+ base = val.nstatic
+ size = base + intrstack
+ if name in ["main", "_entry_point"]:
+ mainrow = CrRow(name=name, cnt=1, base=base, size=size)
+ else:
+ size = next_power_of_2(size + stack_guard_size) - stack_guard_size
+ rows.append(CrRow(name=name, cnt=val.cnt, base=base, size=size))
+ namelen = max(len(r.name) for r in rows)
+ baselen = max(len(str(r.base)) for r in rows)
+ sizesum = sum(r.cnt * (r.size + stack_guard_size) for r in rows)
+ sizelen = len(str(max(sizesum, mainrow.size if mainrow else 0)))
+
+ def print_row(comment: bool, name: str, size: int, eqn: str | None = None) -> None:
+ prefix = "const size_t CONFIG_COROUTINE_STACK_SIZE_"
+ if comment:
+ print(f"/* {name}".ljust(len(prefix) + namelen), end="")
+ else:
+ print(f"{prefix}{name:<{namelen}}", end="")
+ print(f" = {size:>{sizelen}};", end="")
+ if comment:
+ print(" */", end="")
+ elif eqn:
+ print(" ", end="")
+ if eqn:
+ print(f" /* {eqn} */", end="")
+ print()
+
+ for row in sorted(rows):
+ print_row(
+ False,
+ row.name,
+ row.size,
+ f"LM_NEXT_POWER_OF_2({row.base:>{baselen}}+{intrstack}+{stack_guard_size})-{stack_guard_size}",
+ )
+ print_row(True, "TOTAL (inc. stack guard)", sizesum)
+ if mainrow:
+ print_row(
+ True,
+ "MAIN/KERNEL",
+ mainrow.size,
+ f" {mainrow.base:>{baselen}}+{intrstack}",
+ )
+ print()
+ print("/*")
+ print_group(result, location_xform, "Misc")
+
+ for funcname in sorted(result.missing):
+ print(f"warning: missing: {location_xform(funcname)}")
+ for funcname in sorted(result.dynamic):
+ print(f"warning: dynamic-stack-usage: {location_xform(funcname)}")
+
+ print("*/")
+ print("")
+ print("/*")
+ print_group(result, location_xform, "Extra")
+ for funcname in sorted(result.included_funcs):
+ print(f"included: {location_xform(funcname)}")
+ print("*/")