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

Unified Diff: tools/binary_size/libsupersize/archive.py

Issue 2807343005: //tools/binary_size: Record packed .rel.dyn section size (Closed)
Patch Set: Created 3 years, 8 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/binary_size/libsupersize/console.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/binary_size/libsupersize/archive.py
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index 0991f0b8c6be2af46dd509063ffe273bc5130acb..a049b60297ed404c0182612251df556496c36f37 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -11,9 +11,12 @@ import datetime
import gzip
import logging
import os
+import posixpath
import re
import subprocess
import sys
+import tempfile
+import zipfile
import describe
import file_format
@@ -205,12 +208,16 @@ def _ClusterSymbols(symbols):
Groups include:
* Symbols that have [clone] in their name (created by compiler optimization).
* Star symbols (such as "** merge strings", and "** symbol gap")
+
+ To view created groups:
+ Print(size_info.symbols.Filter(lambda s: s.IsGroup()), recursive=True)
"""
# http://unix.stackexchange.com/questions/223013/function-symbol-gets-part-suffix-after-compilation
# Example name suffixes:
- # [clone .part.322]
- # [clone .isra.322]
- # [clone .constprop.1064]
+ # [clone .part.322] # GCC
+ # [clone .isra.322] # GCC
+ # [clone .constprop.1064] # GCC
+ # [clone .11064] # clang
# Step 1: Create name map, find clones, collect star syms into replacements.
logging.debug('Creating name -> symbol map')
@@ -351,6 +358,9 @@ def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False,
_PostProcessSizeInfo(size_info)
if logging.getLogger().isEnabledFor(logging.DEBUG):
+ # Padding is reported in size coverage logs.
+ if raw_only:
+ _CalculatePadding(size_info.raw_symbols)
for line in describe.DescribeSizeInfoCoverage(size_info):
logging.info(line)
logging.info('Recorded info for %d symbols', len(size_info.raw_symbols))
@@ -399,15 +409,36 @@ def _ParseGnArgs(args_path):
return ["%s=%s" % x for x in sorted(args.iteritems())]
+def _SectionSizesFromApk(apk_file, elf_file, build_id, lazy_paths):
+ with zipfile.ZipFile(apk_file) as apk, \
+ tempfile.NamedTemporaryFile() as f:
+ target = os.path.basename(elf_file)
+ target_info = next((f for f in apk.infolist()
+ if posixpath.basename(f.filename) == target), None)
+ assert target_info, (
+ 'Could not find apk entry for %s in %s' % (target, apk_file))
+ f.write(apk.read(target_info))
+ f.flush()
+ apk_build_id = BuildIdFromElf(f.name, lazy_paths.tool_prefix)
+ assert apk_build_id == build_id, (
+ 'BuildID for %s within %s did not match the one at %s' %
+ (target_info.filename, apk_file, elf_file))
+ return _SectionSizesFromElf(f.name, lazy_paths.tool_prefix)
+
+
def AddArguments(parser):
parser.add_argument('size_file', help='Path to output .size file.')
- parser.add_argument('--elf-file', required=True,
+ parser.add_argument('--apk-file',
+ help='.apk file to measure. When set, --elf-file will be '
+ 'derived (if unset). Providing the .apk allows '
+ 'for the size of packed relocations to be recorded')
+ parser.add_argument('--elf-file',
help='Path to input ELF file. Currently used for '
- 'capturing metadata. Pass "" to skip '
- 'metadata collection.')
+ 'capturing metadata.')
parser.add_argument('--map-file',
help='Path to input .map(.gz) file. Defaults to '
- '{{elf_file}}.map(.gz)?')
+ '{{elf_file}}.map(.gz)?. If given without '
+ '--elf-file, no size metadata will be recorded.')
parser.add_argument('--no-source-paths', action='store_true',
help='Do not use .ninja files to map '
'object_path -> source_path')
@@ -421,26 +452,43 @@ def Run(args, parser):
if not args.size_file.endswith('.size'):
parser.error('size_file must end with .size')
+ any_input = args.apk_file or args.elf_file or args.map_file
+ if not any_input:
+ parser.error('Most pass at least one of --apk-file, --elf-file, --map-file')
+ lazy_paths = paths.LazyPaths(args=args, input_file=any_input)
+
+ elf_file = args.elf_file
+ if args.apk_file and not elf_file:
+ with zipfile.ZipFile(args.apk_file) as z:
+ lib_infos = [f for f in z.infolist()
+ if f.filename.endswith('.so') and f.file_size > 0]
+ assert lib_infos, 'APK has not .so files to measure.'
estevenson 2017/04/12 16:54:42 nit: awkward wording.
agrieve 2017/04/12 19:37:46 Done.
+ # TODO(agrieve): Add support for multiple .so files, and take into account
+ # secondary architectures.
estevenson 2017/04/12 16:54:42 _SectionSizesFromApk() would fail for 64 bit monoc
agrieve 2017/04/12 19:37:45 Didn't hit any of those other errors, but good cat
+ apk_so_path = max(lib_infos, key=lambda x:x.file_size).filename
+ elf_file = os.path.join(lazy_paths.output_directory, 'lib.unstripped',
+ os.path.basename(apk_so_path))
+ logging.debug('Detected --elf-file=%s', elf_file)
+
if args.map_file:
if (not args.map_file.endswith('.map')
and not args.map_file.endswith('.map.gz')):
parser.error('Expected --map-file to end with .map or .map.gz')
map_file_path = args.map_file
else:
- map_file_path = args.elf_file + '.map'
+ map_file_path = elf_file + '.map'
if not os.path.exists(map_file_path):
map_file_path += '.gz'
if not os.path.exists(map_file_path):
parser.error('Could not find .map(.gz)? file. Use --map-file.')
- lazy_paths = paths.LazyPaths(args=args, input_file=args.elf_file)
metadata = None
- if args.elf_file:
+ if elf_file:
logging.debug('Constructing metadata')
- git_rev = _DetectGitRevision(os.path.dirname(args.elf_file))
- build_id = BuildIdFromElf(args.elf_file, lazy_paths.tool_prefix)
+ git_rev = _DetectGitRevision(os.path.dirname(elf_file))
+ build_id = BuildIdFromElf(elf_file, lazy_paths.tool_prefix)
timestamp_obj = datetime.datetime.utcfromtimestamp(os.path.getmtime(
- args.elf_file))
+ elf_file))
timestamp = calendar.timegm(timestamp_obj.timetuple())
gn_args = _ParseGnArgs(os.path.join(lazy_paths.output_directory, 'args.gn'))
@@ -450,7 +498,7 @@ def Run(args, parser):
metadata = {
models.METADATA_GIT_REVISION: git_rev,
models.METADATA_MAP_FILENAME: relative_to_out(map_file_path),
- models.METADATA_ELF_FILENAME: relative_to_out(args.elf_file),
+ models.METADATA_ELF_FILENAME: relative_to_out(elf_file),
models.METADATA_ELF_MTIME: timestamp,
models.METADATA_ELF_BUILD_ID: build_id,
models.METADATA_GN_ARGS: gn_args,
@@ -463,12 +511,21 @@ def Run(args, parser):
if metadata:
size_info.metadata = metadata
logging.debug('Validating section sizes')
- elf_section_sizes = _SectionSizesFromElf(args.elf_file,
- lazy_paths.tool_prefix)
+ elf_section_sizes = _SectionSizesFromElf(elf_file, lazy_paths.tool_prefix)
for k, v in elf_section_sizes.iteritems():
assert v == size_info.section_sizes.get(k), (
'ELF file and .map file do not match.')
+ if args.apk_file:
+ logging.debug('Extracting section sizes from .so within .apk')
+ unpacked_rel_dyn = size_info.section_sizes.get('.rel.dyn', 0)
+ if not unpacked_rel_dyn:
+ logging.warning('Section .rel.dyn did not exist')
+ # TODO(agrieve): Extracting the .so is slow. Do it on a background thread.
+ size_info.section_sizes = _SectionSizesFromApk(
+ args.apk_file, elf_file, build_id, lazy_paths)
+ size_info.section_sizes['.rel.dyn (unpacked)'] = unpacked_rel_dyn
+
logging.info('Recording metadata: \n %s',
'\n '.join(describe.DescribeMetadata(size_info.metadata)))
logging.info('Saving result to %s', args.size_file)
« no previous file with comments | « no previous file | tools/binary_size/libsupersize/console.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698