xref: /aosp_15_r20/external/cronet/build/android/diff_resource_sizes.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python3
2# Copyright 2017 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Runs resource_sizes.py on two apks and outputs the diff."""
7
8
9import argparse
10import json
11import logging
12import os
13import subprocess
14import sys
15
16from pylib.constants import host_paths
17
18with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
19  import perf_tests_results_helper # pylint: disable=import-error
20
21with host_paths.SysPath(host_paths.TRACING_PATH):
22  from tracing.value import convert_chart_json # pylint: disable=import-error
23
24_ANDROID_DIR = os.path.dirname(os.path.abspath(__file__))
25with host_paths.SysPath(os.path.join(_ANDROID_DIR, 'gyp')):
26  from util import build_utils  # pylint: disable=import-error
27
28
29_BASE_CHART = {
30    'format_version': '0.1',
31    'benchmark_name': 'resource_sizes_diff',
32    'benchmark_description': 'APK resource size diff information',
33    'trace_rerun_options': [],
34    'charts': {},
35}
36
37_CHARTJSON_FILENAME = 'results-chart.json'
38_HISTOGRAMS_FILENAME = 'perf_results.json'
39
40
41def DiffResults(chartjson, base_results, diff_results):
42  """Reports the diff between the two given results.
43
44  Args:
45    chartjson: A dictionary that chartjson results will be placed in, or None
46        to only print results.
47    base_results: The chartjson-formatted size results of the base APK.
48    diff_results: The chartjson-formatted size results of the diff APK.
49  """
50  for graph_title, graph in base_results['charts'].items():
51    for trace_title, trace in graph.items():
52      perf_tests_results_helper.ReportPerfResult(
53          chartjson, graph_title, trace_title,
54          diff_results['charts'][graph_title][trace_title]['value']
55              - trace['value'],
56          trace['units'], trace['improvement_direction'],
57          trace['important'])
58
59
60def AddIntermediateResults(chartjson, base_results, diff_results):
61  """Copies the intermediate size results into the output chartjson.
62
63  Args:
64    chartjson: A dictionary that chartjson results will be placed in.
65    base_results: The chartjson-formatted size results of the base APK.
66    diff_results: The chartjson-formatted size results of the diff APK.
67  """
68  for graph_title, graph in base_results['charts'].items():
69    for trace_title, trace in graph.items():
70      perf_tests_results_helper.ReportPerfResult(
71          chartjson, graph_title + '_base_apk', trace_title,
72          trace['value'], trace['units'], trace['improvement_direction'],
73          trace['important'])
74
75  # Both base_results and diff_results should have the same charts/traces, but
76  # loop over them separately in case they don't
77  for graph_title, graph in diff_results['charts'].items():
78    for trace_title, trace in graph.items():
79      perf_tests_results_helper.ReportPerfResult(
80          chartjson, graph_title + '_diff_apk', trace_title,
81          trace['value'], trace['units'], trace['improvement_direction'],
82          trace['important'])
83
84
85def _CreateArgparser():
86  def chromium_path(arg):
87    if arg.startswith('//'):
88      return os.path.join(host_paths.DIR_SOURCE_ROOT, arg[2:])
89    return arg
90
91  argparser = argparse.ArgumentParser(
92      description='Diff resource sizes of two APKs. Arguments not listed here '
93                  'will be passed on to both invocations of resource_sizes.py.')
94  argparser.add_argument('--chromium-output-directory-base',
95                         dest='out_dir_base',
96                         type=chromium_path,
97                         help='Location of the build artifacts for the base '
98                              'APK, i.e. what the size increase/decrease will '
99                              'be measured from.')
100  argparser.add_argument('--chromium-output-directory-diff',
101                         dest='out_dir_diff',
102                         type=chromium_path,
103                         help='Location of the build artifacts for the diff '
104                              'APK.')
105  argparser.add_argument('--chartjson',
106                         action='store_true',
107                         help='DEPRECATED. Use --output-format=chartjson '
108                              'instead.')
109  argparser.add_argument('--output-format',
110                         choices=['chartjson', 'histograms'],
111                         help='Output the results to a file in the given '
112                              'format instead of printing the results.')
113  argparser.add_argument('--include-intermediate-results',
114                         action='store_true',
115                         help='Include the results from the resource_sizes.py '
116                              'runs in the chartjson output.')
117  argparser.add_argument('--output-dir',
118                         default='.',
119                         type=chromium_path,
120                         help='Directory to save chartjson to.')
121  argparser.add_argument('--base-apk',
122                         required=True,
123                         type=chromium_path,
124                         help='Path to the base APK, i.e. what the size '
125                              'increase/decrease will be measured from.')
126  argparser.add_argument('--diff-apk',
127                         required=True,
128                         type=chromium_path,
129                         help='Path to the diff APK, i.e. the APK whose size '
130                              'increase/decrease will be measured against the '
131                              'base APK.')
132  return argparser
133
134
135def main():
136  args, unknown_args = _CreateArgparser().parse_known_args()
137  # TODO(bsheedy): Remove this once all uses of --chartjson are removed.
138  if args.chartjson:
139    args.output_format = 'chartjson'
140
141  chartjson = _BASE_CHART.copy() if args.output_format else None
142
143  with build_utils.TempDir() as base_dir, build_utils.TempDir() as diff_dir:
144    # Run resource_sizes.py on the two APKs
145    resource_sizes_path = os.path.join(_ANDROID_DIR, 'resource_sizes.py')
146    shared_args = (['python', resource_sizes_path, '--output-format=chartjson']
147                   + unknown_args)
148
149    base_args = shared_args + ['--output-dir', base_dir, args.base_apk]
150    if args.out_dir_base:
151      base_args += ['--chromium-output-directory', args.out_dir_base]
152    try:
153      subprocess.check_output(base_args, stderr=subprocess.STDOUT)
154    except subprocess.CalledProcessError as e:
155      print(e.output)
156      raise
157
158    diff_args = shared_args + ['--output-dir', diff_dir, args.diff_apk]
159    if args.out_dir_diff:
160      diff_args += ['--chromium-output-directory', args.out_dir_diff]
161    try:
162      subprocess.check_output(diff_args, stderr=subprocess.STDOUT)
163    except subprocess.CalledProcessError as e:
164      print(e.output)
165      raise
166
167    # Combine the separate results
168    with open(os.path.join(base_dir, _CHARTJSON_FILENAME)) as base_file:
169      base_results = json.load(base_file)
170    with open(os.path.join(diff_dir, _CHARTJSON_FILENAME)) as diff_file:
171      diff_results = json.load(diff_file)
172    DiffResults(chartjson, base_results, diff_results)
173    if args.include_intermediate_results:
174      AddIntermediateResults(chartjson, base_results, diff_results)
175
176    if args.output_format:
177      chartjson_path = os.path.join(os.path.abspath(args.output_dir),
178                                    _CHARTJSON_FILENAME)
179      logging.critical('Dumping diff chartjson to %s', chartjson_path)
180      with open(chartjson_path, 'w') as outfile:
181        json.dump(chartjson, outfile)
182
183      if args.output_format == 'histograms':
184        histogram_result = convert_chart_json.ConvertChartJson(chartjson_path)
185        if histogram_result.returncode != 0:
186          logging.error('chartjson conversion failed with error: %s',
187              histogram_result.stdout)
188          return 1
189
190        histogram_path = os.path.join(os.path.abspath(args.output_dir),
191            'perf_results.json')
192        logging.critical('Dumping diff histograms to %s', histogram_path)
193        with open(histogram_path, 'w') as json_file:
194          json_file.write(histogram_result.stdout)
195  return 0
196
197
198if __name__ == '__main__':
199  sys.exit(main())
200