| OLD | NEW |
| 1 # Copyright 2017 The Chromium Authors. All rights reserved. | 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 """Classes that comprise the data model for binary size analysis. | 4 """Classes that comprise the data model for binary size analysis. |
| 5 | 5 |
| 6 The primary classes are Symbol, and SymbolGroup. | 6 The primary classes are Symbol, and SymbolGroup. |
| 7 | 7 |
| 8 Description of common properties: | 8 Description of common properties: |
| 9 * address: The start address of the symbol. | 9 * address: The start address of the symbol. |
| 10 May be 0 (e.g. for .bss or for SymbolGroups). | 10 May be 0 (e.g. for .bss or for SymbolGroups). |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 are removed from both full_name and name during normalization). | 22 are removed from both full_name and name during normalization). |
| 23 * section_name: E.g. ".text", ".rodata", ".data.rel.local" | 23 * section_name: E.g. ".text", ".rodata", ".data.rel.local" |
| 24 * section: The second character of |section_name|. E.g. "t", "r", "d". | 24 * section: The second character of |section_name|. E.g. "t", "r", "d". |
| 25 """ | 25 """ |
| 26 | 26 |
| 27 import collections | 27 import collections |
| 28 import logging | 28 import logging |
| 29 import os | 29 import os |
| 30 import re | 30 import re |
| 31 | 31 |
| 32 import cluster_symbols |
| 32 import match_util | 33 import match_util |
| 33 | 34 |
| 34 | 35 |
| 35 METADATA_GIT_REVISION = 'git_revision' | 36 METADATA_GIT_REVISION = 'git_revision' |
| 36 METADATA_APK_FILENAME = 'apk_file_name' # Path relative to output_directory. | 37 METADATA_APK_FILENAME = 'apk_file_name' # Path relative to output_directory. |
| 37 METADATA_MAP_FILENAME = 'map_file_name' # Path relative to output_directory. | 38 METADATA_MAP_FILENAME = 'map_file_name' # Path relative to output_directory. |
| 38 METADATA_ELF_ARCHITECTURE = 'elf_arch' # "Machine" field from readelf -h | 39 METADATA_ELF_ARCHITECTURE = 'elf_arch' # "Machine" field from readelf -h |
| 39 METADATA_ELF_FILENAME = 'elf_file_name' # Path relative to output_directory. | 40 METADATA_ELF_FILENAME = 'elf_file_name' # Path relative to output_directory. |
| 40 METADATA_ELF_MTIME = 'elf_mtime' # int timestamp in utc. | 41 METADATA_ELF_MTIME = 'elf_mtime' # int timestamp in utc. |
| 41 METADATA_ELF_BUILD_ID = 'elf_build_id' | 42 METADATA_ELF_BUILD_ID = 'elf_build_id' |
| (...skipping 19 matching lines...) Expand all Loading... |
| 61 if clone_idx != -1: | 62 if clone_idx != -1: |
| 62 return name[:clone_idx] | 63 return name[:clone_idx] |
| 63 return name | 64 return name |
| 64 | 65 |
| 65 | 66 |
| 66 class SizeInfo(object): | 67 class SizeInfo(object): |
| 67 """Represents all size information for a single binary. | 68 """Represents all size information for a single binary. |
| 68 | 69 |
| 69 Fields: | 70 Fields: |
| 70 section_sizes: A dict of section_name -> size. | 71 section_sizes: A dict of section_name -> size. |
| 71 raw_symbols: A flat list of all symbols. | 72 symbols: A SymbolGroup containing all symbols, sorted by address. |
| 72 symbols: A SymbolGroup containing raw_symbols, but with some Symbols grouped | |
| 73 into sub-SymbolGroups. | |
| 74 metadata: A dict. | 73 metadata: A dict. |
| 75 """ | 74 """ |
| 76 __slots__ = ( | 75 __slots__ = ( |
| 77 'section_sizes', | 76 'section_sizes', |
| 78 'raw_symbols', | |
| 79 'symbols', | 77 'symbols', |
| 80 'metadata', | 78 'metadata', |
| 81 ) | 79 ) |
| 82 | 80 |
| 83 """Root size information.""" | 81 """Root size information.""" |
| 84 def __init__(self, section_sizes, raw_symbols, grouped_symbols=None, | 82 def __init__(self, section_sizes, symbols, metadata=None): |
| 85 metadata=None): | |
| 86 self.section_sizes = section_sizes # E.g. {'.text': 0} | 83 self.section_sizes = section_sizes # E.g. {'.text': 0} |
| 87 # List of symbols sorted by address per-section. | 84 self.symbols = symbols |
| 88 self.raw_symbols = raw_symbols | |
| 89 # Root SymbolGroup. Cloned symbols grouped together within sub-SymbolGroups. | |
| 90 self.symbols = grouped_symbols | |
| 91 self.metadata = metadata or {} | 85 self.metadata = metadata or {} |
| 92 | 86 |
| 87 def Cluster(self): |
| 88 """Returns a new SizeInfo with some symbols moved into subgroups. |
| 89 |
| 90 See SymbolGroup.Cluster() for more details. |
| 91 """ |
| 92 return SizeInfo(self.section_sizes, self.symbols.Cluster(), self.metadata) |
| 93 |
| 93 | 94 |
| 94 class SizeInfoDiff(object): | 95 class SizeInfoDiff(object): |
| 95 """What you get when you Diff() two SizeInfo objects. | 96 """What you get when you Diff() two SizeInfo objects. |
| 96 | 97 |
| 97 Fields: | 98 Fields: |
| 98 section_sizes: A dict of section_name -> size delta. | 99 section_sizes: A dict of section_name -> size delta. |
| 99 symbols: A SymbolDiff with all symbols in it. | 100 symbols: A SymbolDiff with all symbols in it. |
| 100 before_metadata: metadata of the "before" SizeInfo. | 101 before_metadata: metadata of the "before" SizeInfo. |
| 101 after_metadata: metadata of the "after" SizeInfo. | 102 after_metadata: metadata of the "after" SizeInfo. |
| 102 """ | 103 """ |
| (...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 return self._padding | 372 return self._padding |
| 372 | 373 |
| 373 @property | 374 @property |
| 374 def aliases(self): | 375 def aliases(self): |
| 375 return None | 376 return None |
| 376 | 377 |
| 377 def IsGroup(self): | 378 def IsGroup(self): |
| 378 return True | 379 return True |
| 379 | 380 |
| 380 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, | 381 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, |
| 381 section_name=None, is_sorted=None): | 382 full_name=None, section_name=None, is_sorted=None): |
| 382 if is_sorted is None: | 383 if is_sorted is None: |
| 383 is_sorted = self.is_sorted | 384 is_sorted = self.is_sorted |
| 384 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, | 385 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, |
| 385 section_name=section_name, is_sorted=is_sorted) | 386 full_name=full_name, section_name=section_name, |
| 387 is_sorted=is_sorted) |
| 388 |
| 389 def Cluster(self): |
| 390 """Returns a new SymbolGroup with some symbols moved into subgroups. |
| 391 |
| 392 Subgroups include: |
| 393 * Symbols that have [clone] in their name (created during inlining). |
| 394 * Star symbols (such as "** merge strings", and "** symbol gap") |
| 395 |
| 396 To view created groups: |
| 397 Print(clustered.Filter(lambda s: s.IsGroup()), recursive=True) |
| 398 """ |
| 399 return self._CreateTransformed(cluster_symbols.ClusterSymbols(self)) |
| 386 | 400 |
| 387 def Sorted(self, cmp_func=None, key=None, reverse=False): | 401 def Sorted(self, cmp_func=None, key=None, reverse=False): |
| 388 # Default to sorting by abs(size) then name. | 402 # Default to sorting by abs(size) then name. |
| 389 if cmp_func is None and key is None: | 403 if cmp_func is None and key is None: |
| 390 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name), | 404 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name), |
| 391 (b.IsBss(), abs(a.size), b.name)) | 405 (b.IsBss(), abs(a.size), b.name)) |
| 392 | 406 |
| 393 after_symbols = sorted(self._symbols, cmp_func, key, reverse) | 407 after_symbols = sorted(self._symbols, cmp_func, key, reverse) |
| 394 return self._CreateTransformed( | 408 return self._CreateTransformed( |
| 395 after_symbols, filtered_symbols=self._filtered_symbols, | 409 after_symbols, filtered_symbols=self._filtered_symbols, |
| (...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 620 symbols.extend(similar) | 634 symbols.extend(similar) |
| 621 super(SymbolDiff, self).__init__(symbols, name=name, full_name=full_name, | 635 super(SymbolDiff, self).__init__(symbols, name=name, full_name=full_name, |
| 622 section_name=section_name) | 636 section_name=section_name) |
| 623 | 637 |
| 624 def __repr__(self): | 638 def __repr__(self): |
| 625 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( | 639 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( |
| 626 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, | 640 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, |
| 627 self.unchanged_count, self.size) | 641 self.unchanged_count, self.size) |
| 628 | 642 |
| 629 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, | 643 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, |
| 630 section_name=None, is_sorted=None): | 644 full_name=None, section_name=None, is_sorted=None): |
| 645 # Printing sorts, so short-circuit the same symbols case. |
| 646 if len(symbols) == len(self._symbols): |
| 647 new_added_ids = self._added_ids |
| 648 new_removed_ids = self._removed_ids |
| 649 else: |
| 650 old_added_ids = self._added_ids |
| 651 old_removed_ids = self._removed_ids |
| 652 |
| 653 def get_status(sym): |
| 654 obj_id = id(sym) |
| 655 if obj_id in old_added_ids: |
| 656 return 0 |
| 657 if obj_id in old_removed_ids: |
| 658 return 1 |
| 659 if sym.IsGroup(): |
| 660 first_status = get_status(sym[0]) |
| 661 if all(get_status(s) == first_status for s in sym[1:]): |
| 662 return first_status |
| 663 return 2 |
| 664 |
| 665 new_added_ids = set() |
| 666 new_removed_ids = set() |
| 667 for sym in symbols: |
| 668 status = get_status(sym) |
| 669 if status == 0: |
| 670 new_added_ids.add(id(sym)) |
| 671 elif status == 1: |
| 672 new_removed_ids.add(id(sym)) |
| 673 |
| 631 ret = SymbolDiff.__new__(SymbolDiff) | 674 ret = SymbolDiff.__new__(SymbolDiff) |
| 632 # Printing sorts, so fast-path the same symbols case. | 675 ret._added_ids = new_added_ids |
| 633 if len(symbols) == len(self._symbols): | 676 ret._removed_ids = new_removed_ids |
| 634 ret._added_ids = self._added_ids | |
| 635 ret._removed_ids = self._removed_ids | |
| 636 else: | |
| 637 ret._added_ids = set(id(s) for s in symbols if self.IsAdded(s)) | |
| 638 ret._removed_ids = set(id(s) for s in symbols if self.IsRemoved(s)) | |
| 639 super(SymbolDiff, ret).__init__( | 677 super(SymbolDiff, ret).__init__( |
| 640 symbols, filtered_symbols=filtered_symbols, name=name, | 678 symbols, filtered_symbols=filtered_symbols, name=name, |
| 641 section_name=section_name, is_sorted=is_sorted) | 679 full_name=full_name, section_name=section_name, is_sorted=is_sorted) |
| 642 return ret | 680 return ret |
| 643 | 681 |
| 644 @property | 682 @property |
| 645 def added_count(self): | 683 def added_count(self): |
| 646 return len(self._added_ids) | 684 return len(self._added_ids) |
| 647 | 685 |
| 648 @property | 686 @property |
| 649 def removed_count(self): | 687 def removed_count(self): |
| 650 return len(self._removed_ids) | 688 return len(self._removed_ids) |
| 651 | 689 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 674 | 712 |
| 675 def _ExtractPrefixBeforeSeparator(string, separator, count=1): | 713 def _ExtractPrefixBeforeSeparator(string, separator, count=1): |
| 676 idx = -len(separator) | 714 idx = -len(separator) |
| 677 prev_idx = None | 715 prev_idx = None |
| 678 for _ in xrange(count): | 716 for _ in xrange(count): |
| 679 idx = string.find(separator, idx + len(separator)) | 717 idx = string.find(separator, idx + len(separator)) |
| 680 if idx < 0: | 718 if idx < 0: |
| 681 break | 719 break |
| 682 prev_idx = idx | 720 prev_idx = idx |
| 683 return string[:prev_idx] | 721 return string[:prev_idx] |
| OLD | NEW |