poky/scripts/buildstats-summary
Ross Burton 9dd8d5d6d2 buildstats-summary: look for buildstats if not specified
If the user hasn't specified a buildstats directory, use the latest
entry under $BUILDDIR.

(From OE-Core rev: aeb69fbe130dca37b39d4065ec983441e0052803)

Signed-off-by: Ross Burton <ross.burton@arm.com>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2024-10-25 15:25:32 +01:00

141 lines
3.5 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Dump a summary of the specified buildstats to the terminal, filtering and
# sorting by walltime.
#
# SPDX-License-Identifier: GPL-2.0-only
import argparse
import dataclasses
import datetime
import enum
import os
import pathlib
import sys
scripts_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(scripts_path, "lib"))
import buildstats
@dataclasses.dataclass
class Task:
recipe: str
task: str
start: datetime.datetime
duration: datetime.timedelta
class Sorting(enum.Enum):
start = 1
duration = 2
# argparse integration
def __str__(self) -> str:
return self.name
def __repr__(self) -> str:
return self.name
@staticmethod
def from_string(s: str):
try:
return Sorting[s]
except KeyError:
return s
def read_buildstats(path: pathlib.Path) -> buildstats.BuildStats:
if not path.exists():
raise Exception(f"No such file or directory: {path}")
if path.is_file():
return buildstats.BuildStats.from_file_json(path)
if (path / "build_stats").is_file():
return buildstats.BuildStats.from_dir(path)
raise Exception(f"Cannot find buildstats in {path}")
def dump_buildstats(args, bs: buildstats.BuildStats):
tasks = []
for recipe in bs.values():
for task, stats in recipe.tasks.items():
t = Task(
recipe.name,
task,
datetime.datetime.fromtimestamp(stats["start_time"]),
datetime.timedelta(seconds=int(stats.walltime)),
)
tasks.append(t)
tasks.sort(key=lambda t: getattr(t, args.sort.name))
minimum = datetime.timedelta(seconds=args.shortest)
highlight = datetime.timedelta(seconds=args.highlight)
for t in tasks:
if t.duration >= minimum:
line = f"{t.duration} {t.recipe}:{t.task}"
if args.highlight and t.duration >= highlight:
print(f"\033[1m{line}\033[0m")
else:
print(line)
def main(argv=None) -> int:
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument(
"buildstats",
metavar="BUILDSTATS",
nargs="?",
type=pathlib.Path,
help="Buildstats file, or latest if not specified",
)
parser.add_argument(
"--sort",
"-s",
type=Sorting.from_string,
choices=list(Sorting),
default=Sorting.start,
help="Sort tasks",
)
parser.add_argument(
"--shortest",
"-t",
type=int,
default=1,
metavar="SECS",
help="Hide tasks shorter than SECS seconds",
)
parser.add_argument(
"--highlight",
"-g",
type=int,
default=60,
metavar="SECS",
help="Highlight tasks longer than SECS seconds (0 disabled)",
)
args = parser.parse_args(argv)
# If a buildstats file wasn't specified, try to find the last one
if not args.buildstats:
try:
builddir = pathlib.Path(os.environ["BUILDDIR"])
buildstats_dir = builddir / "tmp" / "buildstats"
args.buildstats = sorted(buildstats_dir.iterdir())[-1]
except KeyError:
print("Build environment has not been configured, cannot find buildstats")
return 1
bs = read_buildstats(args.buildstats)
dump_buildstats(args, bs)
return 0
if __name__ == "__main__":
sys.exit(main())