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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 self.symbols = grouped_symbols | 75 self.symbols = grouped_symbols |
76 self.metadata = metadata or {} | 76 self.metadata = metadata or {} |
77 | 77 |
78 | 78 |
79 class SizeInfoDiff(object): | 79 class SizeInfoDiff(object): |
80 """What you get when you Diff() two SizeInfo objects. | 80 """What you get when you Diff() two SizeInfo objects. |
81 | 81 |
82 Fields: | 82 Fields: |
83 section_sizes: A dict of section_name -> size delta. | 83 section_sizes: A dict of section_name -> size delta. |
84 symbols: A SymbolDiff with all symbols in it. | 84 symbols: A SymbolDiff with all symbols in it. |
85 old_metadata: metadata of the "old" SizeInfo. | 85 before_metadata: metadata of the "before" SizeInfo. |
86 new_metadata: metadata of the "new" SizeInfo. | 86 after_metadata: metadata of the "after" SizeInfo. |
87 """ | 87 """ |
88 __slots__ = ( | 88 __slots__ = ( |
89 'section_sizes', | 89 'section_sizes', |
90 'symbols', | 90 'symbols', |
91 'old_metadata', | 91 'before_metadata', |
92 'new_metadata', | 92 'after_metadata', |
93 ) | 93 ) |
94 | 94 |
95 def __init__(self, section_sizes, symbols, old_metadata, new_metadata): | 95 def __init__(self, section_sizes, symbols, before_metadata, after_metadata): |
96 self.section_sizes = section_sizes | 96 self.section_sizes = section_sizes |
97 self.symbols = symbols | 97 self.symbols = symbols |
98 self.old_metadata = old_metadata | 98 self.before_metadata = before_metadata |
99 self.new_metadata = new_metadata | 99 self.after_metadata = after_metadata |
100 | 100 |
101 | 101 |
102 class BaseSymbol(object): | 102 class BaseSymbol(object): |
103 """Base class for Symbol and SymbolGroup. | 103 """Base class for Symbol and SymbolGroup. |
104 | 104 |
105 Refer to module docs for field descriptions. | 105 Refer to module docs for field descriptions. |
106 """ | 106 """ |
107 __slots__ = () | 107 __slots__ = () |
108 | 108 |
109 @property | 109 @property |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 return self._symbols.__getitem__(key) | 246 return self._symbols.__getitem__(key) |
247 if isinstance(key, basestring) or key > len(self._symbols): | 247 if isinstance(key, basestring) or key > len(self._symbols): |
248 found = self.WhereAddressInRange(key) | 248 found = self.WhereAddressInRange(key) |
249 if len(found) != 1: | 249 if len(found) != 1: |
250 raise KeyError('%d symbols found at address %s.' % (len(found), key)) | 250 raise KeyError('%d symbols found at address %s.' % (len(found), key)) |
251 return found[0] | 251 return found[0] |
252 return self._symbols[key] | 252 return self._symbols[key] |
253 | 253 |
254 def __sub__(self, other): | 254 def __sub__(self, other): |
255 other_ids = set(id(s) for s in other) | 255 other_ids = set(id(s) for s in other) |
256 new_symbols = [s for s in self if id(s) not in other_ids] | 256 after_symbols = [s for s in self if id(s) not in other_ids] |
257 return self._CreateTransformed(new_symbols, section_name=self.section_name) | 257 return self._CreateTransformed(after_symbols, |
| 258 section_name=self.section_name) |
258 | 259 |
259 def __add__(self, other): | 260 def __add__(self, other): |
260 self_ids = set(id(s) for s in self) | 261 self_ids = set(id(s) for s in self) |
261 new_symbols = self._symbols + [s for s in other if id(s) not in self_ids] | 262 after_symbols = self._symbols + [s for s in other if id(s) not in self_ids] |
262 return self._CreateTransformed(new_symbols, section_name=self.section_name, | 263 return self._CreateTransformed( |
263 is_sorted=False) | 264 after_symbols, section_name=self.section_name, is_sorted=False) |
264 | 265 |
265 @property | 266 @property |
266 def address(self): | 267 def address(self): |
267 first = self._symbols[0].address | 268 first = self._symbols[0].address |
268 return first if all(s.address == first for s in self._symbols) else 0 | 269 return first if all(s.address == first for s in self._symbols) else 0 |
269 | 270 |
270 @property | 271 @property |
271 def is_anonymous(self): | 272 def is_anonymous(self): |
272 first = self._symbols[0].is_anonymous | 273 first = self._symbols[0].is_anonymous |
273 return first if all( | 274 return first if all( |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
306 is_sorted = self.is_sorted | 307 is_sorted = self.is_sorted |
307 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, | 308 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, |
308 section_name=section_name, is_sorted=is_sorted) | 309 section_name=section_name, is_sorted=is_sorted) |
309 | 310 |
310 def Sorted(self, cmp_func=None, key=None, reverse=False): | 311 def Sorted(self, cmp_func=None, key=None, reverse=False): |
311 # Default to sorting by abs(size) then name. | 312 # Default to sorting by abs(size) then name. |
312 if cmp_func is None and key is None: | 313 if cmp_func is None and key is None: |
313 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name), | 314 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name), |
314 (b.IsBss(), abs(a.size), b.name)) | 315 (b.IsBss(), abs(a.size), b.name)) |
315 | 316 |
316 new_symbols = sorted(self._symbols, cmp_func, key, reverse) | 317 after_symbols = sorted(self._symbols, cmp_func, key, reverse) |
317 return self._CreateTransformed( | 318 return self._CreateTransformed( |
318 new_symbols, filtered_symbols=self._filtered_symbols, | 319 after_symbols, filtered_symbols=self._filtered_symbols, |
319 section_name=self.section_name, is_sorted=True) | 320 section_name=self.section_name, is_sorted=True) |
320 | 321 |
321 def SortedByName(self, reverse=False): | 322 def SortedByName(self, reverse=False): |
322 return self.Sorted(key=(lambda s:s.name), reverse=reverse) | 323 return self.Sorted(key=(lambda s:s.name), reverse=reverse) |
323 | 324 |
324 def SortedByAddress(self, reverse=False): | 325 def SortedByAddress(self, reverse=False): |
325 return self.Sorted(key=(lambda s:s.address), reverse=reverse) | 326 return self.Sorted(key=(lambda s:s.address), reverse=reverse) |
326 | 327 |
327 def SortedByCount(self, reverse=False): | 328 def SortedByCount(self, reverse=False): |
328 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1), | 329 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1), |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 | 410 |
410 Args: | 411 Args: |
411 func: Grouping function. Passed a symbol and returns a string for the | 412 func: Grouping function. Passed a symbol and returns a string for the |
412 name of the subgroup to put the symbol in. If None is returned, the | 413 name of the subgroup to put the symbol in. If None is returned, the |
413 symbol is omitted. | 414 symbol is omitted. |
414 min_count: Miniumum number of symbols for a group. If fewer than this many | 415 min_count: Miniumum number of symbols for a group. If fewer than this many |
415 symbols end up in a group, they will not be put within a group. | 416 symbols end up in a group, they will not be put within a group. |
416 Use a negative value to omit symbols entirely rather than | 417 Use a negative value to omit symbols entirely rather than |
417 include them outside of a group. | 418 include them outside of a group. |
418 """ | 419 """ |
419 new_syms = [] | 420 after_syms = [] |
420 filtered_symbols = [] | 421 filtered_symbols = [] |
421 symbols_by_token = collections.defaultdict(list) | 422 symbols_by_token = collections.defaultdict(list) |
422 # Index symbols by |func|. | 423 # Index symbols by |func|. |
423 for symbol in self: | 424 for symbol in self: |
424 token = func(symbol) | 425 token = func(symbol) |
425 if token is None: | 426 if token is None: |
426 filtered_symbols.append(symbol) | 427 filtered_symbols.append(symbol) |
427 symbols_by_token[token].append(symbol) | 428 symbols_by_token[token].append(symbol) |
428 # Create the subgroups. | 429 # Create the subgroups. |
429 include_singles = min_count >= 0 | 430 include_singles = min_count >= 0 |
430 min_count = abs(min_count) | 431 min_count = abs(min_count) |
431 for token, symbols in symbols_by_token.iteritems(): | 432 for token, symbols in symbols_by_token.iteritems(): |
432 if len(symbols) >= min_count: | 433 if len(symbols) >= min_count: |
433 new_syms.append(self._CreateTransformed( | 434 after_syms.append(self._CreateTransformed( |
434 symbols, name=token, section_name=self.section_name, | 435 symbols, name=token, section_name=self.section_name, |
435 is_sorted=False)) | 436 is_sorted=False)) |
436 elif include_singles: | 437 elif include_singles: |
437 new_syms.extend(symbols) | 438 after_syms.extend(symbols) |
438 else: | 439 else: |
439 filtered_symbols.extend(symbols) | 440 filtered_symbols.extend(symbols) |
440 return self._CreateTransformed( | 441 return self._CreateTransformed( |
441 new_syms, filtered_symbols=filtered_symbols, | 442 after_syms, filtered_symbols=filtered_symbols, |
442 section_name=self.section_name, is_sorted=False) | 443 section_name=self.section_name, is_sorted=False) |
443 | 444 |
444 def GroupBySectionName(self): | 445 def GroupBySectionName(self): |
445 return self.GroupBy(lambda s: s.section_name) | 446 return self.GroupBy(lambda s: s.section_name) |
446 | 447 |
447 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0): | 448 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0): |
448 """Groups by symbol namespace (as denoted by ::s). | 449 """Groups by symbol namespace (as denoted by ::s). |
449 | 450 |
450 Does not differentiate between C++ namespaces and C++ classes. | 451 Does not differentiate between C++ namespaces and C++ classes. |
451 | 452 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
582 key = id(sym) | 583 key = id(sym) |
583 return key not in self._added_ids and key not in self._removed_ids | 584 return key not in self._added_ids and key not in self._removed_ids |
584 | 585 |
585 def IsRemoved(self, sym): | 586 def IsRemoved(self, sym): |
586 return id(sym) in self._removed_ids | 587 return id(sym) in self._removed_ids |
587 | 588 |
588 def WhereNotUnchanged(self): | 589 def WhereNotUnchanged(self): |
589 return self.Filter(lambda s: not self.IsSimilar(s) or s.size) | 590 return self.Filter(lambda s: not self.IsSimilar(s) or s.size) |
590 | 591 |
591 | 592 |
592 def Diff(new, old): | 593 def Diff(before, after): |
593 """Diffs two SizeInfo or SymbolGroup objects. | 594 """Diffs two SizeInfo or SymbolGroup objects. |
594 | 595 |
595 When diffing SizeInfos, a SizeInfoDiff is returned. | 596 When diffing SizeInfos, a SizeInfoDiff is returned. |
596 When diffing SymbolGroups, a SymbolDiff is returned. | 597 When diffing SymbolGroups, a SymbolDiff is returned. |
597 | 598 |
598 Returns: | 599 Returns: |
599 Returns a SizeInfo when args are of type SizeInfo. | 600 Returns a SizeInfo when args are of type SizeInfo. |
600 Returns a SymbolDiff when args are of type SymbolGroup. | 601 Returns a SymbolDiff when args are of type SymbolGroup. |
601 """ | 602 """ |
602 if isinstance(new, SizeInfo): | 603 if isinstance(after, SizeInfo): |
603 assert isinstance(old, SizeInfo) | 604 assert isinstance(before, SizeInfo) |
604 section_sizes = { | 605 section_sizes = {k: after.section_sizes[k] - v |
605 k:new.section_sizes[k] - v for k, v in old.section_sizes.iteritems()} | 606 for k, v in before.section_sizes.iteritems()} |
606 symbol_diff = Diff(new.symbols, old.symbols) | 607 symbol_diff = _DiffSymbols(before.symbols, after.symbols) |
607 return SizeInfoDiff(section_sizes, symbol_diff, old.metadata, new.metadata) | 608 return SizeInfoDiff(section_sizes, symbol_diff, before.metadata, |
| 609 after.metadata) |
608 | 610 |
609 assert isinstance(new, SymbolGroup) and isinstance(old, SymbolGroup) | 611 assert isinstance(after, SymbolGroup) and isinstance(before, SymbolGroup) |
610 return _DiffSymbols(new, old) | 612 return _DiffSymbols(before, after) |
611 | 613 |
612 | 614 |
613 def _NegateAll(symbols): | 615 def _NegateAll(symbols): |
614 ret = [] | 616 ret = [] |
615 for symbol in symbols: | 617 for symbol in symbols: |
616 if symbol.IsGroup(): | 618 if symbol.IsGroup(): |
617 duped = SymbolDiff([], _NegateAll(symbol), [], name=symbol.name, | 619 duped = SymbolDiff([], _NegateAll(symbol), [], name=symbol.name, |
618 full_name=symbol.full_name, | 620 full_name=symbol.full_name, |
619 section_name=symbol.section_name) | 621 section_name=symbol.section_name) |
620 else: | 622 else: |
621 duped = copy.copy(symbol) | 623 duped = copy.copy(symbol) |
622 duped.size = -duped.size | 624 duped.size = -duped.size |
623 duped.padding = -duped.padding | 625 duped.padding = -duped.padding |
624 ret.append(duped) | 626 ret.append(duped) |
625 return ret | 627 return ret |
626 | 628 |
627 | 629 |
628 def _DiffSymbols(new_group, old_group): | 630 def _DiffSymbols(before, after): |
629 symbols_by_key = collections.defaultdict(list) | 631 symbols_by_key = collections.defaultdict(list) |
630 for s in old_group: | 632 for s in before: |
631 symbols_by_key[s._Key()].append(s) | 633 symbols_by_key[s._Key()].append(s) |
632 | 634 |
633 added = [] | 635 added = [] |
634 similar = [] | 636 similar = [] |
635 # For similar symbols, padding is zeroed out. In order to not lose the | 637 # For similar symbols, padding is zeroed out. In order to not lose the |
636 # information entirely, store it in aggregate. | 638 # information entirely, store it in aggregate. |
637 padding_by_section_name = collections.defaultdict(int) | 639 padding_by_section_name = collections.defaultdict(int) |
638 for new_sym in new_group: | 640 for after_sym in after: |
639 matching_syms = symbols_by_key.get(new_sym._Key()) | 641 matching_syms = symbols_by_key.get(after_sym._Key()) |
640 if matching_syms: | 642 if matching_syms: |
641 old_sym = matching_syms.pop(0) | 643 before_sym = matching_syms.pop(0) |
642 if old_sym.IsGroup() and new_sym.IsGroup(): | 644 if before_sym.IsGroup() and after_sym.IsGroup(): |
643 merged_sym = _DiffSymbols(new_sym, old_sym) | 645 merged_sym = _DiffSymbols(before_sym, after_sym) |
644 else: | 646 else: |
645 size_diff = new_sym.size_without_padding - old_sym.size_without_padding | 647 size_diff = (after_sym.size_without_padding - |
646 merged_sym = Symbol(new_sym.section_name, size_diff, | 648 before_sym.size_without_padding) |
647 address=new_sym.address, name=new_sym.name, | 649 merged_sym = Symbol(after_sym.section_name, size_diff, |
648 source_path=new_sym.source_path, | 650 address=after_sym.address, name=after_sym.name, |
649 object_path=new_sym.object_path, | 651 source_path=after_sym.source_path, |
650 full_name=new_sym.full_name, | 652 object_path=after_sym.object_path, |
651 is_anonymous=new_sym.is_anonymous) | 653 full_name=after_sym.full_name, |
| 654 is_anonymous=after_sym.is_anonymous) |
652 | 655 |
653 # Diffs are more stable when comparing size without padding, except when | 656 # Diffs are more stable when comparing size without padding, except when |
654 # the symbol is a padding-only symbol. | 657 # the symbol is a padding-only symbol. |
655 if new_sym.size_without_padding == 0 and size_diff == 0: | 658 if after_sym.size_without_padding == 0 and size_diff == 0: |
656 merged_sym.padding = new_sym.padding - old_sym.padding | 659 merged_sym.padding = after_sym.padding - before_sym.padding |
657 else: | 660 else: |
658 padding_by_section_name[new_sym.section_name] += ( | 661 padding_by_section_name[after_sym.section_name] += ( |
659 new_sym.padding - old_sym.padding) | 662 after_sym.padding - before_sym.padding) |
660 | 663 |
661 similar.append(merged_sym) | 664 similar.append(merged_sym) |
662 else: | 665 else: |
663 added.append(new_sym) | 666 added.append(after_sym) |
664 | 667 |
665 removed = [] | 668 removed = [] |
666 for remaining_syms in symbols_by_key.itervalues(): | 669 for remaining_syms in symbols_by_key.itervalues(): |
667 if remaining_syms: | 670 if remaining_syms: |
668 removed.extend(_NegateAll(remaining_syms)) | 671 removed.extend(_NegateAll(remaining_syms)) |
669 | 672 |
670 for section_name, padding in padding_by_section_name.iteritems(): | 673 for section_name, padding in padding_by_section_name.iteritems(): |
671 if padding != 0: | 674 if padding != 0: |
672 similar.append(Symbol(section_name, padding, | 675 similar.append(Symbol(section_name, padding, |
673 name="** aggregate padding of diff'ed symbols")) | 676 name="** aggregate padding of diff'ed symbols")) |
674 return SymbolDiff(added, removed, similar, name=new_group.name, | 677 return SymbolDiff(added, removed, similar, name=after.name, |
675 full_name=new_group.full_name, | 678 full_name=after.full_name, |
676 section_name=new_group.section_name) | 679 section_name=after.section_name) |
677 | 680 |
678 | 681 |
679 def _ExtractPrefixBeforeSeparator(string, separator, count=1): | 682 def _ExtractPrefixBeforeSeparator(string, separator, count=1): |
680 idx = -len(separator) | 683 idx = -len(separator) |
681 prev_idx = None | 684 prev_idx = None |
682 for _ in xrange(count): | 685 for _ in xrange(count): |
683 idx = string.find(separator, idx + len(separator)) | 686 idx = string.find(separator, idx + len(separator)) |
684 if idx < 0: | 687 if idx < 0: |
685 break | 688 break |
686 prev_idx = idx | 689 prev_idx = idx |
687 return string[:prev_idx] | 690 return string[:prev_idx] |
OLD | NEW |