diff options
Diffstat (limited to 'build-aux/measurestack/test_app_plugins.py')
-rw-r--r-- | build-aux/measurestack/test_app_plugins.py | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/build-aux/measurestack/test_app_plugins.py b/build-aux/measurestack/test_app_plugins.py new file mode 100644 index 0000000..da8be65 --- /dev/null +++ b/build-aux/measurestack/test_app_plugins.py @@ -0,0 +1,379 @@ +# build-aux/measurestack/test_app_plugins.py - Tests for app_plugins.py +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +# pylint: disable=unused-variable + +import typing + +from . import analyze, app_plugins, testutil, util +from .analyze import BaseName, Node, QName, SkipModel + + +def test_assert_msg_fail() -> None: + # 1 2 3 4 5 6 7 <= call_depth + # - main() + # - __assert_msg_fail() * + # - __lm_light_printf() + # - fmt_vfctprintf() + # - stdio_putchar() + # - __assert_msg_fail() ** + # - __lm_abort() + # - stdio_flush() (inconsequential) + # - __lm_abort() (inconsequential) + max_call_depth = 7 + exp = [ + "main", + "__assert_msg_fail", + "__lm_light_printf", + "fmt_vfctprintf", + "stdio_putchar", + "__assert_msg_fail", + "__lm_abort", + ] + graph: typing.Sequence[tuple[str, typing.Collection[str]]] = [ + # main.c + ("main", {"__assert_msg_fail"}), + # assert.c + ("__assert_msg_fail", {"__lm_light_printf", "__lm_abort"}), + # intercept.c / libfmt/libmisc.c + ("__lm_abort", {}), + ("__lm_light_printf", {"fmt_vfctprintf", "stdio_flush"}), + ("stdio_flush", {}), + ("stdio_putchar", {"__assert_msg_fail"}), + # printf.c + ("fmt_vfctprintf", {"stdio_putchar"}), + ] + graph_plugin = testutil.GraphProviderPlugin(max_call_depth, graph) + + class SkipPlugin(testutil.NopPlugin): + def skipmodels(self) -> dict[BaseName, SkipModel]: + models = app_plugins.LibMiscPlugin(arg_c_fnames=[]).skipmodels() + assert BaseName("__assert_msg_fail") in models + orig_model = models[BaseName("__assert_msg_fail")] + + def wrapped_model_fn( + chain: typing.Sequence[QName], node: Node, call: QName + ) -> bool: + dbgstr = ( + ("=>".join(str(c) for c in [*chain, node.funcname])) + + "=?=>" + + str(call) + ) + assert dbgstr in [ + "__assert_msg_fail=?=>__lm_light_printf", + "__assert_msg_fail=?=>__lm_abort", + "__assert_msg_fail=>__lm_light_printf=>fmt_vfctprintf=>stdio_putchar=>__assert_msg_fail=?=>__lm_light_printf", + "__assert_msg_fail=>__lm_light_printf=>fmt_vfctprintf=>stdio_putchar=>__assert_msg_fail=?=>__lm_abort", + ] + return orig_model.fn(chain, node, call) + + models[BaseName("__assert_msg_fail")] = SkipModel( + orig_model.nchain, wrapped_model_fn + ) + return models + + def test_filter(name: QName) -> tuple[int, bool]: + if name.base() == BaseName("main"): + return 1, True + return 0, False + + result = analyze.analyze( + ci_fnames=[], + app_func_filters={ + "Main": test_filter, + }, + app=util.PluginApplication( + testutil.nop_location_xform, [graph_plugin, SkipPlugin()] + ), + cfg_max_call_depth=max_call_depth, + ) + + graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("main")].nstatic, exp) + + +def test_fct() -> None: + # 1. | a + | b + | c + |* + # 2. | fmt_vsnprintf + | vprintf + | __lm_light_printf + |* + # 3. | fmt_vfctprintf + | fmt_vfctprintf + | fmt_vfctprintf + | + # 4. | fmt_state_putchar + | fmt_state_putchar + | fmt_state_putchar + | + # 5. | _out_buffer + | stdio_buffered_printer + | libfmt_light_fct + |* + # 6. | | __assert_msg_fail + | __assert_msg_fail + | + # 7. | | a. __lm_light_printf + | a. __lm_light_printf + | + # 8. | | a. fmt_vfctprintf + | a. fmt_vfctprintf + | + # 9. | | a. fmt_state_putchar + | a. fmt_state_putchar + | + # 10. | | a. libfmt_light_fct + | a. libfmt_light_fct + | + # 11. | | a. __assert_msg_fail + | a. __assert_msg_fail + | + # 12. | | a. __lm_abort + | a. __lm_abort + | + # 7. | | b. __lm_abort | b. __lm_abort | + max_call_depth = 12 + exp_a = ["a", "fmt_vsnprintf", "fmt_vfctprintf", "fmt_state_putchar", "_out_buffer"] + exp_b = [ + "b", + "__wrap_vprintf", + "fmt_vfctprintf", + "fmt_state_putchar", + "stdio_buffered_printer", + "__assert_msg_fail", + "__lm_light_printf", + "fmt_vfctprintf", + "fmt_state_putchar", + "libfmt_light_fct", + "__assert_msg_fail", + "__lm_abort", + ] + exp_c = [ + "c", + "__lm_light_printf", + "fmt_vfctprintf", + "fmt_state_putchar", + "libfmt_light_fct", + "__assert_msg_fail", + "__lm_light_printf", + "fmt_vfctprintf", + "fmt_state_putchar", + "libfmt_light_fct", + "__assert_msg_fail", + "__lm_abort", + ] + graph: typing.Sequence[tuple[str, typing.Collection[str]]] = [ + # main.c + ("a", {"fmt_vsnprintf"}), # _out_buffer + ("b", {"vprintf"}), # stdio_buffered_printer + ("c", {"__lm_light_printf"}), # libfmt_light_printf + # wrappers + ("fmt_vsnprintf", {"fmt_vfctprintf"}), + ("__wrap_vprintf", {"fmt_vfctprintf"}), + ("__lm_light_printf", {"fmt_vfctprintf"}), + # printf.c + ("fmt_vfctprintf", {"fmt_state_putchar"}), + ( + "fmt_state_putchar", + {"_out_buffer", "stdio_buffered_printer", "libfmt_light_fct"}, + ), + # fcts + ("_out_buffer", {}), + ("stdio_buffered_printer", {"__assert_msg_fail"}), + ("libfmt_light_fct", {"__assert_msg_fail"}), + # assert.c + ("__assert_msg_fail", {"__lm_light_printf", "__lm_abort"}), + # intercept.c / libfmt/libmisc.c + ("__lm_abort", {}), + ] + graph_plugin = testutil.GraphProviderPlugin(max_call_depth, graph) + + plugins: list[util.Plugin] = [ + graph_plugin, + app_plugins.LibMiscPlugin(arg_c_fnames=[]), + # fmt_vsnprintf => fct=_out_buffer + # if rp2040: + # __wrap_vprintf => fct=stdio_buffered_printer + # stdio_vprintf => fct=stdio_buffered_printer + # __lm_light_printf => fct=libfmt_light_fct + # if host: + # __lm_printf => fct=libfmt_libc_fct + # __lm_light_printf => fct=libfmt_libc_fct + app_plugins.PicoFmtPlugin("rp2040", []), + ] + + def test_filter(name: QName) -> tuple[int, bool]: + if str(name.base()) in ["a", "b", "c"]: + return 1, True + return 0, False + + result = analyze.analyze( + ci_fnames=[], + app_func_filters={ + "Main": test_filter, + }, + app=util.PluginApplication(testutil.nop_location_xform, plugins), + cfg_max_call_depth=max_call_depth, + ) + + graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("a")].nstatic, exp_a) + graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("b")].nstatic, exp_b) + graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("c")].nstatic, exp_c) + + +def test_assert_formatter() -> None: + # _________________________________________________________ + # | | + # | | + # | main | + # | | | + # | _ __wrap_vprintf | + # | / \ | _______________ | + # | | fmt_vfctprintf / \ | + # | | \ fmt_state_printf | | + # | | \____ ____/ | | + # | | \ / | | + # | | _vfctprintf | | + # | | ____/ \____ ^ | + # | | / ?<---snip | | + # | | conv_builtin \ | | + # | | | libfmt_conv_formatter | | + # | | | | | | + # | ^ \ lib9p_msg_Rread_format | | + # | | \ _____________/ | \______/ | + # | | \ / \ | + # | | fmt_state_putchar \ | + # | | ?<-?<--------snip | | + # | | / \_________ | | + # | | / \ | | + # | | stdio_buffered_printer \ | | + # | | \ libfmt_light_fct | | + # | | \ | / | + # | | \_______ | ________/ | + # | | \ | / | + # | | __assert_msg_fail | + # | | ___/ \____ | + # | | snip--->? \ | + # | | / \ | + # | | __lm_light_printf \ | + # | \____________/ __lm_abort | + # | | + # |_________________________________________________________| + # + graph: typing.Sequence[tuple[str, typing.Collection[str]]] = [ + ("main", {"vprintf"}), + ("__wrap_vprintf", {"fmt_vfctprintf"}), + ("fmt_vfctprintf", {"_vfctprintf"}), + ("fmt_state_printf", {"_vfctprintf"}), + ("_vfctprintf", {"conv_builtin", "libfmt_conv_formatter"}), + ("conv_builtin", {"fmt_state_putchar"}), + ("libfmt_conv_formatter", {"lib9p_msg_Rread_format"}), + ( + "lib9p_msg_Rread_format", + {"fmt_state_putchar", "__assert_msg_fail", "fmt_state_printf"}, + ), + ("fmt_state_putchar", {"stdio_buffered_printer", "libfmt_light_fct"}), + ("stdio_buffered_printer", {"__assert_msg_fail"}), + ("libfmt_light_fct", {"__assert_msg_fail"}), + ("__assert_msg_fail", {"__lm_light_printf", "__lm_abort"}), + ("__lm_light_printf", {"fmt_vfctprintf"}), + ("__lm_abort", {}), + ] + + # fct-determining wrappers have their callees marked with "|": + # + # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <= call_depth + # - main() ; + + # - __wrap__vprintf() ; + + # |- fmt_vfctprintf() ; + + # | - _vfctprintf() ; + + # | - conv_builtin() ; + # | - fmt_state_putchar() ; + # | - stdio_buffered_printer() ; + # | - __assert_msg_fail() ; + # | - __lm_light_printf() ; + # | |- fmt_vfctprintf() ; + # | | - _vfctprintf() ; + # | | - conv_builtin() ; + # | | - fmt_state_putchar() ; + # | | - stdio_buffered_printer() ; skip (wrong fct) + # | | - libfmt_light_fct() ; + # | | - __assert_msg_fail() ; + # | | - __lm_light_printf() ; skip (nested __assert_msg_fail) + # | | - __lm_abort() ; + # | | - libfmt_conv_formatter() ; skip (fct won't use %v) + # | - __lm_abort() ; + # | - libfmt_light_fct() ; skip (wrong fct) + # | - libfmt_conv_formatter() ; + + # | - lib9p_msg_Rread_format() ; + + # | - fmt_state_putchar() ; + # | - stdio_buffered_printer() ; + # | - __assert_msg_fail() ; + # | - __lm_light_printf() ; + # | |- fmt_vfctprintf() ; + # | | - _vfctprintf() ; + # | | - conv_builtin() ; + # | | - fmt_state_putchar() ; + # | | - stdio_buffered_printer() ; skip (wrong fct) + # | | - libfmt_light_fct() ; + # | | - __assert_msg_fail() ; + # | | - __lm_light_printf() ; skip (neseted __assert_msg_fail) + # | | - __lm_abort() ; + # | | - libfmt_conv_formatter() ; skip (fct won't use %v) + # | - __lm_abort() ; + # | - libfmt_light_fct() ; skip (wrong fct) + # | - __assert_msg_fail() ; + # | - __lm_light_printf() ; + # | |- fmt_vfctprintf() ; + # | | - _vfctprintf() ; + # | | - conv_builtin() ; + # | | - fmt_state_putchar() ; + # | | - stdio_buffered_printer() ; skip (wrong fct) + # | | - libfmt_light_fct() ; + # | | - __assert_msg_fail() ; + # | | - __lm_light_printf() ; skip (nested__assert_msg_fail) + # | | - __lm_abort() ; + # | | - libfmt_conv_formatter() ; skip (formatter won't use %v) + # | - __lm_abort() ; + # | - fmt_state_printf() ; + + # | - _vfctprintf() ; + + # | - conv_builtin() ; + + # | - fmt_state_putchar() ; + + # | - stdio_buffered_printer() ; + + # | - __assert_msg_fail() ; + + # | - __lm_light_printf() ; + + # | |- fmt_vfctprintf() ; + + # | | - _vfctprintf() ; + + # | | - conv_builtin() ; + + # | | - fmt_state_putchar() ; + + # | | - stdio_buffered_printer() ; skip (wrong fct) + # | | - libfmt_light_fct() ; + + # | | - __assert_msg_fail() ; + + # | | - __lm_light_printf() ; skip (neseted __assert_msg_fail) + # | | - __lm_abort() ; + + # | | - libfmt_conv_formatter() ; skip (fct won't use %v) + # | - __lm_abort() ; + # | - libfmt_light_fct() ; skip (wrong fct) + # | - libfmt_conv_formatter() ; skip (formatter won't use %v) + max_call_depth = 20 + exp = [ + "main", + "__wrap_vprintf", + "fmt_vfctprintf", + "_vfctprintf", + "libfmt_conv_formatter", + "lib9p_msg_Rread_format", + "fmt_state_printf", + "_vfctprintf", + "conv_builtin", + "fmt_state_putchar", + "stdio_buffered_printer", + "__assert_msg_fail", + "__lm_light_printf", + "fmt_vfctprintf", + "_vfctprintf", + "conv_builtin", + "fmt_state_putchar", + "libfmt_light_fct", + "__assert_msg_fail", + "__lm_abort", + ] + + graph_plugin = testutil.GraphProviderPlugin(max_call_depth, graph) + + plugins: list[util.Plugin] = [ + graph_plugin, + app_plugins.LibMiscPlugin(arg_c_fnames=[]), + app_plugins.PicoFmtPlugin("rp2040", [BaseName("lib9p_msg_Rread_format")]), + ] + + def test_filter(name: QName) -> tuple[int, bool]: + if name.base() == BaseName("main"): + return 1, True + return 0, False + + result = analyze.analyze( + ci_fnames=[], + app_func_filters={ + "Main": test_filter, + }, + app=util.PluginApplication(testutil.nop_location_xform, plugins), + cfg_max_call_depth=max_call_depth, + ) + + graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("main")].nstatic, exp) |