blob: 5ca31c3c3e9dca162a8150caef866f201fbbe0e8 [file] [log] [blame]
# Copyright 2021 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from recipe_engine import recipe_api
class BinarySizeApi(recipe_api.RecipeApi):
"""APIs for checking and diffing binary sizes."""
# This exit code indicates that the CI build was not successful and thus the
# the size diff could not be computed.
# Keep in sync with
# https://fuchsia.googlesource.com/infra/infra/+/main/cmd/size_diff/ci.go.
CI_BUILD_NOT_SUCCESSFUL_EXIT_CODE = 2
def check_budgets(self, step_name, binary_sizes_json_input):
"""Check if the input binary sizes object exceeds one or more budgets.
Args:
step_name (str): Name of the step.
binary_sizes_json_input (Path): Path for input binary sizes object
as JSON.
Raises:
StepFailure: one or more budgets were exceeded.
"""
args = [
"budgets",
"-binary-sizes-json-input",
binary_sizes_json_input,
]
step = self._run_size_check(
step_name, args, stderr=self.m.raw_io.output_text(), ok_ret="any"
)
# test_ids should use underscore instead of spaces
prepared_step = self.m.reported_step.prepare_step(
test_id=step_name.replace(" ", "_"), step=step
)
try:
if step.retcode:
step.presentation.status = self.m.step.FAILURE
step.presentation.logs["stderr"] = step.stderr
prepared_step.add_artifact("stderr", step.stderr)
raise self.m.step.StepFailure(
"Binary size checks failed: %s\n\nFor next steps, please refer:"
"http://go/tq-resilience-home/size-stats/size-check-failure-steps\n"
"Contact http://go/fuchsia-size-rotation if help is needed to "
"adjust the size budgets." % step.stderr,
)
finally:
prepared_step.upload()
def diff_ci(
self,
step_name,
gitiles_remote,
base_commit,
ci_builder,
binary_sizes_json_input,
):
"""Compute diff of the input binary sizes object against a binary sizes
object from CI.
Args:
step_name (str): Name of the step.
gitiles_remote (str): Gitiles remote for base commit.
base_commit (str): Base commit as sha1.
ci_builder (builder_pb2.BuilderID): CI builder to inspect.
binary_sizes_json_input (Path): Path for input binary sizes object
as JSON.
Returns:
StepData: Step data of the size diff tool.
dict: Binary size diff with keys:
component_diffs (seq(dict)): Per-component diffs, each with keys:
name (str): Name of the component.
baseline_size (int): Baseline size of the component in bytes.
size (int): Size of the component in bytes.
size_diff (int): Size diff in bytes.
budget (int): Budget of the component in bytes.
creep_budget (int): Creep budget of the component in bytes.
budget_exceeded (bool): Whether the budget is exceeded.
creep_budget_exceeded (bool): Whether the creep budget is
exceeded.
budget_exceeded (bool): Whether one or more budgets are exceeded.
creep_budget_exceeded (bool): Whether one or more creep budgets
are exceeded.
baseline_build_id (int): The baseline build ID that was used.
"""
args = [
"ci",
"-gitiles-remote",
gitiles_remote,
"-base-commit",
base_commit,
"-builder",
self.m.buildbucket_util.full_builder_name(ci_builder),
"-binary-sizes-json-input",
binary_sizes_json_input,
"-json-output",
self.m.json.output(),
]
step = self._run_size_diff(
step_name, args, ok_ret=(0, self.CI_BUILD_NOT_SUCCESSFUL_EXIT_CODE)
)
# If the size diff could not be computed, keep track of this as a
# property, but do not raise an error. Computing the size diff is
# best-effort as of now.
step.presentation.properties["size_ci_build_not_successful"] = step.retcode == 2
if step.retcode:
return (step, None)
diff = step.json.output
step.presentation.properties["size_creep_budget_exceeded"] = diff[
"creep_budget_exceeded"
]
step.presentation.links["ci_build"] = self.m.buildbucket.build_url(
build_id=diff["baseline_build_id"]
)
return (step, diff)
@property
def _size_check_tool(self):
return self.m.ensure_tool(
"size_check", self.resource("size_check/tool_manifest.json")
)
def _run_size_check(self, step_name, args, **kwargs):
return self.m.step(step_name, [self._size_check_tool] + args, **kwargs)
@property
def _size_diff_tool(self):
return self.m.ensure_tool(
"size_diff", self.resource("size_diff/tool_manifest.json")
)
def _run_size_diff(self, step_name, args, **kwargs):
return self.m.step(step_name, [self._size_diff_tool] + args, **kwargs)