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

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

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

Powered by Google App Engine
This is Rietveld 408576698