summaryrefslogtreecommitdiff
path: root/build-aux/measurestack/test_app_plugins.py
blob: 8a29a12d331669dbf0e3bc3c08bee71baa3a0fe0 (plain)
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# 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, QName


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       + |
    max_call_depth = 5
    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",
    ]
    exp_c = [
        "c",
        "__lm_light_printf",
        "fmt_vfctprintf",
        "fmt_state_putchar",
        "libfmt_light_fct",
    ]
    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", {}),
        ("libfmt_light_fct", {}),
    ]
    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                   |
    #    |                          |                          |
    #    |               stdio_buffered_printer                |
    #    |                                                     |
    #    |_____________________________________________________|
    #
    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", "fmt_state_printf"}),
        ("fmt_state_putchar", {"stdio_buffered_printer", "libfmt_light_fct"}),
        ("stdio_buffered_printer", {}),
        ("libfmt_light_fct", {}),  # wrong fct
    ]

    # 1  2  3  4  5  6  7  8  9 10 11 <= call_depth
    # - main()                                                 ; +
    #    - __wrap__vprintf()                                   ; +
    #       - fmt_vfctprintf()                                 ; +
    #          - _vfctprintf()                                 ; +
    #             - conv_builtin()                             ;
    #                - fmt_state_putchar()                     ;
    #                   - stdio_buffered_printer()             ;
    #             - libfmt_conv_formatter()                    ; +
    #                - lib9p_msg_Rread_format()                ; +
    #                   - fmt_state_putchar()                  ;
    #                      - stdio_buffered_printer()          ;
    #                   - fmt_state_printf()                   ; +
    #                      - _vfctprintf()                     ; +
    #                         - conv_builtin()                 ; +
    #                            - fmt_state_putchar()         ; +
    #                               - stdio_buffered_printer() ; +
    #                         - libfmt_conv_formatter()        ; skip (formatter won't use %v)
    max_call_depth = 11
    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",
    ]

    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)