summaryrefslogtreecommitdiffstats
path: root/tools/cuda-bridge/check_module_layout.py
blob: 336cc7351a289133fb1f7dbd9efb8bd612c3a6bf (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
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause
#
# check_module_layout.py - mechanically compare the frozen CUBB2/CBD2 structs
# in kernel/gsp_cuda_bridge.c (module side) and cuda_bridge.c (daemon side).
#
# Parses both struct bodies, computes x86_64 natural-alignment offsets and
# sizes, and reports the first mismatch. The compile-time static asserts in
# each C file pin their own layout; this check pins the two sides to each
# other. Exit code 0 = identical.

import os
import re
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
MODULE = os.path.normpath(os.path.join(HERE, "..", "..", "kernel",
                                       "gsp_cuda_bridge.c"))
DAEMON = os.path.join(HERE, "cuda_bridge.c")

TYPE_SIZE = {
    "u8": 1, "u32": 4, "u64": 8,
    "uint8_t": 1, "uint32_t": 4, "uint64_t": 8,
    "char": 1,
}
CONST = {"CUBB2_NAME_MAX": 64}


def parse_struct(path, name):
    text = open(path).read()
    m = re.search(r"struct\s+%s\s*\{([^{}]*)\}\s*;" % re.escape(name), text,
                  re.S)
    if not m:
        m = re.search(r"typedef\s+struct\s*\{([^{}]*)\}\s*%s_t\s*;"
                      % re.escape(name), text, re.S)
    if not m:
        raise SystemExit("%s: struct %s not found" % (path, name))
    fields = []
    for raw in m.group(1).splitlines():
        line = raw.split("/*")[0].strip()
        if not line or line.startswith("*"):
            continue
        m2 = re.match(r"^([A-Za-z_][A-Za-z0-9_]*)\s+([A-Za-z_][A-Za-z0-9_]*)"
                      r"\s*(?:\[\s*(.+?)\s*\])?\s*;$", line)
        if not m2:
            raise SystemExit("%s: cannot parse field %r" % (path, line))
        typ, fname, count_expr = m2.group(1), m2.group(2), m2.group(3)
        count = 1
        if count_expr is not None:
            count = int(eval(count_expr, {"__builtins__": {}}, CONST))
        if typ not in TYPE_SIZE:
            raise SystemExit("%s: unknown type %s" % (path, typ))
        fields.append((typ, fname, count))
    return fields


def layout(fields):
    out = []
    off = 0
    align = 1
    for typ, fname, count in fields:
        sz = TYPE_SIZE[typ]
        al = sz
        off = (off + al - 1) // al * al
        out.append((fname, typ, count, off, sz * count))
        off += sz * count
        align = max(align, al)
    total = (off + align - 1) // align * align
    return out, total


def compare(name, mpath, dpath):
    mod = layout(parse_struct(mpath, name))
    dae = layout(parse_struct(dpath, name))
    mfields, mtotal = mod
    dfields, dtotal = dae
    ok = True
    print("=== struct %s (module %s | daemon %s) ===" % (name, mpath, dpath))
    if mtotal != dtotal:
        print("TOTAL MISMATCH: module=%d daemon=%d" % (mtotal, dtotal))
        ok = False
    if len(mfields) != len(dfields):
        print("FIELD COUNT MISMATCH: module=%d daemon=%d"
              % (len(mfields), len(dfields)))
        return False
    print("%-20s %-10s %-18s %-18s" % ("field", "type", "module off/size",
                                       "daemon off/size"))
    for (mn, mt, mc, mo, ms), (dn, dt, dc, do, ds) in zip(mfields, dfields):
        same = (mn == dn and mc == dc and mo == do and ms == ds)
        print("%-20s %-10s 0x%02x/%-12d 0x%02x/%-12d%s"
              % (mn, "%s|%s" % (mt, dt), mo, ms, do, ds,
                 "" if same else "  <-- MISMATCH"))
        if not same:
            ok = False
    print("total: module=0x%x daemon=0x%x %s"
          % (mtotal, dtotal, "MATCH" if mtotal == dtotal else "MISMATCH"))
    return ok


def main():
    ok = True
    for name in ("cubb2_job", "cubb2_done"):
        ok = compare(name, MODULE, DAEMON) and ok
    print("LAYOUT-CHECK %s" % ("PASS" if ok else "FAIL"))
    return 0 if ok else 1


if __name__ == "__main__":
    sys.exit(main())