Commit fe79dde8 authored by agrieve's avatar agrieve Committed by Commit bot

Reland of Add dex info and static initializers to resource_sizes.py

First attempt:
https://codereview.chromium.org/2243343004/

Reason for reland:
Now ignores placeholder .so files

The goal is to have a single script that reports all .apk
size related metrics.

* Uses method_count.py to get dex info.
* Already had static initializer logic, but now shows it even without
--so_with_symbols_path.
* Tweaks the static initializer count logic to sum up rather for all
.so files rather than report the same metric repeatedly.

TBR=rnephew@chromium.org,amineer@chromium.org
BUG=609365

Review-Url: https://codereview.chromium.org/2294093006
Cr-Commit-Position: refs/heads/master@{#415725}
parent c3e7c7c1
...@@ -48,10 +48,10 @@ import perf_tests_results_helper # pylint: disable=import-error ...@@ -48,10 +48,10 @@ import perf_tests_results_helper # pylint: disable=import-error
# https://source.android.com/devices/tech/dalvik/dex-format.html # https://source.android.com/devices/tech/dalvik/dex-format.html
_CONTRIBUTORS_TO_DEX_CACHE = {'type_ids_size': 'types', CONTRIBUTORS_TO_DEX_CACHE = {'type_ids_size': 'types',
'string_ids_size': 'strings', 'string_ids_size': 'strings',
'method_ids_size': 'methods', 'method_ids_size': 'methods',
'field_ids_size': 'fields'} 'field_ids_size': 'fields'}
def _ExtractSizesFromDexFile(dex_path): def _ExtractSizesFromDexFile(dex_path):
...@@ -61,7 +61,7 @@ def _ExtractSizesFromDexFile(dex_path): ...@@ -61,7 +61,7 @@ def _ExtractSizesFromDexFile(dex_path):
# Each method, type, field, and string contributes 4 bytes (1 reference) # Each method, type, field, and string contributes 4 bytes (1 reference)
# to our DexCache size. # to our DexCache size.
counts['dex_cache_size'] = ( counts['dex_cache_size'] = (
sum(counts[x] for x in _CONTRIBUTORS_TO_DEX_CACHE)) * 4 sum(counts[x] for x in CONTRIBUTORS_TO_DEX_CACHE)) * 4
return counts return counts
m = re.match(r'([a-z_]+_size) *: (\d+)', line) m = re.match(r'([a-z_]+_size) *: (\d+)', line)
if m: if m:
...@@ -69,7 +69,7 @@ def _ExtractSizesFromDexFile(dex_path): ...@@ -69,7 +69,7 @@ def _ExtractSizesFromDexFile(dex_path):
raise Exception('Unexpected end of output.') raise Exception('Unexpected end of output.')
def _ExtractSizesFromZip(path): def ExtractSizesFromZip(path):
tmpdir = tempfile.mkdtemp(suffix='_dex_extract') tmpdir = tempfile.mkdtemp(suffix='_dex_extract')
try: try:
counts = collections.defaultdict(int) counts = collections.defaultdict(int)
...@@ -109,7 +109,7 @@ def main(): ...@@ -109,7 +109,7 @@ def main():
'and --apk-name was not provided.' % args.dexfile) 'and --apk-name was not provided.' % args.dexfile)
if os.path.splitext(args.dexfile)[1] in ('.zip', '.apk', '.jar'): if os.path.splitext(args.dexfile)[1] in ('.zip', '.apk', '.jar'):
sizes = _ExtractSizesFromZip(args.dexfile) sizes = ExtractSizesFromZip(args.dexfile)
else: else:
sizes = _ExtractSizesFromDexFile(args.dexfile) sizes = _ExtractSizesFromDexFile(args.dexfile)
...@@ -118,7 +118,7 @@ def main(): ...@@ -118,7 +118,7 @@ def main():
'%s_%s' % (args.apk_name, name), 'total', [sizes[value_key]], '%s_%s' % (args.apk_name, name), 'total', [sizes[value_key]],
description or name) description or name)
for dex_header_name, readable_name in _CONTRIBUTORS_TO_DEX_CACHE.iteritems(): for dex_header_name, readable_name in CONTRIBUTORS_TO_DEX_CACHE.iteritems():
print_result(readable_name, dex_header_name) print_result(readable_name, dex_header_name)
print_result( print_result(
'DexCache_size', 'dex_cache_size', 'bytes of permanent dirty memory') 'DexCache_size', 'dex_cache_size', 'bytes of permanent dirty memory')
......
...@@ -24,6 +24,7 @@ import zlib ...@@ -24,6 +24,7 @@ import zlib
import devil_chromium import devil_chromium
from devil.utils import cmd_helper from devil.utils import cmd_helper
import method_count
from pylib import constants from pylib import constants
from pylib.constants import host_paths from pylib.constants import host_paths
...@@ -37,6 +38,7 @@ with host_paths.SysPath(_GRIT_PATH, 1): ...@@ -37,6 +38,7 @@ with host_paths.SysPath(_GRIT_PATH, 1):
with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
import perf_tests_results_helper # pylint: disable=import-error import perf_tests_results_helper # pylint: disable=import-error
# Python had a bug in zipinfo parsing that triggers on ChromeModern.apk # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk
# https://bugs.python.org/issue14315 # https://bugs.python.org/issue14315
def _PatchedDecodeExtra(self): def _PatchedDecodeExtra(self):
...@@ -354,29 +356,53 @@ def _GetResourceIdNameMap(): ...@@ -354,29 +356,53 @@ def _GetResourceIdNameMap():
return id_name_map return id_name_map
def PrintStaticInitializersCount(so_with_symbols_path, chartjson=None): def _PrintStaticInitializersCountFromApk(apk_filename, chartjson=None):
"""Emits the performance result for static initializers found in the provided print 'Finding static initializers (can take a minute)'
shared library. Additionally, files for which static initializers were with zipfile.ZipFile(apk_filename) as z:
found are printed on the standard output. infolist = z.infolist()
out_dir = constants.GetOutDirectory()
si_count = 0
for zip_info in infolist:
# Check file size to account for placeholder libraries.
if zip_info.filename.endswith('.so') and zip_info.file_size > 0:
unstripped_path = os.path.join(out_dir, 'lib.unstripped',
os.path.basename(zip_info.filename))
if os.path.exists(unstripped_path):
si_count += _PrintStaticInitializersCount(unstripped_path)
else:
raise Exception('Unstripped .so not found. Looked here: %s',
unstripped_path)
ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', si_count,
'count')
def _PrintStaticInitializersCount(so_with_symbols_path):
"""Counts the number of static initializers in the given shared library.
Additionally, files for which static initializers were found are printed
on the standard output.
Args: Args:
so_with_symbols_path: Path to the unstripped libchrome.so file. so_with_symbols_path: Path to the unstripped libchrome.so file.
Returns:
The number of static initializers found.
""" """
# GetStaticInitializers uses get-static-initializers.py to get a list of all # GetStaticInitializers uses get-static-initializers.py to get a list of all
# static initializers. This does not work on all archs (particularly arm). # static initializers. This does not work on all archs (particularly arm).
# TODO(rnephew): Get rid of warning when crbug.com/585588 is fixed. # TODO(rnephew): Get rid of warning when crbug.com/585588 is fixed.
si_count = CountStaticInitializers(so_with_symbols_path) si_count = CountStaticInitializers(so_with_symbols_path)
static_initializers = GetStaticInitializers(so_with_symbols_path) static_initializers = GetStaticInitializers(so_with_symbols_path)
if si_count != len(static_initializers): static_initializers_count = len(static_initializers) - 1 # Minus summary.
if si_count != static_initializers_count:
print ('There are %d files with static initializers, but ' print ('There are %d files with static initializers, but '
'dump-static-initializers found %d:' % 'dump-static-initializers found %d:' %
(si_count, len(static_initializers))) (si_count, static_initializers_count))
else: else:
print 'Found %d files with static initializers:' % si_count print '%s - Found %d files with static initializers:' % (
os.path.basename(so_with_symbols_path), si_count)
print '\n'.join(static_initializers) print '\n'.join(static_initializers)
ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', return si_count
si_count, 'count')
def _FormatBytes(byts): def _FormatBytes(byts):
"""Pretty-print a number of bytes.""" """Pretty-print a number of bytes."""
...@@ -400,20 +426,34 @@ def _CalculateCompressedSize(file_path): ...@@ -400,20 +426,34 @@ def _CalculateCompressedSize(file_path):
return total_size return total_size
def _PrintDexAnalysis(apk_filename, chartjson=None):
sizes = method_count.ExtractSizesFromZip(apk_filename)
graph_title = os.path.basename(apk_filename) + '_Dex'
dex_metrics = method_count.CONTRIBUTORS_TO_DEX_CACHE
for key, label in dex_metrics.iteritems():
ReportPerfResult(chartjson, graph_title, label, sizes[key], 'entries')
graph_title = '%sCache' % graph_title
ReportPerfResult(chartjson, graph_title, 'DexCache', sizes['dex_cache_size'],
'bytes')
def main(argv): def main(argv):
usage = """Usage: %prog [options] file1 file2 ... usage = """Usage: %prog [options] file1 file2 ...
Pass any number of files to graph their sizes. Any files with the extension Pass any number of files to graph their sizes. Any files with the extension
'.apk' will be broken down into their components on a separate graph.""" '.apk' will be broken down into their components on a separate graph."""
option_parser = optparse.OptionParser(usage=usage) option_parser = optparse.OptionParser(usage=usage)
option_parser.add_option('--so-path', help='Path to libchrome.so.') option_parser.add_option('--so-path',
help='Obsolete. Pass .so as positional arg instead.')
option_parser.add_option('--so-with-symbols-path', option_parser.add_option('--so-with-symbols-path',
help='Path to libchrome.so with symbols.') help='Mostly obsolete. Use .so within .apk instead.')
option_parser.add_option('--min-pak-resource-size', type='int', option_parser.add_option('--min-pak-resource-size', type='int',
default=20*1024, default=20*1024,
help='Minimum byte size of displayed pak resources.') help='Minimum byte size of displayed pak resources.')
option_parser.add_option('--build_type', dest='build_type', default='Debug', option_parser.add_option('--build_type', dest='build_type', default='Debug',
help='Sets the build type, default is Debug.') help='Obsoleted by --chromium-output-directory.')
option_parser.add_option('--chromium-output-directory', option_parser.add_option('--chromium-output-directory',
help='Location of the build artifacts. ' help='Location of the build artifacts. '
'Takes precidence over --build_type.') 'Takes precidence over --build_type.')
...@@ -444,8 +484,9 @@ Pass any number of files to graph their sizes. Any files with the extension ...@@ -444,8 +484,9 @@ Pass any number of files to graph their sizes. Any files with the extension
devil_chromium.Initialize() devil_chromium.Initialize()
if options.so_with_symbols_path: if options.so_with_symbols_path:
PrintStaticInitializersCount( si_count = _PrintStaticInitializersCount(options.so_with_symbols_path)
options.so_with_symbols_path, chartjson=chartjson) ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', si_count,
'count')
PrintResourceSizes(files, chartjson=chartjson) PrintResourceSizes(files, chartjson=chartjson)
...@@ -453,6 +494,9 @@ Pass any number of files to graph their sizes. Any files with the extension ...@@ -453,6 +494,9 @@ Pass any number of files to graph their sizes. Any files with the extension
if f.endswith('.apk'): if f.endswith('.apk'):
PrintApkAnalysis(f, chartjson=chartjson) PrintApkAnalysis(f, chartjson=chartjson)
PrintPakAnalysis(f, options.min_pak_resource_size) PrintPakAnalysis(f, options.min_pak_resource_size)
_PrintDexAnalysis(f, chartjson=chartjson)
if not options.so_with_symbols_path:
_PrintStaticInitializersCountFromApk(f, chartjson=chartjson)
if chartjson: if chartjson:
results_path = os.path.join(options.output_dir, 'results-chart.json') results_path = os.path.join(options.output_dir, 'results-chart.json')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment