• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16import csv
17import datetime
18import logging
19import re
20import statistics
21import subprocess
22import textwrap
23from pathlib import Path
24from typing import Callable
25
26from typing.io import TextIO
27
28import util
29
30
31def normalize_rebuild(line: dict) -> dict:
32  line['description'] = re.sub(r'^(rebuild)-[\d+](.*)$', '\\1\\2',
33                               line['description'])
34  return line
35
36
37def groupby(xs: list[dict], keyfn: Callable[[dict], str]) -> dict[
38  str, list[dict]]:
39  grouped = {}
40  for x in xs:
41    k = keyfn(x)
42    grouped.setdefault(k, []).append(x)
43  return grouped
44
45
46def write_table(out: TextIO, rows: list[list[str]]):
47  for r in rows:
48    for c in r:
49      out.write(str(c) + ',')
50    out.write('\n')
51  return
52
53
54def _get_build_types(xs: list[dict]) -> list[str]:
55  build_types = []
56  for x in xs:
57    b = x["build_type"]
58    if b not in build_types:
59      build_types.append(b)
60  return build_types
61
62
63def summarize_metrics(log_dir: Path):
64  filename = log_dir if log_dir.is_file() else log_dir.joinpath(
65      util.METRICS_TABLE)
66  with open(filename) as f:
67    csv_lines = [normalize_rebuild(line) for line in csv.DictReader(f)]
68
69  lines: list[dict] = []
70  for line in csv_lines:
71    if line["build_result"] == "FAILED":
72      logging.warning(f"{line['description']} / {line['build_type']}")
73    else:
74      lines.append(line)
75
76  build_types = _get_build_types(lines)
77  headers = ["cuj", "targets"] + build_types
78  rows: list[list[str]] = [headers]
79
80  by_cuj = groupby(lines, lambda l: l["description"])
81  for (cuj, cuj_rows) in by_cuj.items():
82    for (targets, target_rows) in groupby(cuj_rows,
83                                          lambda l: l["targets"]).items():
84      row = [cuj, targets]
85      by_build_type = groupby(target_rows, lambda l: l["build_type"])
86      for build_type in build_types:
87        selected_lines = by_build_type.get(build_type)
88        if not selected_lines:
89          row.append('')
90        else:
91          times = [util.period_to_seconds(sl['time']) for sl in selected_lines]
92          cell = util.hhmmss(
93              datetime.timedelta(seconds=statistics.median(times)))
94          if len(selected_lines) > 1:
95            cell = f'{cell}[N={len(selected_lines)}]'
96          row.append(cell)
97      rows.append(row)
98
99  with open(log_dir.joinpath(util.SUMMARY_TABLE), mode='wt') as f:
100    write_table(f, rows)
101
102
103def display_summarized_metrics(log_dir: Path):
104  f = log_dir.joinpath(util.SUMMARY_TABLE)
105  cmd = f'grep -v rebuild {f} | grep -v WARMUP | column -t -s,'
106  output = subprocess.check_output(cmd, shell=True, text=True)
107  logging.info(textwrap.dedent(f'''
108  %s
109  TIPS:
110  To view condensed summary:
111  %s
112  '''), output, cmd)
113