| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Prints the size of each given file and optionally computes the size of | 6 """Prints the size of each given file and optionally computes the size of |
| 7 libchrome.so without the dependencies added for building with android NDK. | 7 libchrome.so without the dependencies added for building with android NDK. |
| 8 Also breaks down the contents of the APK to determine the installed size | 8 Also breaks down the contents of the APK to determine the installed size |
| 9 and assign size contributions to different classes of file. | 9 and assign size contributions to different classes of file. |
| 10 """ | 10 """ |
| 11 | 11 |
| 12 import argparse | 12 import argparse |
| 13 import collections | 13 import collections |
| 14 from contextlib import contextmanager | 14 from contextlib import contextmanager |
| 15 import json | 15 import json |
| 16 import logging | 16 import logging |
| 17 import operator | 17 import operator |
| 18 import os | 18 import os |
| 19 import re | 19 import re |
| 20 import struct | 20 import struct |
| 21 import sys | 21 import sys |
| 22 import tempfile | 22 import tempfile |
| 23 import zipfile | 23 import zipfile |
| 24 import zlib | 24 import zlib |
| 25 | 25 |
| 26 from binary_size import apk_downloader | |
| 27 import devil_chromium | 26 import devil_chromium |
| 28 from devil.android.sdk import build_tools | 27 from devil.android.sdk import build_tools |
| 29 from devil.utils import cmd_helper | 28 from devil.utils import cmd_helper |
| 30 from devil.utils import lazy | 29 from devil.utils import lazy |
| 31 import method_count | 30 import method_count |
| 32 from pylib import constants | 31 from pylib import constants |
| 33 from pylib.constants import host_paths | 32 from pylib.constants import host_paths |
| 34 | 33 |
| 35 _AAPT_PATH = lazy.WeakConstant(lambda: build_tools.GetPath('aapt')) | 34 _AAPT_PATH = lazy.WeakConstant(lambda: build_tools.GetPath('aapt')) |
| 36 _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit') | 35 _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit') |
| 37 _BUILD_UTILS_PATH = os.path.join( | 36 _BUILD_UTILS_PATH = os.path.join( |
| 38 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'gyp') | 37 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'gyp') |
| 39 _APK_PATCH_SIZE_ESTIMATOR_PATH = os.path.join( | |
| 40 host_paths.DIR_SOURCE_ROOT, 'third_party', 'apk-patch-size-estimator') | |
| 41 | 38 |
| 42 # Prepend the grit module from the source tree so it takes precedence over other | 39 # Prepend the grit module from the source tree so it takes precedence over other |
| 43 # grit versions that might present in the search path. | 40 # grit versions that might present in the search path. |
| 44 with host_paths.SysPath(_GRIT_PATH, 1): | 41 with host_paths.SysPath(_GRIT_PATH, 1): |
| 45 from grit.format import data_pack # pylint: disable=import-error | 42 from grit.format import data_pack # pylint: disable=import-error |
| 46 | 43 |
| 47 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): | 44 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): |
| 48 import perf_tests_results_helper # pylint: disable=import-error | 45 import perf_tests_results_helper # pylint: disable=import-error |
| 49 | 46 |
| 50 with host_paths.SysPath(_BUILD_UTILS_PATH, 1): | 47 with host_paths.SysPath(_BUILD_UTILS_PATH, 1): |
| 51 from util import build_utils # pylint: disable=import-error | 48 from util import build_utils # pylint: disable=import-error |
| 52 | 49 |
| 53 with host_paths.SysPath(_APK_PATCH_SIZE_ESTIMATOR_PATH): | |
| 54 import apk_patch_size_estimator # pylint: disable=import-error | |
| 55 | |
| 56 | 50 |
| 57 # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk | 51 # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk |
| 58 # https://bugs.python.org/issue14315 | 52 # https://bugs.python.org/issue14315 |
| 59 def _PatchedDecodeExtra(self): | 53 def _PatchedDecodeExtra(self): |
| 60 # Try to decode the extra field. | 54 # Try to decode the extra field. |
| 61 extra = self.extra | 55 extra = self.extra |
| 62 unpack = struct.unpack | 56 unpack = struct.unpack |
| 63 while len(extra) >= 4: | 57 while len(extra) >= 4: |
| 64 tp, ln = unpack('<HH', extra[:4]) | 58 tp, ln = unpack('<HH', extra[:4]) |
| 65 if tp == 1: | 59 if tp == 1: |
| (...skipping 607 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 673 graph_title = os.path.basename(apk_filename) + '_Dex' | 667 graph_title = os.path.basename(apk_filename) + '_Dex' |
| 674 dex_metrics = method_count.CONTRIBUTORS_TO_DEX_CACHE | 668 dex_metrics = method_count.CONTRIBUTORS_TO_DEX_CACHE |
| 675 for key, label in dex_metrics.iteritems(): | 669 for key, label in dex_metrics.iteritems(): |
| 676 ReportPerfResult(chartjson, graph_title, label, sizes[key], 'entries') | 670 ReportPerfResult(chartjson, graph_title, label, sizes[key], 'entries') |
| 677 | 671 |
| 678 graph_title = '%sCache' % graph_title | 672 graph_title = '%sCache' % graph_title |
| 679 ReportPerfResult(chartjson, graph_title, 'DexCache', sizes['dex_cache_size'], | 673 ReportPerfResult(chartjson, graph_title, 'DexCache', sizes['dex_cache_size'], |
| 680 'bytes') | 674 'bytes') |
| 681 | 675 |
| 682 | 676 |
| 683 def _PrintPatchSizeEstimate(new_apk, builder, bucket, chartjson=None): | |
| 684 apk_name = os.path.basename(new_apk) | |
| 685 title = apk_name + '_PatchSizeEstimate' | |
| 686 # Reference APK paths have spaces replaced by underscores. | |
| 687 builder = builder.replace(' ', '_') | |
| 688 old_apk = apk_downloader.MaybeDownloadApk( | |
| 689 builder, apk_downloader.CURRENT_MILESTONE, apk_name, | |
| 690 apk_downloader.DEFAULT_DOWNLOAD_PATH, bucket) | |
| 691 if old_apk: | |
| 692 # Use a temp dir in case patch size functions fail to clean up temp files. | |
| 693 with build_utils.TempDir() as tmp: | |
| 694 tmp_name = os.path.join(tmp, 'patch.tmp') | |
| 695 bsdiff = apk_patch_size_estimator.calculate_bsdiff( | |
| 696 old_apk, new_apk, None, tmp_name) | |
| 697 ReportPerfResult(chartjson, title, 'BSDiff (gzipped)', bsdiff, 'bytes') | |
| 698 fbf = apk_patch_size_estimator.calculate_filebyfile( | |
| 699 old_apk, new_apk, None, tmp_name) | |
| 700 ReportPerfResult(chartjson, title, 'FileByFile (gzipped)', fbf, 'bytes') | |
| 701 | |
| 702 | |
| 703 @contextmanager | 677 @contextmanager |
| 704 def Unzip(zip_file, filename=None): | 678 def Unzip(zip_file, filename=None): |
| 705 """Utility for temporary use of a single file in a zip archive.""" | 679 """Utility for temporary use of a single file in a zip archive.""" |
| 706 with build_utils.TempDir() as unzipped_dir: | 680 with build_utils.TempDir() as unzipped_dir: |
| 707 unzipped_files = build_utils.ExtractAll( | 681 unzipped_files = build_utils.ExtractAll( |
| 708 zip_file, unzipped_dir, True, pattern=filename) | 682 zip_file, unzipped_dir, True, pattern=filename) |
| 709 if len(unzipped_files) == 0: | 683 if len(unzipped_files) == 0: |
| 710 raise Exception( | 684 raise Exception( |
| 711 '%s not found in %s' % (filename, zip_file)) | 685 '%s not found in %s' % (filename, zip_file)) |
| 712 yield unzipped_files[0] | 686 yield unzipped_files[0] |
| (...skipping 18 matching lines...) Expand all Loading... |
| 731 help='Location of the build artifacts.') | 705 help='Location of the build artifacts.') |
| 732 argparser.add_argument('--chartjson', action='store_true', | 706 argparser.add_argument('--chartjson', action='store_true', |
| 733 help='Sets output mode to chartjson.') | 707 help='Sets output mode to chartjson.') |
| 734 argparser.add_argument('--output-dir', default='.', | 708 argparser.add_argument('--output-dir', default='.', |
| 735 help='Directory to save chartjson to.') | 709 help='Directory to save chartjson to.') |
| 736 argparser.add_argument('--no-output-dir', action='store_true', | 710 argparser.add_argument('--no-output-dir', action='store_true', |
| 737 help='Skip all measurements that rely on having ' | 711 help='Skip all measurements that rely on having ' |
| 738 'output-dir') | 712 'output-dir') |
| 739 argparser.add_argument('-d', '--device', | 713 argparser.add_argument('-d', '--device', |
| 740 help='Dummy option for perf runner.') | 714 help='Dummy option for perf runner.') |
| 741 argparser.add_argument('--estimate-patch-size', action='store_true', | |
| 742 help='Include patch size estimates. Useful for perf ' | |
| 743 'builders where a reference APK is available but adds ' | |
| 744 '~3 mins to run time.') | |
| 745 argparser.add_argument('--reference-apk-builder', | |
| 746 default=apk_downloader.DEFAULT_BUILDER, | |
| 747 help='Builder name to use for reference APK for patch ' | |
| 748 'size estimates.') | |
| 749 argparser.add_argument('--reference-apk-bucket', | |
| 750 default=apk_downloader.DEFAULT_BUCKET, | |
| 751 help='Storage bucket holding reference APKs.') | |
| 752 argparser.add_argument('apk', help='APK file path.') | 715 argparser.add_argument('apk', help='APK file path.') |
| 753 args = argparser.parse_args() | 716 args = argparser.parse_args() |
| 754 | 717 |
| 755 chartjson = _BASE_CHART.copy() if args.chartjson else None | 718 chartjson = _BASE_CHART.copy() if args.chartjson else None |
| 756 | 719 |
| 757 if args.chromium_output_directory: | 720 if args.chromium_output_directory: |
| 758 constants.SetOutputDirectory(args.chromium_output_directory) | 721 constants.SetOutputDirectory(args.chromium_output_directory) |
| 759 if not args.no_output_dir: | 722 if not args.no_output_dir: |
| 760 constants.CheckOutputDirectory() | 723 constants.CheckOutputDirectory() |
| 761 devil_chromium.Initialize() | 724 devil_chromium.Initialize() |
| 762 build_vars = _ReadBuildVars(constants.GetOutDirectory()) | 725 build_vars = _ReadBuildVars(constants.GetOutDirectory()) |
| 763 tools_prefix = build_vars['android_tool_prefix'] | 726 tools_prefix = build_vars['android_tool_prefix'] |
| 764 else: | 727 else: |
| 765 tools_prefix = '' | 728 tools_prefix = '' |
| 766 | 729 |
| 767 PrintApkAnalysis(args.apk, tools_prefix, chartjson=chartjson) | 730 PrintApkAnalysis(args.apk, tools_prefix, chartjson=chartjson) |
| 768 _PrintDexAnalysis(args.apk, chartjson=chartjson) | 731 _PrintDexAnalysis(args.apk, chartjson=chartjson) |
| 769 if args.estimate_patch_size: | |
| 770 _PrintPatchSizeEstimate( | |
| 771 args.apk, args.builder, args.bucket, chartjson=chartjson) | |
| 772 if not args.no_output_dir: | 732 if not args.no_output_dir: |
| 773 PrintPakAnalysis(args.apk, args.min_pak_resource_size) | 733 PrintPakAnalysis(args.apk, args.min_pak_resource_size) |
| 774 _PrintStaticInitializersCountFromApk( | 734 _PrintStaticInitializersCountFromApk( |
| 775 args.apk, tools_prefix, chartjson=chartjson) | 735 args.apk, tools_prefix, chartjson=chartjson) |
| 776 if chartjson: | 736 if chartjson: |
| 777 results_path = os.path.join(args.output_dir, 'results-chart.json') | 737 results_path = os.path.join(args.output_dir, 'results-chart.json') |
| 778 logging.critical('Dumping json to %s', results_path) | 738 logging.critical('Dumping json to %s', results_path) |
| 779 with open(results_path, 'w') as json_file: | 739 with open(results_path, 'w') as json_file: |
| 780 json.dump(chartjson, json_file) | 740 json.dump(chartjson, json_file) |
| 781 | 741 |
| 782 | 742 |
| 783 if __name__ == '__main__': | 743 if __name__ == '__main__': |
| 784 sys.exit(main()) | 744 sys.exit(main()) |
| OLD | NEW |