| 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 collections | 12 import collections |
| 13 import json | 13 import json |
| 14 import logging | 14 import logging |
| 15 import operator | 15 import operator |
| 16 import optparse | 16 import optparse |
| 17 import os | 17 import os |
| 18 import re | 18 import re |
| 19 import shutil | |
| 20 import struct | 19 import struct |
| 21 import sys | 20 import sys |
| 22 import tempfile | 21 import tempfile |
| 23 import zipfile | 22 import zipfile |
| 24 import zlib | 23 import zlib |
| 25 | 24 |
| 26 import devil_chromium | 25 import devil_chromium |
| 27 from devil.android.sdk import build_tools | 26 from devil.android.sdk import build_tools |
| 28 from devil.utils import cmd_helper | 27 from devil.utils import cmd_helper |
| 29 from devil.utils import lazy | 28 from devil.utils import lazy |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 'benchmark_name': 'resource_sizes', | 92 'benchmark_name': 'resource_sizes', |
| 94 'benchmark_description': 'APK resource size information.', | 93 'benchmark_description': 'APK resource size information.', |
| 95 'trace_rerun_options': [], | 94 'trace_rerun_options': [], |
| 96 'charts': {} | 95 'charts': {} |
| 97 } | 96 } |
| 98 _DUMP_STATIC_INITIALIZERS_PATH = os.path.join( | 97 _DUMP_STATIC_INITIALIZERS_PATH = os.path.join( |
| 99 host_paths.DIR_SOURCE_ROOT, 'tools', 'linux', 'dump-static-initializers.py') | 98 host_paths.DIR_SOURCE_ROOT, 'tools', 'linux', 'dump-static-initializers.py') |
| 100 # Pragma exists when enable_resource_whitelist_generation=true. | 99 # Pragma exists when enable_resource_whitelist_generation=true. |
| 101 _RC_HEADER_RE = re.compile( | 100 _RC_HEADER_RE = re.compile( |
| 102 r'^#define (?P<name>\w+) (?:_Pragma\(.*?\) )?(?P<id>\d+)$') | 101 r'^#define (?P<name>\w+) (?:_Pragma\(.*?\) )?(?P<id>\d+)$') |
| 103 _READELF_SIZES_METRICS = { | |
| 104 'text': ['.text'], | |
| 105 'data': ['.data', '.rodata'], | |
| 106 'relocations': ['.rel.dyn', '.rel.plt', '.data.rel.ro', '.data.rel.ro.loca'], | |
| 107 'unwind': ['.ARM.extab', '.ARM.exidx'], | |
| 108 'symbols': ['.dynsym', '.dynstr', '.dynamic', '.shstrtab', '.got', '.plt'], | |
| 109 'bss': ['.bss'], | |
| 110 # Group any section headers not listed above into the "other" group in case | |
| 111 # section headers change over time. | |
| 112 # 'other': ['.hash', '.init_array', '.fini_array', '.comment', | |
| 113 # '.note.gnu.gold-ve', '.ARM.attributes', '.note.gnu.build-i', | |
| 114 # '.gnu.version', '.gnu.version_d', '.gnu.version_r'] | |
| 115 } | |
| 116 | |
| 117 | |
| 118 def _ExtractMainLibSectionSizesFromApk(apk_path, main_lib_path): | |
| 119 tmpdir = tempfile.mkdtemp(suffix='_apk_extract') | |
| 120 grouped_section_sizes = collections.defaultdict(int) | |
| 121 try: | |
| 122 with zipfile.ZipFile(apk_path, 'r') as z: | |
| 123 extracted_lib_path = z.extract(main_lib_path, tmpdir) | |
| 124 section_sizes = _CreateSectionNameSizeMap(extracted_lib_path) | |
| 125 | |
| 126 for group_name, section_names in _READELF_SIZES_METRICS.iteritems(): | |
| 127 for section_name in section_names: | |
| 128 grouped_section_sizes[group_name] += section_sizes.pop(section_name) | |
| 129 | |
| 130 grouped_section_sizes['other'] = sum(s for s in section_sizes.values()) | |
| 131 | |
| 132 return grouped_section_sizes | |
| 133 finally: | |
| 134 shutil.rmtree(tmpdir) | |
| 135 | |
| 136 | |
| 137 def _CreateSectionNameSizeMap(so_path): | |
| 138 stdout = cmd_helper.GetCmdOutput(['readelf', '-S', so_path]) | |
| 139 section_sizes = {} | |
| 140 # Matches [ 2] .dynsym DYNSYM 00000158 000158 004f70 10 A 3 1 4 | |
| 141 for match in re.finditer(r'\[[\s\d]+\] (\..*)$', stdout, re.MULTILINE): | |
| 142 items = match.group(1).split() | |
| 143 section_sizes[items[0]] = int(items[4], 16) | |
| 144 | |
| 145 return section_sizes | |
| 146 | 102 |
| 147 | 103 |
| 148 def CountStaticInitializers(so_path): | 104 def CountStaticInitializers(so_path): |
| 149 # Static initializers expected in official builds. Note that this list is | 105 # Static initializers expected in official builds. Note that this list is |
| 150 # built using 'nm' on libchrome.so which results from a GCC official build | 106 # built using 'nm' on libchrome.so which results from a GCC official build |
| 151 # (i.e. Clang is not supported currently). | 107 # (i.e. Clang is not supported currently). |
| 152 def get_elf_section_size(readelf_stdout, section_name): | 108 def get_elf_section_size(readelf_stdout, section_name): |
| 153 # Matches: .ctors PROGBITS 000000000516add0 5169dd0 000010 00 WA 0 0 8 | 109 # Matches: .ctors PROGBITS 000000000516add0 5169dd0 000010 00 WA 0 0 8 |
| 154 match = re.search(r'\.%s.*$' % re.escape(section_name), | 110 match = re.search(r'\.%s.*$' % re.escape(section_name), |
| 155 readelf_stdout, re.MULTILINE) | 111 readelf_stdout, re.MULTILINE) |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 # Size of main .so vs remaining. | 358 # Size of main .so vs remaining. |
| 403 main_lib_info = native_code.FindLargest() | 359 main_lib_info = native_code.FindLargest() |
| 404 if main_lib_info: | 360 if main_lib_info: |
| 405 main_lib_size = main_lib_info.file_size | 361 main_lib_size = main_lib_info.file_size |
| 406 ReportPerfResult(chartjson, apk_basename + '_Specifics', | 362 ReportPerfResult(chartjson, apk_basename + '_Specifics', |
| 407 'main lib size', main_lib_size, 'bytes') | 363 'main lib size', main_lib_size, 'bytes') |
| 408 secondary_size = native_code.ComputeUncompressedSize() - main_lib_size | 364 secondary_size = native_code.ComputeUncompressedSize() - main_lib_size |
| 409 ReportPerfResult(chartjson, apk_basename + '_Specifics', | 365 ReportPerfResult(chartjson, apk_basename + '_Specifics', |
| 410 'other lib size', secondary_size, 'bytes') | 366 'other lib size', secondary_size, 'bytes') |
| 411 | 367 |
| 412 main_lib_section_sizes = _ExtractMainLibSectionSizesFromApk( | |
| 413 apk_filename, main_lib_info.filename) | |
| 414 for metric_name, size in main_lib_section_sizes.iteritems(): | |
| 415 ReportPerfResult(chartjson, apk_basename + '_MainLibInfo', | |
| 416 metric_name, size, 'bytes') | |
| 417 | |
| 418 # Main metric that we want to monitor for jumps. | 368 # Main metric that we want to monitor for jumps. |
| 419 normalized_apk_size = total_apk_size | 369 normalized_apk_size = total_apk_size |
| 420 # Always look at uncompressed .dex & .so. | 370 # Always look at uncompressed .dex & .so. |
| 421 normalized_apk_size -= java_code.ComputeZippedSize() | 371 normalized_apk_size -= java_code.ComputeZippedSize() |
| 422 normalized_apk_size += java_code.ComputeUncompressedSize() | 372 normalized_apk_size += java_code.ComputeUncompressedSize() |
| 423 normalized_apk_size -= native_code.ComputeZippedSize() | 373 normalized_apk_size -= native_code.ComputeZippedSize() |
| 424 normalized_apk_size += native_code.ComputeUncompressedSize() | 374 normalized_apk_size += native_code.ComputeUncompressedSize() |
| 425 # Avoid noise caused when strings change and translations haven't yet been | 375 # Avoid noise caused when strings change and translations haven't yet been |
| 426 # updated. | 376 # updated. |
| 427 english_pak = translations.FindByPattern(r'.*/en[-_][Uu][Ss]\.l?pak') | 377 english_pak = translations.FindByPattern(r'.*/en[-_][Uu][Ss]\.l?pak') |
| (...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 707 | 657 |
| 708 if chartjson: | 658 if chartjson: |
| 709 results_path = os.path.join(options.output_dir, 'results-chart.json') | 659 results_path = os.path.join(options.output_dir, 'results-chart.json') |
| 710 logging.critical('Dumping json to %s', results_path) | 660 logging.critical('Dumping json to %s', results_path) |
| 711 with open(results_path, 'w') as json_file: | 661 with open(results_path, 'w') as json_file: |
| 712 json.dump(chartjson, json_file) | 662 json.dump(chartjson, json_file) |
| 713 | 663 |
| 714 | 664 |
| 715 if __name__ == '__main__': | 665 if __name__ == '__main__': |
| 716 sys.exit(main(sys.argv)) | 666 sys.exit(main(sys.argv)) |
| OLD | NEW |