summaryrefslogtreecommitdiffstats
path: root/tools/binary_size
diff options
context:
space:
mode:
Diffstat (limited to 'tools/binary_size')
-rw-r--r--tools/binary_size/binary_size_utils.py8
-rwxr-xr-xtools/binary_size/explain_binary_size_delta.py234
-rwxr-xr-xtools/binary_size/explain_binary_size_delta_unittest.py417
-rwxr-xr-xtools/binary_size/run_binary_size_analysis.py4
4 files changed, 526 insertions, 137 deletions
diff --git a/tools/binary_size/binary_size_utils.py b/tools/binary_size/binary_size_utils.py
index f265f61..5521ba7 100644
--- a/tools/binary_size/binary_size_utils.py
+++ b/tools/binary_size/binary_size_utils.py
@@ -23,7 +23,7 @@ def ParseNm(nm_lines):
"""
# Match lines with size, symbol, optional location, optional discriminator
- sym_re = re.compile(r'^[0-9a-f]{8,} ' # address (8+ hex digits)
+ sym_re = re.compile(r'^([0-9a-f]{8,}) ' # address (8+ hex digits)
'([0-9a-f]{8,}) ' # size (8+ hex digits)
'(.) ' # symbol type, one character
'([^\t]+)' # symbol name, separated from next by tab
@@ -39,12 +39,12 @@ def ParseNm(nm_lines):
line = line.rstrip()
match = sym_re.match(line)
if match:
- size, sym_type, sym = match.groups()[0:3]
+ address, size, sym_type, sym = match.groups()[0:4]
size = int(size, 16)
if sym_type in ('B', 'b'):
continue # skip all BSS for now.
- path = match.group(4)
- yield sym, sym_type, size, path
+ path = match.group(5)
+ yield sym, sym_type, size, path, address
continue
match = addr_re.match(line)
if match:
diff --git a/tools/binary_size/explain_binary_size_delta.py b/tools/binary_size/explain_binary_size_delta.py
index d6eba26..8bc6c5a 100755
--- a/tools/binary_size/explain_binary_size_delta.py
+++ b/tools/binary_size/explain_binary_size_delta.py
@@ -38,6 +38,8 @@ dumps. Example:
"""
import collections
+from collections import Counter
+from math import ceil
import operator
import optparse
import os
@@ -46,11 +48,81 @@ import sys
import binary_size_utils
+def CalculateSharedAddresses(symbols):
+ """Checks how many symbols share the same memory space. This returns a
+Counter result where result[address] will tell you how many times address was
+used by symbols."""
+ count = Counter()
+ for _, _, _, _, address in symbols:
+ count[address] += 1
+
+ return count
+
+
+def CalculateEffectiveSize(share_count, address, symbol_size):
+ """Given a raw symbol_size and an address, this method returns the
+ size we should blame on this symbol considering it might share the
+ machine code/data with other symbols. Using the raw symbol_size for
+ each symbol would in those cases over estimate the true cost of that
+ block.
+
+ """
+ shared_count = share_count[address]
+ if shared_count == 1:
+ return symbol_size
+
+ assert shared_count > 1
+ return int(ceil(symbol_size / float(shared_count)))
+
+class SymbolDelta(object):
+ """Stores old size, new size and some metadata."""
+ def __init__(self, shared):
+ self.old_size = None
+ self.new_size = None
+ self.shares_space_with_other_symbols = shared
+
+ def __eq__(self, other):
+ return (self.old_size == other.old_size and
+ self.new_size == other.new_size and
+ self.shares_space_with_other_symbols ==
+ other.shares_space_with_other_symbols)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def copy_symbol_delta(self):
+ symbol_delta = SymbolDelta(self.shares_space_with_other_symbols)
+ symbol_delta.old_size = self.old_size
+ symbol_delta.new_size = self.new_size
+ return symbol_delta
+
+class DeltaInfo(SymbolDelta):
+ """Summary of a the change for one symbol between two instances."""
+ def __init__(self, file_path, symbol_type, symbol_name, shared):
+ SymbolDelta.__init__(self, shared)
+ self.file_path = file_path
+ self.symbol_type = symbol_type
+ self.symbol_name = symbol_name
+
+ def __eq__(self, other):
+ return (self.file_path == other.file_path and
+ self.symbol_type == other.symbol_type and
+ self.symbol_name == other.symbol_name and
+ SymbolDelta.__eq__(self, other))
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def ExtractSymbolDelta(self):
+ """Returns a copy of the SymbolDelta for this DeltaInfo."""
+ return SymbolDelta.copy_symbol_delta(self)
+
def Compare(symbols1, symbols2):
"""Executes a comparison of the symbols in symbols1 and symbols2.
Returns:
tuple of lists: (added_symbols, removed_symbols, changed_symbols, others)
+ where each list contains DeltaInfo objects.
"""
added = [] # tuples
removed = [] # tuples
@@ -59,9 +131,12 @@ def Compare(symbols1, symbols2):
cache1 = {}
cache2 = {}
- # Make a map of (file, symbol_type) : (symbol_name, symbol_size)
- for cache, symbols in ((cache1, symbols1), (cache2, symbols2)):
- for symbol_name, symbol_type, symbol_size, file_path in symbols:
+ # Make a map of (file, symbol_type) : (symbol_name, effective_symbol_size)
+ share_count1 = CalculateSharedAddresses(symbols1)
+ share_count2 = CalculateSharedAddresses(symbols2)
+ for cache, symbols, share_count in ((cache1, symbols1, share_count1),
+ (cache2, symbols2, share_count2)):
+ for symbol_name, symbol_type, symbol_size, file_path, address in symbols:
if 'vtable for ' in symbol_name:
symbol_type = '@' # hack to categorize these separately
if file_path:
@@ -70,10 +145,15 @@ def Compare(symbols1, symbols2):
file_path = file_path.replace('\\', '/')
else:
file_path = '(No Path)'
+ # Take into consideration that multiple symbols might share the same
+ # block of code.
+ effective_symbol_size = CalculateEffectiveSize(share_count, address,
+ symbol_size)
key = (file_path, symbol_type)
bucket = cache.setdefault(key, {})
size_list = bucket.setdefault(symbol_name, [])
- size_list.append(symbol_size)
+ size_list.append((effective_symbol_size,
+ effective_symbol_size != symbol_size))
# Now diff them. We iterate over the elements in cache1. For each symbol
# that we find in cache2, we record whether it was deleted, changed, or
@@ -81,52 +161,69 @@ def Compare(symbols1, symbols2):
# in cache2 at the end of the iteration over cache1 are the 'new' symbols.
for key, bucket1 in cache1.items():
bucket2 = cache2.get(key)
+ file_path, symbol_type = key;
if not bucket2:
# A file was removed. Everything in bucket1 is dead.
for symbol_name, symbol_size_list in bucket1.items():
- for symbol_size in symbol_size_list:
- removed.append((key[0], key[1], symbol_name, symbol_size, None))
+ for (symbol_size, shared) in symbol_size_list:
+ delta_info = DeltaInfo(file_path, symbol_type, symbol_name, shared)
+ delta_info.old_size = symbol_size
+ removed.append(delta_info)
else:
# File still exists, look for changes within.
for symbol_name, symbol_size_list in bucket1.items():
size_list2 = bucket2.get(symbol_name)
if size_list2 is None:
# Symbol no longer exists in bucket2.
- for symbol_size in symbol_size_list:
- removed.append((key[0], key[1], symbol_name, symbol_size, None))
+ for (symbol_size, shared) in symbol_size_list:
+ delta_info = DeltaInfo(file_path, symbol_type, symbol_name, shared)
+ delta_info.old_size = symbol_size
+ removed.append(delta_info)
else:
del bucket2[symbol_name] # Symbol is not new, delete from cache2.
if len(symbol_size_list) == 1 and len(size_list2) == 1:
- symbol_size = symbol_size_list[0]
- size2 = size_list2[0]
+ symbol_size, shared1 = symbol_size_list[0]
+ size2, shared2 = size_list2[0]
+ delta_info = DeltaInfo(file_path, symbol_type, symbol_name,
+ shared1 or shared2)
+ delta_info.old_size = symbol_size
+ delta_info.new_size = size2
if symbol_size != size2:
# Symbol has change size in bucket.
- changed.append((key[0], key[1], symbol_name, symbol_size, size2))
+ changed.append(delta_info)
else:
# Symbol is unchanged.
- unchanged.append((key[0], key[1], symbol_name, symbol_size,
- size2))
+ unchanged.append(delta_info)
else:
# Complex comparison for when a symbol exists multiple times
# in the same file (where file can be "unknown file").
symbol_size_counter = collections.Counter(symbol_size_list)
delta_counter = collections.Counter(symbol_size_list)
delta_counter.subtract(size_list2)
- for symbol_size in sorted(delta_counter.keys()):
- delta = delta_counter[symbol_size]
- unchanged_count = symbol_size_counter[symbol_size]
+ for delta_counter_key in sorted(delta_counter.keys()):
+ delta = delta_counter[delta_counter_key]
+ unchanged_count = symbol_size_counter[delta_counter_key]
+ (symbol_size, shared) = delta_counter_key
if delta > 0:
unchanged_count -= delta
for _ in range(unchanged_count):
- unchanged.append((key[0], key[1], symbol_name, symbol_size,
- symbol_size))
+ delta_info = DeltaInfo(file_path, symbol_type,
+ symbol_name, shared)
+ delta_info.old_size = symbol_size
+ delta_info.new_size = symbol_size
+ unchanged.append(delta_info)
if delta > 0: # Used to be more of these than there is now.
for _ in range(delta):
- removed.append((key[0], key[1], symbol_name, symbol_size,
- None))
+ delta_info = DeltaInfo(file_path, symbol_type,
+ symbol_name, shared)
+ delta_info.old_size = symbol_size
+ removed.append(delta_info)
elif delta < 0: # More of this (symbol,size) now.
for _ in range(-delta):
- added.append((key[0], key[1], symbol_name, None, symbol_size))
+ delta_info = DeltaInfo(file_path, symbol_type,
+ symbol_name, shared)
+ delta_info.new_size = symbol_size
+ added.append(delta_info)
if len(bucket2) == 0:
del cache1[key] # Entire bucket is empty, delete from cache2
@@ -135,11 +232,15 @@ def Compare(symbols1, symbols2):
# the encountered symbols from cache2. What's left in cache2 is the new
# symbols.
for key, bucket2 in cache2.iteritems():
+ file_path, symbol_type = key;
for symbol_name, symbol_size_list in bucket2.items():
- for symbol_size in symbol_size_list:
- added.append((key[0], key[1], symbol_name, None, symbol_size))
+ for (symbol_size, shared) in symbol_size_list:
+ delta_info = DeltaInfo(file_path, symbol_type, symbol_name, shared)
+ delta_info.new_size = symbol_size
+ added.append(delta_info)
return (added, removed, changed, unchanged)
+
def DeltaStr(number):
"""Returns the number as a string with a '+' prefix if it's > 0 and
a '-' prefix if it's < 0."""
@@ -149,6 +250,16 @@ def DeltaStr(number):
return result
+def SharedInfoStr(symbol_info):
+ """Returns a string (prefixed by space) explaining that numbers are
+ adjusted because of shared space between symbols, or an empty string
+ if space had not been shared."""
+
+ if symbol_info.shares_space_with_other_symbols:
+ return " (adjusted sizes because of memory sharing)"
+
+ return ""
+
class CrunchStatsData(object):
"""Stores a summary of data of a certain kind."""
def __init__(self, symbols):
@@ -166,8 +277,7 @@ def CrunchStats(added, removed, changed, unchanged, showsources, showsymbols):
grown = []
shrunk = []
for item in changed:
- file_path, symbol_type, symbol_name, size1, size2 = item
- if size1 < size2:
+ if item.old_size < item.new_size:
grown.append(item)
else:
shrunk.append(item)
@@ -178,14 +288,15 @@ def CrunchStats(added, removed, changed, unchanged, showsources, showsymbols):
shrunk_symbols = CrunchStatsData(shrunk)
sections = [new_symbols, removed_symbols, grown_symbols, shrunk_symbols]
for section in sections:
- for file_path, symbol_type, symbol_name, size1, size2 in section.symbols:
- section.sources.add(file_path)
- if size1 is not None:
- section.before_size += size1
- if size2 is not None:
- section.after_size += size2
- bucket = section.symbols_by_path.setdefault(file_path, [])
- bucket.append((symbol_name, symbol_type, size1, size2))
+ for item in section.symbols:
+ section.sources.add(item.file_path)
+ if item.old_size is not None:
+ section.before_size += item.old_size
+ if item.new_size is not None:
+ section.after_size += item.new_size
+ bucket = section.symbols_by_path.setdefault(item.file_path, [])
+ bucket.append((item.symbol_name, item.symbol_type,
+ item.ExtractSymbolDelta()))
total_change = sum(s.after_size - s.before_size for s in sections)
summary = 'Total change: %s bytes' % DeltaStr(total_change)
@@ -213,9 +324,9 @@ def CrunchStats(added, removed, changed, unchanged, showsources, showsymbols):
maybe_unchanged_sources = set()
unchanged_symbols_size = 0
- for file_path, symbol_type, symbol_name, size1, size2 in unchanged:
- maybe_unchanged_sources.add(file_path)
- unchanged_symbols_size += size1 # == size2
+ for item in unchanged:
+ maybe_unchanged_sources.add(item.file_path)
+ unchanged_symbols_size += item.old_size # == item.new_size
print(' %d unchanged, totalling %d bytes' %
(len(unchanged), unchanged_symbols_size))
@@ -256,14 +367,14 @@ def CrunchStats(added, removed, changed, unchanged, showsources, showsymbols):
if not entry:
entry = {'plus': 0, 'minus': 0}
delta_by_path[path] = entry
- for symbol_name, symbol_type, size1, size2 in \
+ for symbol_name, symbol_type, symbol_delta in \
section.symbols_by_path[path]:
- if size1 is None:
- delta = size2
- elif size2 is None:
- delta = -size1
+ if symbol_delta.old_size is None:
+ delta = symbol_delta.new_size
+ elif symbol_delta.new_size is None:
+ delta = -symbol_delta.old_size
else:
- delta = size2 - size1
+ delta = symbol_delta.new_size - symbol_delta.old_size
if delta > 0:
entry['plus'] += delta
@@ -288,34 +399,45 @@ def CrunchStats(added, removed, changed, unchanged, showsources, showsymbols):
print header
print divider
if showsymbols:
+ def ExtractNewSize(tup):
+ symbol_delta = tup[2]
+ return symbol_delta.new_size
+ def ExtractOldSize(tup):
+ symbol_delta = tup[2]
+ return symbol_delta.old_size
if path in new_symbols.symbols_by_path:
print ' New symbols:'
- for symbol_name, symbol_type, size1, size2 in \
+ for symbol_name, symbol_type, symbol_delta in \
sorted(new_symbols.symbols_by_path[path],
- key=operator.itemgetter(3),
+ key=ExtractNewSize,
reverse=True):
- print (' %8s: %s type=%s, size=%d bytes' %
- (DeltaStr(size2), symbol_name, symbol_type, size2))
+ print (' %8s: %s type=%s, size=%d bytes%s' %
+ (DeltaStr(symbol_delta.new_size), symbol_name, symbol_type,
+ symbol_delta.new_size, SharedInfoStr(symbol_delta)))
if path in removed_symbols.symbols_by_path:
print ' Removed symbols:'
- for symbol_name, symbol_type, size1, size2 in \
+ for symbol_name, symbol_type, symbol_delta in \
sorted(removed_symbols.symbols_by_path[path],
- key=operator.itemgetter(2)):
- print (' %8s: %s type=%s, size=%d bytes' %
- (DeltaStr(-size1), symbol_name, symbol_type, size1))
+ key=ExtractOldSize):
+ print (' %8s: %s type=%s, size=%d bytes%s' %
+ (DeltaStr(-symbol_delta.old_size), symbol_name, symbol_type,
+ symbol_delta.old_size,
+ SharedInfoStr(symbol_delta)))
for (changed_symbols_by_path, type_str) in [
(grown_symbols.symbols_by_path, "Grown"),
(shrunk_symbols.symbols_by_path, "Shrunk")]:
if path in changed_symbols_by_path:
print ' %s symbols:' % type_str
def changed_symbol_sortkey(item):
- symbol_name, _symbol_type, size1, size2 = item
- return (size1 - size2, symbol_name)
- for symbol_name, symbol_type, size1, size2 in \
+ symbol_name, _symbol_type, symbol_delta = item
+ return (symbol_delta.old_size - symbol_delta.new_size, symbol_name)
+ for symbol_name, symbol_type, symbol_delta in \
sorted(changed_symbols_by_path[path], key=changed_symbol_sortkey):
- print (' %8s: %s type=%s, (was %d bytes, now %d bytes)'
- % (DeltaStr(size2 - size1), symbol_name,
- symbol_type, size1, size2))
+ print (' %8s: %s type=%s, (was %d bytes, now %d bytes)%s'
+ % (DeltaStr(symbol_delta.new_size - symbol_delta.old_size),
+ symbol_name, symbol_type,
+ symbol_delta.old_size, symbol_delta.new_size,
+ SharedInfoStr(symbol_delta)))
def main():
diff --git a/tools/binary_size/explain_binary_size_delta_unittest.py b/tools/binary_size/explain_binary_size_delta_unittest.py
index 87ecb36..d818d83 100755
--- a/tools/binary_size/explain_binary_size_delta_unittest.py
+++ b/tools/binary_size/explain_binary_size_delta_unittest.py
@@ -15,114 +15,122 @@ import explain_binary_size_delta
class ExplainBinarySizeDeltaTest(unittest.TestCase):
def testCompare(self):
- # List entries have form: symbol_name, symbol_type, symbol_size, file_path
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
symbol_list1 = (
# File with one symbol, left as-is.
- ( 'unchanged', 't', 1000, '/file_unchanged' ),
+ ( 'unchanged', 't', 1000, '/file_unchanged', 0x1 ),
# File with one symbol, changed.
- ( 'changed', 't', 1000, '/file_all_changed' ),
+ ( 'changed', 't', 1000, '/file_all_changed', 0x2 ),
# File with one symbol, deleted.
- ( 'removed', 't', 1000, '/file_all_deleted' ),
+ ( 'removed', 't', 1000, '/file_all_deleted', 0x3 ),
# File with two symbols, one unchanged, one changed, same bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed' ),
- ( 'changed', 't', 1000, '/file_pair_unchanged_changed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed', 0x4 ),
+ ( 'changed', 't', 1000, '/file_pair_unchanged_changed', 0x5 ),
# File with two symbols, one unchanged, one deleted, same bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed' ),
- ( 'removed', 't', 1000, '/file_pair_unchanged_removed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed', 0x6 ),
+ ( 'removed', 't', 1000, '/file_pair_unchanged_removed', 0x7 ),
# File with two symbols, one unchanged, one added, same bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_added' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_added', 0x8 ),
# File with two symbols, one unchanged, one changed, different bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed' ),
- ( 'changed', '@', 1000, '/file_pair_unchanged_diffbuck_changed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed', 0x9 ),
+ ( 'changed', '@', 1000, '/file_pair_unchanged_diffbuck_changed', 0xa ),
# File with two symbols, one unchanged, one deleted, different bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed' ),
- ( 'removed', '@', 1000, '/file_pair_unchanged_diffbuck_removed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed', 0xb ),
+ ( 'removed', '@', 1000, '/file_pair_unchanged_diffbuck_removed', 0xc ),
# File with two symbols, one unchanged, one added, different bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added', 0xd ),
# File with four symbols, one added, one removed,
# one changed, one unchanged
- ( 'size_changed', 't', 1000, '/file_tetra' ),
- ( 'removed', 't', 1000, '/file_tetra' ),
- ( 'unchanged', 't', 1000, '/file_tetra' ),
+ ( 'size_changed', 't', 1000, '/file_tetra', 0xe ),
+ ( 'removed', 't', 1000, '/file_tetra', 0xf ),
+ ( 'unchanged', 't', 1000, '/file_tetra', 0x10 ),
)
symbol_list2 = (
# File with one symbol, left as-is.
- ( 'unchanged', 't', 1000, '/file_unchanged' ),
+ ( 'unchanged', 't', 1000, '/file_unchanged', 0x1 ),
# File with one symbol, changed.
- ( 'changed', 't', 2000, '/file_all_changed' ),
+ ( 'changed', 't', 2000, '/file_all_changed', 0x2 ),
# File with two symbols, one unchanged, one changed, same bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed' ),
- ( 'changed', 't', 2000, '/file_pair_unchanged_changed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed', 0x3 ),
+ ( 'changed', 't', 2000, '/file_pair_unchanged_changed', 0x4 ),
# File with two symbols, one unchanged, one deleted, same bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed', 0x5 ),
# File with two symbols, one unchanged, one added, same bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_added' ),
- ( 'added', 't', 1000, '/file_pair_unchanged_added' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_added', 0x6 ),
+ ( 'added', 't', 1000, '/file_pair_unchanged_added', 0x7 ),
# File with two symbols, one unchanged, one changed, different bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed' ),
- ( 'changed', '@', 2000, '/file_pair_unchanged_diffbuck_changed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed', 0x8 ),
+ ( 'changed', '@', 2000, '/file_pair_unchanged_diffbuck_changed', 0x9 ),
# File with two symbols, one unchanged, one deleted, different bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed', 0xa ),
# File with two symbols, one unchanged, one added, different bucket
- ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added' ),
- ( 'added', '@', 1000, '/file_pair_unchanged_diffbuck_added' ),
+ ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added', 0xb ),
+ ( 'added', '@', 1000, '/file_pair_unchanged_diffbuck_added', 0xc ),
# File with four symbols, one added, one removed,
# one changed, one unchanged
- ( 'size_changed', 't', 2000, '/file_tetra' ),
- ( 'unchanged', 't', 1000, '/file_tetra' ),
- ( 'added', 't', 1000, '/file_tetra' ),
+ ( 'size_changed', 't', 2000, '/file_tetra', 0xd ),
+ ( 'unchanged', 't', 1000, '/file_tetra', 0xe ),
+ ( 'added', 't', 1000, '/file_tetra', 0xf ),
# New file with one symbol added
- ( 'added', 't', 1000, '/file_new' ),
+ ( 'added', 't', 1000, '/file_new', 0x10 ),
)
# Here we go
(added, removed, changed, unchanged) = \
explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+ def delta(file_path, symbol_type, symbol_name, old_size, new_size):
+ delta_info = explain_binary_size_delta.DeltaInfo(
+ file_path, symbol_type, symbol_name, False)
+ delta_info.old_size = old_size
+ delta_info.new_size = new_size
+ return delta_info
+
# File with one symbol, left as-is.
- assert ('/file_unchanged', 't', 'unchanged', 1000, 1000) in unchanged
+ assert delta('/file_unchanged', 't', 'unchanged', 1000, 1000) in unchanged
# File with one symbol, changed.
- assert ('/file_all_changed', 't', 'changed', 1000, 2000) in changed
+ assert delta('/file_all_changed', 't', 'changed', 1000, 2000) in changed
# File with one symbol, deleted.
- assert ('/file_all_deleted', 't', 'removed', 1000, None) in removed
+ assert delta('/file_all_deleted', 't', 'removed', 1000, None) in removed
# New file with one symbol added
- assert ('/file_new', 't', 'added', None, 1000) in added
+ assert delta('/file_new', 't', 'added', None, 1000) in added
# File with two symbols, one unchanged, one changed, same bucket
- assert ('/file_pair_unchanged_changed',
+ assert delta('/file_pair_unchanged_changed',
't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_pair_unchanged_changed',
+ assert delta('/file_pair_unchanged_changed',
't', 'changed', 1000, 2000) in changed
# File with two symbols, one unchanged, one removed, same bucket
- assert ('/file_pair_unchanged_removed',
+ assert delta('/file_pair_unchanged_removed',
't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_pair_unchanged_removed',
+ assert delta('/file_pair_unchanged_removed',
't', 'removed', 1000, None) in removed
# File with two symbols, one unchanged, one added, same bucket
- assert ('/file_pair_unchanged_added',
+ assert delta('/file_pair_unchanged_added',
't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_pair_unchanged_added',
+ assert delta('/file_pair_unchanged_added',
't', 'added', None, 1000) in added
# File with two symbols, one unchanged, one changed, different bucket
- assert ('/file_pair_unchanged_diffbuck_changed',
+ assert delta('/file_pair_unchanged_diffbuck_changed',
't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_pair_unchanged_diffbuck_changed',
+ assert delta('/file_pair_unchanged_diffbuck_changed',
'@', 'changed', 1000, 2000) in changed
# File with two symbols, one unchanged, one removed, different bucket
- assert ('/file_pair_unchanged_diffbuck_removed',
+ assert delta('/file_pair_unchanged_diffbuck_removed',
't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_pair_unchanged_diffbuck_removed',
+ assert delta('/file_pair_unchanged_diffbuck_removed',
'@', 'removed', 1000, None) in removed
# File with two symbols, one unchanged, one added, different bucket
- assert ('/file_pair_unchanged_diffbuck_added',
+ assert delta('/file_pair_unchanged_diffbuck_added',
't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_pair_unchanged_diffbuck_added',
+ assert delta('/file_pair_unchanged_diffbuck_added',
'@', 'added', None, 1000) in added
# File with four symbols, one added, one removed, one changed, one unchanged
- assert ('/file_tetra', 't', 'size_changed', 1000, 2000) in changed
- assert ('/file_tetra', 't', 'unchanged', 1000, 1000) in unchanged
- assert ('/file_tetra', 't', 'added', None, 1000) in added
- assert ('/file_tetra', 't', 'removed', 1000, None) in removed
+ assert delta('/file_tetra', 't', 'size_changed', 1000, 2000) in changed
+ assert delta('/file_tetra', 't', 'unchanged', 1000, 1000) in unchanged
+ assert delta('/file_tetra', 't', 'added', None, 1000) in added
+ assert delta('/file_tetra', 't', 'removed', 1000, None) in removed
# Now check final stats.
orig_stdout = sys.stdout
@@ -218,20 +226,20 @@ Per-source Analysis:
self.maxDiff = None
self.assertMultiLineEqual(expected_output, result)
- print "explain_binary_size_delta_unittest: All tests passed"
def testCompareStringEntries(self):
- # List entries have form: symbol_name, symbol_type, symbol_size, file_path
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
symbol_list1 = (
# File with one string.
- ( '.L.str107', 'r', 8, '/file_with_strs' ),
+ ( '.L.str107', 'r', 8, '/file_with_strs', 0x1 ),
)
symbol_list2 = (
# Two files with one string each, same name.
- ( '.L.str107', 'r', 8, '/file_with_strs' ),
- ( '.L.str107', 'r', 7, '/other_file_with_strs' ),
+ ( '.L.str107', 'r', 8, '/file_with_strs', 0x1 ),
+ ( '.L.str107', 'r', 7, '/other_file_with_strs', 0x2 ),
)
# Here we go
@@ -272,29 +280,29 @@ Per-source Analysis:
self.maxDiff = None
self.assertMultiLineEqual(expected_output, result)
- print "explain_binary_size_delta_unittest: All tests passed"
def testCompareStringEntriesWithNoFile(self):
- # List entries have form: symbol_name, symbol_type, symbol_size, file_path
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
symbol_list1 = (
- ( '.L.str104', 'r', 21, '??' ), # Will change size.
- ( '.L.str105', 'r', 17, '??' ), # Same.
- ( '.L.str106', 'r', 13, '??' ), # Will be removed.
- ( '.L.str106', 'r', 3, '??' ), # Same.
- ( '.L.str106', 'r', 3, '??' ), # Will be removed.
- ( '.L.str107', 'r', 8, '??' ), # Will be removed (other sizes).
+ ( '.L.str104', 'r', 21, '??', 0x1 ), # Will change size.
+ ( '.L.str105', 'r', 17, '??', 0x2 ), # Same.
+ ( '.L.str106', 'r', 13, '??', 0x3 ), # Will be removed.
+ ( '.L.str106', 'r', 3, '??', 0x4 ), # Same.
+ ( '.L.str106', 'r', 3, '??', 0x5 ), # Will be removed.
+ ( '.L.str107', 'r', 8, '??', 0x6 ), # Will be removed (other sizes).
)
symbol_list2 = (
# Two files with one string each, same name.
- ( '.L.str104', 'r', 19, '??' ), # Changed.
- ( '.L.str105', 'r', 11, '??' ), # New size for multi-symbol.
- ( '.L.str105', 'r', 17, '??' ), # New of same size for multi-symbol.
- ( '.L.str105', 'r', 17, '??' ), # Same.
- ( '.L.str106', 'r', 3, '??' ), # Same.
- ( '.L.str107', 'r', 5, '??' ), # New size for symbol.
- ( '.L.str107', 'r', 7, '??' ), # New size for symbol.
- ( '.L.str108', 'r', 8, '??' ), # New symbol.
+ ( '.L.str104', 'r', 19, '??', 0x1 ), # Changed.
+ ( '.L.str105', 'r', 11, '??', 0x2 ), # New size for multi-symbol.
+ ( '.L.str105', 'r', 17, '??', 0x3 ), # New of same size for multi-symbol.
+ ( '.L.str105', 'r', 17, '??', 0x4 ), # Same.
+ ( '.L.str106', 'r', 3, '??', 0x5 ), # Same.
+ ( '.L.str107', 'r', 5, '??', 0x6 ), # New size for symbol.
+ ( '.L.str107', 'r', 7, '??', 0x7 ), # New size for symbol.
+ ( '.L.str108', 'r', 8, '??', 0x8 ), # New symbol.
)
# Here we go
@@ -348,7 +356,266 @@ Per-source Analysis:
self.maxDiff = None
self.assertMultiLineEqual(expected_output, result)
- print "explain_binary_size_delta_unittest: All tests passed"
+
+ def testCompareSharedSpace(self):
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
+ symbol_list1 = (
+ # File with two symbols, same address.
+ ( 'sym1', 'r', 8, '/file', 0x1 ),
+ ( 'sym2', 'r', 8, '/file', 0x1 ),
+ )
+
+ symbol_list2 = (
+ # File with two symbols, same address.
+ ( 'sym1', 'r', 4, '/file', 0x1 ),
+ ( 'sym2', 'r', 4, '/file', 0x1 ),
+ )
+
+ # Here we go
+ (added, removed, changed, unchanged) = \
+ explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+ # Now check final stats.
+ orig_stdout = sys.stdout
+ output_collector = cStringIO.StringIO()
+ sys.stdout = output_collector
+ try:
+ explain_binary_size_delta.CrunchStats(added, removed, changed,
+ unchanged, True, True)
+ finally:
+ sys.stdout = orig_stdout
+ result = output_collector.getvalue()
+
+ expected_output = """\
+Total change: -4 bytes
+======================
+ 2 shrunk, for a net change of -4 bytes (8 bytes before, 4 bytes after) \
+across 1 sources
+ 0 unchanged, totalling 0 bytes
+Source stats:
+ 1 sources encountered.
+ 0 completely new.
+ 0 removed completely.
+ 1 partially changed.
+ 0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ -4 - Source: /file - (gained 0, lost 4)
+----------------------------------------
+ Shrunk symbols:
+ -2: sym1 type=r, (was 4 bytes, now 2 bytes) (adjusted sizes because \
+of memory sharing)
+ -2: sym2 type=r, (was 4 bytes, now 2 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+ self.maxDiff = None
+ self.assertMultiLineEqual(expected_output, result)
+
+
+ def testCompareSharedSpaceDuplicateSymbols(self):
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
+ symbol_list1 = (
+ # File with two symbols, same address.
+ ( 'sym1', 'r', 7, '/file', 0x2 ),
+ ( 'sym1', 'r', 8, '/file', 0x1 ),
+ ( 'sym2', 'r', 8, '/file', 0x1 ),
+ )
+
+ symbol_list2 = (
+ # File with two symbols, same address.
+ ( 'sym1', 'r', 7, '/file', 0x2 ),
+ ( 'sym1', 'r', 4, '/file', 0x1 ),
+ ( 'sym2', 'r', 4, '/file', 0x1 ),
+ )
+
+ # Here we go
+ (added, removed, changed, unchanged) = \
+ explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+ # Now check final stats.
+ orig_stdout = sys.stdout
+ output_collector = cStringIO.StringIO()
+ sys.stdout = output_collector
+ try:
+ explain_binary_size_delta.CrunchStats(added, removed, changed,
+ unchanged, True, True)
+ finally:
+ sys.stdout = orig_stdout
+ result = output_collector.getvalue()
+
+ expected_output = """\
+Total change: -4 bytes
+======================
+ 1 added, totalling +2 bytes across 1 sources
+ 1 removed, totalling -4 bytes across 1 sources
+ 1 shrunk, for a net change of -2 bytes (4 bytes before, 2 bytes after) \
+across 1 sources
+ 1 unchanged, totalling 7 bytes
+Source stats:
+ 1 sources encountered.
+ 0 completely new.
+ 0 removed completely.
+ 1 partially changed.
+ 0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ -4 - Source: /file - (gained 2, lost 6)
+----------------------------------------
+ New symbols:
+ +2: sym1 type=r, size=2 bytes (adjusted sizes because of memory \
+sharing)
+ Removed symbols:
+ -4: sym1 type=r, size=4 bytes (adjusted sizes because of memory \
+sharing)
+ Shrunk symbols:
+ -2: sym2 type=r, (was 4 bytes, now 2 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+ self.maxDiff = None
+ self.assertMultiLineEqual(expected_output, result)
+
+ def testCompareSharedSpaceBecomingUnshared(self):
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
+ symbol_list1 = (
+ # File with two symbols, same address.
+ ( 'sym1', 'r', 8, '/file', 0x1 ),
+ ( 'sym2', 'r', 8, '/file', 0x1 ),
+ )
+
+ symbol_list2 = (
+ # File with two symbols, not the same address.
+ ( 'sym1', 'r', 8, '/file', 0x1 ),
+ ( 'sym2', 'r', 6, '/file', 0x2 ),
+ )
+
+ # Here we go
+ (added, removed, changed, unchanged) = \
+ explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+ # Now check final stats.
+ orig_stdout = sys.stdout
+ output_collector = cStringIO.StringIO()
+ sys.stdout = output_collector
+ try:
+ explain_binary_size_delta.CrunchStats(added, removed, changed,
+ unchanged, True, True)
+ finally:
+ sys.stdout = orig_stdout
+ result = output_collector.getvalue()
+
+ expected_output = """\
+Total change: +6 bytes
+======================
+ 2 grown, for a net change of +6 bytes (8 bytes before, 14 bytes after) \
+across 1 sources
+ 0 unchanged, totalling 0 bytes
+Source stats:
+ 1 sources encountered.
+ 0 completely new.
+ 0 removed completely.
+ 1 partially changed.
+ 0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ +6 - Source: /file - (gained 6, lost 0)
+----------------------------------------
+ Grown symbols:
+ +4: sym1 type=r, (was 4 bytes, now 8 bytes) (adjusted sizes because \
+of memory sharing)
+ +2: sym2 type=r, (was 4 bytes, now 6 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+ self.maxDiff = None
+ self.assertMultiLineEqual(expected_output, result)
+
+ def testCompareSymbolsBecomingUnshared(self):
+ # List entries have form:
+ # symbol_name, symbol_type, symbol_size, file_path, memory_address
+ symbol_list1 = (
+ # File with two symbols, not the same address.
+ ( 'sym1', 'r', 8, '/file', 0x1 ),
+ ( 'sym2', 'r', 6, '/file', 0x2 ),
+ )
+
+ symbol_list2 = (
+ # File with two symbols, same address.
+ ( 'sym1', 'r', 8, '/file', 0x1 ),
+ ( 'sym2', 'r', 8, '/file', 0x1 ),
+ )
+
+ # Here we go
+ (added, removed, changed, unchanged) = \
+ explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+ # Now check final stats.
+ orig_stdout = sys.stdout
+ output_collector = cStringIO.StringIO()
+ sys.stdout = output_collector
+ try:
+ explain_binary_size_delta.CrunchStats(added, removed, changed,
+ unchanged, True, True)
+ finally:
+ sys.stdout = orig_stdout
+ result = output_collector.getvalue()
+
+ expected_output = """\
+Total change: -6 bytes
+======================
+ 2 shrunk, for a net change of -6 bytes (14 bytes before, 8 bytes after) \
+across 1 sources
+ 0 unchanged, totalling 0 bytes
+Source stats:
+ 1 sources encountered.
+ 0 completely new.
+ 0 removed completely.
+ 1 partially changed.
+ 0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ -6 - Source: /file - (gained 0, lost 6)
+----------------------------------------
+ Shrunk symbols:
+ -2: sym2 type=r, (was 6 bytes, now 4 bytes) (adjusted sizes because \
+of memory sharing)
+ -4: sym1 type=r, (was 8 bytes, now 4 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+ self.maxDiff = None
+ self.assertMultiLineEqual(expected_output, result)
+
+ def testDeltaInfo(self):
+ x = explain_binary_size_delta.DeltaInfo("path", "t", "sym_name", False)
+ assert x == x
+ y = explain_binary_size_delta.DeltaInfo("path", "t", "sym_name", False)
+ assert x == y
+
+ y.new_size = 12
+ assert x != y
+
+ x.new_size = 12
+ assert x == y
+
+ z = explain_binary_size_delta.DeltaInfo("path", "t", "sym_name", True)
+ assert not (x == z)
+ assert x != z
+
+ w = explain_binary_size_delta.DeltaInfo("other_path", "t", "sym_name", True)
+ assert w != z
if __name__ == '__main__':
unittest.main()
diff --git a/tools/binary_size/run_binary_size_analysis.py b/tools/binary_size/run_binary_size_analysis.py
index 68982528..241fa64 100755
--- a/tools/binary_size/run_binary_size_analysis.py
+++ b/tools/binary_size/run_binary_size_analysis.py
@@ -169,7 +169,7 @@ def MakeCompactTree(symbols, symbol_path_origin_dir):
NODE_MAX_DEPTH_KEY: 0}
seen_symbol_with_path = False
cwd = os.path.abspath(os.getcwd())
- for symbol_name, symbol_type, symbol_size, file_path in symbols:
+ for symbol_name, symbol_type, symbol_size, file_path, _address in symbols:
if 'vtable for ' in symbol_name:
symbol_type = '@' # hack to categorize these separately
@@ -231,7 +231,7 @@ def DumpCompactTree(symbols, symbol_path_origin_dir, outfile):
def MakeSourceMap(symbols):
sources = {}
- for _sym, _symbol_type, size, path in symbols:
+ for _sym, _symbol_type, size, path, _address in symbols:
key = None
if path:
key = os.path.normpath(path)