Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b3b093e authored by Luca Farsi's avatar Luca Farsi
Browse files

Implement metrics agent for built_test_suites

Implement the metrics agent for build_test_suites. This will report
metrics for optimization decisions and build/analysis times for the
build_test_suites script.

Test: atest build_test_suites_test
Bug: 372973116
Change-Id: I6b6b726b93de6ab3fbb49a95ebadd5338859fcaf
parent 03b57b0f
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -103,13 +103,10 @@ python_binary_host {
        "test_mapping_module_retriever.py",
        "build_context.py",
        "test_discovery_agent.py",
        "metrics_agent.py",
        "buildbot.py",
    ],
    main: "build_test_suites.py",
    version: {
        py3: {
            embedded_launcher: true,
        },
    },
    libs: [
        "soong-metrics-proto-py",
    ],
@@ -123,6 +120,8 @@ python_library_host {
        "test_mapping_module_retriever.py",
        "build_context.py",
        "test_discovery_agent.py",
        "metrics_agent.py",
        "buildbot.py",
    ],
}

ci/metrics_agent.py

0 → 100644
+116 −0
Original line number Diff line number Diff line
# Copyright 2024, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""MetricsAgent is a singleton class that collects metrics for optimized build."""

from enum import Enum
import time
import metrics_pb2
import os
import logging


class MetricsAgent:
  _SOONG_METRICS_PATH = 'logs/soong_metrics'
  _DIST_DIR = 'DIST_DIR'
  _instance = None

  def __init__(self):
    raise RuntimeError(
        'MetricsAgent cannot be instantialized, use instance() instead'
    )

  @classmethod
  def instance(cls):
    if not cls._instance:
      cls._instance = cls.__new__(cls)
      cls._instance._proto = metrics_pb2.OptimizedBuildMetrics()
      cls._instance._init_proto()
      cls._instance._target_results = dict()

    return cls._instance

  def _init_proto(self):
    self._proto.analysis_perf.name = 'Optimized build analysis time.'
    self._proto.packaging_perf.name = 'Optimized build total packaging time.'

  def analysis_start(self):
    self._proto.analysis_perf.start_time = time.time_ns()

  def analysis_end(self):
    self._proto.analysis_perf.real_time = (
        time.time_ns() - self._proto.analysis_perf.start_time
    )

  def packaging_start(self):
    self._proto.packaging_perf.start_time = time.time_ns()

  def packaging_end(self):
    self._proto.packaging_perf.real_time = (
        time.time_ns() - self._proto.packaging_perf.start_time
    )

  def report_optimized_target(self, name: str):
    target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult()
    target_result.name = name
    target_result.optimized = True
    self._target_results[name] = target_result

  def report_unoptimized_target(self, name: str, optimization_rationale: str):
    target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult()
    target_result.name = name
    target_result.optimization_rationale = optimization_rationale
    target_result.optimized = False
    self._target_results[name] = target_result

  def target_packaging_start(self, name: str):
    target_result = self._target_results.get(name)
    target_result.packaging_perf.start_time = time.time_ns()
    self._target_results[name] = target_result

  def target_packaging_end(self, name: str):
    target_result = self._target_results.get(name)
    target_result.packaging_perf.real_time = (
        time.time_ns() - target_result.packaging_perf.start_time
    )

  def add_target_artifact(
      self,
      target_name: str,
      artifact_name: str,
      size: int,
      included_modules: set[str],
  ):
    target_result = self.target_results.get(target_name)
    artifact = (
        metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact()
    )
    artifact.name = artifact_name
    artifact.size = size
    for module in included_modules:
      artifact.included_modules.add(module)
    target_result.output_artifacts.add(artifact)

  def end_reporting(self):
    for target_result in self._target_results.values():
      self._proto.target_result.append(target_result)
    soong_metrics_proto = metrics_pb2.MetricsBase()
    # Read in existing metrics that should have been written out by the soong
    # build command so that we don't overwrite them.
    with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'rb') as f:
      soong_metrics_proto.ParseFromString(f.read())
    soong_metrics_proto.optimized_build_metrics.CopyFrom(self._proto)
    logging.info(soong_metrics_proto)
    with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'wb') as f:
      f.write(soong_metrics_proto.SerializeToString())