Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: build/android/resource_sizes.py

Issue 2294093006: Reland of 🚀 Add dex info and static initializers to resource_sizes.py (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Ignore empty .so files Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/android/method_count.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 struct 19 import struct
20 import sys 20 import sys
21 import tempfile 21 import tempfile
22 import zipfile 22 import zipfile
23 import zlib 23 import zlib
24 24
25 import devil_chromium 25 import devil_chromium
26 from devil.utils import cmd_helper 26 from devil.utils import cmd_helper
27 import method_count
27 from pylib import constants 28 from pylib import constants
28 from pylib.constants import host_paths 29 from pylib.constants import host_paths
29 30
30 _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit') 31 _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit')
31 32
32 # Prepend the grit module from the source tree so it takes precedence over other 33 # Prepend the grit module from the source tree so it takes precedence over other
33 # grit versions that might present in the search path. 34 # grit versions that might present in the search path.
34 with host_paths.SysPath(_GRIT_PATH, 1): 35 with host_paths.SysPath(_GRIT_PATH, 1):
35 from grit.format import data_pack # pylint: disable=import-error 36 from grit.format import data_pack # pylint: disable=import-error
36 37
37 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): 38 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
38 import perf_tests_results_helper # pylint: disable=import-error 39 import perf_tests_results_helper # pylint: disable=import-error
39 40
41
40 # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk 42 # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk
41 # https://bugs.python.org/issue14315 43 # https://bugs.python.org/issue14315
42 def _PatchedDecodeExtra(self): 44 def _PatchedDecodeExtra(self):
43 # Try to decode the extra field. 45 # Try to decode the extra field.
44 extra = self.extra 46 extra = self.extra
45 unpack = struct.unpack 47 unpack = struct.unpack
46 while len(extra) >= 4: 48 while len(extra) >= 4:
47 tp, ln = unpack('<HH', extra[:4]) 49 tp, ln = unpack('<HH', extra[:4])
48 if tp == 1: 50 if tp == 1:
49 if ln >= 24: 51 if ln >= 24:
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 if m: 349 if m:
348 i = int(m.group('id')) 350 i = int(m.group('id'))
349 name = m.group('name') 351 name = m.group('name')
350 if i in id_name_map and name != id_name_map[i]: 352 if i in id_name_map and name != id_name_map[i]:
351 print 'WARNING: Resource ID conflict %s (%s vs %s)' % ( 353 print 'WARNING: Resource ID conflict %s (%s vs %s)' % (
352 i, id_name_map[i], name) 354 i, id_name_map[i], name)
353 id_name_map[i] = name 355 id_name_map[i] = name
354 return id_name_map 356 return id_name_map
355 357
356 358
357 def PrintStaticInitializersCount(so_with_symbols_path, chartjson=None): 359 def _PrintStaticInitializersCountFromApk(apk_filename, chartjson=None):
358 """Emits the performance result for static initializers found in the provided 360 print 'Finding static initializers (can take a minute)'
359 shared library. Additionally, files for which static initializers were 361 with zipfile.ZipFile(apk_filename) as z:
360 found are printed on the standard output. 362 infolist = z.infolist()
363 out_dir = constants.GetOutDirectory()
364 si_count = 0
365 for zip_info in infolist:
366 # Check file size to account for placeholder libraries.
367 if zip_info.filename.endswith('.so') and zip_info.file_size > 0:
368 unstripped_path = os.path.join(out_dir, 'lib.unstripped',
369 os.path.basename(zip_info.filename))
370 if os.path.exists(unstripped_path):
371 si_count += _PrintStaticInitializersCount(unstripped_path)
372 else:
373 raise Exception('Unstripped .so not found. Looked here: %s',
374 unstripped_path)
375 ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', si_count,
376 'count')
377
378
379 def _PrintStaticInitializersCount(so_with_symbols_path):
380 """Counts the number of static initializers in the given shared library.
381 Additionally, files for which static initializers were found are printed
382 on the standard output.
361 383
362 Args: 384 Args:
363 so_with_symbols_path: Path to the unstripped libchrome.so file. 385 so_with_symbols_path: Path to the unstripped libchrome.so file.
386
387 Returns:
388 The number of static initializers found.
364 """ 389 """
365 # GetStaticInitializers uses get-static-initializers.py to get a list of all 390 # GetStaticInitializers uses get-static-initializers.py to get a list of all
366 # static initializers. This does not work on all archs (particularly arm). 391 # static initializers. This does not work on all archs (particularly arm).
367 # TODO(rnephew): Get rid of warning when crbug.com/585588 is fixed. 392 # TODO(rnephew): Get rid of warning when crbug.com/585588 is fixed.
368 si_count = CountStaticInitializers(so_with_symbols_path) 393 si_count = CountStaticInitializers(so_with_symbols_path)
369 static_initializers = GetStaticInitializers(so_with_symbols_path) 394 static_initializers = GetStaticInitializers(so_with_symbols_path)
370 if si_count != len(static_initializers): 395 static_initializers_count = len(static_initializers) - 1 # Minus summary.
396 if si_count != static_initializers_count:
371 print ('There are %d files with static initializers, but ' 397 print ('There are %d files with static initializers, but '
372 'dump-static-initializers found %d:' % 398 'dump-static-initializers found %d:' %
373 (si_count, len(static_initializers))) 399 (si_count, static_initializers_count))
374 else: 400 else:
375 print 'Found %d files with static initializers:' % si_count 401 print '%s - Found %d files with static initializers:' % (
402 os.path.basename(so_with_symbols_path), si_count)
376 print '\n'.join(static_initializers) 403 print '\n'.join(static_initializers)
377 404
378 ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', 405 return si_count
379 si_count, 'count')
380 406
381 def _FormatBytes(byts): 407 def _FormatBytes(byts):
382 """Pretty-print a number of bytes.""" 408 """Pretty-print a number of bytes."""
383 if byts > 2**20.0: 409 if byts > 2**20.0:
384 byts /= 2**20.0 410 byts /= 2**20.0
385 return '%.2fm' % byts 411 return '%.2fm' % byts
386 if byts > 2**10.0: 412 if byts > 2**10.0:
387 byts /= 2**10.0 413 byts /= 2**10.0
388 return '%.2fk' % byts 414 return '%.2fk' % byts
389 return str(byts) 415 return str(byts)
390 416
391 417
392 def _CalculateCompressedSize(file_path): 418 def _CalculateCompressedSize(file_path):
393 CHUNK_SIZE = 256 * 1024 419 CHUNK_SIZE = 256 * 1024
394 compressor = zlib.compressobj() 420 compressor = zlib.compressobj()
395 total_size = 0 421 total_size = 0
396 with open(file_path, 'rb') as f: 422 with open(file_path, 'rb') as f:
397 for chunk in iter(lambda: f.read(CHUNK_SIZE), ''): 423 for chunk in iter(lambda: f.read(CHUNK_SIZE), ''):
398 total_size += len(compressor.compress(chunk)) 424 total_size += len(compressor.compress(chunk))
399 total_size += len(compressor.flush()) 425 total_size += len(compressor.flush())
400 return total_size 426 return total_size
401 427
402 428
429 def _PrintDexAnalysis(apk_filename, chartjson=None):
430 sizes = method_count.ExtractSizesFromZip(apk_filename)
431
432 graph_title = os.path.basename(apk_filename) + '_Dex'
433 dex_metrics = method_count.CONTRIBUTORS_TO_DEX_CACHE
434 for key, label in dex_metrics.iteritems():
435 ReportPerfResult(chartjson, graph_title, label, sizes[key], 'entries')
436
437 graph_title = '%sCache' % graph_title
438 ReportPerfResult(chartjson, graph_title, 'DexCache', sizes['dex_cache_size'],
439 'bytes')
440
441
403 def main(argv): 442 def main(argv):
404 usage = """Usage: %prog [options] file1 file2 ... 443 usage = """Usage: %prog [options] file1 file2 ...
405 444
406 Pass any number of files to graph their sizes. Any files with the extension 445 Pass any number of files to graph their sizes. Any files with the extension
407 '.apk' will be broken down into their components on a separate graph.""" 446 '.apk' will be broken down into their components on a separate graph."""
408 option_parser = optparse.OptionParser(usage=usage) 447 option_parser = optparse.OptionParser(usage=usage)
409 option_parser.add_option('--so-path', help='Path to libchrome.so.') 448 option_parser.add_option('--so-path',
449 help='Obsolete. Pass .so as positional arg instead.')
410 option_parser.add_option('--so-with-symbols-path', 450 option_parser.add_option('--so-with-symbols-path',
411 help='Path to libchrome.so with symbols.') 451 help='Mostly obsolete. Use .so within .apk instead.')
412 option_parser.add_option('--min-pak-resource-size', type='int', 452 option_parser.add_option('--min-pak-resource-size', type='int',
413 default=20*1024, 453 default=20*1024,
414 help='Minimum byte size of displayed pak resources.') 454 help='Minimum byte size of displayed pak resources.')
415 option_parser.add_option('--build_type', dest='build_type', default='Debug', 455 option_parser.add_option('--build_type', dest='build_type', default='Debug',
416 help='Sets the build type, default is Debug.') 456 help='Obsoleted by --chromium-output-directory.')
417 option_parser.add_option('--chromium-output-directory', 457 option_parser.add_option('--chromium-output-directory',
418 help='Location of the build artifacts. ' 458 help='Location of the build artifacts. '
419 'Takes precidence over --build_type.') 459 'Takes precidence over --build_type.')
420 option_parser.add_option('--chartjson', action="store_true", 460 option_parser.add_option('--chartjson', action="store_true",
421 help='Sets output mode to chartjson.') 461 help='Sets output mode to chartjson.')
422 option_parser.add_option('--output-dir', default='.', 462 option_parser.add_option('--output-dir', default='.',
423 help='Directory to save chartjson to.') 463 help='Directory to save chartjson to.')
424 option_parser.add_option('-d', '--device', 464 option_parser.add_option('-d', '--device',
425 help='Dummy option for perf runner.') 465 help='Dummy option for perf runner.')
426 options, args = option_parser.parse_args(argv) 466 options, args = option_parser.parse_args(argv)
(...skipping 10 matching lines...) Expand all
437 # more. 477 # more.
438 if options.so_path: 478 if options.so_path:
439 files.append(options.so_path) 479 files.append(options.so_path)
440 480
441 if not files: 481 if not files:
442 option_parser.error('Must specify a file') 482 option_parser.error('Must specify a file')
443 483
444 devil_chromium.Initialize() 484 devil_chromium.Initialize()
445 485
446 if options.so_with_symbols_path: 486 if options.so_with_symbols_path:
447 PrintStaticInitializersCount( 487 si_count = _PrintStaticInitializersCount(options.so_with_symbols_path)
448 options.so_with_symbols_path, chartjson=chartjson) 488 ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', si_count,
489 'count')
449 490
450 PrintResourceSizes(files, chartjson=chartjson) 491 PrintResourceSizes(files, chartjson=chartjson)
451 492
452 for f in files: 493 for f in files:
453 if f.endswith('.apk'): 494 if f.endswith('.apk'):
454 PrintApkAnalysis(f, chartjson=chartjson) 495 PrintApkAnalysis(f, chartjson=chartjson)
455 PrintPakAnalysis(f, options.min_pak_resource_size) 496 PrintPakAnalysis(f, options.min_pak_resource_size)
497 _PrintDexAnalysis(f, chartjson=chartjson)
498 if not options.so_with_symbols_path:
499 _PrintStaticInitializersCountFromApk(f, chartjson=chartjson)
456 500
457 if chartjson: 501 if chartjson:
458 results_path = os.path.join(options.output_dir, 'results-chart.json') 502 results_path = os.path.join(options.output_dir, 'results-chart.json')
459 logging.critical('Dumping json to %s', results_path) 503 logging.critical('Dumping json to %s', results_path)
460 with open(results_path, 'w') as json_file: 504 with open(results_path, 'w') as json_file:
461 json.dump(chartjson, json_file) 505 json.dump(chartjson, json_file)
462 506
463 507
464 if __name__ == '__main__': 508 if __name__ == '__main__':
465 sys.exit(main(sys.argv)) 509 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « build/android/method_count.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698