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 | |
26 import devil_chromium | 27 import devil_chromium |
27 from devil.android.sdk import build_tools | 28 from devil.android.sdk import build_tools |
28 from devil.utils import cmd_helper | 29 from devil.utils import cmd_helper |
29 from devil.utils import lazy | 30 from devil.utils import lazy |
30 import method_count | 31 import method_count |
31 from pylib import constants | 32 from pylib import constants |
32 from pylib.constants import host_paths | 33 from pylib.constants import host_paths |
33 | 34 |
34 _AAPT_PATH = lazy.WeakConstant(lambda: build_tools.GetPath('aapt')) | 35 _AAPT_PATH = lazy.WeakConstant(lambda: build_tools.GetPath('aapt')) |
35 _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit') | 36 _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit') |
36 _BUILD_UTILS_PATH = os.path.join( | 37 _BUILD_UTILS_PATH = os.path.join( |
37 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'gyp') | 38 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') | |
38 | 41 |
39 # Prepend the grit module from the source tree so it takes precedence over other | 42 # Prepend the grit module from the source tree so it takes precedence over other |
40 # grit versions that might present in the search path. | 43 # grit versions that might present in the search path. |
41 with host_paths.SysPath(_GRIT_PATH, 1): | 44 with host_paths.SysPath(_GRIT_PATH, 1): |
42 from grit.format import data_pack # pylint: disable=import-error | 45 from grit.format import data_pack # pylint: disable=import-error |
43 | 46 |
44 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): | 47 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): |
45 import perf_tests_results_helper # pylint: disable=import-error | 48 import perf_tests_results_helper # pylint: disable=import-error |
46 | 49 |
47 with host_paths.SysPath(_BUILD_UTILS_PATH, 1): | 50 with host_paths.SysPath(_BUILD_UTILS_PATH, 1): |
48 from util import build_utils # pylint: disable=import-error | 51 from util import build_utils # pylint: disable=import-error |
49 | 52 |
53 with host_paths.SysPath(_APK_PATCH_SIZE_ESTIMATOR_PATH): | |
54 import apk_patch_size_estimator as patch_size # pylint: disable=import-error | |
agrieve
2017/03/20 17:53:40
nit: you have a double space in here, although eve
estevenson
2017/03/20 18:02:37
Done.
| |
55 | |
50 | 56 |
51 # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk | 57 # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk |
52 # https://bugs.python.org/issue14315 | 58 # https://bugs.python.org/issue14315 |
53 def _PatchedDecodeExtra(self): | 59 def _PatchedDecodeExtra(self): |
54 # Try to decode the extra field. | 60 # Try to decode the extra field. |
55 extra = self.extra | 61 extra = self.extra |
56 unpack = struct.unpack | 62 unpack = struct.unpack |
57 while len(extra) >= 4: | 63 while len(extra) >= 4: |
58 tp, ln = unpack('<HH', extra[:4]) | 64 tp, ln = unpack('<HH', extra[:4]) |
59 if tp == 1: | 65 if tp == 1: |
(...skipping 607 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
667 graph_title = os.path.basename(apk_filename) + '_Dex' | 673 graph_title = os.path.basename(apk_filename) + '_Dex' |
668 dex_metrics = method_count.CONTRIBUTORS_TO_DEX_CACHE | 674 dex_metrics = method_count.CONTRIBUTORS_TO_DEX_CACHE |
669 for key, label in dex_metrics.iteritems(): | 675 for key, label in dex_metrics.iteritems(): |
670 ReportPerfResult(chartjson, graph_title, label, sizes[key], 'entries') | 676 ReportPerfResult(chartjson, graph_title, label, sizes[key], 'entries') |
671 | 677 |
672 graph_title = '%sCache' % graph_title | 678 graph_title = '%sCache' % graph_title |
673 ReportPerfResult(chartjson, graph_title, 'DexCache', sizes['dex_cache_size'], | 679 ReportPerfResult(chartjson, graph_title, 'DexCache', sizes['dex_cache_size'], |
674 'bytes') | 680 'bytes') |
675 | 681 |
676 | 682 |
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 = patch_size.calculate_bsdiff(old_apk, new_apk, None, tmp_name) | |
696 ReportPerfResult(chartjson, title, 'BSDiff (gzipped)', bsdiff, 'bytes') | |
697 fbf = patch_size.calculate_filebyfile(old_apk, new_apk, None, tmp_name) | |
698 ReportPerfResult(chartjson, title, 'FileByFile (gzipped)', fbf, 'bytes') | |
699 | |
700 | |
677 @contextmanager | 701 @contextmanager |
678 def Unzip(zip_file, filename=None): | 702 def Unzip(zip_file, filename=None): |
679 """Utility for temporary use of a single file in a zip archive.""" | 703 """Utility for temporary use of a single file in a zip archive.""" |
680 with build_utils.TempDir() as unzipped_dir: | 704 with build_utils.TempDir() as unzipped_dir: |
681 unzipped_files = build_utils.ExtractAll( | 705 unzipped_files = build_utils.ExtractAll( |
682 zip_file, unzipped_dir, True, pattern=filename) | 706 zip_file, unzipped_dir, True, pattern=filename) |
683 if len(unzipped_files) == 0: | 707 if len(unzipped_files) == 0: |
684 raise Exception( | 708 raise Exception( |
685 '%s not found in %s' % (filename, zip_file)) | 709 '%s not found in %s' % (filename, zip_file)) |
686 yield unzipped_files[0] | 710 yield unzipped_files[0] |
(...skipping 18 matching lines...) Expand all Loading... | |
705 help='Location of the build artifacts.') | 729 help='Location of the build artifacts.') |
706 argparser.add_argument('--chartjson', action='store_true', | 730 argparser.add_argument('--chartjson', action='store_true', |
707 help='Sets output mode to chartjson.') | 731 help='Sets output mode to chartjson.') |
708 argparser.add_argument('--output-dir', default='.', | 732 argparser.add_argument('--output-dir', default='.', |
709 help='Directory to save chartjson to.') | 733 help='Directory to save chartjson to.') |
710 argparser.add_argument('--no-output-dir', action='store_true', | 734 argparser.add_argument('--no-output-dir', action='store_true', |
711 help='Skip all measurements that rely on having ' | 735 help='Skip all measurements that rely on having ' |
712 'output-dir') | 736 'output-dir') |
713 argparser.add_argument('-d', '--device', | 737 argparser.add_argument('-d', '--device', |
714 help='Dummy option for perf runner.') | 738 help='Dummy option for perf runner.') |
739 argparser.add_argument('--estimate-patch-size', action='store_true', | |
740 help='Include patch size estimates. Useful for perf ' | |
741 'builders where a reference APK is available but adds ' | |
742 '~3 mins to run time.') | |
743 argparser.add_argument('--builder', default=apk_downloader.DEFAULT_BUILDER, | |
744 help='Builder name to use for reference APK for patch ' | |
745 'size estimates.') | |
746 argparser.add_argument('--bucket', default=apk_downloader.DEFAULT_BUCKET, | |
agrieve
2017/03/20 17:53:40
name bikeshed: how about --reference-apk-bucket an
estevenson
2017/03/20 18:02:36
Done.
| |
747 help='Storage bucket holding reference APKs.') | |
715 argparser.add_argument('apk', help='APK file path.') | 748 argparser.add_argument('apk', help='APK file path.') |
716 args = argparser.parse_args() | 749 args = argparser.parse_args() |
717 | 750 |
718 chartjson = _BASE_CHART.copy() if args.chartjson else None | 751 chartjson = _BASE_CHART.copy() if args.chartjson else None |
719 | 752 |
720 if args.chromium_output_directory: | 753 if args.chromium_output_directory: |
721 constants.SetOutputDirectory(args.chromium_output_directory) | 754 constants.SetOutputDirectory(args.chromium_output_directory) |
722 if not args.no_output_dir: | 755 if not args.no_output_dir: |
723 constants.CheckOutputDirectory() | 756 constants.CheckOutputDirectory() |
724 devil_chromium.Initialize() | 757 devil_chromium.Initialize() |
725 build_vars = _ReadBuildVars(constants.GetOutDirectory()) | 758 build_vars = _ReadBuildVars(constants.GetOutDirectory()) |
726 tools_prefix = build_vars['android_tool_prefix'] | 759 tools_prefix = build_vars['android_tool_prefix'] |
727 else: | 760 else: |
728 tools_prefix = '' | 761 tools_prefix = '' |
729 | 762 |
730 PrintApkAnalysis(args.apk, tools_prefix, chartjson=chartjson) | 763 PrintApkAnalysis(args.apk, tools_prefix, chartjson=chartjson) |
731 _PrintDexAnalysis(args.apk, chartjson=chartjson) | 764 _PrintDexAnalysis(args.apk, chartjson=chartjson) |
765 if args.estimate_patch_size: | |
766 _PrintPatchSizeEstimate( | |
767 args.apk, args.builder, args.bucket, chartjson=chartjson) | |
732 if not args.no_output_dir: | 768 if not args.no_output_dir: |
733 PrintPakAnalysis(args.apk, args.min_pak_resource_size) | 769 PrintPakAnalysis(args.apk, args.min_pak_resource_size) |
734 _PrintStaticInitializersCountFromApk( | 770 _PrintStaticInitializersCountFromApk( |
735 args.apk, tools_prefix, chartjson=chartjson) | 771 args.apk, tools_prefix, chartjson=chartjson) |
736 if chartjson: | 772 if chartjson: |
737 results_path = os.path.join(args.output_dir, 'results-chart.json') | 773 results_path = os.path.join(args.output_dir, 'results-chart.json') |
738 logging.critical('Dumping json to %s', results_path) | 774 logging.critical('Dumping json to %s', results_path) |
739 with open(results_path, 'w') as json_file: | 775 with open(results_path, 'w') as json_file: |
740 json.dump(chartjson, json_file) | 776 json.dump(chartjson, json_file) |
741 | 777 |
742 | 778 |
743 if __name__ == '__main__': | 779 if __name__ == '__main__': |
744 sys.exit(main()) | 780 sys.exit(main()) |
OLD | NEW |