Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: tools/binary_size/libsupersize/models.py

Issue 2817813003: supersize: Add "diff" command (Closed)
Patch Set: review commetns Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/binary_size/libsupersize/main.py ('k') | tools/binary_size/libsupersize/paths.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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]
OLDNEW
« no previous file with comments | « tools/binary_size/libsupersize/main.py ('k') | tools/binary_size/libsupersize/paths.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698