summaryrefslogtreecommitdiff
path: root/tools/utilities/weak_order_diff.py
blob: d90ed62ad0b4303bf318535cd60af523ef1e89b5 (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
#!/usr/bin/env python3

import os
import re
from pathlib import Path
import subprocess
from argparse import ArgumentParser


def is_windows() -> bool:
    return os.name == "nt"


EXE = ".exe" if is_windows() else ""


def get_symbols(o_path: Path, diff_data: bool):
    readelf_path = f"build/binutils/powerpc-eabi-readelf{EXE}"
    output = subprocess.check_output([readelf_path, "-Ws", o_path]).decode("ascii")
    symbols = []
    for line in output.split("\n")[3:]:
        if line == "":
            continue
        words = line.split()
        if len(words) == 7 and words[-1] == "UND":
            continue
        _, offset, size, sym_type, scope, vis, section_index, name = words

        if diff_data:
            # Only diff data.
            if sym_type == "FUNC":
                continue
            if sym_type in ["FILE", "NOTYPE", "SECTION"]:
                continue
            if vis == "HIDDEN":
                continue
            if re.search(r"^@\d+$", name):
                name = "@"
            if re.search(r"^lbl_[0-9a-f]+_(?:data|bss)_[0-9a-f]+$", name):
                continue
            match = re.search(r"^(\S+\$)\d+$", name)
            if match:
                name = match.group(1)
        else:
            # Only diff functions.
            if sym_type != "FUNC":
                continue
            if vis == "HIDDEN":
                continue

        symbols.append((sym_type, int(section_index), int(offset, 16), name))

    symbols.sort()
    symbol_names = [sym[-1] for sym in symbols]
    return symbol_names


def print_symbols_with_unmatched_order_for_object(
    src_path: str, version: str, diff_data: bool
):
    assert src_path.startswith("src/")
    relative_o_path = src_path[len("src/"):]
    relative_o_path = relative_o_path.rsplit(".")[0] + ".o"
    target_o = Path("build") / version / "obj" / relative_o_path
    base_o = Path("build") / version / "src" / relative_o_path
    if not target_o.exists():
        rel_name = relative_o_path.split("/")[-1].split(".")[0]
        target_o = Path("build") / version / rel_name / "obj" / relative_o_path

    subprocess.check_output(["ninja", base_o])

    target_symbols = get_symbols(target_o, diff_data)
    base_symbols = get_symbols(base_o, diff_data)
    target_symbols_set = set(target_symbols)
    base_symbols = [sym for sym in base_symbols if sym in target_symbols_set]
    base_idx = 0
    matched_count = 0
    unmatched_count = 0
    for target_sym in target_symbols:
        if base_idx == len(base_symbols):
            base_sym = None
        else:
            base_sym = base_symbols[base_idx]

        if target_sym == base_sym:
            base_idx += 1
            matched_count += 1
        elif target_sym not in base_symbols:
            print("MISSING SYMBOL:", target_sym)
        else:
            base_idx = base_symbols.index(target_sym)
            base_idx += 1
            unmatched_count += 1
            print(target_sym)

    print("====================================")
    print("Number of order differences:", unmatched_count)


def main():
    parser = ArgumentParser(
        description="Print differences in weak function order for an object."
    )
    parser.add_argument(
        "src_path",
        type=str,
        default="src/d/actor/d_a_player_main.cpp",
        nargs="?",
        help="""relative path to the source file to diff (e.g. src/d/actor/d_a_player_main.cpp).""",
    )
    parser.add_argument(
        "-v",
        "--version",
        type=str,
        default="GZLE01",
        help="version to build",
    )
    parser.add_argument(
        "--data",
        action="store_true",
        help="""diffs data instead of functions.""",
    )
    args = parser.parse_args()

    print_symbols_with_unmatched_order_for_object(args.src_path, args.version, args.data)


if __name__ == "__main__":
    main()