|  | #!/usr/bin/env python3 | 
|  | import argparse | 
|  | import csv | 
|  | import re | 
|  | import sys | 
|  | import os | 
|  | from statistics import geometric_mean | 
|  |  | 
|  | TIMING_LOG_RE = re.compile(r"(.*)/(.*).tmp(.*)") | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser( | 
|  | description="BOLT NFC stat parser", | 
|  | formatter_class=argparse.ArgumentDefaultsHelpFormatter, | 
|  | ) | 
|  | parser.add_argument( | 
|  | "input", nargs="+", help="timing.log files produced by llvm-bolt-wrapper" | 
|  | ) | 
|  | parser.add_argument( | 
|  | "--check_longer_than", | 
|  | default=2, | 
|  | type=float, | 
|  | help="Only warn on tests longer than X seconds for at least one side", | 
|  | ) | 
|  | parser.add_argument( | 
|  | "--threshold_single", | 
|  | default=10, | 
|  | type=float, | 
|  | help="Threshold for a single test result swing, abs percent", | 
|  | ), | 
|  | parser.add_argument( | 
|  | "--threshold_agg", | 
|  | default=5, | 
|  | type=float, | 
|  | help="Threshold for geomean test results swing, abs percent", | 
|  | ), | 
|  | parser.add_argument("--verbose", "-v", action="store_true") | 
|  | args = parser.parse_args() | 
|  |  | 
|  | def fmt_delta(value, exc_threshold, above_bound=True): | 
|  | formatted_value = format(value, "+.2%") | 
|  | if not above_bound: | 
|  | formatted_value += "?" | 
|  | elif exc_threshold and sys.stdout.isatty():  # terminal supports colors | 
|  | return f"\033[1m{formatted_value}\033[0m" | 
|  | return formatted_value | 
|  |  | 
|  | # Ratios for geomean computation | 
|  | time_ratios = [] | 
|  | mem_ratios = [] | 
|  | # Whether any test exceeds the single test threshold (mem or time) | 
|  | threshold_single = False | 
|  | # Whether geomean exceeds aggregate test threshold (mem or time) | 
|  | threshold_agg = False | 
|  |  | 
|  | if args.verbose: | 
|  | print(f"# Individual test threshold: +-{args.threshold_single}%") | 
|  | print(f"# Aggregate (geomean) test threshold: +-{args.threshold_agg}%") | 
|  | print( | 
|  | f"# Checking time swings for tests with runtime >" | 
|  | f"{args.check_longer_than}s - otherwise marked as ?" | 
|  | ) | 
|  | print("Test/binary BOLT_wall_time BOLT_max_rss") | 
|  |  | 
|  | for input_file in args.input: | 
|  | input_dir = os.path.dirname(input_file) | 
|  | with open(input_file) as timing_file: | 
|  | timing_reader = csv.reader(timing_file, delimiter=";") | 
|  | for row in timing_reader: | 
|  | test_name = row[0] | 
|  | m = TIMING_LOG_RE.match(row[0]) | 
|  | if m: | 
|  | test_name = f"{input_dir}/{m.groups()[1]}/{m.groups()[2]}" | 
|  | else: | 
|  | # Prepend input dir to unparsed test name | 
|  | test_name = input_dir + "#" + test_name | 
|  | time_a, time_b = float(row[1]), float(row[3]) | 
|  | mem_a, mem_b = int(row[2]), int(row[4]) | 
|  | # Check if time is above bound for at least one side | 
|  | time_above_bound = any( | 
|  | [x > args.check_longer_than for x in [time_a, time_b]] | 
|  | ) | 
|  | # Compute B/A ratios (for % delta and geomean) | 
|  | time_ratio = time_b / time_a if time_a else float('nan') | 
|  | mem_ratio = mem_b / mem_a if mem_a else float('nan') | 
|  | # Keep ratios for geomean | 
|  | if time_above_bound and time_ratio > 0:  # must be >0 for gmean | 
|  | time_ratios += [time_ratio] | 
|  | mem_ratios += [mem_ratio] | 
|  | # Deltas: (B/A)-1 = (B-A)/A | 
|  | time_delta = time_ratio - 1 | 
|  | mem_delta = mem_ratio - 1 | 
|  | # Check individual test results vs single test threshold | 
|  | time_exc = ( | 
|  | 100.0 * abs(time_delta) > args.threshold_single and time_above_bound | 
|  | ) | 
|  | mem_exc = 100.0 * abs(mem_delta) > args.threshold_single | 
|  | if time_exc or mem_exc: | 
|  | threshold_single = True | 
|  | # Print deltas with formatting in verbose mode | 
|  | if args.verbose or time_exc or mem_exc: | 
|  | print( | 
|  | test_name, | 
|  | fmt_delta(time_delta, time_exc, time_above_bound), | 
|  | fmt_delta(mem_delta, mem_exc), | 
|  | ) | 
|  |  | 
|  | time_gmean_delta = geometric_mean(time_ratios) - 1 | 
|  | mem_gmean_delta = geometric_mean(mem_ratios) - 1 | 
|  | time_agg_threshold = 100.0 * abs(time_gmean_delta) > args.threshold_agg | 
|  | mem_agg_threshold = 100.0 * abs(mem_gmean_delta) > args.threshold_agg | 
|  | if time_agg_threshold or mem_agg_threshold: | 
|  | threshold_agg = True | 
|  | if time_agg_threshold or mem_agg_threshold or args.verbose: | 
|  | print( | 
|  | "Geomean", | 
|  | fmt_delta(time_gmean_delta, time_agg_threshold), | 
|  | fmt_delta(mem_gmean_delta, mem_agg_threshold), | 
|  | ) | 
|  | exit(threshold_single or threshold_agg) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |