summaryrefslogtreecommitdiff
path: root/build-aux/stack.c.gen
diff options
context:
space:
mode:
Diffstat (limited to 'build-aux/stack.c.gen')
-rwxr-xr-xbuild-aux/stack.c.gen460
1 files changed, 267 insertions, 193 deletions
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 = "<synthetic>"
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<func>[^(]+)\(.*")
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<obj>[^,]+), (?P<meth>[^,)]+)[,)].*")
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<handler>\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<grp>[TR])msg_(?P<meth>validate|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<meth>\S+)\s*=\s*(?P<impl>[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("")