summaryrefslogtreecommitdiff
path: root/build-aux/measurestack/test_app_plugins.py
blob: da8be65e6f200cd77e3362b01106c8053f3deb2e (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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
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)