summaryrefslogtreecommitdiff
path: root/build-aux/measurestack/analyze.py
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-01 04:54:14 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-01 05:09:45 -0600
commitab02d73a5bf6369d567d408d0544748dfd0303ea (patch)
tree04170be556fc797da75f4896407478b63c98c77e /build-aux/measurestack/analyze.py
parent352da947a02ff7658deb7729efa8b2cf7ce7a5cc (diff)
measurestack: Try to tidy analyze.py a bit
Diffstat (limited to 'build-aux/measurestack/analyze.py')
-rw-r--r--build-aux/measurestack/analyze.py81
1 files changed, 51 insertions, 30 deletions
diff --git a/build-aux/measurestack/analyze.py b/build-aux/measurestack/analyze.py
index a5ac6e5..d4ca721 100644
--- a/build-aux/measurestack/analyze.py
+++ b/build-aux/measurestack/analyze.py
@@ -21,6 +21,8 @@ __all__ = [
"analyze",
]
+# types ########################################################################
+
class BaseName:
# class ##########################################################
@@ -156,6 +158,8 @@ class Application(typing.Protocol):
def skip_call(self, chain: typing.Sequence[QName], funcname: QName) -> bool: ...
+# code #########################################################################
+
re_node_label = re.compile(
r"(?P<funcname>[^\n]+)\n"
+ r"(?P<location>[^\n]+:[0-9]+:[0-9]+)\n"
@@ -166,13 +170,37 @@ re_node_label = re.compile(
)
-def analyze(
- *,
+class _Graph:
+ graph: dict[QName, Node]
+ qualified: dict[BaseName, set[QName]]
+
+ def resolve_funcname(self, funcname: QName) -> QName | None:
+ # Handle `ld --wrap` functions
+ if QName(f"__wrap_{str(funcname)}") in self.graph:
+ return QName(f"__wrap_{str(funcname)}")
+ if (
+ str(funcname).startswith("__real_")
+ and QName(str(funcname)[len("__real_") :]) in self.graph
+ ):
+ funcname = QName(str(funcname)[len("__real_") :])
+
+ # Usual case
+ if funcname in self.graph:
+ return funcname
+
+ # Handle `__weak` functions
+ if ":" not in str(funcname):
+ qnames = self.qualified.get(BaseName(str(funcname)), set())
+ if len(qnames) == 1:
+ return next(name for name in qnames)
+
+ return None
+
+
+def _make_graph(
ci_fnames: typing.Collection[str],
- app_func_filters: dict[str, typing.Callable[[QName], tuple[int, bool]]],
app: Application,
- cfg_max_call_depth: int,
-) -> AnalyzeResult:
+) -> _Graph:
graph: dict[QName, Node] = {}
qualified: dict[BaseName, set[QName]] = {}
@@ -249,34 +277,27 @@ def analyze(
raise ValueError(f"duplicate node {node.funcname}")
graph[node.funcname] = node
+ ret = _Graph()
+ ret.graph = graph
+ ret.qualified = qualified
+ return ret
+
+
+def analyze(
+ *,
+ ci_fnames: typing.Collection[str],
+ app_func_filters: dict[str, typing.Callable[[QName], tuple[int, bool]]],
+ app: Application,
+ cfg_max_call_depth: int,
+) -> AnalyzeResult:
+ graphdata = _make_graph(ci_fnames, app)
+
missing: set[QName] = set()
dynamic: set[QName] = set()
included_funcs: set[QName] = set()
dbg = False
- def resolve_funcname(funcname: QName) -> QName | None:
- # Handle `ld --wrap` functions
- if QName(f"__wrap_{str(funcname)}") in graph:
- return QName(f"__wrap_{str(funcname)}")
- if (
- str(funcname).startswith("__real_")
- and QName(str(funcname)[len("__real_") :]) in graph
- ):
- funcname = QName(str(funcname)[len("__real_") :])
-
- # Usual case
- if funcname in graph:
- return funcname
-
- # Handle `__weak` functions
- if ":" not in str(funcname):
- qnames = qualified.get(BaseName(str(funcname)), set())
- if len(qnames) == 1:
- return next(name for name in qnames)
-
- return None
-
track_inclusion: bool = True
def nstatic(
@@ -286,7 +307,7 @@ def analyze(
) -> int:
nonlocal dbg
nonlocal track_inclusion
- funcname = resolve_funcname(orig_funcname)
+ funcname = graphdata.resolve_funcname(orig_funcname)
if not funcname:
if chain and app.skip_call(chain, orig_funcname):
if dbg:
@@ -305,7 +326,7 @@ def analyze(
if len(chain) == cfg_max_call_depth:
raise ValueError(f"max call depth exceeded: {[*chain, funcname]}")
- node = graph[funcname]
+ node = graphdata.graph[funcname]
if dbg:
print(f"//dbg: {'- '*len(chain)}{funcname}\t{node.nstatic}")
if node.usage_kind == "dynamic" or node.ndynamic > 0:
@@ -325,7 +346,7 @@ def analyze(
groups: dict[str, AnalyzeResultGroup] = {}
for grp_name, grp_filter in app_func_filters.items():
rows: dict[QName, AnalyzeResultVal] = {}
- for funcname in graph:
+ for funcname in graphdata.graph:
cnt, track_inclusion = grp_filter(funcname)
if cnt:
rows[funcname] = AnalyzeResultVal(nstatic=nstatic(funcname), cnt=cnt)