Chromium Code Reviews| Index: tools/binary_size/libsupersize/models.py |
| diff --git a/tools/binary_size/libsupersize/models.py b/tools/binary_size/libsupersize/models.py |
| index 026a8ec7a70eef724b6652a505dd3d29eb8de9aa..ce7f6e89fdfd7d38cefebce44f671c97c8fa9d83 100644 |
| --- a/tools/binary_size/libsupersize/models.py |
| +++ b/tools/binary_size/libsupersize/models.py |
| @@ -9,17 +9,18 @@ Description of common properties: |
| * address: The start address of the symbol. |
| 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|. |
| + 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. |
| + * name: Names with templates and parameter list removed. |
| + Never None, but will be '' for anonymous symbols. |
| + * template_name: Name with parameter list removed (but templates left in). |
| + Never None, but will be '' for anonymous symbols. |
| + * full_name: Name with template and parameter list left in. |
| Never None, but will be '' for anonymous symbols. |
| - * full_name: Symbols names with parameter list left in. |
| - Never None, but will be '' for anonymous symbols, and for symbols that do |
| - not contain a parameter list. |
| * is_anonymous: True when the symbol exists in an anonymous namespace (which |
| - are removed from both full_name and name during normalization). |
| + are removed from both full_name and name during normalization). |
| * section_name: E.g. ".text", ".rodata", ".data.rel.local" |
| * section: The second character of |section_name|. E.g. "t", "r", "d". |
| """ |
| @@ -68,22 +69,25 @@ class SizeInfo(object): |
| """ |
| __slots__ = ( |
| 'section_sizes', |
| + 'raw_symbols', |
|
estevenson
2017/05/09 22:15:12
nit: could update the docs above.
agrieve
2017/05/10 00:51:38
Done.
|
| 'symbols', |
| 'metadata', |
| ) |
| """Root size information.""" |
| - def __init__(self, section_sizes, symbols, metadata=None): |
| + def __init__(self, section_sizes, raw_symbols, metadata=None, symbols=None): |
| self.section_sizes = section_sizes # E.g. {'.text': 0} |
| - self.symbols = symbols |
| + self.raw_symbols = raw_symbols |
| + self.symbols = symbols or SymbolGroup(raw_symbols) |
| self.metadata = metadata or {} |
| - def Cluster(self): |
| + def Clustered(self): |
| """Returns a new SizeInfo with some symbols moved into subgroups. |
| - See SymbolGroup.Cluster() for more details. |
| + See SymbolGroup.Clustered() for more details. |
| """ |
| - return SizeInfo(self.section_sizes, self.symbols.Cluster(), self.metadata) |
| + return SizeInfo(self.section_sizes, self.raw_symbols, self.metadata, |
| + symbols=self.symbols.Clustered()) |
| class SizeInfoDiff(object): |
| @@ -194,9 +198,10 @@ class Symbol(BaseSymbol): |
| __slots__ = ( |
| 'address', |
| 'full_name', |
| + 'template_name', |
| + 'name', |
| 'flags', |
| 'object_path', |
| - 'name', |
| 'aliases', |
| 'padding', |
| 'section_name', |
| @@ -205,12 +210,13 @@ 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, aliases=None): |
| + full_name=None, template_name=None, name=None, source_path=None, |
| + object_path=None, flags=0, aliases=None): |
| self.section_name = section_name |
| self.address = address or 0 |
| - self.name = name or '' |
| self.full_name = full_name or '' |
| + self.template_name = template_name or '' |
| + self.name = name or '' |
| self.source_path = source_path or '' |
| self.object_path = object_path or '' |
| self.size = size_without_padding |
| @@ -257,20 +263,23 @@ class SymbolGroup(BaseSymbol): |
| '_symbols', |
| '_filtered_symbols', |
| 'full_name', |
| + 'template_name', |
| 'name', |
| 'section_name', |
| 'is_sorted', |
| ) |
| - def __init__(self, symbols, filtered_symbols=None, name=None, |
| - full_name=None, section_name=None, is_sorted=False): |
| + # template_name and full_name are useful when clustering symbol clones. |
| + def __init__(self, symbols, filtered_symbols=None, full_name=None, |
| + template_name=None, name='', 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.full_name = full_name if full_name is not None else name |
| + self.template_name = template_name if template_name is not None else name |
| self.name = name or '' |
| - self.full_name = full_name |
| self.section_name = section_name or '.*' |
| self.is_sorted = is_sorted |
| @@ -386,15 +395,17 @@ class SymbolGroup(BaseSymbol): |
| def CountUniqueSymbols(self): |
| return sum(1 for s in self.IterUniqueSymbols()) |
| - def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, |
| - full_name=None, section_name=None, is_sorted=None): |
| + def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None, |
| + template_name=None, name=None, section_name=None, |
| + is_sorted=None): |
| if is_sorted is None: |
| is_sorted = self.is_sorted |
| - return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, |
| - full_name=full_name, section_name=section_name, |
| + return SymbolGroup(symbols, filtered_symbols=filtered_symbols, |
| + full_name=full_name, template_name=template_name, |
| + name=name, section_name=section_name, |
| is_sorted=is_sorted) |
| - def Cluster(self): |
| + def Clustered(self): |
| """Returns a new SymbolGroup with some symbols moved into subgroups. |
| Subgroups include: |
| @@ -453,19 +464,26 @@ class SymbolGroup(BaseSymbol): |
| ret.section_name = section |
| return ret |
| + def WhereIsTemplate(self): |
| + return self.Filter(lambda s: s.template_name is not s.name) |
| + |
| def WhereSourceIsGenerated(self): |
| return self.Filter(lambda s: s.generated_source) |
| def WhereGeneratedByToolchain(self): |
| return self.Filter(lambda s: s.IsGeneratedByToolchain()) |
| - def WhereNameMatches(self, pattern): |
| + def WhereFullNameMatches(self, pattern): |
| regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) |
| - return self.Filter(lambda s: regex.search(s.name)) |
| + return self.Filter(lambda s: regex.search(s.full_name)) |
| - def WhereFullNameMatches(self, pattern): |
| + def WhereTemplateNameMatches(self, pattern): |
| regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) |
| - return self.Filter(lambda s: regex.search(s.full_name or s.name)) |
| + return self.Filter(lambda s: regex.search(s.template_name)) |
| + |
| + def WhereNameMatches(self, pattern): |
| + regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) |
| + return self.Filter(lambda s: regex.search(s.name)) |
| def WhereObjectPathMatches(self, pattern): |
| regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) |
| @@ -483,10 +501,12 @@ class SymbolGroup(BaseSymbol): |
| def WhereMatches(self, pattern): |
| """Looks for |pattern| within all paths & names.""" |
| regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) |
| - return self.Filter(lambda s: (regex.search(s.source_path) or |
| - regex.search(s.object_path) or |
| - regex.search(s.full_name or '') or |
| - regex.search(s.name))) |
| + return self.Filter(lambda s: ( |
| + regex.search(s.source_path) or |
| + regex.search(s.object_path) or |
| + regex.search(s.full_name) or |
| + s.full_name is not s.template_name and regex.search(s.template_name) or |
| + s.full_name is not s.name and regex.search(s.name))) |
| def WhereAddressInRange(self, start, end=None): |
| """Searches for addesses within [start, end). |
| @@ -503,7 +523,7 @@ class SymbolGroup(BaseSymbol): |
| return self.Filter(lambda s: s.source_path or s.object_path) |
| def WhereHasAnyAttribution(self): |
| - return self.Filter(lambda s: s.name or s.source_path or s.object_path) |
| + return self.Filter(lambda s: s.full_name or s.source_path or s.object_path) |
| def Inverted(self): |
| """Returns the symbols that were filtered out by the previous filter. |
| @@ -519,7 +539,7 @@ class SymbolGroup(BaseSymbol): |
| return self._CreateTransformed( |
| self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False) |
| - def GroupBy(self, func, min_count=0): |
| + def GroupedBy(self, func, min_count=0): |
| """Returns a SymbolGroup of SymbolGroups, indexed by |func|. |
| Symbols within each subgroup maintain their relative ordering. |
| @@ -559,40 +579,35 @@ class SymbolGroup(BaseSymbol): |
| section_name=self.section_name, is_sorted=False) |
| return grouped |
| - def GroupBySectionName(self): |
| - return self.GroupBy(lambda s: s.section_name) |
| + def GroupedBySectionName(self): |
| + return self.GroupedBy(lambda s: s.section_name) |
| - def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0): |
| - """Groups by symbol namespace (as denoted by ::s). |
| + def GroupedByName(self, depth=0, min_count=0): |
| + """Groups by symbol name, where |depth| controls how many ::s to include. |
| - Does not differentiate between C++ namespaces and C++ classes. |
| + Does not differentiate between namespaces/classes/functions. |
| Args: |
| - depth: When 0 (default), groups by entire namespace. When 1, groups by |
| - top-level name, when 2, groups by top 2 names, etc. |
| - fallback: Use this value when no namespace exists. |
| + depth: 0 (default): Groups by entire name. Useful for grouping templates. |
| + >0: Groups by this many name parts. |
| + Example: 1 -> std::, 2 -> std::map |
| + <0: Groups by entire name minus this many name parts |
| + Example: -1 -> std::map, -2 -> std:: |
| 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. |
| """ |
| - def extract_namespace(symbol): |
| - # Remove template params. |
| - name = symbol.name |
| - template_idx = name.find('<') |
| - if template_idx: |
| - name = name[:template_idx] |
| - |
| - # Remove after the final :: (not part of the namespace). |
| - colon_idx = name.rfind('::') |
| - if colon_idx == -1: |
| - return fallback |
| - name = name[:colon_idx] |
| - |
| - return _ExtractPrefixBeforeSeparator(name, '::', depth) |
| - return self.GroupBy(extract_namespace, min_count=min_count) |
| - |
| - def GroupByPath(self, depth=0, fallback='{no path}', |
| + if depth >= 0: |
| + extract_namespace = ( |
| + lambda s: _ExtractPrefixBeforeSeparator(s.name, '::', depth)) |
| + else: |
| + depth = -depth |
| + extract_namespace = ( |
| + lambda s: _ExtractSuffixAfterSeparator(s.name, '::', depth)) |
| + return self.GroupedBy(extract_namespace, min_count=min_count) |
| + |
| + def GroupedByPath(self, depth=0, fallback='{no path}', |
| fallback_to_object_path=True, min_count=0): |
| """Groups by source_path. |
| @@ -613,7 +628,7 @@ class SymbolGroup(BaseSymbol): |
| path = symbol.object_path |
| path = path or fallback |
| return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth) |
| - return self.GroupBy(extract_path, min_count=min_count) |
| + return self.GroupedBy(extract_path, min_count=min_count) |
| class SymbolDiff(SymbolGroup): |
| @@ -628,24 +643,23 @@ class SymbolDiff(SymbolGroup): |
| '_removed_ids', |
| ) |
| - def __init__(self, added, removed, similar, name=None, full_name=None, |
| - section_name=None): |
| + def __init__(self, added, removed, similar): |
| self._added_ids = set(id(s) for s in added) |
| self._removed_ids = set(id(s) for s in removed) |
| symbols = [] |
| symbols.extend(added) |
| symbols.extend(removed) |
| symbols.extend(similar) |
| - super(SymbolDiff, self).__init__(symbols, name=name, full_name=full_name, |
| - section_name=section_name) |
| + super(SymbolDiff, self).__init__(symbols) |
| def __repr__(self): |
| return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( |
| 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, |
| self.unchanged_count, self.size) |
| - def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, |
| - full_name=None, section_name=None, is_sorted=None): |
| + def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None, |
| + template_name=None, name=None, section_name=None, |
| + is_sorted=None): |
| # Printing sorts, so short-circuit the same symbols case. |
| if len(symbols) == len(self._symbols): |
| new_added_ids = self._added_ids |
| @@ -679,8 +693,9 @@ class SymbolDiff(SymbolGroup): |
| ret._added_ids = new_added_ids |
| ret._removed_ids = new_removed_ids |
| super(SymbolDiff, ret).__init__( |
| - symbols, filtered_symbols=filtered_symbols, name=name, |
| - full_name=full_name, section_name=section_name, is_sorted=is_sorted) |
| + symbols, filtered_symbols=filtered_symbols, full_name=full_name, |
| + template_name=template_name, name=name, section_name=section_name, |
| + is_sorted=is_sorted) |
| return ret |
| @property |
| @@ -714,7 +729,7 @@ class SymbolDiff(SymbolGroup): |
| return self.Filter(lambda s: not self.IsSimilar(s) or s.size) |
| -def _ExtractPrefixBeforeSeparator(string, separator, count=1): |
| +def _ExtractPrefixBeforeSeparator(string, separator, count): |
| idx = -len(separator) |
| prev_idx = None |
| for _ in xrange(count): |
| @@ -723,3 +738,13 @@ def _ExtractPrefixBeforeSeparator(string, separator, count=1): |
| break |
| prev_idx = idx |
| return string[:prev_idx] |
| + |
| + |
| +def _ExtractSuffixAfterSeparator(string, separator, count): |
| + prev_idx = len(string) + 1 |
| + for _ in xrange(count): |
| + idx = string.rfind(separator, 0, prev_idx - 1) |
| + if idx < 0: |
| + break |
| + prev_idx = idx |
| + return string[:prev_idx] |