| Index: tools/binary_size/libsupersize/archive.py
|
| diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
|
| index 97f12f402b5f9d71234d8f0ffdf003a0e401214e..d2b715d885824d2978c8c10a6c720094fc1ca276 100644
|
| --- a/tools/binary_size/libsupersize/archive.py
|
| +++ b/tools/binary_size/libsupersize/archive.py
|
| @@ -11,7 +11,6 @@ import datetime
|
| import gzip
|
| import logging
|
| import os
|
| -import multiprocessing
|
| import posixpath
|
| import re
|
| import subprocess
|
| @@ -26,6 +25,7 @@ import helpers
|
| import linker_map_parser
|
| import models
|
| import ninja_parser
|
| +import nm
|
| import paths
|
|
|
|
|
| @@ -173,29 +173,30 @@ def _CalculatePadding(symbols):
|
| continue
|
| if symbol.address <= 0 or prev_symbol.address <= 0:
|
| continue
|
| - # Padding-only symbols happen for ** symbol gaps.
|
| - prev_is_padding_only = prev_symbol.size_without_padding == 0
|
| - if symbol.address == prev_symbol.address and not prev_is_padding_only:
|
| - assert False, 'Found duplicate symbols:\n%r\n%r' % (prev_symbol, symbol)
|
| - # Even with symbols at the same address removed, overlaps can still
|
| - # happen. In this case, padding will be negative (and this is fine).
|
| +
|
| + if symbol.address == prev_symbol.address:
|
| + # Padding-only symbols happen for ** symbol gaps.
|
| + prev_is_padding_only = prev_symbol.size_without_padding == 0
|
| + if not prev_is_padding_only:
|
| + # Must be an alias. Clone its padding.
|
| + assert symbol.num_aliases > 1, (
|
| + 'Found duplicate symbols:\n%r\n%r' % (prev_symbol, symbol))
|
| + symbol.padding = prev_symbol.padding
|
| + symbol.size = prev_symbol.size
|
| + continue
|
| +
|
| padding = symbol.address - prev_symbol.end_address
|
| - # These thresholds were found by manually auditing arm32 Chrome.
|
| - # E.g.: Set them to 0 and see what warnings get logged.
|
| + # These thresholds were found by experimenting with arm32 Chrome.
|
| + # E.g.: Set them to 0 and see what warnings get logged, then take max value.
|
| # TODO(agrieve): See if these thresholds make sense for architectures
|
| # other than arm32.
|
| if not symbol.name.startswith('*') and (
|
| symbol.section in 'rd' and padding >= 256 or
|
| symbol.section in 't' and padding >= 64):
|
| - # For nm data, this is caused by data that has no associated symbol.
|
| - # The linker map file lists them with no name, but with a file.
|
| - # Example:
|
| - # .data 0x02d42764 0x120 .../V8SharedWorkerGlobalScope.o
|
| - # Where as most look like:
|
| - # .data.MANGLED_NAME...
|
| - logging.debug('Large padding of %d between:\n A) %r\n B) %r' % (
|
| - padding, prev_symbol, symbol))
|
| - continue
|
| + # Should not happen.
|
| + logging.warning('Large padding of %d between:\n A) %r\n B) %r' % (
|
| + padding, prev_symbol, symbol))
|
| + #continue
|
| symbol.padding = padding
|
| symbol.size += padding
|
| assert symbol.size >= 0, (
|
| @@ -306,6 +307,46 @@ def _ClusterSymbols(symbols):
|
| return grouped_symbols
|
|
|
|
|
| +def _AddSymbolAliases(symbols, aliases_by_address):
|
| + # Step 1: Create list of (index_of_symbol, name_list).
|
| + replacements = []
|
| + num_new_symbols = 0
|
| + for i, s in enumerate(symbols):
|
| + name_list = aliases_by_address.get(s.address)
|
| + if not name_list:
|
| + continue
|
| +
|
| + if s.name not in name_list:
|
| + logging.warning('Name missing from aliases: %s %s', s.name, name_list)
|
| + continue
|
| +
|
| + replacements.append((i, name_list))
|
| + num_new_symbols += len(name_list) - 1
|
| +
|
| + # Step 2: Create new symbols.
|
| + src_cursor_end = len(symbols)
|
| + symbols += [None] * num_new_symbols
|
| + dst_cursor_end = len(symbols)
|
| + num_copied = 0
|
| + for src_index, name_list in reversed(replacements):
|
| + chunk_size = src_cursor_end - src_index - 1
|
| + dst_cursor_end -= chunk_size
|
| + src_cursor_end -= chunk_size
|
| + symbols[dst_cursor_end:dst_cursor_end + chunk_size] = (
|
| + symbols[src_cursor_end:src_cursor_end + chunk_size])
|
| + sym = symbols[src_index]
|
| + src_cursor_end -= 1
|
| + assert src_cursor_end == src_index
|
| + num_aliases = len(name_list)
|
| + for name in name_list:
|
| + dst_cursor_end -= 1
|
| + symbols[dst_cursor_end] = models.Symbol(
|
| + sym.section_name, sym.size, address=sym.address, name=name,
|
| + num_aliases=num_aliases)
|
| +
|
| + assert dst_cursor_end == src_cursor_end
|
| +
|
| +
|
| def LoadAndPostProcessSizeInfo(path):
|
| """Returns a SizeInfo for the given |path|."""
|
| logging.debug('Loading results from: %s', path)
|
| @@ -325,7 +366,7 @@ def _PostProcessSizeInfo(size_info):
|
| logging.info('Processed %d symbols', len(size_info.raw_symbols))
|
|
|
|
|
| -def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False,
|
| +def CreateSizeInfo(map_path, elf_path, lazy_paths=None, no_source_paths=False,
|
| raw_only=False):
|
| """Creates a SizeInfo from the given map file."""
|
| if not no_source_paths:
|
| @@ -334,10 +375,20 @@ def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False,
|
| # tool_prefix needed for c++filt.
|
| lazy_paths.VerifyToolPrefix()
|
|
|
| + if elf_path:
|
| + nm_result = helpers.ForkAndCall(nm.CollectAliasesByAddress, elf_path,
|
| + lazy_paths.tool_prefix)
|
| +
|
| with _OpenMaybeGz(map_path) as map_file:
|
| section_sizes, raw_symbols = (
|
| linker_map_parser.MapFileParser().Parse(map_file))
|
|
|
| + if elf_path:
|
| + logging.debug('Validating section sizes')
|
| + elf_section_sizes = _SectionSizesFromElf(elf_path, lazy_paths.tool_prefix)
|
| + for k, v in elf_section_sizes.iteritems():
|
| + assert v == section_sizes.get(k), 'ELF file and .map file do not match.'
|
| +
|
| if not no_source_paths:
|
| logging.info('Extracting source paths from .ninja files')
|
| all_found = _ExtractSourcePaths(raw_symbols, lazy_paths.output_directory)
|
| @@ -348,6 +399,13 @@ def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False,
|
| # Unmangle prints its own log statement.
|
| _UnmangleRemainingSymbols(raw_symbols, lazy_paths.tool_prefix)
|
| logging.info('Normalizing object paths')
|
| +
|
| + # See which symbols are shared.
|
| + if elf_path:
|
| + logging.info('Adding aliased symbols reported by nm')
|
| + aliases_by_address = nm_result.get()
|
| + _AddSymbolAliases(raw_symbols, aliases_by_address)
|
| +
|
| _NormalizeObjectPaths(raw_symbols)
|
| size_info = models.SizeInfo(section_sizes, raw_symbols)
|
|
|
| @@ -514,21 +572,15 @@ def Run(args, parser):
|
| if apk_path:
|
| metadata[models.METADATA_APK_FILENAME] = relative_to_out(apk_path)
|
| # Extraction takes around 1 second, so do it in parallel.
|
| - pool_of_one = multiprocessing.Pool(1)
|
| - apk_elf_result = pool_of_one.apply_async(
|
| - _ElfInfoFromApk, (apk_path, apk_so_path, lazy_paths.tool_prefix))
|
| - pool_of_one.close()
|
| + apk_elf_result = helpers.ForkAndCall(
|
| + _ElfInfoFromApk, apk_path, apk_so_path, lazy_paths.tool_prefix)
|
|
|
| size_info = CreateSizeInfo(
|
| - map_path, lazy_paths, no_source_paths=args.no_source_paths, raw_only=True)
|
| + map_path, elf_path, lazy_paths, no_source_paths=args.no_source_paths,
|
| + raw_only=True)
|
|
|
| if metadata:
|
| size_info.metadata = metadata
|
| - logging.debug('Validating section sizes')
|
| - elf_section_sizes = _SectionSizesFromElf(elf_path, 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 apk_path:
|
| logging.debug('Extracting section sizes from .so within .apk')
|
|
|