diff_resource_sizes.py 8.1 KB

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