Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Describe the size difference of two binaries. | 6 """Describe the size difference of two binaries. |
| 7 | 7 |
| 8 Generates a description of the size difference of two binaries based | 8 Generates a description of the size difference of two binaries based |
| 9 on the difference of the size of various symbols. | 9 on the difference of the size of various symbols. |
| 10 | 10 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 --nm-out /tmp/nm2.dump | 31 --nm-out /tmp/nm2.dump |
| 32 | 32 |
| 33 # cleanup useless files | 33 # cleanup useless files |
| 34 rm -r /tmp/throwaway | 34 rm -r /tmp/throwaway |
| 35 | 35 |
| 36 # run this tool | 36 # run this tool |
| 37 explain_binary_size_delta.py --nm1 /tmp/nm1.dump --nm2 /tmp/nm2.dump | 37 explain_binary_size_delta.py --nm1 /tmp/nm1.dump --nm2 /tmp/nm2.dump |
| 38 """ | 38 """ |
| 39 | 39 |
| 40 import collections | 40 import collections |
| 41 from collections import Counter | |
| 42 from math import ceil | |
| 41 import operator | 43 import operator |
| 42 import optparse | 44 import optparse |
| 43 import os | 45 import os |
| 44 import sys | 46 import sys |
| 45 | 47 |
| 46 import binary_size_utils | 48 import binary_size_utils |
| 47 | 49 |
| 48 | 50 |
| 51 def CalculateSharedAddresses(symbols): | |
| 52 """Checks how many symbols share the same memory space.""" | |
|
Andrew Hayden (chromium.org)
2015/02/16 12:47:38
Please document the return type.
Daniel Bratell
2015/02/18 13:31:53
Done.
| |
| 53 count = Counter() | |
| 54 for _, _, _, _, address in symbols: | |
| 55 count[address] += 1 | |
| 56 | |
| 57 # metacount = Counter() | |
|
Andrew Hayden (chromium.org)
2015/02/16 12:47:38
Please remove the commented lines 57-61 or uncomme
Daniel Bratell
2015/02/18 13:31:53
Done.
| |
| 58 # for share_count in count.itervalues(): | |
| 59 # metacount[share_count] += 1 | |
| 60 # for (key, value) in metacount.items(): | |
| 61 # print("%d symbols were shared %d times" % ((value * key), key)) | |
| 62 return count | |
| 63 | |
| 49 def Compare(symbols1, symbols2): | 64 def Compare(symbols1, symbols2): |
| 50 """Executes a comparison of the symbols in symbols1 and symbols2. | 65 """Executes a comparison of the symbols in symbols1 and symbols2. |
| 51 | 66 |
| 52 Returns: | 67 Returns: |
| 53 tuple of lists: (added_symbols, removed_symbols, changed_symbols, others) | 68 tuple of lists: (added_symbols, removed_symbols, changed_symbols, others) |
| 54 """ | 69 """ |
| 55 added = [] # tuples | 70 added = [] # tuples |
| 56 removed = [] # tuples | 71 removed = [] # tuples |
| 57 changed = [] # tuples | 72 changed = [] # tuples |
| 58 unchanged = [] # tuples | 73 unchanged = [] # tuples |
| 59 | 74 |
| 60 cache1 = {} | 75 cache1 = {} |
| 61 cache2 = {} | 76 cache2 = {} |
| 62 # Make a map of (file, symbol_type) : (symbol_name, symbol_size) | 77 # Make a map of (file, symbol_type) : (symbol_name, effective_symbol_size) |
| 63 for cache, symbols in ((cache1, symbols1), (cache2, symbols2)): | 78 share_count1 = CalculateSharedAddresses(symbols1) |
| 64 for symbol_name, symbol_type, symbol_size, file_path in symbols: | 79 share_count2 = CalculateSharedAddresses(symbols2) |
| 80 for cache, symbols, share_count in ((cache1, symbols1, share_count1), | |
| 81 (cache2, symbols2, share_count2)): | |
| 82 for symbol_name, symbol_type, symbol_size, file_path, address in symbols: | |
| 65 if 'vtable for ' in symbol_name: | 83 if 'vtable for ' in symbol_name: |
| 66 symbol_type = '@' # hack to categorize these separately | 84 symbol_type = '@' # hack to categorize these separately |
| 67 if file_path: | 85 if file_path: |
| 68 file_path = os.path.normpath(file_path) | 86 file_path = os.path.normpath(file_path) |
| 69 else: | 87 else: |
| 70 file_path = '(No Path)' | 88 file_path = '(No Path)' |
| 89 shared_count = share_count[address] | |
| 90 if shared_count == 1: | |
| 91 effective_symbol_size = symbol_size | |
| 92 else: | |
| 93 assert shared_count > 1 | |
| 94 effective_symbol_size = int(ceil(symbol_size / float(shared_count))) | |
|
Andrew Hayden (chromium.org)
2015/02/16 12:47:38
For clarity, I'd prefer that we turn this line int
Daniel Bratell
2015/02/18 13:31:53
Done.
| |
| 71 key = (file_path, symbol_type) | 95 key = (file_path, symbol_type) |
| 72 bucket = cache.setdefault(key, {}) | 96 bucket = cache.setdefault(key, {}) |
| 73 size_list = bucket.setdefault(symbol_name, []) | 97 size_list = bucket.setdefault(symbol_name, []) |
| 74 size_list.append(symbol_size) | 98 size_list.append(effective_symbol_size) |
| 75 | 99 |
| 76 # Now diff them. We iterate over the elements in cache1. For each symbol | 100 # Now diff them. We iterate over the elements in cache1. For each symbol |
| 77 # that we find in cache2, we record whether it was deleted, changed, or | 101 # that we find in cache2, we record whether it was deleted, changed, or |
| 78 # unchanged. We then remove it from cache2; all the symbols that remain | 102 # unchanged. We then remove it from cache2; all the symbols that remain |
| 79 # in cache2 at the end of the iteration over cache1 are the 'new' symbols. | 103 # in cache2 at the end of the iteration over cache1 are the 'new' symbols. |
| 80 for key, bucket1 in cache1.items(): | 104 for key, bucket1 in cache1.items(): |
| 81 bucket2 = cache2.get(key) | 105 bucket2 = cache2.get(key) |
| 82 if not bucket2: | 106 if not bucket2: |
| 83 # A file was removed. Everything in bucket1 is dead. | 107 # A file was removed. Everything in bucket1 is dead. |
| 84 for symbol_name, symbol_size_list in bucket1.items(): | 108 for symbol_name, symbol_size_list in bucket1.items(): |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 else: | 194 else: |
| 171 shrunk.append(item) | 195 shrunk.append(item) |
| 172 | 196 |
| 173 new_symbols = CrunchStatsData(added) | 197 new_symbols = CrunchStatsData(added) |
| 174 removed_symbols = CrunchStatsData(removed) | 198 removed_symbols = CrunchStatsData(removed) |
| 175 grown_symbols = CrunchStatsData(grown) | 199 grown_symbols = CrunchStatsData(grown) |
| 176 shrunk_symbols = CrunchStatsData(shrunk) | 200 shrunk_symbols = CrunchStatsData(shrunk) |
| 177 sections = [new_symbols, removed_symbols, grown_symbols, shrunk_symbols] | 201 sections = [new_symbols, removed_symbols, grown_symbols, shrunk_symbols] |
| 178 for section in sections: | 202 for section in sections: |
| 179 for file_path, symbol_type, symbol_name, size1, size2 in section.symbols: | 203 for file_path, symbol_type, symbol_name, size1, size2 in section.symbols: |
| 204 assert not '&' in file_path, "File path: " + file_path | |
|
Andrew Hayden (chromium.org)
2015/02/16 12:47:38
Is there some context for this being added?
Daniel Bratell
2015/02/18 13:31:53
I really can't remember. Probably remains from som
| |
| 180 section.sources.add(file_path) | 205 section.sources.add(file_path) |
| 181 if size1 is not None: | 206 if size1 is not None: |
| 182 section.before_size += size1 | 207 section.before_size += size1 |
| 183 if size2 is not None: | 208 if size2 is not None: |
| 184 section.after_size += size2 | 209 section.after_size += size2 |
| 185 bucket = section.symbols_by_path.setdefault(file_path, []) | 210 bucket = section.symbols_by_path.setdefault(file_path, []) |
| 186 bucket.append((symbol_name, symbol_type, size1, size2)) | 211 bucket.append((symbol_name, symbol_type, size1, size2)) |
| 187 | 212 |
| 188 total_change = sum(s.after_size - s.before_size for s in sections) | 213 total_change = sum(s.after_size - s.before_size for s in sections) |
| 189 summary = 'Total change: %s bytes' % DeltaStr(total_change) | 214 summary = 'Total change: %s bytes' % DeltaStr(total_change) |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 351 with file(path, 'r') as nm_input: | 376 with file(path, 'r') as nm_input: |
| 352 if opts.verbose: | 377 if opts.verbose: |
| 353 print 'parsing ' + path + '...' | 378 print 'parsing ' + path + '...' |
| 354 symbols.append(list(binary_size_utils.ParseNm(nm_input))) | 379 symbols.append(list(binary_size_utils.ParseNm(nm_input))) |
| 355 (added, removed, changed, unchanged) = Compare(symbols[0], symbols[1]) | 380 (added, removed, changed, unchanged) = Compare(symbols[0], symbols[1]) |
| 356 CrunchStats(added, removed, changed, unchanged, | 381 CrunchStats(added, removed, changed, unchanged, |
| 357 opts.showsources | opts.showsymbols, opts.showsymbols) | 382 opts.showsources | opts.showsymbols, opts.showsymbols) |
| 358 | 383 |
| 359 if __name__ == '__main__': | 384 if __name__ == '__main__': |
| 360 sys.exit(main()) | 385 sys.exit(main()) |
| OLD | NEW |