File: test-print-system-info.py

package info (click to toggle)
aws-crt-python 0.24.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 75,932 kB
  • sloc: ansic: 418,984; python: 23,626; makefile: 6,035; sh: 4,075; ruby: 208; java: 82; perl: 73; cpp: 25; xml: 11
file content (159 lines) | stat: -rwxr-xr-x 4,953 bytes parent folder | download
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
#!/usr/bin/env python3
import argparse
import os
from pathlib import Path
import platform
import re
import subprocess
from typing import Dict, List, Optional

PARSER = argparse.ArgumentParser(
    description="Run print-sys-info to detect CPU features. Fail if the results seem wrong.")
PARSER.add_argument("--print-sys-info-path",
                    help="Path to print-sys-info app")
PARSER.add_argument("--build-dir",
                    help="Search this dir for print-sys-info app")


FEATURE_ALIASES = {
    'avx512': ['avx512f'],
    'clmul': ['pclmulqdq'],
    'crc': ['crc32'],
    'crypto': ['aes'],
}


def main():
    args = PARSER.parse_args()

    if args.print_sys_info_path:
        app = Path(args.print_sys_info_path)
    else:
        app = find_app(args.build_dir)

    # run print-sys-info. get feature names and whether it thinks they're supported
    app_features_presence: Dict[str, bool] = detect_features_from_app(app)

    # get feature info from OS (i.e. read /proc/cpuinfo on linux), get back list of supported features
    os_features_list = detect_features_from_os()

    # For each feature that print-sys-info says was (or wasn't) there,
    # check the os_features_list and make sure it is (or isn't_ present.
    for (feature, app_presence) in app_features_presence.items():
        os_presence = feature in os_features_list
        if not os_presence:
            # sometimes a feature has a mildly different name across platforms
            for alias in FEATURE_ALIASES.get(feature, []):
                if alias in os_features_list:
                    os_presence = True
                    break

        if app_presence != os_presence:
            exit(f"FAILED: aws-c-common and OS disagree on whether CPU supports feature '{feature}'\n" +
                 f"\taws-c-common:{app_presence} OS:{os_presence}")

    print("SUCCESS: aws-c-common and OS agree on CPU features")


def find_app(build_dir: Optional[str]) -> Path:
    if build_dir is None:
        build_dir = find_build_dir()
    else:
        build_dir = Path(build_dir).absolute()

    app_name = 'print-sys-info'
    if os.name == 'nt':
        app_name = app_name + '.exe'

    for file in build_dir.glob(f"**/{app_name}"):
        return file

    exit(f"FAILED: Can't find '{app_name}' under: {build_dir}"
         "\nPass --build-dir to hint location.")


def find_build_dir() -> Path:
    dir = Path(__file__).parent.absolute()
    while dir is not None:
        for build_dir in dir.glob('build'):
            return build_dir
        dir = dir.parent

    exit("FAILED: Can't find build dir. Pass --build-dir to hint location.")


def detect_features_from_app(app_path: Path) -> Dict[str, bool]:
    result = subprocess.run([str(app_path)],
                            universal_newlines=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    print(f"--- {app_path} ---")
    print(result.stderr)
    print(result.stdout)
    if result.returncode != 0:
        exit(f"FAILED: {app_path.name} exited with {result.returncode}")

    lines = result.stdout.splitlines()

    machine = platform.machine().lower()
    if machine in ['x86_64', 'amd64']:
        machine = 'amd'
    elif machine.startswith('arm') or machine == 'aarch64':
        machine = 'arm'
    else:
        print(f"SKIP TEST: unknown platform.machine(): {machine}")
        exit(0)

    # looking for lines like:
    #        'arm_crypto': true,
    #        'amd_sse4_1': false
    features = {}
    for line in lines:
        m = re.search(f"'{machine}_(.+)': (false|true)", line)
        if m:
            name = m.group(1)
            is_present = m.group(2) == 'true'
            features[name] = is_present

    # if aws-c-common compiled with -DUSE_CPU_EXTENSIONS=OFF, skip this this test
    for line in lines:
        m = re.search(f"'use cpu extensions': false", line)
        if m:
            print("SKIP TEST: aws-c-common compiled with -DUSE_CPU_EXTENSIONS=OFF")
            exit(0)

    if not features:
        raise RuntimeError("Cannot find features text in stdout ???")

    return features


def detect_features_from_os() -> List[str]:
    features = []

    cpuinfo_path = '/proc/cpuinfo'
    try:
        with open(cpuinfo_path) as f:
            cpuinfo_text = f.read()
    except:
        print(f"SKIP TEST: currently, this test only works on machines with /proc/cpuinfo")
        exit(0)

    # looking for line like:
    # flags           : fpu vme de pse ...
    # OR
    # features        : fp asimd evtstrm ...
    print(f"--- {cpuinfo_path} ---")
    for line in cpuinfo_text.splitlines():
        line = line.lower()
        print(line)
        m = re.match(r"(flags|features)\s+:(.*)", line)
        if m:
            features = m.group(2).split()
            return features

    exit(f"FAILED: Cannot detect CPU features in {cpuinfo_path}")


if __name__ == '__main__':
    main()