| Index: tools/binary_size/file_format.py
|
| diff --git a/tools/binary_size/file_format.py b/tools/binary_size/file_format.py
|
| index 4f1bbb9180bb67ed2dbd89b9f26a52fad747a748..2e916778518b6b86eec36f1e80e25e200038cf61 100644
|
| --- a/tools/binary_size/file_format.py
|
| +++ b/tools/binary_size/file_format.py
|
| @@ -4,82 +4,148 @@
|
|
|
| """Deals with loading & saving .size files."""
|
|
|
| -import ast
|
| +import collections
|
| import gzip
|
| import models
|
| +import logging
|
| +import os
|
|
|
|
|
| # File format version for .size files.
|
| -_SERIALIZATION_VERSION = 1
|
| -
|
| -
|
| -def EndsWithMaybeGz(path, suffix):
|
| - return path.endswith(suffix) or path.endswith(suffix + '.gz')
|
| -
|
| -
|
| -def OpenMaybeGz(path, mode=None):
|
| - """Calls `gzip.open()` if |path| ends in ".gz", otherwise calls `open()`."""
|
| - if path.endswith('.gz'):
|
| - if mode and 'w' in mode:
|
| - return gzip.GzipFile(path, mode, 1)
|
| - return gzip.open(path, mode)
|
| - return open(path, mode or 'r')
|
| -
|
| -
|
| -def _SaveSizeInfoToFile(result, file_obj):
|
| - """Saves the result to the given file object."""
|
| - # Store one bucket per line.
|
| - file_obj.write('%d\n' % _SERIALIZATION_VERSION)
|
| - file_obj.write('%r\n' % result.section_sizes)
|
| - file_obj.write('%d\n' % len(result.symbols))
|
| - prev_section_name = None
|
| - # Store symbol fields as tab-separated.
|
| - # Store only non-derived fields.
|
| - for symbol in result.symbols:
|
| - if symbol.section_name != prev_section_name:
|
| - file_obj.write('%s\n' % symbol.section_name)
|
| - prev_section_name = symbol.section_name
|
| - # Don't write padding nor name since these are derived values.
|
| - file_obj.write('%x\t%x\t%s\t%s\n' % (
|
| - symbol.address, symbol.size_without_padding,
|
| - symbol.function_signature or symbol.name, symbol.path))
|
| -
|
| -
|
| -def _LoadSizeInfoFromFile(file_obj):
|
| - """Loads a result from the given file."""
|
| - lines = iter(file_obj)
|
| - actual_version = int(next(lines))
|
| +_SERIALIZATION_VERSION = 'Size File Format v1'
|
| +
|
| +
|
| +def _LogSize(file_obj, desc):
|
| + if not logging.getLogger().isEnabledFor(logging.DEBUG):
|
| + return
|
| + file_obj.flush()
|
| + size = os.fstat(file_obj.fileno()).st_size
|
| + logging.debug('File size with %s: %d' % (desc, size))
|
| +
|
| +
|
| +def _SaveSizeInfoToFile(size_info, file_obj):
|
| + file_obj.write('%s\n' % _SERIALIZATION_VERSION)
|
| + file_obj.write('# Created by //tools/binary_size\n')
|
| + file_obj.write('%s\n' % '\t'.join(size_info.section_sizes))
|
| + file_obj.write('%s\n' % '\t'.join(
|
| + str(v) for v in size_info.section_sizes.itervalues()))
|
| + _LogSize(file_obj, 'header') # For libchrome: 450 bytes.
|
| +
|
| + # Store a single copy of all paths and have them referenced by index.
|
| + # Using an OrderedDict makes the indices more repetitive (better compression).
|
| + path_tuples = collections.OrderedDict.fromkeys(
|
| + (s.object_path, s.source_path) for s in size_info.symbols)
|
| + for i, key in enumerate(path_tuples):
|
| + path_tuples[key] = i
|
| + file_obj.write('%d\n' % len(path_tuples))
|
| + file_obj.writelines('%s\t%s\n' % pair for pair in path_tuples)
|
| + _LogSize(file_obj, 'paths') # For libchrome, adds 200kb.
|
| +
|
| + # Symbol counts by section.
|
| + by_section = size_info.symbols.GroupBySectionName().SortedByName()
|
| + file_obj.write('%s\n' % '\t'.join(g.name for g in by_section))
|
| + file_obj.write('%s\n' % '\t'.join(str(len(g)) for g in by_section))
|
| +
|
| + def write_numeric(func, delta=False):
|
| + for group in by_section:
|
| + prev_value = 0
|
| + last_sym = group[-1]
|
| + for symbol in group:
|
| + value = func(symbol)
|
| + if delta:
|
| + value, prev_value = value - prev_value, value
|
| + file_obj.write(str(value))
|
| + if symbol is not last_sym:
|
| + file_obj.write(' ')
|
| + file_obj.write('\n')
|
| +
|
| + write_numeric(lambda s: s.address, delta=True)
|
| + _LogSize(file_obj, 'addresses') # For libchrome, adds 300kb.
|
| + # Do not write padding, it will be recalcualted from addresses on load.
|
| + write_numeric(lambda s: s.size_without_padding)
|
| + _LogSize(file_obj, 'sizes') # For libchrome, adds 300kb
|
| + write_numeric(lambda s: path_tuples[(s.object_path, s.source_path)],
|
| + delta=True)
|
| + _LogSize(file_obj, 'path indices') # For libchrome: adds 125kb.
|
| +
|
| + for group in by_section:
|
| + for symbol in group:
|
| + # Do not write name when full_name exists. It will be derived on load.
|
| + file_obj.write(symbol.full_name or symbol.name)
|
| + if symbol.is_anonymous:
|
| + file_obj.write('\t1')
|
| + file_obj.write('\n')
|
| + _LogSize(file_obj, 'names (final)') # For libchrome: adds 3.5mb.
|
| +
|
| +
|
| +def _LoadSizeInfoFromFile(lines):
|
| + """Loads a size_info from the given file."""
|
| + actual_version = next(lines)[:-1]
|
| assert actual_version == _SERIALIZATION_VERSION, (
|
| 'Version mismatch. Need to write some upgrade code.')
|
| -
|
| - section_sizes = ast.literal_eval(next(lines))
|
| - num_syms = int(next(lines))
|
| - symbol_list = [None] * num_syms
|
| - section_name = None
|
| - for i in xrange(num_syms):
|
| - line = next(lines)[:-1]
|
| - if '\t' not in line:
|
| - section_name = line
|
| + # Comment line.
|
| + next(lines)
|
| + section_names = next(lines)[:-1].split('\t')
|
| + section_values = next(lines)[:-1].split('\t')
|
| + section_sizes = {k: int(v) for k, v in zip(section_names, section_values)}
|
| +
|
| + num_path_tuples = int(next(lines))
|
| + path_tuples = [None] * num_path_tuples
|
| + for i in xrange(num_path_tuples):
|
| + path_tuples[i] = next(lines)[:-1].split('\t')
|
| +
|
| + section_names = next(lines)[:-1].split('\t')
|
| + section_counts = [int(c) for c in next(lines)[:-1].split('\t')]
|
| +
|
| + def read_numeric(delta=False):
|
| + ret = []
|
| + delta_multiplier = int(delta)
|
| + for _ in section_counts:
|
| + value = 0
|
| + fields = next(lines).split(' ')
|
| + for i, f in enumerate(fields):
|
| + value = value * delta_multiplier + int(f)
|
| + fields[i] = value
|
| + ret.append(fields)
|
| + return ret
|
| +
|
| + addresses = read_numeric(delta=True)
|
| + sizes = read_numeric(delta=False)
|
| + path_indices = read_numeric(delta=True)
|
| +
|
| + symbol_list = [None] * sum(section_counts)
|
| + symbol_idx = 0
|
| + for section_index, cur_section_name in enumerate(section_names):
|
| + for i in xrange(section_counts[section_index]):
|
| line = next(lines)[:-1]
|
| - new_sym = models.Symbol.__new__(models.Symbol)
|
| - parts = line.split('\t')
|
| - new_sym.section_name = section_name
|
| - new_sym.address = int(parts[0], 16)
|
| - new_sym.size = int(parts[1], 16)
|
| - new_sym.name = parts[2]
|
| - new_sym.path = parts[3]
|
| - new_sym.padding = 0 # Derived
|
| - new_sym.function_signature = None # Derived
|
| - symbol_list[i] = new_sym
|
| + is_anonymous = line.endswith('\t1')
|
| + name = line[:-2] if is_anonymous else line
|
| +
|
| + new_sym = models.Symbol.__new__(models.Symbol)
|
| + new_sym.section_name = cur_section_name
|
| + new_sym.address = addresses[section_index][i]
|
| + new_sym.size = sizes[section_index][i]
|
| + new_sym.name = name
|
| + paths = path_tuples[path_indices[section_index][i]]
|
| + new_sym.object_path = paths[0]
|
| + new_sym.source_path = paths[1]
|
| + new_sym.is_anonymous = is_anonymous
|
| + new_sym.padding = 0 # Derived
|
| + new_sym.full_name = None # Derived
|
| + symbol_list[symbol_idx] = new_sym
|
| + symbol_idx += 1
|
|
|
| - return models.SizeInfo(models.SymbolGroup(symbol_list), section_sizes)
|
| + symbols = models.SymbolGroup(symbol_list)
|
| + return models.SizeInfo(symbols, section_sizes)
|
|
|
|
|
| -def SaveSizeInfo(result, path):
|
| - with OpenMaybeGz(path, 'wb') as f:
|
| - _SaveSizeInfoToFile(result, f)
|
| +def SaveSizeInfo(size_info, path):
|
| + """Saves |size_info| to |path}."""
|
| + with gzip.open(path, 'wb') as f:
|
| + _SaveSizeInfoToFile(size_info, f)
|
|
|
|
|
| def LoadSizeInfo(path):
|
| - with OpenMaybeGz(path) as f:
|
| - return _LoadSizeInfoFromFile(f)
|
| + """Returns a SizeInfo loaded from |path|."""
|
| + with gzip.open(path) as f:
|
| + return _LoadSizeInfoFromFile(iter(f))
|
|
|