summaryrefslogtreecommitdiff
path: root/build-aux/measurestack/analyze.py
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-28 15:55:07 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-06 11:50:46 -0600
commit3adb478bb5257ab48f366f58f2a2ce3ae1115b97 (patch)
treedc532f0291b2a9003fd4bfb90368c1944fbb3e82 /build-aux/measurestack/analyze.py
parentce19c341c2fb1d1386d449b2d135b4a86d9a375f (diff)
measurestack: Add a `dbg_sort` toggle
Diffstat (limited to 'build-aux/measurestack/analyze.py')
-rw-r--r--build-aux/measurestack/analyze.py46
1 files changed, 43 insertions, 3 deletions
diff --git a/build-aux/measurestack/analyze.py b/build-aux/measurestack/analyze.py
index 86c51f2..3601707 100644
--- a/build-aux/measurestack/analyze.py
+++ b/build-aux/measurestack/analyze.py
@@ -3,6 +3,7 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+import random
import re
import sys
import typing
@@ -13,6 +14,8 @@ from . import vcg
dbg_dumpgraph = False
# Whether to print "//dbg-nstatic:" lines that trace nstatic() execution
dbg_nstatic = False
+# Whether to sort things for consistently-ordered execution, or shuffle things to detect bugs
+dbg_sort: typing.Literal["unsorted", "sorted", "shuffled"] = "unsorted"
# pylint: disable=unused-variable
__all__ = [
@@ -20,6 +23,7 @@ __all__ = [
"QName",
"UsageKind",
"Node",
+ "maybe_sorted",
"AnalyzeResultVal",
"AnalyzeResultGroup",
"AnalyzeResult",
@@ -306,6 +310,39 @@ class _Graph:
return self._resolve_cache[funcname]
+if typing.TYPE_CHECKING:
+ from _typeshed import SupportsRichComparisonT as _T_sortable
+
+_T = typing.TypeVar("_T")
+
+
+@typing.overload
+def maybe_sorted(
+ unsorted: typing.Iterable["_T_sortable"], /, *, key: None = None
+) -> typing.Iterable["_T_sortable"]: ...
+@typing.overload
+def maybe_sorted(
+ unsorted: typing.Iterable[_T], /, *, key: typing.Callable[[_T], "_T_sortable"]
+) -> typing.Iterable[_T]: ...
+
+
+def maybe_sorted(
+ unsorted: typing.Iterable[_T],
+ /,
+ *,
+ key: typing.Callable[[_T], "_T_sortable"] | None = None,
+) -> typing.Iterable[_T]:
+ match dbg_sort:
+ case "unsorted":
+ return unsorted
+ case "sorted":
+ return sorted(unsorted, key=key) # type: ignore
+ case "shuffled":
+ ret = [*unsorted]
+ random.shuffle(ret)
+ return ret
+
+
def _make_graph(
ci_fnames: typing.Collection[str],
app: Application,
@@ -368,7 +405,7 @@ def _make_graph(
raise ValueError(f"unknown caller: {caller}")
if callee == QName("__indirect_call"):
callees, missing_ok = app.indirect_callees(elem)
- for callee in callees:
+ for callee in maybe_sorted(callees):
if callee not in graph[caller].calls:
graph[caller].calls[callee] = missing_ok
else:
@@ -376,12 +413,15 @@ def _make_graph(
case _:
raise ValueError(f"unknown elem type {elem.typ!r}")
- for ci_fname in ci_fnames:
+ for ci_fname in maybe_sorted(ci_fnames):
with open(ci_fname, "r", encoding="utf-8") as fh:
for elem in vcg.parse_vcg(fh):
handle_elem(elem)
- for node in app.extra_nodes():
+ def sort_key(node: Node) -> QName:
+ return node.funcname
+
+ for node in maybe_sorted(app.extra_nodes(), key=sort_key):
if node.funcname in graph:
raise ValueError(f"duplicate node {node.funcname}")
graph[node.funcname] = node