Commit e65eb3f1 authored by Samuel Huang's avatar Samuel Huang Committed by Commit Bot

[Lacros] Add bottom-most part of pipeline to monitor Lacros binary / resource sizes.

This CL adds the script and the target needed to extract Lacros binary /
resource size histogram JSON files, which are needed for size monitoring
on Chrome Performance Dashboard.

Following the methodology of how Android Chrome creates resource size
histograms (in //build/android), the key parts are:
* Target "resource_sizes_lacros_chrome" to create wrapper script in
  $OUT_DIR/bin for the main script. This will be called by build bots
  (hook to be added in follow-up).
* lacros_resource_sizes.py to read Lacros build artifact sizes, and to
  export the result to histogram JSON files.

lacros_resource_sizes.py is specialized for Lacros to simplify code. The
script accounts for each file in the ZIP file created by the
chromeos-amd64-generic-lacros-rel builder, but files can be aggregated
by a list and/or by directories. Changes in ZIP file content will require
updates to lacros_resource_sizes.py for proper size accounting.

Bug: 1106626
Change-Id: I9e42eeb2d54105b47fe37730ba098256bd67de54
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2346910Reviewed-by: default avatarSamuel Huang <huangs@chromium.org>
Reviewed-by: default avatarDirk Pranke <dpranke@google.com>
Reviewed-by: default avatarErik Chen <erikchen@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Commit-Queue: Samuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798187}
parent 684b09ba
...@@ -1397,6 +1397,7 @@ _GENERIC_PYDEPS_FILES = [ ...@@ -1397,6 +1397,7 @@ _GENERIC_PYDEPS_FILES = [
'build/android/gyp/zip.pydeps', 'build/android/gyp/zip.pydeps',
'build/android/incremental_install/generate_android_manifest.pydeps', 'build/android/incremental_install/generate_android_manifest.pydeps',
'build/android/incremental_install/write_installer_json.pydeps', 'build/android/incremental_install/write_installer_json.pydeps',
'build/lacros/lacros_resource_sizes.pydeps',
'build/protoc_java.pydeps', 'build/protoc_java.pydeps',
'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps', 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
'chrome/test/chromedriver/test/run_py_tests.pydeps', 'chrome/test/chromedriver/test/run_py_tests.pydeps',
......
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/python.gni")
python_library("lacros_resource_sizes_py") {
pydeps_file = "lacros_resource_sizes.pydeps"
data_deps = [ "//third_party/catapult/tracing:convert_chart_json" ]
}
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/util/generate_wrapper.gni")
# Generates a script in the bin directory that runs
# //build/lacros/lacros_resource_sizes.py for the provided configuration.
template("lacros_resource_sizes_test") {
generate_wrapper(target_name) {
forward_variables_from(invoker, [ "data_deps" ])
executable = "//build/lacros/lacros_resource_sizes.py"
wrapper_script = "$root_out_dir/bin/${target_name}"
deps = [ "//build/lacros:lacros_resource_sizes_py" ]
executable_args = [
"--chromium-output-directory",
"@WrappedPath(.)",
]
}
}
#!/usr/bin/env python
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Reports binary size metrics for LaCrOS build artifacts.
More information at //docs/speed/binary_size/metrics.md.
"""
import argparse
import collections
import contextlib
import json
import logging
import os
import sys
@contextlib.contextmanager
def _SysPath(path):
"""Library import context that temporarily appends |path| to |sys.path|."""
if path and path not in sys.path:
sys.path.insert(0, path)
else:
path = None # Indicates that |sys.path| is not modified.
try:
yield
finally:
if path:
sys.path.pop(0)
DIR_SOURCE_ROOT = os.environ.get(
'CHECKOUT_SOURCE_ROOT',
os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)))
BUILD_COMMON_PATH = os.path.join(DIR_SOURCE_ROOT, 'build', 'util', 'lib',
'common')
TRACING_PATH = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'catapult',
'tracing')
with _SysPath(BUILD_COMMON_PATH):
import perf_tests_results_helper # pylint: disable=import-error
with _SysPath(TRACING_PATH):
from tracing.value import convert_chart_json # pylint: disable=import-error
_BASE_CHART = {
'format_version': '0.1',
'benchmark_name': 'resource_sizes',
'benchmark_description': 'LaCrOS resource size information.',
'trace_rerun_options': [],
'charts': {}
}
_Item = collections.namedtuple('_Item', ['paths', 'title'])
# This list should be synched with chromeos-amd64-generic-lacros-rel builder
# contents, specified in
# //infra/config/subprojects/chromium/master-only/ci.star
_TRACKED_ITEMS = [
_Item(paths=['chrome'], title='File: chrome'),
_Item(paths=['crashpad_handler'], title='File: crashpad_handler'),
_Item(paths=['icudtl.dat'], title='File: icudtl.dat'),
_Item(paths=['nacl_helper'], title='File: nacl_helper'),
_Item(paths=['nacl_irt_x86_64.nexe'], title='File: nacl_irt_x86_64.nexe'),
_Item(paths=['resources.pak'], title='File: resources.pak'),
_Item(paths=[
'chrome_100_percent.pak', 'chrome_200_percent.pak', 'headless_lib.pak'
],
title='Group: Other PAKs'),
_Item(paths=['snapshot_blob.bin'], title='Group: Misc'),
_Item(paths=['locales/'], title='Dir: locales'),
_Item(paths=['swiftshader/'], title='Dir: swiftshader'),
]
def _get_single_filesize(filename):
"""Returns the size of a file, or 0 if file is not found."""
try:
return os.path.getsize(filename)
except OSError:
logging.critical('Failed to get size: %s', filename)
return 0
def _get_total_pathsize(base_dir, paths):
"""Computes total file sizes given by a list of paths, best-effort.
Args:
base_dir: Base directory for all elements in |paths|.
paths: A list of filenames or directory names to specify files whose sizes
to be counted. Directories are recursed. There's no de-duping effort.
Non-existing files or directories are ignored (with warning message).
"""
total_size = 0
for path in paths:
full_path = os.path.join(base_dir, path)
if os.path.exists(full_path):
if os.path.isdir(full_path):
for dirpath, _, filenames in os.walk(full_path):
for filename in filenames:
total_size += _get_single_filesize(os.path.join(dirpath, filename))
else: # Assume is file.
total_size += _get_single_filesize(full_path)
else:
logging.critical('Not found: %s', path)
return total_size
def _dump_chart_json(output_dir, chartjson):
"""Writes chart histogram to JSON files.
Output files:
results-chart.json contains the chart JSON.
perf_results.json contains histogram JSON for Catapult.
Args:
output_dir: Directory to place the JSON files.
chartjson: Source JSON data for output files.
"""
results_path = os.path.join(output_dir, 'results-chart.json')
logging.critical('Dumping chartjson to %s', results_path)
with open(results_path, 'w') as json_file:
json.dump(chartjson, json_file, indent=2)
# We would ideally generate a histogram set directly instead of generating
# chartjson then converting. However, perf_tests_results_helper is in
# //build, which doesn't seem to have any precedent for depending on
# anything in Catapult. This can probably be fixed, but since this doesn't
# need to be super fast or anything, converting is a good enough solution
# for the time being.
histogram_result = convert_chart_json.ConvertChartJson(results_path)
if histogram_result.returncode != 0:
raise Exception('chartjson conversion failed with error: ' +
histogram_result.stdout)
histogram_path = os.path.join(output_dir, 'perf_results.json')
logging.critical('Dumping histograms to %s', histogram_path)
with open(histogram_path, 'w') as json_file:
json_file.write(histogram_result.stdout)
def _run_resource_sizes(args):
"""Main flow to extract and output size data."""
chartjson = _BASE_CHART.copy()
for item in _TRACKED_ITEMS:
total_size = _get_total_pathsize(args.out_dir, item.paths)
perf_tests_results_helper.ReportPerfResult(chart_data=chartjson,
graph_title=item.title,
trace_title='size',
value=total_size,
units='bytes')
_dump_chart_json(args.output_dir, chartjson)
def main():
"""Parses arguments and runs high level flows."""
argparser = argparse.ArgumentParser(description='Writes LaCrOS size metrics.')
argparser.add_argument('--chromium-output-directory',
dest='out_dir',
required=True,
type=os.path.realpath,
help='Location of the build artifacts.')
output_group = argparser.add_mutually_exclusive_group()
output_group.add_argument('--output-dir',
default='.',
help='Directory to save chartjson to.')
output_group.add_argument(
'--isolated-script-test-output',
type=os.path.realpath,
help='File to which results will be written in the simplified JSON '
'output format.')
args = argparser.parse_args()
isolated_script_output = {'valid': False, 'failures': []}
if args.isolated_script_test_output:
test_name = 'lacros_resource_sizes'
args.output_dir = os.path.join(
os.path.dirname(args.isolated_script_test_output), test_name)
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
try:
_run_resource_sizes(args)
isolated_script_output = {'valid': True, 'failures': []}
finally:
if args.isolated_script_test_output:
results_path = os.path.join(args.output_dir, 'test_results.json')
with open(results_path, 'w') as output_file:
json.dump(isolated_script_output, output_file)
with open(args.isolated_script_test_output, 'w') as output_file:
json.dump(isolated_script_output, output_file)
if __name__ == '__main__':
main()
# Generated by running:
# build/print_python_deps.py --root build/lacros --output build/lacros/lacros_resource_sizes.pydeps build/lacros/lacros_resource_sizes.py
../../third_party/catapult/third_party/vinn/vinn/__init__.py
../../third_party/catapult/third_party/vinn/vinn/_vinn.py
../../third_party/catapult/tracing/tracing/__init__.py
../../third_party/catapult/tracing/tracing/value/__init__.py
../../third_party/catapult/tracing/tracing/value/convert_chart_json.py
../../third_party/catapult/tracing/tracing_project.py
../util/lib/common/perf_result_data_type.py
../util/lib/common/perf_tests_results_helper.py
lacros_resource_sizes.py
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import("//build/config/chromeos/ui_mode.gni") import("//build/config/chromeos/ui_mode.gni")
import("//build/config/linux/gtk/gtk.gni") import("//build/config/linux/gtk/gtk.gni")
import("//build/lacros/lacros_resource_sizes.gni")
# Code lives in the lacros-chrome browser only, not ash-chrome. # Code lives in the lacros-chrome browser only, not ash-chrome.
assert(chromeos_is_browser_only) assert(chromeos_is_browser_only)
...@@ -28,3 +29,7 @@ component("lacros") { ...@@ -28,3 +29,7 @@ component("lacros") {
"lacros_chrome_service_impl.h", "lacros_chrome_service_impl.h",
] ]
} }
lacros_resource_sizes_test("resource_sizes_lacros_chrome") {
data_deps = [ "//chrome:chrome" ]
}
...@@ -87,3 +87,8 @@ For Googlers, more information available at [go/chrome-apk-size](https://goto.go ...@@ -87,3 +87,8 @@ For Googlers, more information available at [go/chrome-apk-size](https://goto.go
* [Telemetry Graph](https://chromeperf.appspot.com/report?sid=33f59871f4e9fa3d155be3c13a068d35e6e621bcc98d9b7b103e0c8485e21097) * [Telemetry Graph](https://chromeperf.appspot.com/report?sid=33f59871f4e9fa3d155be3c13a068d35e6e621bcc98d9b7b103e0c8485e21097)
* Uncompressed size of classes.dex, locale .pak files, etc * Uncompressed size of classes.dex, locale .pak files, etc
* Reported only for things that are compressed within the .apk * Reported only for things that are compressed within the .apk
## Metrics for LaCrOS
* Sizes are collected by
[//build/lacros/lacros_resource_sizes.py](https://cs.chromium.org/chromium/src/build/lacros/lacros_resource_sizes.py).
...@@ -1422,6 +1422,8 @@ ci.fyi_builder( ...@@ -1422,6 +1422,8 @@ ci.fyi_builder(
# The format of these properties is defined at archive/properties.proto # The format of these properties is defined at archive/properties.proto
"$build/archive": { "$build/archive": {
"archive_datas": [ "archive_datas": [
# The list of files and dirs should be synched with
# _TRACKED_ITEMS in //build/lacros/lacros_resource_sizes.py.
{ {
"files": [ "files": [
"chrome", "chrome",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment