Index: tools/binary_size/libsupersize/models.py |
diff --git a/tools/binary_size/libsupersize/models.py b/tools/binary_size/libsupersize/models.py |
index 281e335f3c3ee76d26208e2286d713e34dcd5b31..6c70c67884513ad6c7cdca254e46c301ca557c19 100644 |
--- a/tools/binary_size/libsupersize/models.py |
+++ b/tools/binary_size/libsupersize/models.py |
@@ -30,7 +30,6 @@ import logging |
import os |
import re |
-import cluster_symbols |
import match_util |
@@ -58,6 +57,7 @@ FLAG_UNLIKELY = 4 |
FLAG_REL = 8 |
FLAG_REL_LOCAL = 16 |
FLAG_GENERATED_SOURCE = 32 |
+FLAG_CLONE = 64 |
DIFF_STATUS_UNCHANGED = 0 |
DIFF_STATUS_CHANGED = 1 |
@@ -70,34 +70,37 @@ class SizeInfo(object): |
Fields: |
section_sizes: A dict of section_name -> size. |
- raw_symbols: A list of all symbols, sorted by address. |
- symbols: A SymbolGroup containing all symbols. By default, these are the |
- same as raw_symbols, but may contain custom groupings when it is |
- desirable to convey the result of a query along with section_sizes and |
- metadata. |
+ raw_symbols: A SymbolGroup containing all top-level symbols (no groups). |
+ symbols: A SymbolGroup where symbols have been grouped by full_name (where |
+ applicable). May be re-assigned when it is desirable to show custom |
+ groupings while still printing metadata and section_sizes. |
metadata: A dict. |
""" |
__slots__ = ( |
'section_sizes', |
'raw_symbols', |
- 'symbols', |
+ '_symbols', |
'metadata', |
) |
"""Root size information.""" |
def __init__(self, section_sizes, raw_symbols, metadata=None, symbols=None): |
+ if isinstance(raw_symbols, list): |
+ raw_symbols = SymbolGroup(raw_symbols) |
self.section_sizes = section_sizes # E.g. {'.text': 0} |
self.raw_symbols = raw_symbols |
- self.symbols = symbols or SymbolGroup(raw_symbols) |
+ self._symbols = symbols |
self.metadata = metadata or {} |
- def Clustered(self): |
- """Returns a new SizeInfo with some symbols moved into subgroups. |
+ @property |
+ def symbols(self): |
+ if self._symbols is None: |
+ self._symbols = self.raw_symbols._Clustered() |
+ return self._symbols |
- See SymbolGroup.Clustered() for more details. |
- """ |
- return SizeInfo(self.section_sizes, self.raw_symbols, self.metadata, |
- symbols=self.symbols.Clustered()) |
+ @symbols.setter |
+ def symbols(self, value): |
+ self._symbols = value |
class SizeInfoDiff(object): |
@@ -105,22 +108,38 @@ class SizeInfoDiff(object): |
Fields: |
section_sizes: A dict of section_name -> size delta. |
- symbols: A SymbolDiff with all symbols in it. |
+ raw_symbols: A SymbolDiff with all top-level symbols in it (no groups). |
+ symbols: A SymbolDiff where symbols have been grouped by full_name (where |
+ applicable). May be re-assigned when it is desirable to show custom |
+ groupings while still printing metadata and section_sizes. |
before_metadata: metadata of the "before" SizeInfo. |
after_metadata: metadata of the "after" SizeInfo. |
""" |
__slots__ = ( |
'section_sizes', |
- 'symbols', |
+ 'raw_symbols', |
+ '_symbols', |
'before_metadata', |
'after_metadata', |
) |
- def __init__(self, section_sizes, symbols, before_metadata, after_metadata): |
+ def __init__(self, section_sizes, raw_symbols, before_metadata, |
+ after_metadata): |
self.section_sizes = section_sizes |
- self.symbols = symbols |
+ self.raw_symbols = raw_symbols |
self.before_metadata = before_metadata |
self.after_metadata = after_metadata |
+ self._symbols = None |
+ |
+ @property |
+ def symbols(self): |
+ if self._symbols is None: |
+ self._symbols = self.raw_symbols._Clustered() |
+ return self._symbols |
+ |
+ @symbols.setter |
+ def symbols(self, value): |
+ self._symbols = value |
class BaseSymbol(object): |
@@ -183,6 +202,8 @@ class BaseSymbol(object): |
parts.append('rel.loc') |
if flags & FLAG_GENERATED_SOURCE: |
parts.append('gen') |
+ if flags & FLAG_CLONE: |
+ parts.append('clone') |
# Not actually a part of flags, but useful to show it here. |
if self.aliases: |
parts.append('{} aliases'.format(self.num_aliases)) |
@@ -415,18 +436,6 @@ class SymbolGroup(BaseSymbol): |
name=name, section_name=section_name, |
is_sorted=is_sorted) |
- def Clustered(self): |
- """Returns a new SymbolGroup with some symbols moved into subgroups. |
- |
- Subgroups include: |
- * Symbols that have [clone] in their name (created during inlining). |
- * Star symbols (such as "** merge strings", and "** symbol gap") |
- |
- To view created groups: |
- Print(clustered.Filter(lambda s: s.IsGroup()), recursive=True) |
- """ |
- return self._CreateTransformed(cluster_symbols.ClusterSymbols(self)) |
- |
def Sorted(self, cmp_func=None, key=None, reverse=False): |
if cmp_func is None and key is None: |
cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.pss), a.name), |
@@ -462,6 +471,9 @@ class SymbolGroup(BaseSymbol): |
filtered_symbols=filtered_and_kept[0], |
section_name=self.section_name) |
+ def WhereIsGroup(self): |
+ return self.Filter(lambda s: s.IsGroup()) |
+ |
def WhereSizeBiggerThan(self, min_size): |
return self.Filter(lambda s: s.size >= min_size) |
@@ -552,45 +564,109 @@ class SymbolGroup(BaseSymbol): |
return self._CreateTransformed( |
self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False) |
- def GroupedBy(self, func, min_count=0): |
+ def GroupedBy(self, func, min_count=0, group_factory=None): |
"""Returns a SymbolGroup of SymbolGroups, indexed by |func|. |
Symbols within each subgroup maintain their relative ordering. |
Args: |
func: Grouping function. Passed a symbol and returns a string for the |
- name of the subgroup to put the symbol in. If None is returned, the |
- symbol is omitted. |
+ name of the subgroup to put the symbol in. If None is returned, the |
+ symbol is omitted. |
min_count: Miniumum number of symbols for a group. If fewer than this many |
- symbols end up in a group, they will not be put within a group. |
- Use a negative value to omit symbols entirely rather than |
- include them outside of a group. |
+ symbols end up in a group, they will not be put within a group. |
+ Use a negative value to omit symbols entirely rather than |
+ include them outside of a group. |
+ group_factory: Function to create SymbolGroup from a list of Symbols. |
""" |
+ if group_factory is None: |
+ group_factory = lambda token, symbols: self._CreateTransformed( |
+ symbols, full_name=token, template_name=token, name=token, |
+ section_name=self.section_name) |
+ |
after_syms = [] |
filtered_symbols = [] |
- symbols_by_token = collections.defaultdict(list) |
+ symbols_by_token = collections.OrderedDict() |
# Index symbols by |func|. |
for symbol in self: |
token = func(symbol) |
if token is None: |
filtered_symbols.append(symbol) |
- symbols_by_token[token].append(symbol) |
+ else: |
+ # Optimization: Store a list only when >1 symbol. |
+ # Saves 200-300ms for _Clustered(). |
+ prev = symbols_by_token.setdefault(token, symbol) |
+ if prev is not symbol: |
+ if prev.__class__ == list: |
+ prev.append(symbol) |
+ else: |
+ symbols_by_token[token] = [prev, symbol] |
# Create the subgroups. |
include_singles = min_count >= 0 |
min_count = abs(min_count) |
- for token, symbols in symbols_by_token.iteritems(): |
- if len(symbols) >= min_count: |
- after_syms.append(self._CreateTransformed( |
- symbols, name=token, section_name=self.section_name, |
- is_sorted=False)) |
- elif include_singles: |
- after_syms.extend(symbols) |
+ for token, symbol_or_list in symbols_by_token.iteritems(): |
+ count = 1 |
+ if symbol_or_list.__class__ == list: |
+ count = len(symbol_or_list) |
+ |
+ if count >= min_count: |
+ if count == 1: |
+ symbol_or_list = [symbol_or_list] |
+ after_syms.append(group_factory(token, symbol_or_list)) |
else: |
- filtered_symbols.extend(symbols) |
- grouped = self._CreateTransformed( |
+ target_list = after_syms if include_singles else filtered_symbols |
+ if count == 1: |
+ target_list.append(symbol_or_list) |
+ else: |
+ target_list.extend(symbol_or_list) |
+ |
+ return self._CreateTransformed( |
after_syms, filtered_symbols=filtered_symbols, |
- section_name=self.section_name, is_sorted=False) |
- return grouped |
+ section_name=self.section_name) |
+ |
+ def _Clustered(self): |
+ """Returns a new SymbolGroup with some symbols moved into subgroups. |
+ |
+ Method is private since it only ever makes sense to call it from |
+ SizeInfo.symbols. |
+ |
+ The main function of clustering is to put symbols that were broken into |
+ multiple parts under a group so that they once again look like a single |
+ symbol. It also groups together symbols like "** merge strings". |
+ |
+ To view created groups: |
+ Print(size_info.symbols.WhereIsGroup()) |
+ """ |
+ def cluster_func(symbol): |
+ name = symbol.full_name |
+ if not name: |
+ # min_count=2 will ensure order is maintained while not being grouped. |
+ # "&" to distinguish from real symbol names, id() to ensure uniqueness. |
+ return '&' + hex(id(symbol)) |
+ if name.startswith('*'): |
+ # "symbol gap 3" -> "symbol gaps" |
+ name = re.sub(r'\s+\d+( \(.*\))?$', 's', name) |
+ return name |
+ |
+ # Use a custom factory to fill in name & template_name. |
+ def group_factory(full_name, symbols): |
+ sym = symbols[0] |
+ if full_name.startswith('*'): |
+ return self._CreateTransformed( |
+ symbols, full_name=full_name, template_name=full_name, |
+ name=full_name, section_name=sym.section_name) |
+ return self._CreateTransformed( |
+ symbols, full_name=full_name, template_name=sym.template_name, |
+ name=sym.name, section_name=sym.section_name) |
+ |
+ # A full second faster to cluster per-section. Plus, don't need create |
+ # (section_name, name) tuples in cluster_func. |
+ ret = [] |
+ for section in self.GroupedBySectionName(): |
+ ret.extend(section.GroupedBy( |
+ cluster_func, min_count=2, group_factory=group_factory)) |
+ |
+ return self._CreateTransformed(ret) |
def GroupedBySectionName(self): |
return self.GroupedBy(lambda s: s.section_name) |