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') |