summaryrefslogtreecommitdiff
path: root/bin/extract-kernel-ws.py
blob: 9f263e21d38910b4adde7e354716b04e53dd8511 (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
#!/usr/bin/env python3

import argparse
import numpy as np
import sys
import logging


def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__
    )
    parser.add_argument(
        "--log-level",
        metavar="LEVEL",
        choices=["debug", "info", "warning", "error"],
        default="warning",
        help="Set log level",
    )
    parser.add_argument(
        "--output-format",
        metavar="FORMAT",
        choices=["dfatool", "valgrind-ws"],
        default="dfatool",
        help="Set output format",
    )
    parser.add_argument(
        "benchmark_file",
        type=str,
        help="Benchmark file used to run valgrind-ws",
    )
    parser.add_argument(
        "ws_output",
        type=str,
        help="valgrind-ws output file",
    )

    args = parser.parse_args()
    benchmark_filename = args.benchmark_file.split("/")[-1]

    if args.log_level:
        numeric_level = getattr(logging, args.log_level.upper(), None)
        if not isinstance(numeric_level, int):
            print(f"Invalid log level: {args.log_level}", file=sys.stderr)
            sys.exit(1)
        logging.basicConfig(
            level=numeric_level,
            format="{asctime} {levelname}:{name}:{message}",
            style="{",
        )

    with open(args.benchmark_file, "r") as f:
        start_range = [None, None]
        end_range = [None, None]
        in_nop = False
        for lineno, line in enumerate(f):
            line = line.strip()
            if line == "#if NOP_SYNC":
                in_nop = True
                if start_range[0] is None:
                    start_range[0] = lineno
                else:
                    end_range[0] = lineno
            if in_nop and line.startswith("#endif"):
                in_nop = False
                if start_range[1] is None:
                    start_range[1] = lineno
                else:
                    end_range[1] = lineno

    logging.debug(f"start_range = {start_range}, end_range = {end_range}")

    page_size = None
    ws_log = list()
    sample_info = dict()
    with open(args.ws_output, "r") as f:
        in_ws_log = False
        in_sample_info = False
        for line in f:
            line = line.strip()
            if in_ws_log and line == "":
                in_ws_log = False
            if in_sample_info and line == "":
                in_sample_info = False
            if page_size is None and line.startswith("Page size:"):
                page_size = int(line.split()[2])

            if in_ws_log:
                t, wss_i, wss_d, info_ref = line.split()
                ws_log.append((int(t), int(wss_i), int(wss_d), info_ref))
            elif in_sample_info:
                _, info_ref, _, locs = line.split()
                info_ref = info_ref.removesuffix("]")
                locs = locs.removeprefix("loc=")
                sample_info[info_ref] = list()
                for loc in filter(lambda x: len(x), locs.split("|")):
                    filename, lineno = loc.split(":")
                    sample_info[info_ref].append((filename, int(lineno)))

            if line == "t WSS_insn WSS_data info":
                in_ws_log = True
            if line == "Sample info:":
                in_sample_info = True

    if page_size is None:
        raise RuntimeError("Unable to determine page size fom {args.ws_output}")

    logging.debug(f"sample_info = {sample_info}")
    next_in_kernel = False
    in_kernel = False
    insn_working_set_sizes = list()
    data_working_set_sizes = list()
    kernel_range = [None, None]
    for t, wss_i, wss_d, info_ref in ws_log:
        if next_in_kernel:
            next_in_kernel = False
            in_kernel = True
            kernel_range[0] = t

        if info_ref != "-":
            for filename, lineno in sample_info[info_ref]:
                if (
                    filename == benchmark_filename
                    and start_range[0] <= lineno <= start_range[1]
                ):
                    next_in_kernel = True
                elif (
                    filename == benchmark_filename
                    and end_range[0] <= lineno <= end_range[1]
                ):
                    in_kernel = False

        if in_kernel:
            data_working_set_sizes.append(wss_d * page_size)
            insn_working_set_sizes.append(wss_i * page_size)
            kernel_range[1] = t

    if args.output_format == "dfatool":
        print(
            f"wss_data_mean_bytes={np.mean(data_working_set_sizes)}"
            + f" wss_data_median_bytes={np.median(data_working_set_sizes)}"
            + f" wss_data_stddev={np.std(data_working_set_sizes)}"
            + f" wss_insn_mean_bytes={np.mean(insn_working_set_sizes)}"
            + f" wss_insn_median_bytes={np.median(insn_working_set_sizes)}"
            + f" wss_insn_stddev={np.std(insn_working_set_sizes)}"
        )
    elif args.output_format == "valgrind-ws":
        with open(args.ws_output, "r") as f:
            in_ws_log = False
            for line in f:
                if in_ws_log and line.strip() == "":
                    in_ws_log = False

                if in_ws_log:
                    ts = int(line.strip().split()[0])
                    if kernel_range[0] <= ts <= kernel_range[1]:
                        print(line, end="")
                else:
                    print(line, end="")

                if line.strip() == "t WSS_insn WSS_data info":
                    in_ws_log = True


if __name__ == "__main__":
    main()