milestone_apk_sizes.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #!/usr/bin/env python3
  2. # Copyright 2019 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. """Prints the large commits given a .csv file from a telemetry size graph."""
  6. # Our version of pylint doesn't know about python3 yet.
  7. # pylint: disable=unexpected-keyword-arg
  8. import argparse
  9. import collections
  10. import csv
  11. import json
  12. import os
  13. import posixpath
  14. import logging
  15. import multiprocessing.dummy
  16. import subprocess
  17. import sys
  18. import tempfile
  19. import zipfile
  20. _DIR_SOURCE_ROOT = os.path.normpath(
  21. os.path.join(os.path.dirname(__file__), '../..'))
  22. sys.path.insert(1, os.path.join(_DIR_SOURCE_ROOT, 'build/android/pylib'))
  23. from utils import app_bundle_utils
  24. _GSUTIL = os.path.join(_DIR_SOURCE_ROOT, 'third_party/depot_tools/gsutil.py')
  25. _RESOURCE_SIZES = os.path.join(_DIR_SOURCE_ROOT,
  26. 'build/android/resource_sizes.py')
  27. _AAPT2 = os.path.join(_DIR_SOURCE_ROOT,
  28. 'third_party/android_build_tools/aapt2/aapt2')
  29. _KEYSTORE = os.path.join(_DIR_SOURCE_ROOT,
  30. 'build/android/chromium-debug.keystore')
  31. _KEYSTORE_PASSWORD = 'chromium'
  32. _KEYSTORE_ALIAS = 'chromiumdebugkey'
  33. class _Artifact:
  34. def __init__(self, prefix, name, staging_dir):
  35. self.name = name
  36. self._gs_url = posixpath.join(prefix, name)
  37. self._path = os.path.join(staging_dir, name)
  38. self._resource_sizes_json = None
  39. os.makedirs(os.path.dirname(self._path), exist_ok=True)
  40. def FetchAndMeasure(self):
  41. args = [_GSUTIL, 'cp', self._gs_url, self._path]
  42. logging.warning(' '.join(args))
  43. if not os.path.exists(self._path):
  44. subprocess.check_call(args)
  45. path_to_measure = self._path
  46. if self.name.endswith('.aab'):
  47. path_to_measure += '.apks'
  48. app_bundle_utils.GenerateBundleApks(self._path,
  49. path_to_measure,
  50. _AAPT2,
  51. _KEYSTORE,
  52. _KEYSTORE_PASSWORD,
  53. _KEYSTORE_ALIAS,
  54. minimal=True)
  55. args = [
  56. _RESOURCE_SIZES,
  57. '--output-format',
  58. 'chartjson',
  59. '--output-file',
  60. '-',
  61. path_to_measure,
  62. ]
  63. logging.warning(' '.join(args))
  64. self._resource_sizes_json = json.loads(subprocess.check_output(args))
  65. def GetCompressedSize(self):
  66. return self._resource_sizes_json['charts']['TransferSize'][
  67. 'Transfer size (deflate)']['value']
  68. def GetApkSize(self):
  69. return self._resource_sizes_json['charts']['InstallSize']['APK size'][
  70. 'value']
  71. def GetAndroidGoSize(self):
  72. return self._resource_sizes_json['charts']['InstallSize'][
  73. 'Estimated installed size (Android Go)']['value']
  74. def AddSize(self, metrics):
  75. metrics[self.name] = self.GetApkSize()
  76. def AddMethodCount(self, metrics):
  77. metrics[self.name + ' (method count)'] = self._resource_sizes_json[
  78. 'charts']['Dex']['unique methods']['value']
  79. def AddDfmSizes(self, metrics, base_name):
  80. for k, v in sorted(self._resource_sizes_json['charts'].items()):
  81. if k.startswith('DFM_') and k != 'DFM_test_dummy':
  82. if k == 'DFM_base':
  83. name = 'base ({})'.format(base_name)
  84. else:
  85. name = k[4:]
  86. metrics['DFM: ' + name] = v['Size with hindi']['value']
  87. def PrintLibraryCompression(self):
  88. with zipfile.ZipFile(self._path) as z:
  89. for info in z.infolist():
  90. if info.filename.endswith('.so'):
  91. sys.stdout.write('{}/{} compressed: {} uncompressed: {}\n'.format(
  92. self.name, posixpath.basename(info.filename), info.compress_size,
  93. info.file_size))
  94. def _DumpCsv(metrics):
  95. csv_writer = csv.DictWriter(
  96. sys.stdout, fieldnames=list(metrics.keys()), delimiter='\t')
  97. csv_writer.writeheader()
  98. csv_writer.writerow(metrics)
  99. def _DownloadAndAnalyze(signed_prefix, unsigned_prefix, staging_dir):
  100. artifacts = []
  101. def make_artifact(name, prefix=signed_prefix):
  102. artifacts.append(_Artifact(prefix, name, staging_dir))
  103. return artifacts[-1]
  104. webview = make_artifact('arm/AndroidWebviewStable.aab')
  105. webview64 = make_artifact('arm_64/AndroidWebviewStable.aab')
  106. chrome_modern = make_artifact('arm/ChromeModernStable.aab')
  107. chrome_modern64 = make_artifact('arm_64/ChromeModernStable.aab')
  108. monochrome = make_artifact('arm/MonochromeStable.aab')
  109. monochrome64 = make_artifact('arm_64/MonochromeStable.aab')
  110. trichrome_chrome = make_artifact('arm/TrichromeChromeGoogleStable.aab')
  111. trichrome_webview = make_artifact('arm/TrichromeWebViewGoogleStable.aab')
  112. trichrome_library = make_artifact('arm/TrichromeLibraryGoogleStable.apk')
  113. trichrome64_chrome = make_artifact('arm_64/TrichromeChromeGoogleStable.aab')
  114. trichrome64_webview = make_artifact('arm_64/TrichromeWebViewGoogleStable.aab')
  115. trichrome64_library = make_artifact('arm_64/TrichromeLibraryGoogleStable.apk')
  116. trichrome_system_apks = [
  117. make_artifact('arm/TrichromeWebViewGoogleSystemStable.apk'),
  118. make_artifact('arm/TrichromeLibraryGoogleSystemStable.apk'),
  119. make_artifact(
  120. 'arm/for-signing-only/TrichromeChromeGoogleSystemStable.apk',
  121. prefix=unsigned_prefix),
  122. ]
  123. trichrome64_system_apks = [
  124. make_artifact('arm_64/TrichromeWebViewGoogleSystemStable.apk'),
  125. make_artifact('arm_64/TrichromeLibraryGoogleSystemStable.apk'),
  126. make_artifact(
  127. 'arm_64/for-signing-only/TrichromeChromeGoogleSystemStable.apk',
  128. prefix=unsigned_prefix),
  129. ]
  130. trichrome_system_stubs = [
  131. make_artifact('arm/TrichromeWebViewGoogleSystemStubStable.apk'),
  132. make_artifact('arm/TrichromeLibraryGoogleSystemStubStable.apk'),
  133. make_artifact(
  134. 'arm/for-signing-only/TrichromeChromeGoogleSystemStubStable.apk',
  135. prefix=unsigned_prefix),
  136. ]
  137. # Download and run resource_sizes.py concurrently.
  138. pool = multiprocessing.dummy.Pool()
  139. pool.map(_Artifact.FetchAndMeasure, artifacts)
  140. pool.close()
  141. # Add metrics in the order that we want them in the .csv output.
  142. metrics = collections.OrderedDict()
  143. chrome_modern.AddSize(metrics)
  144. chrome_modern64.AddSize(metrics)
  145. webview.AddSize(metrics)
  146. webview64.AddSize(metrics)
  147. monochrome.AddSize(metrics)
  148. monochrome64.AddSize(metrics)
  149. trichrome_chrome.AddSize(metrics)
  150. trichrome_webview.AddSize(metrics)
  151. trichrome_library.AddSize(metrics)
  152. # Separate where spreadsheet has computed columns for easier copy/paste.
  153. _DumpCsv(metrics)
  154. metrics = collections.OrderedDict()
  155. trichrome64_chrome.AddSize(metrics)
  156. trichrome64_webview.AddSize(metrics)
  157. trichrome64_library.AddSize(metrics)
  158. _DumpCsv(metrics)
  159. metrics = collections.OrderedDict()
  160. webview.PrintLibraryCompression()
  161. metrics['System Image Size (arm32)'] = sum(x.GetApkSize()
  162. for x in trichrome_system_apks)
  163. metrics['System Image Size (arm64)'] = sum(x.GetApkSize()
  164. for x in trichrome64_system_apks)
  165. go_install_size = (trichrome_chrome.GetAndroidGoSize() +
  166. trichrome_webview.GetAndroidGoSize() +
  167. trichrome_library.GetAndroidGoSize())
  168. metrics['Android Go (TriChrome) Install Size'] = go_install_size
  169. compressed_system_apks_size = sum(x.GetCompressedSize()
  170. for x in trichrome_system_apks)
  171. stubs_sizes = sum(x.GetApkSize() for x in trichrome_system_stubs)
  172. metrics['Android Go (Trichrome) Compressed System Image'] = (
  173. compressed_system_apks_size + stubs_sizes)
  174. monochrome.AddMethodCount(metrics)
  175. # Separate where spreadsheet has computed columns for easier copy/paste.
  176. _DumpCsv(metrics)
  177. metrics = collections.OrderedDict()
  178. trichrome_chrome.AddDfmSizes(metrics, 'Chrome')
  179. trichrome_webview.AddDfmSizes(metrics, 'WebView')
  180. _DumpCsv(metrics)
  181. def main():
  182. parser = argparse.ArgumentParser()
  183. parser.add_argument('--version', required=True, help='e.g.: "75.0.3770.143"')
  184. parser.add_argument(
  185. '--signed-bucket',
  186. required=True,
  187. help='GCS bucket to find files in. (e.g. "gs://bucket/subdir")')
  188. parser.add_argument('--keep-files',
  189. action='store_true',
  190. help='Do not delete downloaded files.')
  191. options = parser.parse_args()
  192. signed_prefix = posixpath.join(options.signed_bucket, options.version)
  193. unsigned_prefix = signed_prefix.replace('signed', 'unsigned')
  194. with tempfile.TemporaryDirectory() as staging_dir:
  195. if options.keep_files:
  196. staging_dir = 'milestone_apk_sizes-staging'
  197. os.makedirs(staging_dir, exist_ok=True)
  198. _DownloadAndAnalyze(signed_prefix, unsigned_prefix, staging_dir)
  199. if options.keep_files:
  200. print('Saved files to', staging_dir)
  201. if __name__ == '__main__':
  202. main()