| #!/usr/bin/env python |
| # Copyright 2017 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. |
| |
| """Runs resource_sizes.py on two apks and outputs the diff.""" |
| |
| from __future__ import print_function |
| |
| import argparse |
| import json |
| import logging |
| import os |
| import subprocess |
| import sys |
| |
| from pylib.constants import host_paths |
| from pylib.utils import shared_preference_utils |
| |
| with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): |
| import perf_tests_results_helper # pylint: disable=import-error |
| |
| with host_paths.SysPath(host_paths.TRACING_PATH): |
| from tracing.value import convert_chart_json # pylint: disable=import-error |
| |
| _ANDROID_DIR = os.path.dirname(os.path.abspath(__file__)) |
| with host_paths.SysPath(os.path.join(_ANDROID_DIR, 'gyp')): |
| from util import build_utils # pylint: disable=import-error |
| |
| |
| _BASE_CHART = { |
| 'format_version': '0.1', |
| 'benchmark_name': 'resource_sizes_diff', |
| 'benchmark_description': 'APK resource size diff information', |
| 'trace_rerun_options': [], |
| 'charts': {}, |
| } |
| |
| _CHARTJSON_FILENAME = 'results-chart.json' |
| _HISTOGRAMS_FILENAME = 'perf_results.json' |
| |
| |
| def DiffResults(chartjson, base_results, diff_results): |
| """Reports the diff between the two given results. |
| |
| Args: |
| chartjson: A dictionary that chartjson results will be placed in, or None |
| to only print results. |
| base_results: The chartjson-formatted size results of the base APK. |
| diff_results: The chartjson-formatted size results of the diff APK. |
| """ |
| for graph_title, graph in base_results['charts'].iteritems(): |
| for trace_title, trace in graph.iteritems(): |
| perf_tests_results_helper.ReportPerfResult( |
| chartjson, graph_title, trace_title, |
| diff_results['charts'][graph_title][trace_title]['value'] |
| - trace['value'], |
| trace['units'], trace['improvement_direction'], |
| trace['important']) |
| |
| |
| def AddIntermediateResults(chartjson, base_results, diff_results): |
| """Copies the intermediate size results into the output chartjson. |
| |
| Args: |
| chartjson: A dictionary that chartjson results will be placed in. |
| base_results: The chartjson-formatted size results of the base APK. |
| diff_results: The chartjson-formatted size results of the diff APK. |
| """ |
| for graph_title, graph in base_results['charts'].iteritems(): |
| for trace_title, trace in graph.iteritems(): |
| perf_tests_results_helper.ReportPerfResult( |
| chartjson, graph_title + '_base_apk', trace_title, |
| trace['value'], trace['units'], trace['improvement_direction'], |
| trace['important']) |
| |
| # Both base_results and diff_results should have the same charts/traces, but |
| # loop over them separately in case they don't |
| for graph_title, graph in diff_results['charts'].iteritems(): |
| for trace_title, trace in graph.iteritems(): |
| perf_tests_results_helper.ReportPerfResult( |
| chartjson, graph_title + '_diff_apk', trace_title, |
| trace['value'], trace['units'], trace['improvement_direction'], |
| trace['important']) |
| |
| |
| def _CreateArgparser(): |
| def chromium_path(arg): |
| if arg.startswith('//'): |
| return os.path.join(host_paths.DIR_SOURCE_ROOT, arg[2:]) |
| return arg |
| |
| argparser = argparse.ArgumentParser( |
| description='Diff resource sizes of two APKs. Arguments not listed here ' |
| 'will be passed on to both invocations of resource_sizes.py.') |
| argparser.add_argument('--chromium-output-directory-base', |
| dest='out_dir_base', |
| type=chromium_path, |
| help='Location of the build artifacts for the base ' |
| 'APK, i.e. what the size increase/decrease will ' |
| 'be measured from.') |
| argparser.add_argument('--chromium-output-directory-diff', |
| dest='out_dir_diff', |
| type=chromium_path, |
| help='Location of the build artifacts for the diff ' |
| 'APK.') |
| argparser.add_argument('--chartjson', |
| action='store_true', |
| help='DEPRECATED. Use --output-format=chartjson ' |
| 'instead.') |
| argparser.add_argument('--output-format', |
| choices=['chartjson', 'histograms'], |
| help='Output the results to a file in the given ' |
| 'format instead of printing the results.') |
| argparser.add_argument('--include-intermediate-results', |
| action='store_true', |
| help='Include the results from the resource_sizes.py ' |
| 'runs in the chartjson output.') |
| argparser.add_argument('--output-dir', |
| default='.', |
| type=chromium_path, |
| help='Directory to save chartjson to.') |
| argparser.add_argument('--base-apk', |
| required=True, |
| type=chromium_path, |
| help='Path to the base APK, i.e. what the size ' |
| 'increase/decrease will be measured from.') |
| argparser.add_argument('--diff-apk', |
| required=True, |
| type=chromium_path, |
| help='Path to the diff APK, i.e. the APK whose size ' |
| 'increase/decrease will be measured against the ' |
| 'base APK.') |
| return argparser |
| |
| |
| def main(): |
| args, unknown_args = _CreateArgparser().parse_known_args() |
| # TODO(bsheedy): Remove this once all uses of --chartjson are removed. |
| if args.chartjson: |
| args.output_format = 'chartjson' |
| |
| chartjson = _BASE_CHART.copy() if args.output_format else None |
| |
| with build_utils.TempDir() as base_dir, build_utils.TempDir() as diff_dir: |
| # Run resource_sizes.py on the two APKs |
| resource_sizes_path = os.path.join(_ANDROID_DIR, 'resource_sizes.py') |
| shared_args = (['python', resource_sizes_path, '--output-format=chartjson'] |
| + unknown_args) |
| |
| base_args = shared_args + ['--output-dir', base_dir, args.base_apk] |
| if args.out_dir_base: |
| base_args += ['--chromium-output-directory', args.out_dir_base] |
| try: |
| subprocess.check_output(base_args, stderr=subprocess.STDOUT) |
| except subprocess.CalledProcessError as e: |
| print(e.output) |
| raise |
| |
| diff_args = shared_args + ['--output-dir', diff_dir, args.diff_apk] |
| if args.out_dir_diff: |
| diff_args += ['--chromium-output-directory', args.out_dir_diff] |
| try: |
| subprocess.check_output(diff_args, stderr=subprocess.STDOUT) |
| except subprocess.CalledProcessError as e: |
| print(e.output) |
| raise |
| |
| # Combine the separate results |
| base_file = os.path.join(base_dir, _CHARTJSON_FILENAME) |
| diff_file = os.path.join(diff_dir, _CHARTJSON_FILENAME) |
| base_results = shared_preference_utils.ExtractSettingsFromJson(base_file) |
| diff_results = shared_preference_utils.ExtractSettingsFromJson(diff_file) |
| DiffResults(chartjson, base_results, diff_results) |
| if args.include_intermediate_results: |
| AddIntermediateResults(chartjson, base_results, diff_results) |
| |
| if args.output_format: |
| chartjson_path = os.path.join(os.path.abspath(args.output_dir), |
| _CHARTJSON_FILENAME) |
| logging.critical('Dumping diff chartjson to %s', chartjson_path) |
| with open(chartjson_path, 'w') as outfile: |
| json.dump(chartjson, outfile) |
| |
| if args.output_format == 'histograms': |
| histogram_result = convert_chart_json.ConvertChartJson(chartjson_path) |
| if histogram_result.returncode != 0: |
| logging.error('chartjson conversion failed with error: %s', |
| histogram_result.stdout) |
| return 1 |
| |
| histogram_path = os.path.join(os.path.abspath(args.output_dir), |
| 'perf_results.json') |
| logging.critical('Dumping diff histograms to %s', histogram_path) |
| with open(histogram_path, 'w') as json_file: |
| json_file.write(histogram_result.stdout) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |