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

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

Issue 2809043003: //tools/binary_size: Group [clone] and ** symbols (Closed)
Patch Set: review comments 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/map2size.py ('k') | tools/binary_size/testdata/ActualDiff.golden » ('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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 'r': '.rodata', 44 'r': '.rodata',
45 't': '.text', 45 't': '.text',
46 } 46 }
47 47
48 48
49 class SizeInfo(object): 49 class SizeInfo(object):
50 """Represents all size information for a single binary. 50 """Represents all size information for a single binary.
51 51
52 Fields: 52 Fields:
53 section_sizes: A dict of section_name -> size. 53 section_sizes: A dict of section_name -> size.
54 symbols: A SymbolGroup with all symbols in it. 54 raw_symbols: A flat list of all symbols.
55 symbols: A SymbolGroup containing raw_symbols, but with some Symbols grouped
56 into sub-SymbolGroups.
55 metadata: A dict. 57 metadata: A dict.
56 """ 58 """
57 __slots__ = ( 59 __slots__ = (
58 'section_sizes', 60 'section_sizes',
61 'raw_symbols',
59 'symbols', 62 'symbols',
60 'metadata', 63 'metadata',
61 ) 64 )
62 65
63 """Root size information.""" 66 """Root size information."""
64 def __init__(self, section_sizes, symbols, metadata=None): 67 def __init__(self, section_sizes, raw_symbols, grouped_symbols=None,
68 metadata=None):
65 self.section_sizes = section_sizes # E.g. {'.text': 0} 69 self.section_sizes = section_sizes # E.g. {'.text': 0}
66 self.symbols = symbols # List of symbols sorted by address per-section. 70 # List of symbols sorted by address per-section.
71 self.raw_symbols = raw_symbols
72 # Root SymbolGroup. Cloned symbols grouped together within sub-SymbolGroups.
73 self.symbols = grouped_symbols
67 self.metadata = metadata or {} 74 self.metadata = metadata or {}
68 75
69 76
70 class SizeInfoDiff(object): 77 class SizeInfoDiff(object):
71 """What you get when you Diff() two SizeInfo objects. 78 """What you get when you Diff() two SizeInfo objects.
72 79
73 Fields: 80 Fields:
74 section_sizes: A dict of section_name -> size delta. 81 section_sizes: A dict of section_name -> size delta.
75 symbols: A SymbolDiff with all symbols in it. 82 symbols: A SymbolDiff with all symbols in it.
76 old_metadata: metadata of the "old" SizeInfo. 83 old_metadata: metadata of the "old" SizeInfo.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 # TODO(agrieve): Also match generated functions such as: 130 # TODO(agrieve): Also match generated functions such as:
124 # startup._GLOBAL__sub_I_page_allocator.cc 131 # startup._GLOBAL__sub_I_page_allocator.cc
125 return self.name.endswith(']') and not self.name.endswith('[]') 132 return self.name.endswith(']') and not self.name.endswith('[]')
126 133
127 def _Key(self): 134 def _Key(self):
128 """Returns a tuple that can be used to see if two Symbol are the same. 135 """Returns a tuple that can be used to see if two Symbol are the same.
129 136
130 Keys are not guaranteed to be unique within a SymbolGroup. For example, it 137 Keys are not guaranteed to be unique within a SymbolGroup. For example, it
131 is common to have multiple "** merge strings" symbols, which will have a 138 is common to have multiple "** merge strings" symbols, which will have a
132 common key.""" 139 common key."""
133 return (self.section_name, self.full_name or self.name) 140 stripped_full_name = self.full_name
141 if stripped_full_name:
142 clone_idx = stripped_full_name.find(' [clone ')
143 if clone_idx != -1:
144 stripped_full_name = stripped_full_name[:clone_idx]
145 return (self.section_name, stripped_full_name or self.name)
134 146
135 147
136 class Symbol(BaseSymbol): 148 class Symbol(BaseSymbol):
137 """Represents a single symbol within a binary. 149 """Represents a single symbol within a binary.
138 150
139 Refer to module docs for field descriptions. 151 Refer to module docs for field descriptions.
140 """ 152 """
141 153
142 __slots__ = ( 154 __slots__ = (
143 'address', 155 'address',
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 * group['0x1234'] # By symbol address 198 * group['0x1234'] # By symbol address
187 * without_group2 = group1 - group2 199 * without_group2 = group1 - group2
188 * unioned = group1 + group2 200 * unioned = group1 + group2
189 """ 201 """
190 202
191 __slots__ = ( 203 __slots__ = (
192 '_padding', 204 '_padding',
193 '_size', 205 '_size',
194 '_symbols', 206 '_symbols',
195 '_filtered_symbols', 207 '_filtered_symbols',
208 'full_name',
196 'name', 209 'name',
197 'section_name', 210 'section_name',
198 'is_sorted', 211 'is_sorted',
199 ) 212 )
200 213
201 def __init__(self, symbols, filtered_symbols=None, name=None, 214 def __init__(self, symbols, filtered_symbols=None, name=None,
202 section_name=None, is_sorted=False): 215 full_name=None, section_name=None, is_sorted=False):
203 self._padding = None 216 self._padding = None
204 self._size = None 217 self._size = None
205 self._symbols = symbols 218 self._symbols = symbols
206 self._filtered_symbols = filtered_symbols or [] 219 self._filtered_symbols = filtered_symbols or []
207 self.name = name or '' 220 self.name = name or ''
221 self.full_name = full_name
208 self.section_name = section_name or '.*' 222 self.section_name = section_name or '.*'
209 self.is_sorted = is_sorted 223 self.is_sorted = is_sorted
210 224
211 def __repr__(self): 225 def __repr__(self):
212 return 'Group(name=%s,count=%d,size=%d)' % ( 226 return 'Group(name=%s,count=%d,size=%d)' % (
213 self.name, len(self), self.size) 227 self.name, len(self), self.size)
214 228
215 def __iter__(self): 229 def __iter__(self):
216 return iter(self._symbols) 230 return iter(self._symbols)
217 231
(...skipping 23 matching lines...) Expand all
241 return self._CreateTransformed(new_symbols, section_name=self.section_name) 255 return self._CreateTransformed(new_symbols, section_name=self.section_name)
242 256
243 def __add__(self, other): 257 def __add__(self, other):
244 self_ids = set(id(s) for s in self) 258 self_ids = set(id(s) for s in self)
245 new_symbols = self._symbols + [s for s in other if id(s) not in self_ids] 259 new_symbols = self._symbols + [s for s in other if id(s) not in self_ids]
246 return self._CreateTransformed(new_symbols, section_name=self.section_name, 260 return self._CreateTransformed(new_symbols, section_name=self.section_name,
247 is_sorted=False) 261 is_sorted=False)
248 262
249 @property 263 @property
250 def address(self): 264 def address(self):
251 return 0 265 first = self._symbols[0].address
252 266 return first if all(s.address == first for s in self._symbols) else 0
253 @property
254 def full_name(self):
255 return None
256 267
257 @property 268 @property
258 def is_anonymous(self): 269 def is_anonymous(self):
259 return False 270 first = self._symbols[0].is_anonymous
271 return first if all(
272 s.is_anonymous == first for s in self._symbols) else False
260 273
261 @property 274 @property
262 def object_path(self): 275 def object_path(self):
263 return None 276 first = self._symbols[0].object_path
277 return first if all(s.object_path == first for s in self._symbols) else None
264 278
265 @property 279 @property
266 def source_path(self): 280 def source_path(self):
267 return None 281 first = self._symbols[0].source_path
282 return first if all(s.source_path == first for s in self._symbols) else None
268 283
269 @property 284 @property
270 def size(self): 285 def size(self):
271 if self._size is None: 286 if self._size is None:
272 if self.IsBss(): 287 if self.IsBss():
273 self._size = sum(s.size for s in self) 288 self._size = sum(s.size for s in self)
274 self._size = sum(s.size for s in self if not s.IsBss()) 289 self._size = sum(s.size for s in self if not s.IsBss())
275 return self._size 290 return self._size
276 291
277 @property 292 @property
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
503 518
504 All Symbols contained within have a |size| which is actually the size delta. 519 All Symbols contained within have a |size| which is actually the size delta.
505 Additionally, metadata is kept about which symbols were added / removed / 520 Additionally, metadata is kept about which symbols were added / removed /
506 changed. 521 changed.
507 """ 522 """
508 __slots__ = ( 523 __slots__ = (
509 '_added_ids', 524 '_added_ids',
510 '_removed_ids', 525 '_removed_ids',
511 ) 526 )
512 527
513 def __init__(self, added, removed, similar): 528 def __init__(self, added, removed, similar, name=None, full_name=None,
529 section_name=None):
514 self._added_ids = set(id(s) for s in added) 530 self._added_ids = set(id(s) for s in added)
515 self._removed_ids = set(id(s) for s in removed) 531 self._removed_ids = set(id(s) for s in removed)
516 symbols = [] 532 symbols = []
517 symbols.extend(added) 533 symbols.extend(added)
518 symbols.extend(removed) 534 symbols.extend(removed)
519 symbols.extend(similar) 535 symbols.extend(similar)
520 super(SymbolDiff, self).__init__(symbols) 536 super(SymbolDiff, self).__init__(symbols, name=name, full_name=full_name,
537 section_name=section_name)
521 538
522 def __repr__(self): 539 def __repr__(self):
523 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( 540 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % (
524 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, 541 'SymbolGroup', self.added_count, self.removed_count, self.changed_count,
525 self.unchanged_count, self.size) 542 self.unchanged_count, self.size)
526 543
527 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, 544 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None,
528 section_name=None, is_sorted=None): 545 section_name=None, is_sorted=None):
529 ret = SymbolDiff.__new__(SymbolDiff) 546 ret = SymbolDiff.__new__(SymbolDiff)
530 # Printing sorts, so fast-path the same symbols case. 547 # Printing sorts, so fast-path the same symbols case.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
581 Returns a SymbolDiff when args are of type SymbolGroup. 598 Returns a SymbolDiff when args are of type SymbolGroup.
582 """ 599 """
583 if isinstance(new, SizeInfo): 600 if isinstance(new, SizeInfo):
584 assert isinstance(old, SizeInfo) 601 assert isinstance(old, SizeInfo)
585 section_sizes = { 602 section_sizes = {
586 k:new.section_sizes[k] - v for k, v in old.section_sizes.iteritems()} 603 k:new.section_sizes[k] - v for k, v in old.section_sizes.iteritems()}
587 symbol_diff = Diff(new.symbols, old.symbols) 604 symbol_diff = Diff(new.symbols, old.symbols)
588 return SizeInfoDiff(section_sizes, symbol_diff, old.metadata, new.metadata) 605 return SizeInfoDiff(section_sizes, symbol_diff, old.metadata, new.metadata)
589 606
590 assert isinstance(new, SymbolGroup) and isinstance(old, SymbolGroup) 607 assert isinstance(new, SymbolGroup) and isinstance(old, SymbolGroup)
608 return _DiffSymbols(new, old)
609
610
611 def _NegateAll(symbols):
612 ret = []
613 for symbol in symbols:
614 if symbol.IsGroup():
615 duped = SymbolDiff([], _NegateAll(symbol), [], name=symbol.name,
616 full_name=symbol.full_name,
617 section_name=symbol.section_name)
618 else:
619 duped = copy.copy(symbol)
620 duped.size = -duped.size
621 duped.padding = -duped.padding
622 ret.append(duped)
623 return ret
624
625
626 def _DiffSymbols(new_group, old_group):
591 symbols_by_key = collections.defaultdict(list) 627 symbols_by_key = collections.defaultdict(list)
592 for s in old: 628 for s in old_group:
593 symbols_by_key[s._Key()].append(s) 629 symbols_by_key[s._Key()].append(s)
594 630
595 added = [] 631 added = []
596 removed = []
597 similar = [] 632 similar = []
598 # For similar symbols, padding is zeroed out. In order to not lose the 633 # For similar symbols, padding is zeroed out. In order to not lose the
599 # information entirely, store it in aggregate. 634 # information entirely, store it in aggregate.
600 padding_by_section_name = collections.defaultdict(int) 635 padding_by_section_name = collections.defaultdict(int)
601 for new_sym in new: 636 for new_sym in new_group:
602 matching_syms = symbols_by_key.get(new_sym._Key()) 637 matching_syms = symbols_by_key.get(new_sym._Key())
603 if matching_syms: 638 if matching_syms:
604 old_sym = matching_syms.pop(0) 639 old_sym = matching_syms.pop(0)
605 # More stable/useful to compare size without padding. 640 if old_sym.IsGroup() and new_sym.IsGroup():
606 size_diff = (new_sym.size_without_padding - 641 merged_sym = _DiffSymbols(new_sym, old_sym)
607 old_sym.size_without_padding) 642 else:
608 merged_sym = Symbol(new_sym.section_name, size_diff, 643 size_diff = new_sym.size_without_padding - old_sym.size_without_padding
609 address=new_sym.address, name=new_sym.name, 644 merged_sym = Symbol(new_sym.section_name, size_diff,
610 source_path=new_sym.source_path, 645 address=new_sym.address, name=new_sym.name,
611 object_path=new_sym.object_path, 646 source_path=new_sym.source_path,
612 full_name=new_sym.full_name, 647 object_path=new_sym.object_path,
613 is_anonymous=new_sym.is_anonymous) 648 full_name=new_sym.full_name,
649 is_anonymous=new_sym.is_anonymous)
650
651 # Diffs are more stable when comparing size without padding, except when
652 # the symbol is a padding-only symbol.
653 if new_sym.size_without_padding == 0 and size_diff == 0:
654 merged_sym.padding = new_sym.padding - old_sym.padding
655 else:
656 padding_by_section_name[new_sym.section_name] += (
657 new_sym.padding - old_sym.padding)
658
614 similar.append(merged_sym) 659 similar.append(merged_sym)
615 padding_by_section_name[new_sym.section_name] += (
616 new_sym.padding - old_sym.padding)
617 else: 660 else:
618 added.append(new_sym) 661 added.append(new_sym)
619 662
663 removed = []
620 for remaining_syms in symbols_by_key.itervalues(): 664 for remaining_syms in symbols_by_key.itervalues():
621 for old_sym in remaining_syms: 665 if remaining_syms:
622 duped = copy.copy(old_sym) 666 removed.extend(_NegateAll(remaining_syms))
623 duped.size = -duped.size
624 duped.padding = -duped.padding
625 removed.append(duped)
626 667
627 for section_name, padding in padding_by_section_name.iteritems(): 668 for section_name, padding in padding_by_section_name.iteritems():
628 similar.append(Symbol(section_name, padding, 669 if padding != 0:
629 name="** aggregate padding of diff'ed symbols")) 670 similar.append(Symbol(section_name, padding,
630 return SymbolDiff(added, removed, similar) 671 name="** aggregate padding of diff'ed symbols"))
672 return SymbolDiff(added, removed, similar, name=new_group.name,
673 full_name=new_group.full_name,
674 section_name=new_group.section_name)
631 675
632 676
633 def _ExtractPrefixBeforeSeparator(string, separator, count=1): 677 def _ExtractPrefixBeforeSeparator(string, separator, count=1):
634 idx = -len(separator) 678 idx = -len(separator)
635 prev_idx = None 679 prev_idx = None
636 for _ in xrange(count): 680 for _ in xrange(count):
637 idx = string.find(separator, idx + len(separator)) 681 idx = string.find(separator, idx + len(separator))
638 if idx < 0: 682 if idx < 0:
639 break 683 break
640 prev_idx = idx 684 prev_idx = idx
641 return string[:prev_idx] 685 return string[:prev_idx]
OLDNEW
« no previous file with comments | « tools/binary_size/map2size.py ('k') | tools/binary_size/testdata/ActualDiff.golden » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698