1#!/usr/bin/env python3 2# 3# Dump a summary of the specified buildstats to the terminal, filtering and 4# sorting by walltime. 5# 6# SPDX-License-Identifier: GPL-2.0-only 7 8import argparse 9import dataclasses 10import datetime 11import enum 12import os 13import pathlib 14import sys 15 16scripts_path = os.path.dirname(os.path.realpath(__file__)) 17sys.path.append(os.path.join(scripts_path, "lib")) 18import buildstats 19 20 21@dataclasses.dataclass 22class Task: 23 recipe: str 24 task: str 25 start: datetime.datetime 26 duration: datetime.timedelta 27 28 29class Sorting(enum.Enum): 30 start = 1 31 duration = 2 32 33 # argparse integration 34 def __str__(self) -> str: 35 return self.name 36 37 def __repr__(self) -> str: 38 return self.name 39 40 @staticmethod 41 def from_string(s: str): 42 try: 43 return Sorting[s] 44 except KeyError: 45 return s 46 47 48def read_buildstats(path: pathlib.Path) -> buildstats.BuildStats: 49 if not path.exists(): 50 raise Exception(f"No such file or directory: {path}") 51 if path.is_file(): 52 return buildstats.BuildStats.from_file_json(path) 53 if (path / "build_stats").is_file(): 54 return buildstats.BuildStats.from_dir(path) 55 raise Exception(f"Cannot find buildstats in {path}") 56 57 58def dump_buildstats(args, bs: buildstats.BuildStats): 59 tasks = [] 60 for recipe in bs.values(): 61 for task, stats in recipe.tasks.items(): 62 t = Task( 63 recipe.name, 64 task, 65 datetime.datetime.fromtimestamp(stats["start_time"]), 66 datetime.timedelta(seconds=int(stats.walltime)), 67 ) 68 tasks.append(t) 69 70 tasks.sort(key=lambda t: getattr(t, args.sort.name)) 71 72 minimum = datetime.timedelta(seconds=args.shortest) 73 highlight = datetime.timedelta(seconds=args.highlight) 74 75 for t in tasks: 76 if t.duration >= minimum: 77 line = f"{t.duration} {t.recipe}:{t.task}" 78 if args.highlight and t.duration >= highlight: 79 print(f"\033[1m{line}\033[0m") 80 else: 81 print(line) 82 83 84def main(argv=None) -> int: 85 parser = argparse.ArgumentParser( 86 formatter_class=argparse.ArgumentDefaultsHelpFormatter 87 ) 88 89 parser.add_argument( 90 "buildstats", metavar="BUILDSTATS", help="Buildstats file", type=pathlib.Path 91 ) 92 parser.add_argument( 93 "--sort", 94 "-s", 95 type=Sorting.from_string, 96 choices=list(Sorting), 97 default=Sorting.start, 98 help="Sort tasks", 99 ) 100 parser.add_argument( 101 "--shortest", 102 "-t", 103 type=int, 104 default=1, 105 metavar="SECS", 106 help="Hide tasks shorter than SECS seconds", 107 ) 108 parser.add_argument( 109 "--highlight", 110 "-g", 111 type=int, 112 default=60, 113 metavar="SECS", 114 help="Highlight tasks longer than SECS seconds (0 disabled)", 115 ) 116 117 args = parser.parse_args(argv) 118 119 bs = read_buildstats(args.buildstats) 120 dump_buildstats(args, bs) 121 122 return 0 123 124 125if __name__ == "__main__": 126 sys.exit(main()) 127