blob: 490887adbca8b9eef63942628b39d0e39143794d [file] [log] [blame]
#! /usr/bin/env python
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import os
import re
import shutil
import sys
import tempfile
import zipfile
import devil_chromium
from devil.android.sdk import dexdump
from pylib.constants import host_paths
sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'build', 'util', 'lib',
'common'))
import perf_tests_results_helper # pylint: disable=import-error
# Example dexdump output:
# DEX file header:
# magic : 'dex\n035\0'
# checksum : b664fc68
# signature : ae73...87f1
# file_size : 4579656
# header_size : 112
# link_size : 0
# link_off : 0 (0x000000)
# string_ids_size : 46148
# string_ids_off : 112 (0x000070)
# type_ids_size : 5730
# type_ids_off : 184704 (0x02d180)
# proto_ids_size : 8289
# proto_ids_off : 207624 (0x032b08)
# field_ids_size : 17854
# field_ids_off : 307092 (0x04af94)
# method_ids_size : 33699
# method_ids_off : 449924 (0x06dd84)
# class_defs_size : 2616
# class_defs_off : 719516 (0x0afa9c)
# data_size : 3776428
# data_off : 803228 (0x0c419c)
# For what these mean, refer to:
# https://source.android.com/devices/tech/dalvik/dex-format.html
CONTRIBUTORS_TO_DEX_CACHE = {'type_ids_size': 'types',
'string_ids_size': 'strings',
'method_ids_size': 'methods',
'field_ids_size': 'fields'}
def _ExtractSizesFromDexFile(dex_path):
counts = {}
for line in dexdump.DexDump(dex_path, file_summary=True):
if not line.strip():
# Each method, type, field, and string contributes 4 bytes (1 reference)
# to our DexCache size.
return counts, sum(counts[x] for x in CONTRIBUTORS_TO_DEX_CACHE) * 4
m = re.match(r'([a-z_]+_size) *: (\d+)', line)
if m and m.group(1) in CONTRIBUTORS_TO_DEX_CACHE:
counts[m.group(1)] = int(m.group(2))
raise Exception('Unexpected end of output.')
def ExtractSizesFromZip(path):
tmpdir = tempfile.mkdtemp(suffix='_dex_extract')
try:
counts = {}
total = 0
with zipfile.ZipFile(path, 'r') as z:
for subpath in z.namelist():
if not subpath.endswith('.dex'):
continue
extracted_path = z.extract(subpath, tmpdir)
cur_counts, cur_total = _ExtractSizesFromDexFile(extracted_path)
dex_basename = os.path.basename(extracted_path)
counts[dex_basename] = cur_counts
total += cur_total
return counts, total
finally:
shutil.rmtree(tmpdir)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('filename')
args = parser.parse_args()
devil_chromium.Initialize()
if os.path.splitext(args.filename)[1] in ('.zip', '.apk', '.jar'):
sizes, total_size = ExtractSizesFromZip(args.filename)
else:
single_set_of_sizes, total_size = _ExtractSizesFromDexFile(args.filename)
sizes = {"": single_set_of_sizes}
file_basename = os.path.basename(args.filename)
for classes_dex_file, classes_dex_sizes in sizes.iteritems():
for dex_header_name, readable_name in CONTRIBUTORS_TO_DEX_CACHE.iteritems():
if dex_header_name in classes_dex_sizes:
perf_tests_results_helper.PrintPerfResult(
'%s_%s_%s' % (file_basename, classes_dex_file, readable_name),
'total', [classes_dex_sizes[dex_header_name]], readable_name)
perf_tests_results_helper.PrintPerfResult(
'%s_DexCache_size' % (file_basename), 'total', [total_size],
'bytes of permanent dirty memory')
return 0
if __name__ == '__main__':
sys.exit(main())