blob: bc2479eab6998faacd4d541e1aaa61c75cb8f8df [file] [log] [blame]
Luca Farsi2b3b0932024-11-06 14:41:15 -08001# 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())