summaryrefslogtreecommitdiff
path: root/build-aux
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-13 18:49:15 -0500
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-13 18:49:15 -0500
commitc578a300c7d0d46662fcd0bdce69af95a821bc18 (patch)
treea99de333f2812d7c018820f39d78b8c4e744f705 /build-aux
parent6a719c63ecb83a850c317ea94b8932aa0c857770 (diff)
parent57cc0523f600575feda09bd9fae13f685ce85b0f (diff)
Merge commit '57cc0523f600575feda09bd9fae13f685ce85b0f'
Diffstat (limited to 'build-aux')
-rwxr-xr-xbuild-aux/stack.c.gen660
1 files changed, 381 insertions, 279 deletions
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen
index 22b18d5..92bd8c2 100755
--- a/build-aux/stack.c.gen
+++ b/build-aux/stack.c.gen
@@ -95,6 +95,8 @@ def parse_vcg(reader: typing.TextIO) -> typing.Iterator[VCGElem]:
################################################################################
# Main analysis
+UsageKind: typing.TypeAlias = typing.Literal["static", "dynamic", "dynamic,bounded"]
+
class Node:
# from .title (`static` and `__weak` functions are prefixed with
@@ -103,6 +105,7 @@ class Node:
funcname: str
# .label is "{funcname}\n{location}\n{nstatic} bytes (static}\n{ndynamic} dynamic objects"
location: str
+ usage_kind: UsageKind
nstatic: int
ndynamic: int
@@ -116,6 +119,7 @@ def synthetic_node(name: str, nstatic: int, calls: set[str] = set()) -> Node:
n.funcname = name
n.location = "<synthetic>"
+ n.usage_kind = "static"
n.nstatic = nstatic
n.ndynamic = 0
@@ -137,8 +141,9 @@ def analyze(
re_node_label = re.compile(
r"(?P<funcname>[^\n]+)\n"
+ r"(?P<location>[^\n]+:[0-9]+:[0-9]+)\n"
- + r"(?P<nstatic>[0-9]+) bytes \(static\)\n"
- + r"(?P<ndynamic>[0-9]+) dynamic objects",
+ + r"(?P<nstatic>[0-9]+) bytes \((?P<usage_kind>static|dynamic|dynamic,bounded)\)\n"
+ + r"(?P<ndynamic>[0-9]+) dynamic objects"
+ + r"(?:\n.*)?",
flags=re.MULTILINE,
)
@@ -163,6 +168,9 @@ def analyze(
f"unexpected label value {repr(v)}"
)
node.location = m.group("location")
+ node.usage_kind = typing.cast(
+ UsageKind, m.group("usage_kind")
+ )
node.nstatic = int(m.group("nstatic"))
node.ndynamic = int(m.group("ndynamic"))
case "shape":
@@ -216,8 +224,7 @@ def analyze(
graph[node.funcname] = node
missing: set[str] = set()
-
- print("/*")
+ dynamic: set[str] = set()
dbg = False
@@ -255,41 +262,46 @@ def analyze(
node = graph[funcname]
if dbg:
print(f"//dbg: {funcname}\t{node.nstatic}")
+ if node.usage_kind == "dynamic" or node.ndynamic > 0:
+ dynamic.add(app_location_xform(funcname))
return node.nstatic + max(
[0, *[nstatic(call, chain + [funcname]) for call in node.calls]]
)
- for grp_name, grp_filter in app_func_filters.items():
- namelen = max(
- [len(app_location_xform(name)) for name in graph if grp_filter(name)]
- + [len(grp_name) + 4]
- )
- numlen = max(len(str(nstatic(name))) for name in graph if name.endswith("_cr"))
- sep1 = ("=" * namelen) + " " + "=" * numlen
- sep2 = ("-" * namelen) + " " + "-" * numlen
-
- print("= " + grp_name + " " + sep1[len(grp_name) + 3 :])
+ print("/*")
+ for grp_name, grp_filter in app_func_filters.items():
+ # Gather the data.
nmax = 0
nsum = 0
+ rows: dict[str, int] = {}
for funcname in graph:
if grp_filter(funcname):
n = nstatic(funcname)
- print(
- f"{app_location_xform(funcname).ljust(namelen)} {str(n).rjust(numlen)}"
- )
+ rows[app_location_xform(funcname)] = n
if n > nmax:
nmax = n
nsum += n
+ # Figure sizes.
+ namelen = max([len(k) for k in 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, num in rows.items():
+ print(f"{name.ljust(namelen)} {str(num).rjust(numlen)}")
print(sep2)
print(f"{'Total'.ljust(namelen)} {str(nsum).rjust(numlen)}")
print(f"{'Maximum'.ljust(namelen)} {str(nmax).rjust(numlen)}")
-
print(sep1)
for funcname in sorted(missing):
print(f"warning: missing: {funcname}")
+ for funcname in sorted(dynamic):
+ print(f"warning: dynamic: {funcname}")
print("*/")
@@ -312,11 +324,20 @@ def read_source(location: str) -> str:
def main(
- *, arg_base_dir: str, arg_ci_fnames: list[str], arg_c_fnames: list[str]
+ *,
+ arg_pico_platform: str,
+ arg_base_dir: str,
+ arg_ci_fnames: list[str],
+ arg_c_fnames: list[str],
) -> None:
re_call_other = re.compile(r"(?P<func>[^(]+)\(.*")
+ all_nodes: list[Node] = []
+ hooks_is_intrhandler: list[typing.Callable[[str], bool]] = []
+ hooks_indirect_callees: list[typing.Callable[[str, str], list[str] | None]] = []
+ hooks_skip_call: list[typing.Callable[[list[str], str], bool]] = []
+
# The sbc-harness codebase #######################################
vcalls: dict[str, set[str]] = {}
@@ -341,6 +362,39 @@ def main(
elif re_vtable_start.search(line):
in_vtable = True
+ tmessage_handlers: set[str] | None = None
+ if any(fname.endswith("lib9p/srv.c") for fname in c_fnames):
+ srv_c = next(fname for fname in c_fnames if fname.endswith("lib9p/srv.c"))
+ re_tmessage_handler = re.compile(
+ r"^\s*\[LIB9P_TYP_T[^]]+\]\s*=\s*\(tmessage_handler\)\s*(?P<handler>\S+),\s*$"
+ )
+ tmessage_handlers = set()
+ with open(srv_c, "r") as fh:
+ for line in fh:
+ line = line.rstrip()
+ if m := re_tmessage_handler.fullmatch(line):
+ tmessage_handlers.add(m.group("handler"))
+
+ lib9p_versions: dict[str, set[str]] | None = None
+ if any(fname.endswith("lib9p/9p.c") for fname in c_fnames):
+ generated_c = next(
+ fname for fname in c_fnames if fname.endswith("lib9p/9p.generated.c")
+ )
+ re_lib9p_msg_entry = re.compile(r"^\s*_MSG\((?P<typ>\S+)\),$")
+ lib9p_versions = {
+ "validate": set(),
+ "marshal": set(),
+ "unmarshal": set(),
+ }
+ with open(generated_c, "r") as fh:
+ for line in fh:
+ line = line.rstrip()
+ if m := re_lib9p_msg_entry.fullmatch(line):
+ typ = m.group("typ")
+ lib9p_versions["validate"].add(f"validate_{typ}")
+ lib9p_versions["unmarshal"].add(f"unmarshal_{typ}")
+ lib9p_versions["marshal"].add(f"marshal_{typ}")
+
re_call_vcall = re.compile(r"VCALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*")
def sbc_indirect_callees(loc: str, line: str) -> list[str] | None:
@@ -361,10 +415,22 @@ def main(
"_cr_chan_dequeue",
"_cr_select_dequeue",
]
+ if tmessage_handlers and "/srv.c:" in loc and "tmessage_handlers[typ](" in line:
+ return sorted(tmessage_handlers)
+ if lib9p_versions and "/9p.c:" in loc:
+ for meth in lib9p_versions.keys():
+ if line.startswith(f"table.{meth}("):
+ return sorted(lib9p_versions[meth])
return None
+ hooks_indirect_callees += [sbc_indirect_callees]
+
def sbc_is_thread(name: str) -> bool:
- return name.endswith("_cr") or name == "main"
+ if name.endswith("_cr") and name != "lib9p_srv_read_cr":
+ return True
+ if name == "main":
+ return True
+ return False
def sbc_is_intrhandler(name: str) -> bool:
return name in [
@@ -374,10 +440,19 @@ def main(
"hostnet_handle_sig_io",
]
+ hooks_is_intrhandler += [sbc_is_intrhandler]
+
sbc_gpio_handlers = [
"w5500_intrhandler",
]
+ # 1=just root directory
+ # 2=just files in root directory
+ # 3=just 1 level of subdirectories
+ # 4=just 2 levels of subdirectories
+ # ...
+ sbc_9p_max_depth = 3
+
def sbc_skip_call(chain: list[str], call: str) -> bool:
if (
len(chain) > 1
@@ -386,268 +461,299 @@ def main(
and any(c.endswith(":__assert_msg_fail") for c in chain[:-1])
):
return True
- if call == "_cr_select_dequeue":
+ if (
+ len(chain) >= sbc_9p_max_depth
+ and call.endswith("/srv.c:util_release")
+ and all(
+ c.endswith("/srv.c:util_release") for c in chain[-sbc_9p_max_depth:]
+ )
+ ):
return True
return False
+ hooks_skip_call += [sbc_skip_call]
+
# pico-sdk #######################################################
- def pico_is_intrhandler(name: str) -> bool:
- return name in [
- "gpio_default_irq_handler",
- ]
+ if arg_pico_platform == "rp2040":
+
+ def pico_is_intrhandler(name: str) -> bool:
+ return name in [
+ "gpio_default_irq_handler",
+ ]
- def pico_indirect_callees(loc: str, line: str) -> list[str] | None:
- if "/3rd-party/pico-sdk/" not in loc or "/3rd-party/pico-sdk/lib/" in loc:
+ hooks_is_intrhandler += [pico_is_intrhandler]
+
+ def pico_indirect_callees(loc: str, line: str) -> list[str] | 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)
+ call: str | None = m.group("func") if m else None
+
+ match call:
+ case "connect_internal_flash_func":
+ return ["rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)"]
+ case "flash_exit_xip_func":
+ return ["rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)"]
+ case "flash_range_erase_func":
+ return ["rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE)"]
+ case "flash_flush_cache_func":
+ return ["rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)"]
+ case "rom_table_lookup":
+ return ["rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)"]
+ if "/flash.c:" in loc and "boot2_copyout" in line:
+ return ["_stage2_boot"]
+ if "/gpio.c:" in loc and call == "callback":
+ return sbc_gpio_handlers
+ if "/printf.c:" in loc:
+ if call == "out":
+ return [
+ "_out_buffer",
+ "_out_null",
+ "_out_fct",
+ ]
+ if "->fct(" in line:
+ return ["stdio_buffered_printer"]
+ if "/stdio.c:" in loc:
+ if call == "out_func":
+ return [
+ "stdio_out_chars_crlf",
+ "stdio_out_chars_no_crlf",
+ ]
+ if call and (call.startswith("d->") or call.startswith("driver->")):
+ _, meth = call.split("->", 1)
+ match meth:
+ case "out_chars":
+ return ["stdio_uart_out_chars"]
+ case "out_flush":
+ return ["stdio_uart_out_flush"]
+ case "in_chars":
+ return ["stdio_uart_in_chars"]
return None
- m = re_call_other.fullmatch(line)
- call: str | None = m.group("func") if m else None
-
- match call:
- case "connect_internal_flash_func":
- return ["rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)"]
- case "flash_exit_xip_func":
- return ["rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)"]
- case "flash_range_erase_func":
- return ["rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE)"]
- case "flash_flush_cache_func":
- return ["rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)"]
- case "rom_table_lookup":
- return ["rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)"]
- if "/flash.c:" in loc and "boot2_copyout" in line:
- return ["_stage2_boot"]
- if "/gpio.c:" in loc and call == "callback":
- return sbc_gpio_handlers
- if "/printf.c:" in loc:
- if call == "out":
- return [
- "_out_buffer",
- "_out_null",
- "_out_fct",
- ]
- if "->fct(" in line:
- return ["stdio_buffered_printer"]
- if "/stdio.c:" in loc:
- if call == "out_func":
- return [
- "stdio_out_chars_crlf",
- "stdio_out_chars_no_crlf",
- ]
- if call and (call.startswith("d->") or call.startswith("driver->")):
- _, meth = call.split("->", 1)
- match meth:
- case "out_chars":
- return ["stdio_uart_out_chars"]
- case "out_flush":
- return ["stdio_uart_out_flush"]
- case "in_chars":
- return ["stdio_uart_in_chars"]
- return None
- def pico_skip_call(chain: list[str], call: str) -> bool:
- if call == "_out_buffer" or call == "_out_fct":
- last = ""
- for pcall in chain:
- if pcall in [
- "__wrap_sprintf",
- "__wrap_snprintf",
- "__wrap_vsnprintf",
- "vfctprintf",
- ]:
- last = pcall
- if last == "vfctprintf":
- return call != "_out_fct"
- else:
- return call == "_out_buffer"
- return False
+ hooks_indirect_callees += [pico_indirect_callees]
+
+ def pico_skip_call(chain: list[str], call: str) -> bool:
+ if call == "_out_buffer" or call == "_out_fct":
+ last = ""
+ for pcall in chain:
+ if pcall in [
+ "__wrap_sprintf",
+ "__wrap_snprintf",
+ "__wrap_vsnprintf",
+ "vfctprintf",
+ ]:
+ last = pcall
+ if last == "vfctprintf":
+ return call != "_out_fct"
+ else:
+ return call == "_out_buffer"
+ return False
+
+ hooks_skip_call += [pico_skip_call]
- # src/rp2_common/hardware_divider/include/hardware/divider_helper.S
- save_div_state_and_lr = 5 * 4
- # src/rp2_common/pico_divider/divider_hardware.S
- save_div_state_and_lr_64 = 5 * 4
- pico_nodes: list[Node] = [
- # src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
- synthetic_node("__aeabi_lmul", 4),
+ # src/rp2_common/hardware_divider/include/hardware/divider_helper.S
+ save_div_state_and_lr = 5 * 4
# src/rp2_common/pico_divider/divider_hardware.S
- # s32 aliases
- synthetic_node("div_s32s32", 0, {"divmod_s32s32"}),
- synthetic_node("__aeabi_idiv", 0, {"divmod_s32s32"}),
- synthetic_node("__aeabi_idivmod", 0, {"divmod_s32s32"}),
- # s32 impl
- synthetic_node("divmod_s32s32", 0, {"divmod_s32s32_savestate"}),
- synthetic_node(
- "divmod_s32s32_savestate", save_div_state_and_lr, {"divmod_s32s32_unsafe"}
- ),
- synthetic_node("divmod_s32s32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
- # u32 aliases
- synthetic_node("div_u32u32", 0, {"divmod_u32u32"}),
- synthetic_node("__aeabi_uidiv", 0, {"divmod_u32u32"}),
- synthetic_node("__aeabi_uidivmod", 0, {"divmod_u32u32"}),
- # u32 impl
- synthetic_node("divmod_u32u32", 0, {"divmod_u32u32_savestate"}),
- synthetic_node(
- "divmod_u32u32_savestate", save_div_state_and_lr, {"divmod_u32u32_unsafe"}
- ),
- synthetic_node("divmod_u32u32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
- # s64 aliases
- synthetic_node("div_s64s64", 0, {"divmod_s64s64"}),
- synthetic_node("__aeabi_ldiv", 0, {"divmod_s64s64"}),
- synthetic_node("__aeabi_ldivmod", 0, {"divmod_s64s64"}),
- # s64 impl
- synthetic_node("divmod_s64s64", 0, {"divmod_s64s64_savestate"}),
- synthetic_node(
- "divmod_s64s64_savestate",
- save_div_state_and_lr_64 + (2 * 4),
- {"divmod_s64s64_unsafe"},
- ),
- synthetic_node(
- "divmod_s64s64_unsafe", 4, {"divmod_u64u64_unsafe", "__aeabi_ldiv0"}
- ),
- # u64 aliases
- synthetic_node("div_u64u64", 0, {"divmod_u64u64"}),
- synthetic_node("__aeabi_uldiv", 0, {"divmod_u64u64"}),
- synthetic_node("__aeabi_uldivmod", 0, {"divmod_u64u64"}),
- # u64 impl
- synthetic_node("divmod_u64u64", 0, {"divmod_u64u64_savestate"}),
- synthetic_node(
- "divmod_u64u64_savestate",
- save_div_state_and_lr_64 + (2 * 4),
- {"divmod_u64u64_unsafe"},
- ),
- synthetic_node(
- "divmod_u64u64_unsafe", (1 + 1 + 2 + 5 + 5 + 2) * 4, {"__aeabi_ldiv0"}
- ),
- # *_rem
- synthetic_node("divod_s64s64_rem", 2 * 4, {"divmod_s64s64"}),
- synthetic_node("divod_u64u64_rem", 2 * 4, {"divmod_u64u64"}),
- # src/rp2040/boot_stage2/boot2_${name,,}.S for name=W25Q080,
- # controlled by `#define PICO_BOOT_STAGE2_{name} 1` in
- # src/boards/include/boards/pico.h
- synthetic_node("_stage2_boot", 0), # TODO
- # https://github.com/raspberrypi/pico-bootrom-rp2040
- synthetic_node("rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)", 0), # TODO
- synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)", 0), # TODO
- synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)", 0), # TODO
- synthetic_node("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)", 0), # TODO
- ]
+ save_div_state_and_lr_64 = 5 * 4
+ all_nodes += [
+ # src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
+ synthetic_node("__aeabi_lmul", 4),
+ # src/rp2_common/pico_divider/divider_hardware.S
+ # s32 aliases
+ synthetic_node("div_s32s32", 0, {"divmod_s32s32"}),
+ synthetic_node("__aeabi_idiv", 0, {"divmod_s32s32"}),
+ synthetic_node("__aeabi_idivmod", 0, {"divmod_s32s32"}),
+ # s32 impl
+ synthetic_node("divmod_s32s32", 0, {"divmod_s32s32_savestate"}),
+ synthetic_node(
+ "divmod_s32s32_savestate",
+ save_div_state_and_lr,
+ {"divmod_s32s32_unsafe"},
+ ),
+ synthetic_node("divmod_s32s32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
+ # u32 aliases
+ synthetic_node("div_u32u32", 0, {"divmod_u32u32"}),
+ synthetic_node("__aeabi_uidiv", 0, {"divmod_u32u32"}),
+ synthetic_node("__aeabi_uidivmod", 0, {"divmod_u32u32"}),
+ # u32 impl
+ synthetic_node("divmod_u32u32", 0, {"divmod_u32u32_savestate"}),
+ synthetic_node(
+ "divmod_u32u32_savestate",
+ save_div_state_and_lr,
+ {"divmod_u32u32_unsafe"},
+ ),
+ synthetic_node("divmod_u32u32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
+ # s64 aliases
+ synthetic_node("div_s64s64", 0, {"divmod_s64s64"}),
+ synthetic_node("__aeabi_ldiv", 0, {"divmod_s64s64"}),
+ synthetic_node("__aeabi_ldivmod", 0, {"divmod_s64s64"}),
+ # s64 impl
+ synthetic_node("divmod_s64s64", 0, {"divmod_s64s64_savestate"}),
+ synthetic_node(
+ "divmod_s64s64_savestate",
+ save_div_state_and_lr_64 + (2 * 4),
+ {"divmod_s64s64_unsafe"},
+ ),
+ synthetic_node(
+ "divmod_s64s64_unsafe", 4, {"divmod_u64u64_unsafe", "__aeabi_ldiv0"}
+ ),
+ # u64 aliases
+ synthetic_node("div_u64u64", 0, {"divmod_u64u64"}),
+ synthetic_node("__aeabi_uldiv", 0, {"divmod_u64u64"}),
+ synthetic_node("__aeabi_uldivmod", 0, {"divmod_u64u64"}),
+ # u64 impl
+ synthetic_node("divmod_u64u64", 0, {"divmod_u64u64_savestate"}),
+ synthetic_node(
+ "divmod_u64u64_savestate",
+ save_div_state_and_lr_64 + (2 * 4),
+ {"divmod_u64u64_unsafe"},
+ ),
+ synthetic_node(
+ "divmod_u64u64_unsafe", (1 + 1 + 2 + 5 + 5 + 2) * 4, {"__aeabi_ldiv0"}
+ ),
+ # *_rem
+ synthetic_node("divod_s64s64_rem", 2 * 4, {"divmod_s64s64"}),
+ synthetic_node("divod_u64u64_rem", 2 * 4, {"divmod_u64u64"}),
+ # src/rp2040/boot_stage2/boot2_${name,,}.S for name=W25Q080,
+ # controlled by `#define PICO_BOOT_STAGE2_{name} 1` in
+ # src/boards/include/boards/pico.h
+ synthetic_node("_stage2_boot", 0), # TODO
+ # https://github.com/raspberrypi/pico-bootrom-rp2040
+ synthetic_node(
+ "rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)", 0
+ ), # TODO
+ synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)", 0), # TODO
+ synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)", 0), # TODO
+ synthetic_node("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)", 0), # TODO
+ ]
- # TinyUSB ########################################################
+ # TinyUSB device #################################################
- tusb_config_fname = (
- arg_base_dir + "/cmd/sbc_harness/config/tusb_config.h"
- ) # TODO: FIXME
- re_tud_class = re.compile(
- r"^\s*#\s*define\s+(?P<k>CFG_TUD_(?:\S{3}|AUDIO|VIDEO|MIDI|VENDOR|USBTMC|DFU_RUNTIME|ECM_RNDIS))\s+(?P<v>\S+).*"
- )
- tusb_config: dict[str, bool] = {}
- with open(tusb_config_fname, "r") as fh:
- in_table = False
- for line in fh:
- line = line.rstrip()
- if m := re_tud_class.fullmatch(line):
- k = m.group("k")
- v = m.group("v")
- tusb_config[k] = bool(int(v))
-
- usbd_fname = next(
- fname for fname in c_fnames if fname.endswith("/tinyusb/src/device/usbd.c")
- )
- tud_drivers: dict[str, set[str]] = {}
- re_tud_entry = re.compile(
- r"^\s+\.(?P<meth>\S+)\s*=\s*(?P<impl>[a-zA-Z0-9_]+)(?:,.*)?"
- )
- re_tud_if1 = re.compile(r"^\s*#\s*if (\S+)\s*")
- re_tud_if2 = re.compile(r"^\s*#\s*if (\S+)\s*\|\|\s*(\S+)\s*")
- re_tud_endif = re.compile(r"^\s*#\s*endif\s*")
- with open(usbd_fname, "r") as fh:
- in_table = False
- enabled = True
- for line in fh:
- line = line.rstrip()
- if in_table:
- if m := re_tud_if1.fullmatch(line):
- enabled = tusb_config[m.group(1)]
- elif m := re_tud_if2.fullmatch(line):
- enabled = tusb_config[m.group(1)] or tusb_config[m.group(2)]
- elif re_tud_endif.fullmatch(line):
- enabled = True
- if m := re_tud_entry.fullmatch(line):
- meth = m.group("meth")
- impl = m.group("impl")
- if meth == "name" or not enabled:
- continue
- if meth not in tud_drivers:
- tud_drivers[meth] = set()
- if impl != "NULL":
- tud_drivers[meth].add(impl)
- if line.startswith("}"):
- in_table = False
- elif " _usbd_driver[] = {" in line:
- in_table = True
-
- def tud_indirect_callees(loc: str, line: str) -> list[str] | 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)
- assert m
- call = m.group("func")
- if call == "_ctrl_xfer.complete_cb":
- return [
- # "process_test_mode_cb",
- "tud_vendor_control_xfer_cb",
- *sorted(tud_drivers["control_xfer_cb"]),
- ]
- elif call.startswith("driver->"):
- return sorted(tud_drivers[call[len("driver->") :]])
- elif call == "event.func_call.func":
- # callback from usb_defer_func()
- return []
+ if any(fname.endswith("/tinyusb/src/device/usbd.c") for fname in c_fnames):
+ tusb_config_fname = (
+ arg_base_dir + "/cmd/sbc_harness/config/tusb_config.h"
+ ) # TODO: FIXME
+ re_tud_class = re.compile(
+ r"^\s*#\s*define\s+(?P<k>CFG_TUD_(?:\S{3}|AUDIO|VIDEO|MIDI|VENDOR|USBTMC|DFU_RUNTIME|ECM_RNDIS))\s+(?P<v>\S+).*"
+ )
+ tusb_config: dict[str, bool] = {}
+ with open(tusb_config_fname, "r") as fh:
+ in_table = False
+ for line in fh:
+ line = line.rstrip()
+ if m := re_tud_class.fullmatch(line):
+ k = m.group("k")
+ v = m.group("v")
+ tusb_config[k] = bool(int(v))
- return None
+ usbd_fname = next(
+ fname for fname in c_fnames if fname.endswith("/tinyusb/src/device/usbd.c")
+ )
+ tud_drivers: dict[str, set[str]] = {}
+ re_tud_entry = re.compile(
+ r"^\s+\.(?P<meth>\S+)\s*=\s*(?P<impl>[a-zA-Z0-9_]+)(?:,.*)?"
+ )
+ re_tud_if1 = re.compile(r"^\s*#\s*if (\S+)\s*")
+ re_tud_if2 = re.compile(r"^\s*#\s*if (\S+)\s*\|\|\s*(\S+)\s*")
+ re_tud_endif = re.compile(r"^\s*#\s*endif\s*")
+ with open(usbd_fname, "r") as fh:
+ in_table = False
+ enabled = True
+ for line in fh:
+ line = line.rstrip()
+ if in_table:
+ if m := re_tud_if1.fullmatch(line):
+ enabled = tusb_config[m.group(1)]
+ elif m := re_tud_if2.fullmatch(line):
+ enabled = tusb_config[m.group(1)] or tusb_config[m.group(2)]
+ elif re_tud_endif.fullmatch(line):
+ enabled = True
+ if m := re_tud_entry.fullmatch(line):
+ meth = m.group("meth")
+ impl = m.group("impl")
+ if meth == "name" or not enabled:
+ continue
+ if meth not in tud_drivers:
+ tud_drivers[meth] = set()
+ if impl != "NULL":
+ tud_drivers[meth].add(impl)
+ if line.startswith("}"):
+ in_table = False
+ elif " _usbd_driver[] = {" in line:
+ in_table = True
+
+ def tud_indirect_callees(loc: str, line: str) -> list[str] | 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)
+ assert m
+ call = m.group("func")
+ if call == "_ctrl_xfer.complete_cb":
+ return [
+ # "process_test_mode_cb",
+ "tud_vendor_control_xfer_cb",
+ *sorted(tud_drivers["control_xfer_cb"]),
+ ]
+ elif call.startswith("driver->"):
+ return sorted(tud_drivers[call[len("driver->") :]])
+ elif call == "event.func_call.func":
+ # callback from usb_defer_func()
+ return []
- def tud_skip_call(chain: list[str], call: str) -> bool:
- if call == "usbd_app_driver_get_cb":
- return True
- return False
+ return None
+
+ hooks_indirect_callees += [tud_indirect_callees]
# newlib #########################################################
- newlib_nodes: list[Node] = [
- # malloc
- synthetic_node("free", 0), # TODO
- synthetic_node("malloc", 0), # TODO
- synthetic_node("realloc", 0), # TODO
- synthetic_node("aligned_alloc", 0), # TODO
- synthetic_node("reallocarray", 0), # TODO
- # execution
- synthetic_node("abort", 0), # TODO
- synthetic_node("longjmp", 0), # TODO
- synthetic_node("setjmp", 0), # TODO
- # <strings.h>
- synthetic_node("memcmp", 0), # TODO
- synthetic_node("memcpy", 0), # TODO
- synthetic_node("memset", 0), # TODO
- synthetic_node("strlen", 0), # TODO
- synthetic_node("strncpy", 0), # TODO
- # other
- synthetic_node("random", 0), # TODO
- ]
+ if arg_pico_platform == "rp2040":
+ all_nodes += [
+ # malloc
+ synthetic_node("free", 0), # TODO
+ synthetic_node("malloc", 0), # TODO
+ synthetic_node("realloc", 0), # TODO
+ synthetic_node("aligned_alloc", 0), # TODO
+ synthetic_node("reallocarray", 0), # TODO
+ # execution
+ synthetic_node("abort", 0), # TODO
+ synthetic_node("longjmp", 0), # TODO
+ synthetic_node("setjmp", 0), # TODO
+ # <strings.h>
+ synthetic_node("memcmp", 0), # TODO
+ synthetic_node("memcpy", 0), # TODO
+ synthetic_node("memset", 0), # TODO
+ synthetic_node("strcmp", 0), # TODO
+ synthetic_node("strlen", 0), # TODO
+ synthetic_node("strncpy", 0), # TODO
+ synthetic_node("strnlen", 0), # TODO
+ # other
+ synthetic_node("random", 0), # TODO
+ ]
# libgcc #########################################################
- gcc_nodes: list[Node] = [
- synthetic_node("__aeabi_idiv0", 0), # TODO
- synthetic_node("__aeabi_ldiv0", 0), # TODO
- ]
+ if arg_pico_platform == "rp2040":
+ all_nodes += [
+ synthetic_node("__aeabi_idiv0", 0), # TODO
+ synthetic_node("__aeabi_ldiv0", 0), # TODO
+ ]
- # main ###########################################################
+ # Tie it all together ############################################
def thread_filter(name: str) -> bool:
return sbc_is_thread(name)
def intrhandler_filter(name: str) -> bool:
name = name.rsplit(":", 1)[-1]
- return sbc_is_intrhandler(name) or pico_is_intrhandler(name)
+ for hook in hooks_is_intrhandler:
+ if hook(name):
+ return True
+ return False
def location_xform(loc: str) -> str:
if not loc.startswith("/"):
@@ -660,30 +766,26 @@ def main(
loc = elem.attrs.get("label", "")
line = read_source(loc)
- ret = sbc_indirect_callees(loc, line)
- if ret is not None:
- return ret
+ for hook in hooks_indirect_callees:
+ ret = hook(loc, line)
+ if ret is not None:
+ return ret
- ret = pico_indirect_callees(loc, line)
- if ret is not None:
- return ret
-
- ret = tud_indirect_callees(loc, line)
- if ret is not None:
- return ret
-
- return [f"__indirect_call:" + location_xform(elem.attrs.get("label", ""))]
+ placeholder = "__indirect_call"
+ if m := re_call_other.fullmatch(line):
+ placeholder += ":" + m.group("func")
+ placeholder += " at " + location_xform(elem.attrs.get("label", ""))
+ return [placeholder]
def skip_call(chain: list[str], call: str) -> bool:
- return (
- sbc_skip_call(chain, call)
- or pico_skip_call(chain, call)
- or tud_skip_call(chain, call)
- )
+ for hook in hooks_skip_call:
+ if hook(chain, call):
+ return True
+ return False
analyze(
ci_fnames=arg_ci_fnames,
- extra_nodes=pico_nodes + newlib_nodes + gcc_nodes,
+ extra_nodes=all_nodes,
app_func_filters={
"Threads": thread_filter,
"Interrupt handlers": intrhandler_filter,
@@ -696,19 +798,19 @@ def main(
if __name__ == "__main__":
- base_dir = sys.argv[1]
+ pico_platform = sys.argv[1]
+ base_dir = sys.argv[2]
+ fnames = sys.argv[3:]
re_suffix = re.compile(r"\.c\.o(bj)?$")
-
ci_fnames = [
- re_suffix.sub(".c.ci", fname)
- for fname in sys.argv[2:]
- if re_suffix.search(fname)
+ re_suffix.sub(".c.ci", fname) for fname in fnames if re_suffix.search(fname)
]
- c_fnames = [fname for fname in sys.argv[2:] if fname.endswith(".c")]
+ c_fnames = [fname for fname in fnames if fname.endswith(".c")]
main(
+ arg_pico_platform=pico_platform,
arg_base_dir=base_dir,
arg_ci_fnames=ci_fnames,
arg_c_fnames=c_fnames,