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 |