xref: /aosp_15_r20/build/make/ci/metrics_agent.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1# Copyright 2024, The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""MetricsAgent is a singleton class that collects metrics for optimized build."""
16
17from enum import Enum
18import time
19import metrics_pb2
20import os
21import logging
22
23
24class MetricsAgent:
25  _SOONG_METRICS_PATH = 'logs/soong_metrics'
26  _DIST_DIR = 'DIST_DIR'
27  _instance = None
28
29  def __init__(self):
30    raise RuntimeError(
31        'MetricsAgent cannot be instantialized, use instance() instead'
32    )
33
34  @classmethod
35  def instance(cls):
36    if not cls._instance:
37      cls._instance = cls.__new__(cls)
38      cls._instance._proto = metrics_pb2.OptimizedBuildMetrics()
39      cls._instance._init_proto()
40      cls._instance._target_results = dict()
41
42    return cls._instance
43
44  def _init_proto(self):
45    self._proto.analysis_perf.name = 'Optimized build analysis time.'
46    self._proto.packaging_perf.name = 'Optimized build total packaging time.'
47
48  def analysis_start(self):
49    self._proto.analysis_perf.start_time = time.time_ns()
50
51  def analysis_end(self):
52    self._proto.analysis_perf.real_time = (
53        time.time_ns() - self._proto.analysis_perf.start_time
54    )
55
56  def packaging_start(self):
57    self._proto.packaging_perf.start_time = time.time_ns()
58
59  def packaging_end(self):
60    self._proto.packaging_perf.real_time = (
61        time.time_ns() - self._proto.packaging_perf.start_time
62    )
63
64  def report_optimized_target(self, name: str):
65    target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult()
66    target_result.name = name
67    target_result.optimized = True
68    self._target_results[name] = target_result
69
70  def report_unoptimized_target(self, name: str, optimization_rationale: str):
71    target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult()
72    target_result.name = name
73    target_result.optimization_rationale = optimization_rationale
74    target_result.optimized = False
75    self._target_results[name] = target_result
76
77  def target_packaging_start(self, name: str):
78    target_result = self._target_results.get(name)
79    target_result.packaging_perf.start_time = time.time_ns()
80    self._target_results[name] = target_result
81
82  def target_packaging_end(self, name: str):
83    target_result = self._target_results.get(name)
84    target_result.packaging_perf.real_time = (
85        time.time_ns() - target_result.packaging_perf.start_time
86    )
87
88  def add_target_artifact(
89      self,
90      target_name: str,
91      artifact_name: str,
92      size: int,
93      included_modules: set[str],
94  ):
95    target_result = self.target_results.get(target_name)
96    artifact = (
97        metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact()
98    )
99    artifact.name = artifact_name
100    artifact.size = size
101    for module in included_modules:
102      artifact.included_modules.add(module)
103    target_result.output_artifacts.add(artifact)
104
105  def end_reporting(self):
106    for target_result in self._target_results.values():
107      self._proto.target_result.append(target_result)
108    soong_metrics_proto = metrics_pb2.MetricsBase()
109    # Read in existing metrics that should have been written out by the soong
110    # build command so that we don't overwrite them.
111    with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'rb') as f:
112      soong_metrics_proto.ParseFromString(f.read())
113    soong_metrics_proto.optimized_build_metrics.CopyFrom(self._proto)
114    logging.info(soong_metrics_proto)
115    with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'wb') as f:
116      f.write(soong_metrics_proto.SerializeToString())
117