1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# build-aux/measurestack/app_main.py - Application-specific wrapper around analyze.py
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# 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 ####################################################
libmisc_plugin = app_plugins.LibMiscPlugin(arg_c_fnames)
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
plugins += [
app_plugins.CmdPlugin(),
libmisc_plugin,
app_plugins.LibHWPlugin(arg_pico_platform, libmisc_plugin),
app_plugins.LibCRPlugin(),
app_plugins.LibCRIPCPlugin(),
lib9p_plugin,
]
# 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.PicoSDKPlugin(
get_init_array=get_init_array,
PICO_PANIC_FUNCTION="assert_panic",
),
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 in [
QName("__assert_msg_fail"),
]:
return 1, False
if str(name.base()).endswith("_putb"):
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)
|