Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Unified Diff: tools/binary_size/models.py

Issue 2791433004: //tools/binary_size: source_path information, change file format, fixes (Closed)
Patch Set: fix comment for _DetectToolPrefix Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/binary_size/map2size.py ('k') | tools/binary_size/ninja_parser.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/binary_size/models.py
diff --git a/tools/binary_size/models.py b/tools/binary_size/models.py
index 12c9b5c5ff0c1a4ce46620b52c428ec05b9296b6..f73b0fe56f42d1431ac4da1ddf6208565284345a 100644
--- a/tools/binary_size/models.py
+++ b/tools/binary_size/models.py
@@ -5,6 +5,7 @@
import collections
import copy
+import os
import re
@@ -24,14 +25,19 @@ class SizeInfo(object):
symbols: A SymbolGroup (or SymbolDiff) with all symbols in it.
"""
__slots__ = (
- 'symbols',
'section_sizes',
+ 'symbols',
+ 'tag',
+ 'timestamp',
)
"""Root size information."""
- def __init__(self, symbols, section_sizes):
- self.symbols = symbols
+ def __init__(self, section_sizes, symbols, timestamp=None, tag=''):
self.section_sizes = section_sizes # E.g. {'.text': 0}
+ self.symbols = symbols # List of symbols sorted by address per-section.
+ self.timestamp = timestamp # UTC datetime object.
+ self.tag = tag # E.g. git revision.
+ assert not tag or '\n' not in tag # Simplifies file format.
class BaseSymbol(object):
@@ -71,36 +77,45 @@ class BaseSymbol(object):
Keys are not guaranteed to be unique within a SymbolGroup. For example, it
is common to have multiple "** merge strings" symbols, which will have a
common key."""
- return (self.section_name, self.function_signature or self.name)
+ return (self.section_name, self.full_name or self.name)
class Symbol(BaseSymbol):
"""Represents a single symbol within a binary."""
__slots__ = (
- 'section_name',
'address',
- 'size',
- 'padding',
+ 'full_name',
+ 'is_anonymous',
+ 'object_path',
'name',
- 'function_signature',
- 'path',
+ 'flags',
+ 'padding',
+ 'section_name',
+ 'source_path',
+ 'size',
)
def __init__(self, section_name, size_without_padding, address=None,
- name=None, path=None, function_signature=None):
+ name=None, source_path=None, object_path=None,
+ full_name=None, is_anonymous=False):
self.section_name = section_name
self.address = address or 0
self.name = name or ''
- self.function_signature = function_signature or ''
- self.path = path or ''
+ self.full_name = full_name or ''
+ self.source_path = source_path or ''
+ self.object_path = object_path or ''
self.size = size_without_padding
+ # Change this to be a bitfield of flags if ever there is a need to add
+ # another similar thing.
+ self.is_anonymous = is_anonymous
self.padding = 0
def __repr__(self):
- return '%s@%x(size=%d,padding=%d,name=%s,path=%s)' % (
+ return '%s@%x(size=%d,padding=%d,name=%s,path=%s,anon=%d)' % (
self.section_name, self.address, self.size_without_padding,
- self.padding, self.name, self.path)
+ self.padding, self.name, self.source_path or self.object_path,
+ int(self.is_anonymous))
class SymbolGroup(BaseSymbol):
@@ -152,11 +167,15 @@ class SymbolGroup(BaseSymbol):
return 0
@property
- def function_signature(self):
+ def full_name(self):
return None
@property
- def path(self):
+ def is_anonymous(self):
+ return False
+
+ @property
+ def source_path(self):
return None
@property
@@ -188,6 +207,16 @@ class SymbolGroup(BaseSymbol):
filtered_symbols=self.filtered_symbols,
section_name=self.section_name)
+ def SortedByName(self, reverse=False):
+ return self.Sorted(key=(lambda s:s.name), reverse=reverse)
+
+ def SortedByAddress(self, reverse=False):
+ return self.Sorted(key=(lambda s:s.address), reverse=reverse)
+
+ def SortedByCount(self, reverse=False):
+ return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1),
+ reverse=not reverse)
+
def Filter(self, func):
filtered_and_kept = ([], [])
for symbol in self:
@@ -215,58 +244,135 @@ class SymbolGroup(BaseSymbol):
regex = re.compile(pattern)
return self.Filter(lambda s: regex.search(s.name))
+ def WhereObjectPathMatches(self, pattern):
+ regex = re.compile(pattern)
+ return self.Filter(lambda s: regex.search(s.object_path))
+
+ def WhereSourcePathMatches(self, pattern):
+ regex = re.compile(pattern)
+ return self.Filter(lambda s: regex.search(s.source_path))
+
def WherePathMatches(self, pattern):
regex = re.compile(pattern)
- return self.Filter(lambda s: s.path and regex.search(s.path))
+ return self.Filter(lambda s: regex.search(s.source_path or s.object_path))
def WhereAddressInRange(self, start, end):
return self.Filter(lambda s: s.address >= start and s.address <= end)
def WhereHasAnyAttribution(self):
- return self.Filter(lambda s: s.name or s.path)
+ return self.Filter(lambda s: s.name or s.source_path or s.object_path)
def Inverted(self):
return self._CreateTransformed(self.filtered_symbols,
filtered_symbols=self.symbols)
- def GroupBy(self, func):
+ def GroupBy(self, func, min_count=0):
+ """Returns a SymbolGroup of SymbolGroups, indexed by |func|.
+
+ 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.
+ 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.
+ """
new_syms = []
filtered_symbols = []
symbols_by_token = collections.defaultdict(list)
+ # Index symbols by |func|.
for symbol in self:
token = func(symbol)
- if not token:
+ if token is None:
filtered_symbols.append(symbol)
- continue
symbols_by_token[token].append(symbol)
+ # Create the subgroups.
+ include_singles = min_count >= 0
+ min_count = abs(min_count)
for token, symbols in symbols_by_token.iteritems():
- new_syms.append(self._CreateTransformed(symbols, name=token,
- section_name=self.section_name))
+ if len(symbols) >= min_count:
+ new_syms.append(self._CreateTransformed(symbols, name=token,
+ section_name=self.section_name))
+ elif include_singles:
+ new_syms.extend(symbols)
+ else:
+ filtered_symbols.extend(symbols)
return self._CreateTransformed(new_syms, filtered_symbols=filtered_symbols,
section_name=self.section_name)
- def GroupByNamespace(self, depth=1):
+ def GroupBySectionName(self):
+ return self.GroupBy(lambda s: s.section_name)
+
+ def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0):
+ """Groups by symbol namespace (as denoted by ::s).
+
+ Does not differentiate between C++ namespaces and C++ classes.
+
+ 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.
+ 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):
- # Does not distinguish between classes and namespaces.
- idx = -2
- for _ in xrange(depth):
- idx = symbol.name.find('::', idx + 2)
- if idx != -1:
- ret = symbol.name[:idx]
- if '<' not in ret:
- return ret
- return '{global}'
- return self.GroupBy(extract_namespace)
-
- def GroupByPath(self, depth=1):
+ # 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 GroupBySourcePath(self, depth=0, fallback='{no path}',
+ fallback_to_object_path=True, min_count=0):
+ """Groups by source_path.
+
+ Args:
+ depth: When 0 (default), groups by entire path. When 1, groups by
+ top-level directory, when 2, groups by top 2 directories, etc.
+ fallback: Use this value when no namespace exists.
+ fallback_to_object_path: When True (default), uses object_path when
+ source_path is missing.
+ 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_path(symbol):
- idx = -1
- for _ in xrange(depth):
- idx = symbol.path.find('/', idx + 1)
- if idx != -1:
- return symbol.path[:idx]
- return '{path unknown}'
- return self.GroupBy(extract_path)
+ path = symbol.source_path
+ if fallback_to_object_path and not path:
+ path = symbol.object_path
+ path = path or fallback
+ return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth)
+ return self.GroupBy(extract_path, min_count=min_count)
+
+ def GroupByObjectPath(self, depth=0, fallback='{no path}', min_count=0):
+ """Groups by object_path.
+
+ Args:
+ depth: When 0 (default), groups by entire path. When 1, groups by
+ top-level directory, when 2, groups by top 2 directories, etc.
+ fallback: Use this value when no namespace exists.
+ 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_path(symbol):
+ path = symbol.object_path or fallback
+ return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth)
+ return self.GroupBy(extract_path, min_count=min_count)
class SymbolDiff(SymbolGroup):
@@ -358,7 +464,7 @@ def Diff(new, old):
section_sizes = {
k:new.section_sizes[k] - v for k, v in old.section_sizes.iteritems()}
symbol_diff = Diff(new.symbols, old.symbols)
- return SizeInfo(symbol_diff, section_sizes)
+ return SizeInfo(section_sizes, symbol_diff)
assert isinstance(new, SymbolGroup) and isinstance(old, SymbolGroup)
symbols_by_key = collections.defaultdict(list)
@@ -378,10 +484,12 @@ def Diff(new, old):
# More stable/useful to compare size without padding.
size_diff = (new_sym.size_without_padding -
old_sym.size_without_padding)
- merged_sym = Symbol(old_sym.section_name, size_diff,
- address=old_sym.address, name=old_sym.name,
- path=old_sym.path,
- function_signature=old_sym.function_signature)
+ merged_sym = Symbol(new_sym.section_name, size_diff,
+ address=new_sym.address, name=new_sym.name,
+ source_path=new_sym.source_path,
+ object_path=new_sym.object_path,
+ full_name=new_sym.full_name,
+ is_anonymous=new_sym.is_anonymous)
similar.append(merged_sym)
padding_by_section_name[new_sym.section_name] += (
new_sym.padding - old_sym.padding)
@@ -399,3 +507,14 @@ def Diff(new, old):
similar.append(Symbol(section_name, padding,
name='** aggregate padding of delta symbols'))
return SymbolDiff(added, removed, similar)
+
+
+def _ExtractPrefixBeforeSeparator(string, separator, count=1):
+ idx = -len(separator)
+ prev_idx = None
+ for _ in xrange(count):
+ idx = string.find(separator, idx + len(separator))
+ if idx < 0:
+ break
+ prev_idx = idx
+ return string[:prev_idx]
« no previous file with comments | « tools/binary_size/map2size.py ('k') | tools/binary_size/ninja_parser.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698