# build-aux/measurestack/app_main.py - Application-specific wrapper around analyze.py # # Copyright (C) 2024-2025 Luke T. Shumaker # SPDX-License-Identifier: AGPL-3.0-or-later import os.path import typing from . import analyze, app_output, app_plugins, util from .analyze import BaseName, QName # pylint: disable=unused-variable __all__ = [ "main", ] def main( *, arg_pico_platform: str, arg_base_dir: str, arg_ci_fnames: typing.Collection[str], arg_c_fnames: typing.Collection[str], ) -> None: plugins: list[util.Plugin] = [] # sbc-harness #################################################### lib9p_plugin = app_plugins.Lib9PPlugin(arg_base_dir, arg_c_fnames) def sbc_is_thread(name: QName) -> int: if str(name).endswith("_cr") and name.base() != BaseName("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.base() == ( BaseName("_entry_point") if arg_pico_platform == "rp2040" else BaseName("main") ): return 1 return 0 libobj_plugin = app_plugins.LibObjPlugin(arg_c_fnames) plugins += [ app_plugins.CmdPlugin(), libobj_plugin, app_plugins.LibHWPlugin(arg_pico_platform, libobj_plugin), app_plugins.LibCRPlugin(), app_plugins.LibCRIPCPlugin(), lib9p_plugin, app_plugins.LibMiscPlugin(), ] # 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 += [ app_plugins.PicoFmtPlugin(), app_plugins.PicoSDKPlugin( get_init_array=get_init_array, ), app_plugins.TinyUSBDevicePlugin(arg_c_fnames), app_plugins.NewlibPlugin(), app_plugins.LibGCCPlugin(), ] # Tie it all together ############################################ def thread_filter(name: QName) -> tuple[int, bool]: return sbc_is_thread(name), True def intrhandler_filter(name: QName) -> tuple[int, bool]: for plugin in plugins: if plugin.is_intrhandler(name): return 1, True return 0, False def misc_filter(name: QName) -> tuple[int, bool]: if name.base() in [ BaseName("__lm_printf"), BaseName("__assert_msg_fail"), ]: return 1, False return 0, False extra_includes: list[BaseName] = [] for plugin in plugins: extra_includes.extend(plugin.extra_includes()) def extra_filter(name: QName) -> tuple[int, bool]: nonlocal extra_includes if name.base() in extra_includes: return 1, True return 0, False def _str_location_xform(loc: str) -> str: if not loc.startswith("/"): return loc parts = loc.split(":", 1) parts[0] = "./" + os.path.relpath(parts[0], arg_base_dir) return ":".join(parts) def location_xform(_loc: QName) -> str: return _str_location_xform(str(_loc)) result = analyze.analyze( ci_fnames=arg_ci_fnames, app_func_filters={ "Threads": thread_filter, "Interrupt handlers": intrhandler_filter, "Misc": misc_filter, "Extra": extra_filter, }, app=util.PluginApplication(_str_location_xform, plugins), cfg_max_call_depth=100, ) app_output.print_c(result, location_xform)