From 35d1a8c887ce53ad6326f96ad54b684a1a014e5f Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sun, 9 Mar 2025 01:33:35 -0700 Subject: stack.c.gen: MISC: Rename AppPlugin to CmdPlugin --- build-aux/stack.c.gen | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 60f51fe..095a992 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -404,7 +404,7 @@ class PluginApplication: # Application-specific code -class AppPlugin: +class CmdPlugin: def is_intrhandler(self, name: str) -> bool: return False @@ -1151,7 +1151,7 @@ def main( return 0 plugins += [ - AppPlugin(), + CmdPlugin(), LibObjPlugin(arg_c_fnames), LibHWPlugin(arg_pico_platform), LibCRPlugin(), -- cgit v1.2.3-2-g168b From 42bba273463274c4b54a09a4bd7a86e913712243 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 04:00:54 -0700 Subject: stack.c.gen: INFRA: Have analyze() return thread counts instead of a sum --- build-aux/stack.c.gen | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 095a992..05ca842 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -131,10 +131,13 @@ def synthetic_node( return n +class AnalyzeResultVal(typing.NamedTuple): + nstatic: int + cnt: int + + class AnalyzeResultGroup(typing.NamedTuple): - rows: dict[str, int] - nmax: int - nsum: int + rows: dict[str, AnalyzeResultVal] class AnalyzeResult(typing.NamedTuple): @@ -304,17 +307,14 @@ def analyze( groups: dict[str, AnalyzeResultGroup] = dict() for grp_name, grp_filter in app_func_filters.items(): - nmax = 0 - nsum = 0 - rows: dict[str, int] = {} + rows: dict[str, AnalyzeResultVal] = {} for funcname in graph: if cnt := grp_filter(funcname): n = nstatic(funcname) - rows[app.location_xform(funcname)] = n - if n > nmax: - nmax = n - nsum += cnt * n - groups[grp_name] = AnalyzeResultGroup(rows=rows, nmax=nmax, nsum=nsum) + rows[app.location_xform(funcname)] = AnalyzeResultVal( + nstatic=n, cnt=cnt + ) + groups[grp_name] = AnalyzeResultGroup(rows=rows) return AnalyzeResult( groups=groups, missing=missing, dynamic=dynamic, included_funcs=included_funcs @@ -1210,21 +1210,24 @@ def main( def print_group(grp_name: str) -> None: grp = result.groups[grp_name] + 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(k) for k in grp.rows.keys()] + [len(grp_name) + 4]) - numlen = len(str(grp.nsum)) + numlen = len(str(nsum)) sep1 = ("=" * namelen) + " " + "=" * numlen sep2 = ("-" * namelen) + " " + "-" * numlen # Print. print("= " + grp_name + " " + sep1[len(grp_name) + 3 :]) - for name, num in sorted(grp.rows.items()): - if num == 0: + for name, val in sorted(grp.rows.items()): + if val.nstatic == 0: continue - print(f"{name.ljust(namelen)} {str(num).rjust(numlen)}") + print(f"{name.ljust(namelen)} {str(val.nstatic).rjust(numlen)}") print(sep2) - print(f"{'Total'.ljust(namelen)} {str(grp.nsum).rjust(numlen)}") - print(f"{'Maximum'.ljust(namelen)} {str(grp.nmax).rjust(numlen)}") + print(f"{'Total'.ljust(namelen)} {str(nsum).rjust(numlen)}") + print(f"{'Maximum'.ljust(namelen)} {str(nmax).rjust(numlen)}") print(sep1) def next_power_of_2(x: int) -> int: @@ -1236,10 +1239,12 @@ def main( print_group("Threads") print_group("Interrupt handlers") print("*/") - overhead = result.groups["Interrupt handlers"].nmax + overhead = max(v.nstatic for v in result.groups["Interrupt handlers"].rows.values()) rows: list[tuple[str, int, int]] = [] - for funcname, base in result.groups["Threads"].rows.items(): - rows.append((funcname.split(":")[-1], base, next_power_of_2(base + overhead))) + for funcname, val in result.groups["Threads"].rows.items(): + base = val.nstatic + size = next_power_of_2(base + overhead) + rows.append((funcname.split(":")[-1], base, size)) namelen = max(len(r[0]) for r in rows) baselen = max(len(str(r[1])) for r in rows) sizelen = max(len(str(r[2])) for r in rows) -- cgit v1.2.3-2-g168b From 429d1249022a357b33c5b3536a4170375b4791c7 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 19:47:59 -0700 Subject: stack.c.gen: INFRA: Build QName vs BaseName into the type system This evidently reveals a bug where the printf 'out' skipper is erroneously trying to prune printf.c:_out_buffer, but was failing to do so because it was mixing up qualified vs base names. But now it's "succeeding" and erroneously removing _out_buffer. I'll fix this in a later commit. This didn't affect stack totals (because it was dwarfed by the pico_stdio fct). --- build-aux/stack.c.gen | 460 +++++++++++++++++++++++++++++--------------------- 1 file changed, 267 insertions(+), 193 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 05ca842..02b0879 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -98,11 +98,56 @@ def parse_vcg(reader: typing.TextIO) -> typing.Iterator[VCGElem]: UsageKind: typing.TypeAlias = typing.Literal["static", "dynamic", "dynamic,bounded"] +class BaseName: + _content: str + + def __init__(self, content: str) -> None: + if ":" in content: + raise ValueError(f"invalid non-qualified name: {repr(content)}") + self._content = content + + def __str__(self) -> str: + return self._content + + def __eq__(self, other: typing.Any) -> bool: + assert isinstance(other, BaseName) + return self._content == other._content + + def __lt__(self, other: "BaseName") -> bool: + return self._content < other._content + + def __hash__(self) -> int: + return hash(self._content) + + +class QName: + _content: str + + def __init__(self, content: str) -> None: + self._content = content + + def __str__(self) -> str: + return self._content + + def __eq__(self, other: typing.Any) -> bool: + assert isinstance(other, QName) + return self._content == other._content + + def __lt__(self, other: "QName") -> bool: + return self._content < other._content + + def __hash__(self) -> int: + return hash(self._content) + + def base(self) -> BaseName: + return BaseName(str(self).rsplit(":", 1)[-1].split(".", 1)[0]) + + class Node: # from .title (`static` and `__weak` functions are prefixed with # the compilation unit .c file. For static functions that's fine, # but we'll have to handle it specially for __weak.). - funcname: str + funcname: QName # .label is "{funcname}\n{location}\n{nstatic} bytes (static}\n{ndynamic} dynamic objects" location: str usage_kind: UsageKind @@ -111,7 +156,7 @@ class Node: # edges with .sourcename set to this node, val is if it's # OK/expected that the function be missing. - calls: dict[str, bool] + calls: dict[QName, bool] def synthetic_node( @@ -119,14 +164,14 @@ def synthetic_node( ) -> Node: n = Node() - n.funcname = name + n.funcname = QName(name) n.location = "" n.usage_kind = "static" n.nstatic = nstatic n.ndynamic = 0 - n.calls = dict((c, False) for c in calls) + n.calls = dict((QName(c), False) for c in calls) return n @@ -137,28 +182,29 @@ class AnalyzeResultVal(typing.NamedTuple): class AnalyzeResultGroup(typing.NamedTuple): - rows: dict[str, AnalyzeResultVal] + rows: dict[QName, AnalyzeResultVal] class AnalyzeResult(typing.NamedTuple): groups: dict[str, AnalyzeResultGroup] - missing: set[str] - dynamic: set[str] + missing: set[QName] + dynamic: set[QName] - included_funcs: set[str] + included_funcs: set[QName] class Application(typing.Protocol): def extra_nodes(self) -> typing.Collection[Node]: ... - def location_xform(self, loc: str) -> str: ... - def indirect_callees(self, elem: VCGElem) -> tuple[list[str], bool]: ... - def skip_call(self, chain: list[str], funcname: str) -> bool: ... + def indirect_callees( + self, elem: VCGElem + ) -> tuple[typing.Collection[QName], bool]: ... + def skip_call(self, chain: list[QName], funcname: QName) -> bool: ... def analyze( *, ci_fnames: typing.Collection[str], - app_func_filters: dict[str, typing.Callable[[str], int]], + app_func_filters: dict[str, typing.Callable[[QName], int]], app: Application, cfg_max_call_depth: int, ) -> AnalyzeResult: @@ -171,8 +217,8 @@ def analyze( flags=re.MULTILINE, ) - graph: dict[str, Node] = dict() - qualified: dict[str, set[str]] = dict() + graph: dict[QName, Node] = dict() + qualified: dict[BaseName, set[QName]] = dict() def handle_elem(elem: VCGElem) -> None: match elem.typ: @@ -183,7 +229,7 @@ def analyze( for k, v in elem.attrs.items(): match k: case "title": - node.funcname = v + node.funcname = QName(v) case "label": if elem.attrs.get("shape", "") != "ellipse": m = re_node_label.fullmatch(v) @@ -205,22 +251,22 @@ def analyze( raise ValueError(f"unknown edge key {repr(k)}") if not skip: if node.funcname in graph: - raise ValueError(f"duplicate node {repr(node.funcname)}") + raise ValueError(f"duplicate node {repr(str(node.funcname))}") graph[node.funcname] = node - if ":" in node.funcname: - _, shortname = node.funcname.rsplit(":", 1) - if shortname not in qualified: - qualified[shortname] = set() - qualified[shortname].add(node.funcname) + if ":" in str(node.funcname): + basename = node.funcname.base() + if basename not in qualified: + qualified[basename] = set() + qualified[basename].add(node.funcname) case "edge": - caller: str | None = None - callee: str | None = None + caller: QName | None = None + callee: QName | None = None for k, v in elem.attrs.items(): match k: case "sourcename": - caller = v + caller = QName(v) case "targetname": - callee = v + callee = QName(v) case "label": pass case _: @@ -229,7 +275,7 @@ def analyze( raise ValueError(f"incomplete edge: {repr(elem.attrs)}") if caller not in graph: raise ValueError(f"unknown caller: {caller}") - if callee == "__indirect_call": + if str(callee) == "__indirect_call": callees, missing_ok = app.indirect_callees(elem) for callee in callees: if callee not in graph[caller].calls: @@ -246,39 +292,45 @@ def analyze( for node in app.extra_nodes(): if node.funcname in graph: - raise ValueError(f"duplicate node {repr(node.funcname)}") + raise ValueError(f"duplicate node {repr(str(node.funcname))}") graph[node.funcname] = node - missing: set[str] = set() - dynamic: set[str] = set() - included_funcs: set[str] = set() + missing: set[QName] = set() + dynamic: set[QName] = set() + included_funcs: set[QName] = set() dbg = False - def resolve_funcname(funcname: str) -> str | None: + def resolve_funcname(funcname: QName) -> QName | None: # Handle `ld --wrap` functions - if f"__wrap_{funcname}" in graph: - return f"__wrap_{funcname}" - if funcname.startswith("__real_") and funcname[len("__real_") :] in graph: - funcname = funcname[len("__real_") :] + if QName(f"__wrap_{funcname}") in graph: + return QName(f"__wrap_{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 + if QName(str(funcname)) in graph: + return QName(str(funcname)) # Handle `__weak` functions - if funcname in qualified and len(qualified[funcname]) == 1: - return sorted(qualified[funcname])[0] + if ( + ":" not in str(funcname) + and len(qualified.get(BaseName(str(funcname)), set())) == 1 + ): + return sorted(qualified[BaseName(str(funcname))])[0] return None def nstatic( - orig_funcname: str, chain: list[str] = [], missing_ok: bool = False + orig_funcname: QName, chain: list[QName] = [], missing_ok: bool = False ) -> int: nonlocal dbg funcname = resolve_funcname(orig_funcname) if not funcname: - if app.skip_call(chain, orig_funcname): + if app.skip_call(chain, QName(str(orig_funcname))): return 0 if not missing_ok: missing.add(orig_funcname) @@ -293,7 +345,7 @@ def analyze( if dbg: print(f"//dbg: {funcname}\t{node.nstatic}") if node.usage_kind == "dynamic" or node.ndynamic > 0: - dynamic.add(app.location_xform(funcname)) + dynamic.add(funcname) included_funcs.add(funcname) return node.nstatic + max( [ @@ -307,13 +359,10 @@ def analyze( groups: dict[str, AnalyzeResultGroup] = dict() for grp_name, grp_filter in app_func_filters.items(): - rows: dict[str, AnalyzeResultVal] = {} + rows: dict[QName, AnalyzeResultVal] = {} for funcname in graph: if cnt := grp_filter(funcname): - n = nstatic(funcname) - rows[app.location_xform(funcname)] = AnalyzeResultVal( - nstatic=n, cnt=cnt - ) + rows[funcname] = AnalyzeResultVal(nstatic=nstatic(funcname), cnt=cnt) groups[grp_name] = AnalyzeResultGroup(rows=rows) return AnalyzeResult( @@ -351,12 +400,12 @@ re_call_other = re.compile(r"(?P[^(]+)\(.*") class Plugin(typing.Protocol): - def is_intrhandler(self, name: str) -> bool: ... + def is_intrhandler(self, name: QName) -> bool: ... def extra_nodes(self) -> typing.Collection[Node]: ... def indirect_callees( self, loc: str, line: str - ) -> tuple[list[str], bool] | None: ... - def skip_call(self, chain: list[str], call: str) -> bool: ... + ) -> tuple[typing.Collection[QName], bool] | None: ... + def skip_call(self, chain: list[QName], call: QName) -> bool: ... class PluginApplication: @@ -375,10 +424,7 @@ class PluginApplication: ret.extend(plugin.extra_nodes()) return ret - def location_xform(self, loc: str) -> str: - return self._location_xform(loc) - - def indirect_callees(self, elem: VCGElem) -> tuple[list[str], bool]: + def indirect_callees(self, elem: VCGElem) -> tuple[typing.Collection[QName], bool]: loc = elem.attrs.get("label", "") line = read_source(loc) @@ -390,10 +436,10 @@ class PluginApplication: placeholder = "__indirect_call" if m := re_call_other.fullmatch(line): placeholder += ":" + m.group("func") - placeholder += " at " + self.location_xform(elem.attrs.get("label", "")) - return [placeholder], False + placeholder += " at " + self._location_xform(elem.attrs.get("label", "")) + return [QName(placeholder)], False - def skip_call(self, chain: list[str], funcname: str) -> bool: + def skip_call(self, chain: list[QName], funcname: QName) -> bool: for plugin in self._plugins: if plugin.skip_call(chain, funcname): return True @@ -405,27 +451,29 @@ class PluginApplication: class CmdPlugin: - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: if "/3rd-party/" in loc: return None if "srv->auth" in line: return [], False if "srv->rootdir" in line: - return ["get_root"], False + return [QName("get_root")], False return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False class LibObjPlugin: - objcalls: dict[str, set[str]] # method_name => {method_impls} + objcalls: dict[str, set[QName]] # method_name => {method_impls} def __init__(self, arg_c_fnames: typing.Collection[str]) -> None: ifaces: dict[str, set[str]] = {} # iface_name => {method_names} @@ -461,35 +509,37 @@ class LibObjPlugin: if m := re_lo_implementation.match(line): implementations[m.group("iface")].add(m.group("impl_name")) - objcalls: dict[str, set[str]] = {} # method_name => {method_impls} + objcalls: dict[str, set[QName]] = {} # method_name => {method_impls} for iface_name in ifaces: for method_name in ifaces[iface_name]: - if method_name not in objcalls: + if QName(method_name) not in objcalls: objcalls[method_name] = set() for impl_name in implementations[iface_name]: - objcalls[method_name].add(impl_name + "_" + method_name) + objcalls[method_name].add(QName(impl_name + "_" + method_name)) self.objcalls = objcalls - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: re_call_objcall = re.compile(r"LO_CALL\((?P[^,]+), (?P[^,)]+)[,)].*") if "/3rd-party/" in loc: return None if m := re_call_objcall.fullmatch(line): if m.group("meth") in self.objcalls: - return sorted(self.objcalls[m.group("meth")]), False + return self.objcalls[m.group("meth")], False return [ - f"__indirect_call:{m.group('obj')}.vtable->{m.group('meth')}" + QName(f"__indirect_call:{m.group('obj')}.vtable->{m.group('meth')}") ], False return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False @@ -499,8 +549,8 @@ class LibHWPlugin: def __init__(self, arg_pico_platform: str) -> None: self.pico_platform = arg_pico_platform - def is_intrhandler(self, name: str) -> bool: - return name in [ + def is_intrhandler(self, name: QName) -> bool: + return str(name.base()) in [ "rp2040_hwtimer_intrhandler", "hostclock_handle_sig_alarm", "hostnet_handle_sig_io", @@ -511,72 +561,76 @@ class LibHWPlugin: def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: if "/3rd-party/" in loc: return None if "trigger->cb(trigger->cb_arg)" in line: ret = [ - "alarmclock_sleep_intrhandler", + QName("alarmclock_sleep_intrhandler"), ] if self.pico_platform == "rp2040": ret += [ - "w5500_tcp_alarm_handler", - "w5500_udp_alarm_handler", + QName("w5500_tcp_alarm_handler"), + QName("w5500_udp_alarm_handler"), ] return ret, False if "/rp2040_gpioirq.c:" in loc and "handler->fn" in line: return [ - "w5500_intrhandler", + QName("w5500_intrhandler"), ], False if "/rp2040_dmairq.c:" in loc and "handler->fn" in line: return [ - "rp2040_hwspi_intrhandler", + QName("rp2040_hwspi_intrhandler"), ], False return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False class LibCRPlugin: - def is_intrhandler(self, name: str) -> bool: - return name in [ - "_cr_gdb_intrhandler", - ] + def is_intrhandler(self, name: QName) -> bool: + return str(name.base()) in ("_cr_gdb_intrhandler",) def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False class LibCRIPCPlugin: - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: if "/3rd-party/" in loc: return None if "/chan.c:" in loc and "front->dequeue(" in line: return [ - "_cr_chan_dequeue", - "_cr_select_dequeue", + QName("_cr_chan_dequeue"), + QName("_cr_select_dequeue"), ], False return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False class Lib9PPlugin: - tmessage_handlers: set[str] | None + tmessage_handlers: set[QName] | None lib9p_msgs: set[str] _CONFIG_9P_NUM_SOCKS: int | None CONFIG_9P_SRV_MAX_REQS: int | None @@ -622,7 +676,7 @@ class Lib9PPlugin: # Read sources ######################################################### - tmessage_handlers: set[str] | None = None + tmessage_handlers: set[QName] | None = None if lib9p_srv_c_fname: re_tmessage_handler = re.compile( r"^\s*\[LIB9P_TYP_T[^]]+\]\s*=\s*\(tmessage_handler\)\s*(?P\S+),\s*$" @@ -632,7 +686,7 @@ class Lib9PPlugin: for line in fh: line = line.rstrip() if m := re_tmessage_handler.fullmatch(line): - tmessage_handlers.add(m.group("handler")) + tmessage_handlers.add(QName(m.group("handler"))) self.tmessage_handlers = tmessage_handlers lib9p_msgs: set[str] = set() @@ -646,22 +700,24 @@ class Lib9PPlugin: lib9p_msgs.add(typ) self.lib9p_msgs = lib9p_msgs - def thread_count(self, name: str) -> int: + def thread_count(self, name: QName) -> int: assert self._CONFIG_9P_NUM_SOCKS assert self.CONFIG_9P_SRV_MAX_REQS - if "read" in name: + if "read" in str(name.base()): return self._CONFIG_9P_NUM_SOCKS - elif "write" in name: + elif "write" in str(name.base()): return self._CONFIG_9P_NUM_SOCKS * self.CONFIG_9P_SRV_MAX_REQS return 1 - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: if "/3rd-party/" in loc: return None if ( @@ -670,97 +726,99 @@ class Lib9PPlugin: and "tmessage_handlers[typ](" in line ): # Functions for disabled protocol extensions will be missing. - return sorted(self.tmessage_handlers), True + return self.tmessage_handlers, True if self.lib9p_msgs and "/9p.c:" in loc: for meth in ["validate", "unmarshal", "marshal"]: if line.startswith(f"tentry.{meth}("): # Functions for disabled protocol extensions will be missing. - return sorted(f"{meth}_{msg}" for msg in self.lib9p_msgs), True + return [QName(f"{meth}_{msg}") for msg in self.lib9p_msgs], True return None - def skip_call(self, chain: list[str], call: str) -> bool: - if "lib9p/srv.c:srv_util_pathfree" in call: + def skip_call(self, chain: list[QName], call: QName) -> bool: + if "lib9p/srv.c:srv_util_pathfree" in str(call): assert isinstance(self.CONFIG_9P_SRV_MAX_DEPTH, int) if len(chain) >= self.CONFIG_9P_SRV_MAX_DEPTH and all( - ("lib9p/srv.c:srv_util_pathfree" in c) + ("lib9p/srv.c:srv_util_pathfree" in str(c)) for c in chain[-self.CONFIG_9P_SRV_MAX_DEPTH :] ): return True re_msg_meth = re.compile( r"^lib9p_(?P[TR])msg_(?Pvalidate|unmarshal|marshal)$" ) - wrapper = next((c for c in chain if re_msg_meth.match(c)), None) + wrapper = next((c for c in chain if re_msg_meth.match(str(c))), None) if wrapper: - m = re_msg_meth.match(wrapper) + m = re_msg_meth.match(str(wrapper)) assert m - deny = ":" + m.group("meth") + "_" + ("R" if m.group("grp") == "T" else "T") - if deny in call: + deny = m.group("meth") + "_" + ("R" if m.group("grp") == "T" else "T") + if str(call.base()).startswith(deny): return True return False class LibMiscPlugin: - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: if ( len(chain) > 1 - and chain[-1] == "__assert_msg_fail" - and call.endswith(":__lm_printf") - and "__assert_msg_fail" in chain[:-1] + and str(chain[-1].base()) == "__assert_msg_fail" + and str(call.base()) == "__lm_printf" + and any(str(c.base()) == "__assert_msg_fail" for c in chain[:-1]) ): return True return False class PicoSDKPlugin: - app_init_array: typing.Collection[str] - app_preinit_array: typing.Collection[str] + app_init_array: typing.Collection[QName] + app_preinit_array: typing.Collection[QName] def __init__( self, *, - app_init_array: typing.Collection[str], + app_init_array: typing.Collection[QName], ) -> None: self.app_init_array = app_init_array # git grep '^PICO_RUNTIME_INIT_FUNC\S*(' self.app_preinit_array = [ - # "runtime_init_mutex", # pico_mutex - # "runtime_init_default_alarm_pool", # pico_time - # "runtime_init_boot_locks_reset", # hardware_boot_lock - "runtime_init_per_core_irq_priorities", # hardware_irq - # "spinlock_set_extexclall", # hardware_sync_spin_lock - "__aeabi_bits_init", # pico_bit_ops - # "runtime_init_bootrom_locking_enable", # pico_bootrom, rp2350-only - # "runtime_init_pre_core_tls_setup", # pico_clib_interface, picolibc-only - # "__aeabi_double_init", # pico_double - # "__aeabi_float_init", # pico_float - "__aeabi_mem_init", # pico_mem_ops - "first_per_core_initializer", # pico_runtime + # QName("runtime_init_mutex"), # pico_mutex + # QName("runtime_init_default_alarm_pool"), # pico_time + # QName("runtime_init_boot_locks_reset"), # hardware_boot_lock + QName("runtime_init_per_core_irq_priorities"), # hardware_irq + # QName("spinlock_set_extexclall"), # hardware_sync_spin_lock + QName("__aeabi_bits_init"), # pico_bit_ops + # QName("runtime_init_bootrom_locking_enable"), # pico_bootrom, rp2350-only + # QName("runtime_init_pre_core_tls_setup"), # pico_clib_interface, picolibc-only + # QName("__aeabi_double_init"), # pico_double + # QName("__aeabi_float_init"), # pico_float + QName("__aeabi_mem_init"), # pico_mem_ops + QName("first_per_core_initializer"), # pico_runtime # pico_runtime_init - # "runtime_init_bootrom_reset", # rp2350-only - # "runtime_init_per_core_bootrom_reset", # rp2350-only - # "runtime_init_per_core_h3_irq_registers", # rp2350-only - "runtime_init_early_resets", - "runtime_init_usb_power_down", - # "runtime_init_per_core_enable_coprocessors", # PICO_RUNTIME_SKIP_INIT_PER_CORE_ENABLE_COPROCESSORS - "runtime_init_clocks", - "runtime_init_post_clock_resets", - "runtime_init_rp2040_gpio_ie_disable", - "runtime_init_spin_locks_reset", - "runtime_init_install_ram_vector_table", + # QName("runtime_init_bootrom_reset"), # rp2350-only + # QName("runtime_init_per_core_bootrom_reset"), # rp2350-only + # QName("runtime_init_per_core_h3_irq_registers"), # rp2350-only + QName("runtime_init_early_resets"), + QName("runtime_init_usb_power_down"), + # QName("runtime_init_per_core_enable_coprocessors"), # PICO_RUNTIME_SKIP_INIT_PER_CORE_ENABLE_COPROCESSORS + QName("runtime_init_clocks"), + QName("runtime_init_post_clock_resets"), + QName("runtime_init_rp2040_gpio_ie_disable"), + QName("runtime_init_spin_locks_reset"), + QName("runtime_init_install_ram_vector_table"), ] - def is_intrhandler(self, name: str) -> bool: - return name in [ + def is_intrhandler(self, name: QName) -> bool: + return str(name.base()) in [ "isr_invalid", "isr_nmi", "isr_hardfault", @@ -770,7 +828,9 @@ class PicoSDKPlugin: *[f"isr_irq{n}" for n in range(32)], ] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: if "/3rd-party/pico-sdk/" not in loc or "/3rd-party/pico-sdk/lib/" in loc: return None m = re_call_other.fullmatch(line) @@ -778,63 +838,65 @@ class PicoSDKPlugin: match call: case "connect_internal_flash_func": - return ["rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)"], False + return [ + QName("rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)") + ], False case "flash_exit_xip_func": - return ["rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)"], False + return [QName("rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)")], False case "flash_range_erase_func": - return ["rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE)"], False + return [QName("rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE)")], False case "flash_flush_cache_func": - return ["rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)"], False + return [QName("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)")], False case "rom_table_lookup": - return ["rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)"], False + return [QName("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)")], False if "/flash.c:" in loc and "boot2_copyout" in line: - return ["_stage2_boot"], False + return [QName("_stage2_boot")], False if "/printf.c:" in loc: if call == "out": return [ - "_out_buffer", - "_out_null", - "_out_fct", + QName("_out_buffer"), + QName("_out_null"), + QName("_out_fct"), ], False if "->fct(" in line: - return ["stdio_buffered_printer"], False + return [QName("stdio_buffered_printer")], False if "/stdio.c:" in loc: if call == "out_func": return [ - "stdio_out_chars_crlf", - "stdio_out_chars_no_crlf", + QName("stdio_out_chars_crlf"), + QName("stdio_out_chars_no_crlf"), ], False if call and (call.startswith("d->") or call.startswith("driver->")): _, meth = call.split("->", 1) match meth: case "out_chars": - return ["stdio_uart_out_chars"], False + return [QName("stdio_uart_out_chars")], False case "out_flush": - return ["stdio_uart_out_flush"], False + return [QName("stdio_uart_out_flush")], False case "in_chars": - return ["stdio_uart_in_chars"], False + return [QName("stdio_uart_in_chars")], False if "/newlib_interface.c:" in loc: if line == "*p)();": - return sorted(self.app_init_array), False + return self.app_init_array, False if "/pico_runtime/runtime.c:" in loc: - return sorted(self.app_preinit_array), False + return self.app_preinit_array, False return None - def skip_call(self, chain: list[str], call: str) -> bool: - if call == "_out_buffer" or call == "_out_fct": + def skip_call(self, chain: list[QName], call: QName) -> bool: + if str(call.base()) in ["_out_buffer", "_out_fct"]: last = "" for pcall in chain: - if pcall in [ + if str(pcall.base()) in [ "__wrap_sprintf", "__wrap_snprintf", "__wrap_vsnprintf", "vfctprintf", ]: - last = pcall + last = str(pcall.base()) if last == "vfctprintf": - return call != "_out_fct" + return str(call.base()) != "_out_fct" else: - return call == "_out_buffer" + return str(call.base()) == "_out_buffer" return False def extra_nodes(self) -> typing.Collection[Node]: @@ -967,7 +1029,7 @@ class PicoSDKPlugin: class TinyUSBDevicePlugin: - tud_drivers: dict[str, set[str]] + tud_drivers: dict[str, set[QName]] # method_name => {method_impls} def __init__(self, arg_c_fnames: typing.Collection[str]) -> None: usbd_c_fname = get_zero_or_one( @@ -996,7 +1058,7 @@ class TinyUSBDevicePlugin: v = m.group("v") tusb_config[k] = bool(int(v)) - tud_drivers: dict[str, set[str]] = {} + tud_drivers: dict[str, set[QName]] = {} re_tud_entry = re.compile( r"^\s+\.(?P\S+)\s*=\s*(?P[a-zA-Z0-9_]+)(?:,.*)?" ) @@ -1023,20 +1085,22 @@ class TinyUSBDevicePlugin: if meth not in tud_drivers: tud_drivers[meth] = set() if impl != "NULL": - tud_drivers[meth].add(impl) + tud_drivers[meth].add(QName(impl)) if line.startswith("}"): in_table = False elif " _usbd_driver[] = {" in line: in_table = True self.tud_drivers = tud_drivers - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: return [] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: if "/tinyusb/" not in loc or "/tinyusb/src/host/" in loc or "_host.c:" in loc: return None m = re_call_other.fullmatch(line) @@ -1045,7 +1109,7 @@ class TinyUSBDevicePlugin: if call == "_ctrl_xfer.complete_cb": return [ # "process_test_mode_cb", - "tud_vendor_control_xfer_cb", + QName("tud_vendor_control_xfer_cb"), *sorted(self.tud_drivers["control_xfer_cb"]), ], False elif call.startswith("driver->"): @@ -1056,12 +1120,12 @@ class TinyUSBDevicePlugin: return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False class NewlibPlugin: - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: @@ -1099,15 +1163,17 @@ class NewlibPlugin: synthetic_node("register_fini", 8, {"atexit"}), ] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False class LibGCCPlugin: - def is_intrhandler(self, name: str) -> bool: + def is_intrhandler(self, name: QName) -> bool: return False def extra_nodes(self) -> typing.Collection[Node]: @@ -1120,10 +1186,12 @@ class LibGCCPlugin: synthetic_node("__aeabi_llsr", 0), ] - def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None: + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: return None - def skip_call(self, chain: list[str], call: str) -> bool: + def skip_call(self, chain: list[QName], call: QName) -> bool: return False @@ -1141,12 +1209,16 @@ def main( lib9p_plugin = Lib9PPlugin(arg_base_dir, arg_c_fnames) - def sbc_is_thread(name: str) -> int: - if name.endswith("_cr") and name != "lib9p_srv_read_cr": - if "9p" in name: + def sbc_is_thread(name: QName) -> int: + if str(name).endswith("_cr") and str(name.base()) != "lib9p_srv_read_cr": + if "9p" in str(name.base()) or "lib9p/tests/test_server/main.c:" in str( + name + ): return lib9p_plugin.thread_count(name) return 1 - if name == ("_reset_handler" if arg_pico_platform == "rp2040" else "main"): + if str(name.base()) == ( + "_reset_handler" if arg_pico_platform == "rp2040" else "main" + ): return 1 return 0 @@ -1165,7 +1237,7 @@ def main( if arg_pico_platform == "rp2040": plugins += [ PicoSDKPlugin( - app_init_array=["register_fini"], + app_init_array=[QName("register_fini")], ), TinyUSBDevicePlugin(arg_c_fnames), NewlibPlugin(), @@ -1174,18 +1246,17 @@ def main( # Tie it all together ############################################ - def thread_filter(name: str) -> int: + def thread_filter(name: QName) -> int: return sbc_is_thread(name) - def intrhandler_filter(name: str) -> int: - name = name.rsplit(":", 1)[-1] + def intrhandler_filter(name: QName) -> int: for plugin in plugins: if plugin.is_intrhandler(name): return 1 return 0 - def misc_filter(name: str) -> int: - if name.endswith(":__lm_printf") or name == "__assert_msg_fail": + def misc_filter(name: QName) -> int: + if str(name.base()) in ["__lm_printf", "__assert_msg_fail"]: return 1 return 0 @@ -1214,14 +1285,17 @@ def main( nmax = max(v.nstatic for v in grp.rows.values()) # Figure sizes. - namelen = max([len(k) for k in grp.rows.keys()] + [len(grp_name) + 4]) + namelen = max( + [len(location_xform(str(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 name, val in sorted(grp.rows.items()): + for qname, val in sorted(grp.rows.items()): + name = location_xform(str(qname)) if val.nstatic == 0: continue print(f"{name.ljust(namelen)} {str(val.nstatic).rjust(numlen)}") @@ -1244,7 +1318,7 @@ def main( for funcname, val in result.groups["Threads"].rows.items(): base = val.nstatic size = next_power_of_2(base + overhead) - rows.append((funcname.split(":")[-1], base, size)) + rows.append((str(funcname.base()), base, size)) namelen = max(len(r[0]) for r in rows) baselen = max(len(str(r[1])) for r in rows) sizelen = max(len(str(r[2])) for r in rows) @@ -1260,9 +1334,9 @@ def main( print_group("Misc") for funcname in sorted(result.missing): - print(f"warning: missing: {funcname}") + print(f"warning: missing: {location_xform(str(funcname))}") for funcname in sorted(result.dynamic): - print(f"warning: dynamic-stack-usage: {funcname}") + print(f"warning: dynamic-stack-usage: {location_xform(str(funcname))}") print("*/") print("") -- cgit v1.2.3-2-g168b From 4ab5ea47aac1ce29a9cd7f6839c699414e46338f Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 17:25:18 -0700 Subject: stack.c.gen: DIAG: Improve diagnostic output --- build-aux/stack.c.gen | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 02b0879..50831c2 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -331,11 +331,17 @@ def analyze( funcname = resolve_funcname(orig_funcname) if not funcname: if app.skip_call(chain, QName(str(orig_funcname))): + if dbg: + print(f"//dbg: {'- '*len(chain)}{orig_funcname}\tskip missing") return 0 if not missing_ok: missing.add(orig_funcname) + if dbg: + print(f"//dbg: {'- '*len(chain)}{orig_funcname}\tmissing") return 0 if app.skip_call(chain, funcname): + if dbg: + print(f"//dbg: {'- '*len(chain)}{orig_funcname}\tskip") return 0 if len(chain) == cfg_max_call_depth: @@ -343,7 +349,7 @@ def analyze( node = graph[funcname] if dbg: - print(f"//dbg: {funcname}\t{node.nstatic}") + print(f"//dbg: {'- '*len(chain)}{funcname}\t{node.nstatic}") if node.usage_kind == "dynamic" or node.ndynamic > 0: dynamic.add(funcname) included_funcs.add(funcname) -- cgit v1.2.3-2-g168b From 7e4ef1e64cf639083df342e2e0c904c5207b56cb Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 04:00:54 -0700 Subject: stack.c.gen: DIAG: Include thread-counts in the output --- build-aux/stack.c.gen | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 50831c2..b6e49a5 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -1304,7 +1304,10 @@ def main( name = location_xform(str(qname)) if val.nstatic == 0: continue - print(f"{name.ljust(namelen)} {str(val.nstatic).rjust(numlen)}") + print( + f"{name.ljust(namelen)} {str(val.nstatic).rjust(numlen)}" + + (f" * {val.cnt}" if val.cnt != 1 else "") + ) print(sep2) print(f"{'Total'.ljust(namelen)} {str(nsum).rjust(numlen)}") print(f"{'Maximum'.ljust(namelen)} {str(nmax).rjust(numlen)}") -- cgit v1.2.3-2-g168b From b2e1d0b6e082d1c865f2c1f8178625861cf2f10a Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 7 Mar 2025 17:21:53 -0700 Subject: stack.c.gen: FIX: Never skip top-level functions --- build-aux/stack.c.gen | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index b6e49a5..b1369d1 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -330,7 +330,7 @@ def analyze( nonlocal dbg funcname = resolve_funcname(orig_funcname) if not funcname: - if app.skip_call(chain, QName(str(orig_funcname))): + if chain and app.skip_call(chain, QName(str(orig_funcname))): if dbg: print(f"//dbg: {'- '*len(chain)}{orig_funcname}\tskip missing") return 0 @@ -339,7 +339,7 @@ def analyze( if dbg: print(f"//dbg: {'- '*len(chain)}{orig_funcname}\tmissing") return 0 - if app.skip_call(chain, funcname): + if chain and app.skip_call(chain, funcname): if dbg: print(f"//dbg: {'- '*len(chain)}{orig_funcname}\tskip") return 0 -- cgit v1.2.3-2-g168b From 0041152b25953d19dd2642e379a81141183897eb Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 7 Mar 2025 17:21:30 -0700 Subject: lint-bin, stack.c.gen: FIX: Don't "include" misc --- build-aux/stack.c.gen | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index b1369d1..1f87b54 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -204,7 +204,7 @@ class Application(typing.Protocol): def analyze( *, ci_fnames: typing.Collection[str], - app_func_filters: dict[str, typing.Callable[[QName], int]], + app_func_filters: dict[str, typing.Callable[[QName], tuple[int, bool]]], app: Application, cfg_max_call_depth: int, ) -> AnalyzeResult: @@ -324,10 +324,13 @@ def analyze( return None + track_inclusion: bool = True + def nstatic( orig_funcname: QName, chain: list[QName] = [], missing_ok: bool = False ) -> int: nonlocal dbg + nonlocal track_inclusion funcname = resolve_funcname(orig_funcname) if not funcname: if chain and app.skip_call(chain, QName(str(orig_funcname))): @@ -352,7 +355,8 @@ def analyze( print(f"//dbg: {'- '*len(chain)}{funcname}\t{node.nstatic}") if node.usage_kind == "dynamic" or node.ndynamic > 0: dynamic.add(funcname) - included_funcs.add(funcname) + if track_inclusion: + included_funcs.add(funcname) return node.nstatic + max( [ 0, @@ -367,7 +371,8 @@ def analyze( for grp_name, grp_filter in app_func_filters.items(): rows: dict[QName, AnalyzeResultVal] = {} for funcname in graph: - if cnt := grp_filter(funcname): + cnt, track_inclusion = grp_filter(funcname) + if cnt: rows[funcname] = AnalyzeResultVal(nstatic=nstatic(funcname), cnt=cnt) groups[grp_name] = AnalyzeResultGroup(rows=rows) @@ -1252,19 +1257,19 @@ def main( # Tie it all together ############################################ - def thread_filter(name: QName) -> int: - return sbc_is_thread(name) + def thread_filter(name: QName) -> tuple[int, bool]: + return sbc_is_thread(name), True - def intrhandler_filter(name: QName) -> int: + def intrhandler_filter(name: QName) -> tuple[int, bool]: for plugin in plugins: if plugin.is_intrhandler(name): - return 1 - return 0 + return 1, True + return 0, False - def misc_filter(name: QName) -> int: + def misc_filter(name: QName) -> tuple[int, bool]: if str(name.base()) in ["__lm_printf", "__assert_msg_fail"]: - return 1 - return 0 + return 1, False + return 0, False def location_xform(loc: str) -> str: if not loc.startswith("/"): -- cgit v1.2.3-2-g168b From 7ae3a3769ea26b1be495f5efc3115aa2ce1b4c04 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 7 Mar 2025 23:57:02 -0700 Subject: lint-bin, stack.c.gen: FIX: Handle __*_veneer functions These have to do with ARM Thumb mode. --- build-aux/lint-bin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'build-aux') diff --git a/build-aux/lint-bin b/build-aux/lint-bin index c487f36..78ed19f 100755 --- a/build-aux/lint-bin +++ b/build-aux/lint-bin @@ -104,7 +104,7 @@ lint_stack() { done < <( comm -3 \ <(sed -En 's/^included: (.*:)?//p' "${in_elffile%.elf}_stack.c" | sort -u) \ - <(readelf_funcs "$in_elffile" | sed 's/\.part\.[0-9]*$//' | sort -u)) + <(readelf_funcs "$in_elffile" | sed -E -e 's/\.part\.[0-9]*$//' -e 's/^__(.*)_veneer$/\1/' | sort -u)) } lint_func_blocklist() { -- cgit v1.2.3-2-g168b From a8398b214dd6bb27cf9f8b091f0ed6fae87eb8e4 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 23:40:32 -0700 Subject: stack.c.gen: FIX: Think harder about printf --- build-aux/stack.c.gen | 81 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 23 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 1f87b54..0a77c1a 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -789,6 +789,63 @@ class LibMiscPlugin: return False +class PicoFmtPlugin: + known_out: dict[str, str] + known_fct: dict[str, str] + + def __init__(self) -> None: + self.known_out = { + "": "_out_null", # XXX + "__wrap_sprintf": "_out_buffer", + "__wrap_snprintf": "_out_buffer", + "__wrap_vsnprintf": "_out_buffer", + "vfctprintf": "_out_fct", + } + self.known_fct = { + "stdio_vprintf": "stdio_buffered_printer", + "__wrap_vprintf": "stdio_buffered_printer", + } + + def is_intrhandler(self, name: QName) -> bool: + return False + + def extra_nodes(self) -> typing.Collection[Node]: + return [] + + def indirect_callees( + self, loc: str, line: str + ) -> tuple[typing.Collection[QName], bool] | None: + if "/3rd-party/pico-sdk/" not in loc: + return None + if "/printf.c:" in loc: + m = re_call_other.fullmatch(line) + call: str | None = m.group("func") if m else None + if call == "out": + return [QName(x) for x in self.known_out.values()], False + if "->fct" in line: + return [QName(x) for x in self.known_fct.values()], False + return None + + def skip_call(self, chain: list[QName], call: QName) -> bool: + if str(call.base()) in self.known_out.values(): + out = "" + for pcall in chain: + if str(pcall.base()) in self.known_out: + out = self.known_out[str(pcall.base())] + if ( + out == "_out_buffer" and str(call.base()) == "_out_null" + ): # XXX: Gross hack + out = "_out_null" + return str(call.base()) != out + if str(call.base()) in self.known_fct.values(): + fct = "" + for pcall in chain: + if str(pcall.base()) in self.known_fct: + fct = self.known_fct[str(pcall.base())] + return str(call.base()) != fct + return False + + class PicoSDKPlugin: app_init_array: typing.Collection[QName] app_preinit_array: typing.Collection[QName] @@ -862,15 +919,6 @@ class PicoSDKPlugin: return [QName("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)")], False if "/flash.c:" in loc and "boot2_copyout" in line: return [QName("_stage2_boot")], False - if "/printf.c:" in loc: - if call == "out": - return [ - QName("_out_buffer"), - QName("_out_null"), - QName("_out_fct"), - ], False - if "->fct(" in line: - return [QName("stdio_buffered_printer")], False if "/stdio.c:" in loc: if call == "out_func": return [ @@ -894,20 +942,6 @@ class PicoSDKPlugin: return None def skip_call(self, chain: list[QName], call: QName) -> bool: - if str(call.base()) in ["_out_buffer", "_out_fct"]: - last = "" - for pcall in chain: - if str(pcall.base()) in [ - "__wrap_sprintf", - "__wrap_snprintf", - "__wrap_vsnprintf", - "vfctprintf", - ]: - last = str(pcall.base()) - if last == "vfctprintf": - return str(call.base()) != "_out_fct" - else: - return str(call.base()) == "_out_buffer" return False def extra_nodes(self) -> typing.Collection[Node]: @@ -1247,6 +1281,7 @@ def main( if arg_pico_platform == "rp2040": plugins += [ + PicoFmtPlugin(), PicoSDKPlugin( app_init_array=[QName("register_fini")], ), -- cgit v1.2.3-2-g168b From b8f3fe4923002f3978b0f8d1710b678da629e2c5 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 17:24:17 -0700 Subject: stack.c.gen: INFRA: Add a proper way to handle .init_array.* --- build-aux/stack.c.gen | 62 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 4 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 0a77c1a..2b7feef 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -412,6 +412,12 @@ re_call_other = re.compile(r"(?P[^(]+)\(.*") class Plugin(typing.Protocol): def is_intrhandler(self, name: QName) -> bool: ... + + # init_array returns a list of functions that are placed in the + # `.init_array.*` section; AKA functions marked with + # `__attribute__((constructor))`. + def init_array(self) -> typing.Collection[QName]: ... + def extra_nodes(self) -> typing.Collection[Node]: ... def indirect_callees( self, loc: str, line: str @@ -465,6 +471,9 @@ class CmdPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -532,6 +541,9 @@ class LibObjPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -569,6 +581,9 @@ class LibHWPlugin: "dmairq_handler", ] + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -605,6 +620,9 @@ class LibCRPlugin: def is_intrhandler(self, name: QName) -> bool: return str(name.base()) in ("_cr_gdb_intrhandler",) + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -621,6 +639,9 @@ class LibCRIPCPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -723,6 +744,9 @@ class Lib9PPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -770,6 +794,9 @@ class LibMiscPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -809,6 +836,9 @@ class PicoFmtPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -847,15 +877,18 @@ class PicoFmtPlugin: class PicoSDKPlugin: - app_init_array: typing.Collection[QName] + get_init_array: typing.Callable[[], typing.Collection[QName]] + app_init_array: typing.Collection[QName] | None app_preinit_array: typing.Collection[QName] def __init__( self, *, - app_init_array: typing.Collection[QName], + get_init_array: typing.Callable[[], typing.Collection[QName]], ) -> None: - self.app_init_array = app_init_array + # grep for '__attribute__((constructor))'. + self.get_init_array = get_init_array + self.app_init_array = None # git grep '^PICO_RUNTIME_INIT_FUNC\S*(' self.app_preinit_array = [ @@ -896,6 +929,9 @@ class PicoSDKPlugin: *[f"isr_irq{n}" for n in range(32)], ] + def init_array(self) -> typing.Collection[QName]: + return [] + def indirect_callees( self, loc: str, line: str ) -> tuple[typing.Collection[QName], bool] | None: @@ -936,6 +972,8 @@ class PicoSDKPlugin: return [QName("stdio_uart_in_chars")], False if "/newlib_interface.c:" in loc: if line == "*p)();": + if self.app_init_array is None: + self.app_init_array = self.get_init_array() return self.app_init_array, False if "/pico_runtime/runtime.c:" in loc: return self.app_preinit_array, False @@ -1140,6 +1178,9 @@ class TinyUSBDevicePlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -1173,6 +1214,9 @@ class NewlibPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [QName("register_fini")] + def extra_nodes(self) -> typing.Collection[Node]: # This is accurate to # /usr/arm-none-eabi/lib/thumb/v6-m/nofp/libg.a as of @@ -1221,6 +1265,9 @@ class LibGCCPlugin: def is_intrhandler(self, name: QName) -> bool: return False + def init_array(self) -> typing.Collection[QName]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: # This is accurate to # /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a @@ -1280,10 +1327,17 @@ def main( # pico-sdk ####################################################### if arg_pico_platform == "rp2040": + + def get_init_array() -> typing.Collection[QName]: + ret: list[QName] = [] + for plugin in plugins: + ret.extend(plugin.init_array()) + return ret + plugins += [ PicoFmtPlugin(), PicoSDKPlugin( - app_init_array=[QName("register_fini")], + get_init_array=get_init_array, ), TinyUSBDevicePlugin(arg_c_fnames), NewlibPlugin(), -- cgit v1.2.3-2-g168b From 86d4320a0a8c2987f3f5ec39079c293fb086d9da Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sun, 9 Mar 2025 01:29:48 -0700 Subject: lint-bin, stack.c.gen: INFRA: Add a way to include unused functions --- build-aux/stack.c.gen | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 2b7feef..576e446 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -418,6 +418,12 @@ class Plugin(typing.Protocol): # `__attribute__((constructor))`. def init_array(self) -> typing.Collection[QName]: ... + # extra_includes returns a list of functions that are never + # called, but are included in the binary anyway. This may because + # it is an unused method in a used vtable. This may be because it + # is an atexit() callback (we never exit). + def extra_includes(self) -> typing.Collection[str]: ... + def extra_nodes(self) -> typing.Collection[Node]: ... def indirect_callees( self, loc: str, line: str @@ -474,6 +480,9 @@ class CmdPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -544,6 +553,9 @@ class LibObjPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -584,6 +596,9 @@ class LibHWPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -623,6 +638,9 @@ class LibCRPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -642,6 +660,9 @@ class LibCRIPCPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -747,6 +768,9 @@ class Lib9PPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -797,6 +821,9 @@ class LibMiscPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -839,6 +866,9 @@ class PicoFmtPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -932,6 +962,9 @@ class PicoSDKPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def indirect_callees( self, loc: str, line: str ) -> tuple[typing.Collection[QName], bool] | None: @@ -1181,6 +1214,9 @@ class TinyUSBDevicePlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: return [] @@ -1217,6 +1253,9 @@ class NewlibPlugin: def init_array(self) -> typing.Collection[QName]: return [QName("register_fini")] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: # This is accurate to # /usr/arm-none-eabi/lib/thumb/v6-m/nofp/libg.a as of @@ -1268,6 +1307,9 @@ class LibGCCPlugin: def init_array(self) -> typing.Collection[QName]: return [] + def extra_includes(self) -> typing.Collection[str]: + return [] + def extra_nodes(self) -> typing.Collection[Node]: # This is accurate to # /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a @@ -1360,6 +1402,16 @@ def main( return 1, False return 0, False + extra_includes: list[str] = [] + for plugin in plugins: + extra_includes.extend(plugin.extra_includes()) + + def extra_filter(name: QName) -> tuple[int, bool]: + nonlocal extra_includes + if str(name.base()) in extra_includes: + return 1, True + return 0, False + def location_xform(loc: str) -> str: if not loc.startswith("/"): return loc @@ -1373,6 +1425,7 @@ def main( "Threads": thread_filter, "Interrupt handlers": intrhandler_filter, "Misc": misc_filter, + "Extra": extra_filter, }, app=PluginApplication(location_xform, plugins), cfg_max_call_depth=100, @@ -1444,6 +1497,8 @@ def main( print("*/") print("") print("/*") + if result.groups["Extra"].rows: + print_group("Extra") for funcname in sorted(result.included_funcs): print(f"included: {funcname}") print("*/") -- cgit v1.2.3-2-g168b From 638317a18ce4562440fc3fba95c1cb9f33b5affa Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 17:24:17 -0700 Subject: stack.c.gen: APP: Factor in stack guard size --- build-aux/stack.c.gen | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 576e446..11c5854 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -1470,10 +1470,11 @@ def main( print_group("Interrupt handlers") print("*/") overhead = max(v.nstatic for v in result.groups["Interrupt handlers"].rows.values()) + stack_guard_size = 16 * 2 rows: list[tuple[str, int, int]] = [] for funcname, val in result.groups["Threads"].rows.items(): base = val.nstatic - size = next_power_of_2(base + overhead) + size = next_power_of_2(base + overhead + stack_guard_size) - stack_guard_size rows.append((str(funcname.base()), base, size)) namelen = max(len(r[0]) for r in rows) baselen = max(len(str(r[1])) for r in rows) @@ -1484,7 +1485,9 @@ def main( print("const size_t CONFIG_COROUTINE_STACK_SIZE_", end="") print(f"{row[0].ljust(namelen)} =", end="") print(f" {str(row[2]).rjust(sizelen)};", end="") - print(f" /* LM_NEXT_POWER_OF_2({str(row[1]).rjust(baselen)}+{overhead}) */") + print( + f" /* LM_NEXT_POWER_OF_2({str(row[1]).rjust(baselen)}+{overhead}+{stack_guard_size})-{stack_guard_size} */" + ) print() print("/*") print_group("Misc") -- cgit v1.2.3-2-g168b From ddb67e1d0c133cbb59ee3708edf3a2c1feefeec6 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 7 Mar 2025 23:44:25 -0700 Subject: stack.c.gen: APP: Include a thread total and a kernel size --- build-aux/stack.c.gen | 66 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 14 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 11c5854..d9d128a 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -1469,24 +1469,62 @@ def main( print_group("Threads") print_group("Interrupt handlers") print("*/") - overhead = max(v.nstatic for v in result.groups["Interrupt handlers"].rows.values()) + intrstack = max( + v.nstatic for v in result.groups["Interrupt handlers"].rows.values() + ) stack_guard_size = 16 * 2 - rows: list[tuple[str, int, int]] = [] + + class CrRow(typing.NamedTuple): + name: str + cnt: int + base: int + size: int + + rows: list[CrRow] = [] + main: CrRow | None = None for funcname, val in result.groups["Threads"].rows.items(): + name = str(funcname.base()) base = val.nstatic - size = next_power_of_2(base + overhead + stack_guard_size) - stack_guard_size - rows.append((str(funcname.base()), base, size)) - namelen = max(len(r[0]) for r in rows) - baselen = max(len(str(r[1])) for r in rows) - sizelen = max(len(str(r[2])) for r in rows) + size = base + intrstack + if name in ("main", "_reset_handler"): + main = 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, main.size if main 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.ljust(namelen)}", end="") + print(f" = {str(size).rjust(sizelen)};", end="") + if comment: + print(" */", end="") + elif eqn: + print(" ", end="") + if eqn: + print(f" /* {eqn} */", end="") + print() + for row in sorted(rows): - if row[0] in ("main", "_reset_handler"): - continue - print("const size_t CONFIG_COROUTINE_STACK_SIZE_", end="") - print(f"{row[0].ljust(namelen)} =", end="") - print(f" {str(row[2]).rjust(sizelen)};", end="") - print( - f" /* LM_NEXT_POWER_OF_2({str(row[1]).rjust(baselen)}+{overhead}+{stack_guard_size})-{stack_guard_size} */" + print_row( + False, + row.name, + row.size, + f"LM_NEXT_POWER_OF_2({str(row.base).rjust(baselen)}+{intrstack}+{stack_guard_size})-{stack_guard_size}", + ) + print_row(True, "TOTAL (inc. stack guard)", sizesum) + if main: + print_row( + True, + "MAIN/KERNEL", + main.size, + f" {str(main.base).rjust(baselen)}+{intrstack}", ) print() print("/*") -- cgit v1.2.3-2-g168b From e232ee375afdf8da50757b3c423dc69fef07de5d Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sat, 8 Mar 2025 01:07:43 -0700 Subject: stack.c.gen: APP: The entry-point is _entry_point, not _reset_handler --- build-aux/stack.c.gen | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index d9d128a..5bdb23c 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -1034,6 +1034,7 @@ class PicoSDKPlugin: synthetic_node("isr_pendsv", 0, {"__unhandled_user_irq"}), synthetic_node("isr_systick", 0, {"__unhandled_user_irq"}), synthetic_node(f"__unhandled_user_irq", 0), + synthetic_node("_entry_point", 0, {"_reset_handler"}), synthetic_node("_reset_handler", 0, {"runtime_init", "main", "exit"}), ] @@ -1351,7 +1352,7 @@ def main( return lib9p_plugin.thread_count(name) return 1 if str(name.base()) == ( - "_reset_handler" if arg_pico_platform == "rp2040" else "main" + "_entry_point" if arg_pico_platform == "rp2040" else "main" ): return 1 return 0 @@ -1486,7 +1487,7 @@ def main( name = str(funcname.base()) base = val.nstatic size = base + intrstack - if name in ("main", "_reset_handler"): + if name in ("main", "_entry_point"): main = CrRow(name=name, cnt=1, base=base, size=size) else: size = next_power_of_2(size + stack_guard_size) - stack_guard_size -- cgit v1.2.3-2-g168b From 85a1ab9747d84e8caf4f229fdaed31c5e8d49f7a Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 6 Mar 2025 17:24:33 -0700 Subject: stack.c.gen: ADD-MISSING: Resolve several missing functions --- build-aux/stack.c.gen | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'build-aux') diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 5bdb23c..a8a971e 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -580,9 +580,11 @@ class LibObjPlugin: class LibHWPlugin: pico_platform: str + libobj: LibObjPlugin - def __init__(self, arg_pico_platform: str) -> None: + def __init__(self, arg_pico_platform: str, libobj: LibObjPlugin) -> None: self.pico_platform = arg_pico_platform + self.libobj = libobj def is_intrhandler(self, name: QName) -> bool: return str(name.base()) in [ @@ -607,6 +609,20 @@ class LibHWPlugin: ) -> tuple[typing.Collection[QName], bool] | None: if "/3rd-party/" in loc: return None + for fn in ( + "io_readv", + "io_writev", + "io_close", + "io_close_read", + "io_close_write", + "io_readwritev", + ): + if f"{fn}(" in line: + return self.libobj.indirect_callees(loc, f"LO_CALL(x, {fn[3:]})") + if "io_read(" in line: + return self.libobj.indirect_callees(loc, "LO_CALL(x, readv)") + if "io_writev(" in line: + return self.libobj.indirect_callees(loc, "LO_CALL(x, writev)") if "trigger->cb(trigger->cb_arg)" in line: ret = [ QName("alarmclock_sleep_intrhandler"), @@ -621,7 +637,7 @@ class LibHWPlugin: return [ QName("w5500_intrhandler"), ], False - if "/rp2040_dmairq.c:" in loc and "handler->fn" in line: + if "/rp2040_dma.c:" in loc and "handler->fn" in line: return [ QName("rp2040_hwspi_intrhandler"), ], False @@ -1033,7 +1049,7 @@ class PicoSDKPlugin: synthetic_node("isr_svcall", 0, {"__unhandled_user_irq"}), synthetic_node("isr_pendsv", 0, {"__unhandled_user_irq"}), synthetic_node("isr_systick", 0, {"__unhandled_user_irq"}), - synthetic_node(f"__unhandled_user_irq", 0), + synthetic_node("__unhandled_user_irq", 0), synthetic_node("_entry_point", 0, {"_reset_handler"}), synthetic_node("_reset_handler", 0, {"runtime_init", "main", "exit"}), ] @@ -1255,7 +1271,10 @@ class NewlibPlugin: return [QName("register_fini")] def extra_includes(self) -> typing.Collection[str]: - return [] + return [ + # register_fini() calls atexit(__libc_fini_array) + "__libc_fini_array", + ] def extra_nodes(self) -> typing.Collection[Node]: # This is accurate to @@ -1290,6 +1309,16 @@ class NewlibPlugin: synthetic_node("_getpid_r", 8, {"_getpid"}), synthetic_node("random", 8), synthetic_node("register_fini", 8, {"atexit"}), + synthetic_node("atexit", 8, {"__register_exitproc"}), + synthetic_node( + "__register_exitproc", + 32, + { + "__retarget_lock_acquire_recursive", + "__retarget_lock_release_recursive", + }, + ), + synthetic_node("__libc_fini_array", 16, {"_fini"}), ] def indirect_callees( @@ -1312,13 +1341,14 @@ class LibGCCPlugin: return [] def extra_nodes(self) -> typing.Collection[Node]: - # This is accurate to - # /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a - # as of Parabola's arm-none-eabi-gcc 14.2.0-1. + # This is accurate to Parabola's arm-none-eabi-gcc 14.2.0-1. return [ + # /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a synthetic_node("__aeabi_idiv0", 0), synthetic_node("__aeabi_ldiv0", 0), synthetic_node("__aeabi_llsr", 0), + # /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/crti.o + synthetic_node("_fini", 24), ] def indirect_callees( @@ -1357,10 +1387,12 @@ def main( return 1 return 0 + libobj_plugin = LibObjPlugin(arg_c_fnames) + plugins += [ CmdPlugin(), - LibObjPlugin(arg_c_fnames), - LibHWPlugin(arg_pico_platform), + libobj_plugin, + LibHWPlugin(arg_pico_platform, libobj_plugin), LibCRPlugin(), LibCRIPCPlugin(), lib9p_plugin, -- cgit v1.2.3-2-g168b