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 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
52 't': '.text', | 52 't': '.text', |
53 } | 53 } |
54 | 54 |
55 FLAG_ANONYMOUS = 1 | 55 FLAG_ANONYMOUS = 1 |
56 FLAG_STARTUP = 2 | 56 FLAG_STARTUP = 2 |
57 FLAG_UNLIKELY = 4 | 57 FLAG_UNLIKELY = 4 |
58 FLAG_REL = 8 | 58 FLAG_REL = 8 |
59 FLAG_REL_LOCAL = 16 | 59 FLAG_REL_LOCAL = 16 |
60 FLAG_GENERATED_SOURCE = 32 | 60 FLAG_GENERATED_SOURCE = 32 |
61 | 61 |
| 62 DIFF_STATUS_UNCHANGED = 0 |
| 63 DIFF_STATUS_CHANGED = 1 |
| 64 DIFF_STATUS_ADDED = 2 |
| 65 DIFF_STATUS_REMOVED = 3 |
| 66 |
62 | 67 |
63 class SizeInfo(object): | 68 class SizeInfo(object): |
64 """Represents all size information for a single binary. | 69 """Represents all size information for a single binary. |
65 | 70 |
66 Fields: | 71 Fields: |
67 section_sizes: A dict of section_name -> size. | 72 section_sizes: A dict of section_name -> size. |
68 raw_symbols: A list of all symbols, sorted by address. | 73 raw_symbols: A list of all symbols, sorted by address. |
69 symbols: A SymbolGroup containing all symbols. By default, these are the | 74 symbols: A SymbolGroup containing all symbols. By default, these are the |
70 same as raw_symbols, but may contain custom groupings when it is | 75 same as raw_symbols, but may contain custom groupings when it is |
71 desirable to convey the result of a query along with section_sizes and | 76 desirable to convey the result of a query along with section_sizes and |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 return 'Group(full_name=%s,count=%d,size=%d)' % ( | 297 return 'Group(full_name=%s,count=%d,size=%d)' % ( |
293 self.full_name, len(self), self.size) | 298 self.full_name, len(self), self.size) |
294 | 299 |
295 def __iter__(self): | 300 def __iter__(self): |
296 return iter(self._symbols) | 301 return iter(self._symbols) |
297 | 302 |
298 def __len__(self): | 303 def __len__(self): |
299 return len(self._symbols) | 304 return len(self._symbols) |
300 | 305 |
301 def __eq__(self, other): | 306 def __eq__(self, other): |
302 return self._symbols == other._symbols | 307 return isinstance(other, SymbolGroup) and self._symbols == other._symbols |
303 | 308 |
304 def __getitem__(self, key): | 309 def __getitem__(self, key): |
305 """|key| can be an index or an address. | 310 """|key| can be an index or an address. |
306 | 311 |
307 Raises if multiple symbols map to the address. | 312 Raises if multiple symbols map to the address. |
308 """ | 313 """ |
309 if isinstance(key, slice): | 314 if isinstance(key, slice): |
310 return self._symbols.__getitem__(key) | 315 return self._symbols.__getitem__(key) |
311 if isinstance(key, basestring) or key > len(self._symbols): | 316 if isinstance(key, basestring) or key > len(self._symbols): |
312 found = self.WhereAddressInRange(key) | 317 found = self.WhereAddressInRange(key) |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
642 class SymbolDiff(SymbolGroup): | 647 class SymbolDiff(SymbolGroup): |
643 """A SymbolGroup subclass representing a diff of two other SymbolGroups. | 648 """A SymbolGroup subclass representing a diff of two other SymbolGroups. |
644 | 649 |
645 All Symbols contained within have a |size| which is actually the size delta. | 650 All Symbols contained within have a |size| which is actually the size delta. |
646 Additionally, metadata is kept about which symbols were added / removed / | 651 Additionally, metadata is kept about which symbols were added / removed / |
647 changed. | 652 changed. |
648 """ | 653 """ |
649 __slots__ = ( | 654 __slots__ = ( |
650 '_added_ids', | 655 '_added_ids', |
651 '_removed_ids', | 656 '_removed_ids', |
| 657 '_diff_status', |
| 658 '_changed_count', |
652 ) | 659 ) |
653 | 660 |
654 def __init__(self, added, removed, similar): | 661 def __init__(self, added, removed, similar): |
655 self._added_ids = set(id(s) for s in added) | 662 self._added_ids = set(id(s) for s in added) |
656 self._removed_ids = set(id(s) for s in removed) | 663 self._removed_ids = set(id(s) for s in removed) |
| 664 self._diff_status = DIFF_STATUS_CHANGED |
| 665 self._changed_count = None |
657 symbols = [] | 666 symbols = [] |
658 symbols.extend(added) | 667 symbols.extend(added) |
659 symbols.extend(removed) | 668 symbols.extend(removed) |
660 symbols.extend(similar) | 669 symbols.extend(similar) |
661 super(SymbolDiff, self).__init__(symbols) | 670 super(SymbolDiff, self).__init__(symbols) |
662 | 671 |
663 def __repr__(self): | 672 def __repr__(self): |
664 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( | 673 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( |
665 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, | 674 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, |
666 self.unchanged_count, self.size) | 675 self.unchanged_count, self.size) |
667 | 676 |
668 def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None, | 677 def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None, |
669 template_name=None, name=None, section_name=None, | 678 template_name=None, name=None, section_name=None, |
670 is_sorted=None): | 679 is_sorted=None): |
671 # Printing sorts, so short-circuit the same symbols case. | 680 new_added_ids = set() |
672 if len(symbols) == len(self._symbols): | 681 new_removed_ids = set() |
673 new_added_ids = self._added_ids | 682 group_diff_status = DIFF_STATUS_UNCHANGED |
674 new_removed_ids = self._removed_ids | 683 changed_count = 0 |
675 else: | 684 if symbols: |
676 old_added_ids = self._added_ids | 685 group_diff_status = self.DiffStatus(symbols[0]) |
677 old_removed_ids = self._removed_ids | |
678 | |
679 def get_status(sym): | |
680 obj_id = id(sym) | |
681 if obj_id in old_added_ids: | |
682 return 0 | |
683 if obj_id in old_removed_ids: | |
684 return 1 | |
685 if sym.IsGroup(): | |
686 first_status = get_status(sym[0]) | |
687 if all(get_status(s) == first_status for s in sym[1:]): | |
688 return first_status | |
689 return 2 | |
690 | |
691 new_added_ids = set() | |
692 new_removed_ids = set() | |
693 for sym in symbols: | 686 for sym in symbols: |
694 status = get_status(sym) | 687 status = self.DiffStatus(sym) |
695 if status == 0: | 688 if status != group_diff_status: |
| 689 group_diff_status = DIFF_STATUS_CHANGED |
| 690 if status == DIFF_STATUS_ADDED: |
696 new_added_ids.add(id(sym)) | 691 new_added_ids.add(id(sym)) |
697 elif status == 1: | 692 elif status == DIFF_STATUS_REMOVED: |
698 new_removed_ids.add(id(sym)) | 693 new_removed_ids.add(id(sym)) |
| 694 elif status == DIFF_STATUS_CHANGED: |
| 695 changed_count += 1 |
699 | 696 |
700 ret = SymbolDiff.__new__(SymbolDiff) | 697 ret = SymbolDiff.__new__(SymbolDiff) |
701 ret._added_ids = new_added_ids | 698 ret._added_ids = new_added_ids |
702 ret._removed_ids = new_removed_ids | 699 ret._removed_ids = new_removed_ids |
| 700 ret._diff_status = group_diff_status |
| 701 ret._changed_count = changed_count |
703 super(SymbolDiff, ret).__init__( | 702 super(SymbolDiff, ret).__init__( |
704 symbols, filtered_symbols=filtered_symbols, full_name=full_name, | 703 symbols, filtered_symbols=filtered_symbols, full_name=full_name, |
705 template_name=template_name, name=name, section_name=section_name, | 704 template_name=template_name, name=name, section_name=section_name, |
706 is_sorted=is_sorted) | 705 is_sorted=is_sorted) |
707 return ret | 706 return ret |
708 | 707 |
709 @property | 708 @property |
710 def added_count(self): | 709 def added_count(self): |
711 return len(self._added_ids) | 710 return len(self._added_ids) |
712 | 711 |
713 @property | 712 @property |
714 def removed_count(self): | 713 def removed_count(self): |
715 return len(self._removed_ids) | 714 return len(self._removed_ids) |
716 | 715 |
717 @property | 716 @property |
718 def changed_count(self): | 717 def changed_count(self): |
719 not_changed = self.unchanged_count + self.added_count + self.removed_count | 718 if self._changed_count is None: |
720 return len(self) - not_changed | 719 self._changed_count = sum(1 for s in self if self.IsChanged(s)) |
| 720 return self._changed_count |
721 | 721 |
722 @property | 722 @property |
723 def unchanged_count(self): | 723 def unchanged_count(self): |
724 return sum(1 for s in self if self.IsSimilar(s) and s.size == 0) | 724 return (len(self) - self.changed_count - self.added_count - |
| 725 self.removed_count) |
| 726 |
| 727 def DiffStatus(self, sym): |
| 728 # Groups store their own status, computed during _CreateTransformed(). |
| 729 if sym.IsGroup(): |
| 730 return sym._diff_status |
| 731 sym_id = id(sym) |
| 732 if sym_id in self._added_ids: |
| 733 return DIFF_STATUS_ADDED |
| 734 if sym_id in self._removed_ids: |
| 735 return DIFF_STATUS_REMOVED |
| 736 # 0 --> unchanged |
| 737 # 1 --> changed |
| 738 return int(sym.size != 0) |
| 739 |
| 740 def IsUnchanged(self, sym): |
| 741 return self.DiffStatus(sym) == DIFF_STATUS_UNCHANGED |
| 742 |
| 743 def IsChanged(self, sym): |
| 744 return self.DiffStatus(sym) == DIFF_STATUS_CHANGED |
725 | 745 |
726 def IsAdded(self, sym): | 746 def IsAdded(self, sym): |
727 return id(sym) in self._added_ids | 747 return self.DiffStatus(sym) == DIFF_STATUS_ADDED |
728 | |
729 def IsSimilar(self, sym): | |
730 key = id(sym) | |
731 return key not in self._added_ids and key not in self._removed_ids | |
732 | 748 |
733 def IsRemoved(self, sym): | 749 def IsRemoved(self, sym): |
734 return id(sym) in self._removed_ids | 750 return self.DiffStatus(sym) == DIFF_STATUS_REMOVED |
735 | 751 |
736 def WhereNotUnchanged(self): | 752 def WhereNotUnchanged(self): |
737 return self.Filter(lambda s: not self.IsSimilar(s) or s.size) | 753 return self.Filter(lambda s: not self.IsUnchanged(s)) |
738 | 754 |
739 | 755 |
740 def _ExtractPrefixBeforeSeparator(string, separator, count): | 756 def _ExtractPrefixBeforeSeparator(string, separator, count): |
741 idx = -len(separator) | 757 idx = -len(separator) |
742 prev_idx = None | 758 prev_idx = None |
743 for _ in xrange(count): | 759 for _ in xrange(count): |
744 idx = string.find(separator, idx + len(separator)) | 760 idx = string.find(separator, idx + len(separator)) |
745 if idx < 0: | 761 if idx < 0: |
746 break | 762 break |
747 prev_idx = idx | 763 prev_idx = idx |
748 return string[:prev_idx] | 764 return string[:prev_idx] |
749 | 765 |
750 | 766 |
751 def _ExtractSuffixAfterSeparator(string, separator, count): | 767 def _ExtractSuffixAfterSeparator(string, separator, count): |
752 prev_idx = len(string) + 1 | 768 prev_idx = len(string) + 1 |
753 for _ in xrange(count): | 769 for _ in xrange(count): |
754 idx = string.rfind(separator, 0, prev_idx - 1) | 770 idx = string.rfind(separator, 0, prev_idx - 1) |
755 if idx < 0: | 771 if idx < 0: |
756 break | 772 break |
757 prev_idx = idx | 773 prev_idx = idx |
758 return string[:prev_idx] | 774 return string[:prev_idx] |
OLD | NEW |