diff options
-rwxr-xr-x | bin/extract-kernel-ws.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/bin/extract-kernel-ws.py b/bin/extract-kernel-ws.py new file mode 100755 index 0000000..9f263e2 --- /dev/null +++ b/bin/extract-kernel-ws.py @@ -0,0 +1,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() |