Index: tools/binary_size/libsupersize/models.py |
diff --git a/tools/binary_size/libsupersize/models.py b/tools/binary_size/libsupersize/models.py |
index 93843dcc92f1877aa6b46f17bf19aac6de250545..c2784232a852d8ea72b32b1edc0a46c405815d95 100644 |
--- a/tools/binary_size/libsupersize/models.py |
+++ b/tools/binary_size/libsupersize/models.py |
@@ -10,6 +10,8 @@ Description of common properties: |
May be 0 (e.g. for .bss or for SymbolGroups). |
* size: The number of bytes this symbol takes up, including padding that comes |
before |address|. |
+ * num_aliases: The number of symbols with the same address (including self). |
+ * pss: size / num_aliases. |
* padding: The number of bytes of padding before |address| due to this symbol. |
* name: Symbol names with parameter list removed. |
Never None, but will be '' for anonymous symbols. |
@@ -23,7 +25,7 @@ Description of common properties: |
""" |
import collections |
-import copy |
+import logging |
import os |
import re |
@@ -54,6 +56,13 @@ FLAG_REL = 8 |
FLAG_REL_LOCAL = 16 |
+def _StripCloneSuffix(name): |
+ clone_idx = name.find(' [clone ') |
+ if clone_idx != -1: |
+ return name[:clone_idx] |
+ return name |
+ |
+ |
class SizeInfo(object): |
"""Represents all size information for a single binary. |
@@ -132,10 +141,14 @@ class BaseSymbol(object): |
def is_anonymous(self): |
return bool(self.flags & FLAG_ANONYMOUS) |
+ @property |
+ def num_aliases(self): |
+ return len(self.aliases) if self.aliases else 1 |
+ |
def FlagsString(self): |
# Most flags are 0. |
flags = self.flags |
- if not flags: |
+ if not flags and not self.aliases: |
return '{}' |
parts = [] |
if flags & FLAG_ANONYMOUS: |
@@ -148,6 +161,9 @@ class BaseSymbol(object): |
parts.append('rel') |
if flags & FLAG_REL_LOCAL: |
parts.append('rel.loc') |
+ # Not actually a part of flags, but useful to show it here. |
+ if self.aliases: |
+ parts.append('{} aliases'.format(self.num_aliases)) |
return '{%s}' % ','.join(parts) |
def IsBss(self): |
@@ -169,9 +185,7 @@ class BaseSymbol(object): |
common key.""" |
stripped_full_name = self.full_name |
if stripped_full_name: |
- clone_idx = stripped_full_name.find(' [clone ') |
- if clone_idx != -1: |
- stripped_full_name = stripped_full_name[:clone_idx] |
+ stripped_full_name = _StripCloneSuffix(stripped_full_name) |
return (self.section_name, stripped_full_name or self.name) |
@@ -187,6 +201,7 @@ class Symbol(BaseSymbol): |
'flags', |
'object_path', |
'name', |
+ 'aliases', |
'padding', |
'section_name', |
'source_path', |
@@ -195,7 +210,7 @@ class Symbol(BaseSymbol): |
def __init__(self, section_name, size_without_padding, address=None, |
name=None, source_path=None, object_path=None, full_name=None, |
- flags=0): |
+ flags=0, aliases=None): |
self.section_name = section_name |
self.address = address or 0 |
self.name = name or '' |
@@ -204,13 +219,24 @@ class Symbol(BaseSymbol): |
self.object_path = object_path or '' |
self.size = size_without_padding |
self.flags = flags |
+ self.aliases = aliases |
self.padding = 0 |
def __repr__(self): |
- return ('%s@%x(size_without_padding=%d,padding=%d,name=%s,path=%s,flags=%s)' |
- % (self.section_name, self.address, self.size_without_padding, |
- self.padding, self.name, self.source_path or self.object_path, |
- self.FlagsString())) |
+ template = ('{}@{:x}(size_without_padding={},padding={},name={},' |
+ 'object_path={},source_path={},flags={})') |
+ return template.format( |
+ self.section_name, self.address, self.size_without_padding, |
+ self.padding, self.name, self.object_path, self.source_path, |
+ self.FlagsString()) |
+ |
+ @property |
+ def pss(self): |
+ return float(self.size) / self.num_aliases |
+ |
+ @property |
+ def pss_without_padding(self): |
+ return float(self.size_without_padding) / self.num_aliases |
class SymbolGroup(BaseSymbol): |
@@ -231,6 +257,7 @@ class SymbolGroup(BaseSymbol): |
__slots__ = ( |
'_padding', |
'_size', |
+ '_pss', |
'_symbols', |
'_filtered_symbols', |
'full_name', |
@@ -243,6 +270,7 @@ class SymbolGroup(BaseSymbol): |
full_name=None, section_name=None, is_sorted=False): |
self._padding = None |
self._size = None |
+ self._pss = None |
self._symbols = symbols |
self._filtered_symbols = filtered_symbols or [] |
self.name = name or '' |
@@ -302,27 +330,50 @@ class SymbolGroup(BaseSymbol): |
@property |
def object_path(self): |
first = self._symbols[0].object_path |
- return first if all(s.object_path == first for s in self._symbols) else None |
+ return first if all(s.object_path == first for s in self._symbols) else '' |
@property |
def source_path(self): |
first = self._symbols[0].source_path |
- return first if all(s.source_path == first for s in self._symbols) else None |
+ return first if all(s.source_path == first for s in self._symbols) else '' |
+ |
+ def IterUniqueSymbols(self): |
+ seen_aliases_lists = set() |
+ for s in self: |
+ if not s.aliases: |
+ yield s |
+ elif id(s.aliases) not in seen_aliases_lists: |
+ seen_aliases_lists.add(id(s.aliases)) |
+ yield s |
@property |
def size(self): |
if self._size is None: |
if self.IsBss(): |
self._size = sum(s.size for s in self) |
- self._size = sum(s.size for s in self if not s.IsBss()) |
+ else: |
+ self._size = sum(s.size for s in self.IterUniqueSymbols()) |
return self._size |
@property |
+ def pss(self): |
+ if self._pss is None: |
+ if self.IsBss(): |
+ self._pss = self.size |
+ else: |
+ self._pss = sum(s.pss for s in self) |
+ return self._pss |
+ |
+ @property |
def padding(self): |
if self._padding is None: |
- self._padding = sum(s.padding for s in self) |
+ self._padding = sum(s.padding for s in self.IterUniqueSymbols()) |
return self._padding |
+ @property |
+ def aliases(self): |
+ return None |
+ |
def IsGroup(self): |
return True |
@@ -356,8 +407,14 @@ class SymbolGroup(BaseSymbol): |
def Filter(self, func): |
filtered_and_kept = ([], []) |
- for symbol in self: |
- filtered_and_kept[int(bool(func(symbol)))].append(symbol) |
+ symbol = None |
+ try: |
+ for symbol in self: |
+ filtered_and_kept[int(bool(func(symbol)))].append(symbol) |
+ except: |
+ logging.warning('Filter failed on symbol %r', symbol) |
+ raise |
+ |
return self._CreateTransformed(filtered_and_kept[1], |
filtered_symbols=filtered_and_kept[0], |
section_name=self.section_name) |
@@ -615,95 +672,6 @@ class SymbolDiff(SymbolGroup): |
return self.Filter(lambda s: not self.IsSimilar(s) or s.size) |
-def Diff(before, after): |
- """Diffs two SizeInfo or SymbolGroup objects. |
- |
- When diffing SizeInfos, a SizeInfoDiff is returned. |
- When diffing SymbolGroups, a SymbolDiff is returned. |
- |
- Returns: |
- Returns a SizeInfo when args are of type SizeInfo. |
- Returns a SymbolDiff when args are of type SymbolGroup. |
- """ |
- if isinstance(after, SizeInfo): |
- assert isinstance(before, SizeInfo) |
- section_sizes = {k: after.section_sizes[k] - v |
- for k, v in before.section_sizes.iteritems()} |
- symbol_diff = _DiffSymbols(before.symbols, after.symbols) |
- return SizeInfoDiff(section_sizes, symbol_diff, before.metadata, |
- after.metadata) |
- |
- assert isinstance(after, SymbolGroup) and isinstance(before, SymbolGroup) |
- return _DiffSymbols(before, after) |
- |
- |
-def _NegateAll(symbols): |
- ret = [] |
- for symbol in symbols: |
- if symbol.IsGroup(): |
- duped = SymbolDiff([], _NegateAll(symbol), [], name=symbol.name, |
- full_name=symbol.full_name, |
- section_name=symbol.section_name) |
- else: |
- duped = copy.copy(symbol) |
- duped.size = -duped.size |
- duped.padding = -duped.padding |
- ret.append(duped) |
- return ret |
- |
- |
-def _DiffSymbols(before, after): |
- symbols_by_key = collections.defaultdict(list) |
- for s in before: |
- symbols_by_key[s._Key()].append(s) |
- |
- added = [] |
- similar = [] |
- # For similar symbols, padding is zeroed out. In order to not lose the |
- # information entirely, store it in aggregate. |
- padding_by_section_name = collections.defaultdict(int) |
- for after_sym in after: |
- matching_syms = symbols_by_key.get(after_sym._Key()) |
- if matching_syms: |
- before_sym = matching_syms.pop(0) |
- if before_sym.IsGroup() and after_sym.IsGroup(): |
- merged_sym = _DiffSymbols(before_sym, after_sym) |
- else: |
- size_diff = (after_sym.size_without_padding - |
- before_sym.size_without_padding) |
- merged_sym = Symbol(after_sym.section_name, size_diff, |
- address=after_sym.address, name=after_sym.name, |
- source_path=after_sym.source_path, |
- object_path=after_sym.object_path, |
- full_name=after_sym.full_name, |
- flags=after_sym.flags) |
- |
- # Diffs are more stable when comparing size without padding, except when |
- # the symbol is a padding-only symbol. |
- if after_sym.size_without_padding == 0 and size_diff == 0: |
- merged_sym.padding = after_sym.padding - before_sym.padding |
- else: |
- padding_by_section_name[after_sym.section_name] += ( |
- after_sym.padding - before_sym.padding) |
- |
- similar.append(merged_sym) |
- else: |
- added.append(after_sym) |
- |
- removed = [] |
- for remaining_syms in symbols_by_key.itervalues(): |
- if remaining_syms: |
- removed.extend(_NegateAll(remaining_syms)) |
- |
- for section_name, padding in padding_by_section_name.iteritems(): |
- if padding != 0: |
- similar.append(Symbol(section_name, padding, |
- name="** aggregate padding of diff'ed symbols")) |
- return SymbolDiff(added, removed, similar, name=after.name, |
- full_name=after.full_name, |
- section_name=after.section_name) |
- |
- |
def _ExtractPrefixBeforeSeparator(string, separator, count=1): |
idx = -len(separator) |
prev_idx = None |