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

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

Issue 2859383003: FREEZE.unindexed (Closed)
Patch Set: Created 3 years, 7 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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 'd': '.data', 48 'd': '.data',
49 'r': '.rodata', 49 'r': '.rodata',
50 't': '.text', 50 't': '.text',
51 } 51 }
52 52
53 FLAG_ANONYMOUS = 1 53 FLAG_ANONYMOUS = 1
54 FLAG_STARTUP = 2 54 FLAG_STARTUP = 2
55 FLAG_UNLIKELY = 4 55 FLAG_UNLIKELY = 4
56 FLAG_REL = 8 56 FLAG_REL = 8
57 FLAG_REL_LOCAL = 16 57 FLAG_REL_LOCAL = 16
58 FLAG_GENERATED_SOURCE = 32
58 59
59 60
60 class SizeInfo(object): 61 class SizeInfo(object):
61 """Represents all size information for a single binary. 62 """Represents all size information for a single binary.
62 63
63 Fields: 64 Fields:
64 section_sizes: A dict of section_name -> size. 65 section_sizes: A dict of section_name -> size.
65 symbols: A SymbolGroup containing all symbols, sorted by address. 66 symbols: A SymbolGroup containing all symbols, sorted by address.
66 metadata: A dict. 67 metadata: A dict.
67 """ 68 """
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 130
130 @property 131 @property
131 def end_address(self): 132 def end_address(self):
132 return self.address + self.size_without_padding 133 return self.address + self.size_without_padding
133 134
134 @property 135 @property
135 def is_anonymous(self): 136 def is_anonymous(self):
136 return bool(self.flags & FLAG_ANONYMOUS) 137 return bool(self.flags & FLAG_ANONYMOUS)
137 138
138 @property 139 @property
140 def generated_source(self):
141 return bool(self.flags & FLAG_GENERATED_SOURCE)
142
143 @generated_source.setter
144 def generated_source(self, value):
145 if value:
146 self.flags |= FLAG_GENERATED_SOURCE
147 else:
148 self.flags &= ~FLAG_GENERATED_SOURCE
149
150 @property
139 def num_aliases(self): 151 def num_aliases(self):
140 return len(self.aliases) if self.aliases else 1 152 return len(self.aliases) if self.aliases else 1
141 153
142 def FlagsString(self): 154 def FlagsString(self):
143 # Most flags are 0. 155 # Most flags are 0.
144 flags = self.flags 156 flags = self.flags
145 if not flags and not self.aliases: 157 if not flags and not self.aliases:
146 return '{}' 158 return '{}'
147 parts = [] 159 parts = []
148 if flags & FLAG_ANONYMOUS: 160 if flags & FLAG_ANONYMOUS:
149 parts.append('anon') 161 parts.append('anon')
150 if flags & FLAG_STARTUP: 162 if flags & FLAG_STARTUP:
151 parts.append('startup') 163 parts.append('startup')
152 if flags & FLAG_UNLIKELY: 164 if flags & FLAG_UNLIKELY:
153 parts.append('unlikely') 165 parts.append('unlikely')
154 if flags & FLAG_REL: 166 if flags & FLAG_REL:
155 parts.append('rel') 167 parts.append('rel')
156 if flags & FLAG_REL_LOCAL: 168 if flags & FLAG_REL_LOCAL:
157 parts.append('rel.loc') 169 parts.append('rel.loc')
170 if flags & FLAG_GENERATED_SOURCE:
171 parts.append('gen')
158 # Not actually a part of flags, but useful to show it here. 172 # Not actually a part of flags, but useful to show it here.
159 if self.aliases: 173 if self.aliases:
160 parts.append('{} aliases'.format(self.num_aliases)) 174 parts.append('{} aliases'.format(self.num_aliases))
161 return '{%s}' % ','.join(parts) 175 return '{%s}' % ','.join(parts)
162 176
163 def IsBss(self): 177 def IsBss(self):
164 return self.section_name == '.bss' 178 return self.section_name == '.bss'
165 179
166 def IsGroup(self): 180 def IsGroup(self):
167 return False 181 return False
168 182
169 def IsGenerated(self): 183 def IsGeneratedByToolchain(self):
170 # TODO(agrieve): Also match generated functions such as: 184 return '.' in self.name or (
171 # startup._GLOBAL__sub_I_page_allocator.cc 185 self.name.endswith(']') and not self.name.endswith('[]'))
172 return self.name.endswith(']') and not self.name.endswith('[]')
173 186
174 187
175 class Symbol(BaseSymbol): 188 class Symbol(BaseSymbol):
176 """Represents a single symbol within a binary. 189 """Represents a single symbol within a binary.
177 190
178 Refer to module docs for field descriptions. 191 Refer to module docs for field descriptions.
179 """ 192 """
180 193
181 __slots__ = ( 194 __slots__ = (
182 'address', 195 'address',
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 section_name=self.section_name) 308 section_name=self.section_name)
296 309
297 def __add__(self, other): 310 def __add__(self, other):
298 self_ids = set(id(s) for s in self) 311 self_ids = set(id(s) for s in self)
299 after_symbols = self._symbols + [s for s in other if id(s) not in self_ids] 312 after_symbols = self._symbols + [s for s in other if id(s) not in self_ids]
300 return self._CreateTransformed( 313 return self._CreateTransformed(
301 after_symbols, section_name=self.section_name, is_sorted=False) 314 after_symbols, section_name=self.section_name, is_sorted=False)
302 315
303 @property 316 @property
304 def address(self): 317 def address(self):
305 first = self._symbols[0].address 318 first = self._symbols[0].address if self else 0
306 return first if all(s.address == first for s in self._symbols) else 0 319 return first if all(s.address == first for s in self._symbols) else 0
307 320
308 @property 321 @property
309 def flags(self): 322 def flags(self):
310 first = self._symbols[0].flags 323 first = self._symbols[0].flags if self else 0
311 return first if all(s.flags == first for s in self._symbols) else 0 324 return first if all(s.flags == first for s in self._symbols) else 0
312 325
313 @property 326 @property
314 def object_path(self): 327 def object_path(self):
315 first = self._symbols[0].object_path 328 first = self._symbols[0].object_path if self else ''
316 return first if all(s.object_path == first for s in self._symbols) else '' 329 return first if all(s.object_path == first for s in self._symbols) else ''
317 330
318 @property 331 @property
319 def source_path(self): 332 def source_path(self):
320 first = self._symbols[0].source_path 333 first = self._symbols[0].source_path if self else ''
321 return first if all(s.source_path == first for s in self._symbols) else '' 334 return first if all(s.source_path == first for s in self._symbols) else ''
322 335
323 def IterUniqueSymbols(self): 336 def IterUniqueSymbols(self):
324 seen_aliases_lists = set() 337 seen_aliases_lists = set()
325 for s in self: 338 for s in self:
326 if not s.aliases: 339 if not s.aliases:
327 yield s 340 yield s
328 elif id(s.aliases) not in seen_aliases_lists: 341 elif id(s.aliases) not in seen_aliases_lists:
329 seen_aliases_lists.add(id(s.aliases)) 342 seen_aliases_lists.add(id(s.aliases))
330 yield s 343 yield s
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 Subgroups include: 390 Subgroups include:
378 * Symbols that have [clone] in their name (created during inlining). 391 * Symbols that have [clone] in their name (created during inlining).
379 * Star symbols (such as "** merge strings", and "** symbol gap") 392 * Star symbols (such as "** merge strings", and "** symbol gap")
380 393
381 To view created groups: 394 To view created groups:
382 Print(clustered.Filter(lambda s: s.IsGroup()), recursive=True) 395 Print(clustered.Filter(lambda s: s.IsGroup()), recursive=True)
383 """ 396 """
384 return self._CreateTransformed(cluster_symbols.ClusterSymbols(self)) 397 return self._CreateTransformed(cluster_symbols.ClusterSymbols(self))
385 398
386 def Sorted(self, cmp_func=None, key=None, reverse=False): 399 def Sorted(self, cmp_func=None, key=None, reverse=False):
387 # Default to sorting by abs(size) then name.
388 if cmp_func is None and key is None: 400 if cmp_func is None and key is None:
389 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name), 401 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.pss), a.name),
390 (b.IsBss(), abs(a.size), b.name)) 402 (b.IsBss(), abs(a.pss), b.name))
391 403
392 after_symbols = sorted(self._symbols, cmp_func, key, reverse) 404 after_symbols = sorted(self._symbols, cmp_func, key, reverse)
393 return self._CreateTransformed( 405 return self._CreateTransformed(
394 after_symbols, filtered_symbols=self._filtered_symbols, 406 after_symbols, filtered_symbols=self._filtered_symbols,
395 section_name=self.section_name, is_sorted=True) 407 section_name=self.section_name, is_sorted=True)
396 408
397 def SortedByName(self, reverse=False): 409 def SortedByName(self, reverse=False):
398 return self.Sorted(key=(lambda s:s.name), reverse=reverse) 410 return self.Sorted(key=(lambda s:s.name), reverse=reverse)
399 411
400 def SortedByAddress(self, reverse=False): 412 def SortedByAddress(self, reverse=False):
401 return self.Sorted(key=(lambda s:s.address), reverse=reverse) 413 return self.Sorted(key=(lambda s:(s.address, s.object_path, s.name)),
414 reverse=reverse)
402 415
403 def SortedByCount(self, reverse=False): 416 def SortedByCount(self, reverse=False):
404 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1), 417 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1),
405 reverse=not reverse) 418 reverse=not reverse)
406 419
407 def Filter(self, func): 420 def Filter(self, func):
408 filtered_and_kept = ([], []) 421 filtered_and_kept = ([], [])
409 symbol = None 422 symbol = None
410 try: 423 try:
411 for symbol in self: 424 for symbol in self:
(...skipping 11 matching lines...) Expand all
423 436
424 def WhereInSection(self, section): 437 def WhereInSection(self, section):
425 if len(section) == 1: 438 if len(section) == 1:
426 ret = self.Filter(lambda s: s.section == section) 439 ret = self.Filter(lambda s: s.section == section)
427 ret.section_name = SECTION_TO_SECTION_NAME[section] 440 ret.section_name = SECTION_TO_SECTION_NAME[section]
428 else: 441 else:
429 ret = self.Filter(lambda s: s.section_name == section) 442 ret = self.Filter(lambda s: s.section_name == section)
430 ret.section_name = section 443 ret.section_name = section
431 return ret 444 return ret
432 445
433 def WhereIsGenerated(self): 446 def WhereSourceIsGenerated(self):
434 return self.Filter(lambda s: s.IsGenerated()) 447 return self.Filter(lambda s: s.generated_source)
448
449 def WhereGeneratedByToolchain(self):
450 return self.Filter(lambda s: s.IsGeneratedByToolchain())
435 451
436 def WhereNameMatches(self, pattern): 452 def WhereNameMatches(self, pattern):
437 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 453 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
438 return self.Filter(lambda s: regex.search(s.name)) 454 return self.Filter(lambda s: regex.search(s.name))
439 455
456 def WhereFullNameMatches(self, pattern):
457 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
458 return self.Filter(lambda s: regex.search(s.full_name or s.name))
459
440 def WhereObjectPathMatches(self, pattern): 460 def WhereObjectPathMatches(self, pattern):
441 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 461 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
442 return self.Filter(lambda s: regex.search(s.object_path)) 462 return self.Filter(lambda s: regex.search(s.object_path))
443 463
444 def WhereSourcePathMatches(self, pattern): 464 def WhereSourcePathMatches(self, pattern):
445 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 465 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
446 return self.Filter(lambda s: regex.search(s.source_path)) 466 return self.Filter(lambda s: regex.search(s.source_path))
447 467
448 def WherePathMatches(self, pattern): 468 def WherePathMatches(self, pattern):
449 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 469 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
(...skipping 12 matching lines...) Expand all
462 """Searches for addesses within [start, end). 482 """Searches for addesses within [start, end).
463 483
464 Args may be ints or hex strings. Default value for |end| is |start| + 1. 484 Args may be ints or hex strings. Default value for |end| is |start| + 1.
465 """ 485 """
466 if isinstance(start, basestring): 486 if isinstance(start, basestring):
467 start = int(start, 16) 487 start = int(start, 16)
468 if end is None: 488 if end is None:
469 end = start + 1 489 end = start + 1
470 return self.Filter(lambda s: s.address >= start and s.address < end) 490 return self.Filter(lambda s: s.address >= start and s.address < end)
471 491
492 def WhereHasPath(self):
493 return self.Filter(lambda s: s.source_path or s.object_path)
494
472 def WhereHasAnyAttribution(self): 495 def WhereHasAnyAttribution(self):
473 return self.Filter(lambda s: s.name or s.source_path or s.object_path) 496 return self.Filter(lambda s: s.name or s.source_path or s.object_path)
474 497
475 def Inverted(self): 498 def Inverted(self):
476 """Returns the symbols that were filtered out by the previous filter. 499 """Returns the symbols that were filtered out by the previous filter.
477 500
478 Applies only when the previous call was a filter. 501 Applies only when the previous call was a filter.
479 502
480 Example: 503 Example:
481 # Symbols that do not have "third_party" in their path. 504 # Symbols that do not have "third_party" in their path.
(...skipping 30 matching lines...) Expand all
512 min_count = abs(min_count) 535 min_count = abs(min_count)
513 for token, symbols in symbols_by_token.iteritems(): 536 for token, symbols in symbols_by_token.iteritems():
514 if len(symbols) >= min_count: 537 if len(symbols) >= min_count:
515 after_syms.append(self._CreateTransformed( 538 after_syms.append(self._CreateTransformed(
516 symbols, name=token, section_name=self.section_name, 539 symbols, name=token, section_name=self.section_name,
517 is_sorted=False)) 540 is_sorted=False))
518 elif include_singles: 541 elif include_singles:
519 after_syms.extend(symbols) 542 after_syms.extend(symbols)
520 else: 543 else:
521 filtered_symbols.extend(symbols) 544 filtered_symbols.extend(symbols)
522 return self._CreateTransformed( 545 grouped = self._CreateTransformed(
523 after_syms, filtered_symbols=filtered_symbols, 546 after_syms, filtered_symbols=filtered_symbols,
524 section_name=self.section_name, is_sorted=False) 547 section_name=self.section_name, is_sorted=False)
548 # Grouping is rarely an intermediate step, so assume sorting is useful.
549 return grouped.Sorted()
525 550
526 def GroupBySectionName(self): 551 def GroupBySectionName(self):
527 return self.GroupBy(lambda s: s.section_name) 552 return self.GroupBy(lambda s: s.section_name)
528 553
529 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0): 554 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0):
530 """Groups by symbol namespace (as denoted by ::s). 555 """Groups by symbol namespace (as denoted by ::s).
531 556
532 Does not differentiate between C++ namespaces and C++ classes. 557 Does not differentiate between C++ namespaces and C++ classes.
533 558
534 Args: 559 Args:
(...skipping 14 matching lines...) Expand all
549 574
550 # Remove after the final :: (not part of the namespace). 575 # Remove after the final :: (not part of the namespace).
551 colon_idx = name.rfind('::') 576 colon_idx = name.rfind('::')
552 if colon_idx == -1: 577 if colon_idx == -1:
553 return fallback 578 return fallback
554 name = name[:colon_idx] 579 name = name[:colon_idx]
555 580
556 return _ExtractPrefixBeforeSeparator(name, '::', depth) 581 return _ExtractPrefixBeforeSeparator(name, '::', depth)
557 return self.GroupBy(extract_namespace, min_count=min_count) 582 return self.GroupBy(extract_namespace, min_count=min_count)
558 583
559 def GroupBySourcePath(self, depth=0, fallback='{no path}', 584 def GroupByPath(self, depth=0, fallback='{no path}',
560 fallback_to_object_path=True, min_count=0): 585 fallback_to_object_path=True, min_count=0):
561 """Groups by source_path. 586 """Groups by source_path.
562 587
563 Args: 588 Args:
564 depth: When 0 (default), groups by entire path. When 1, groups by 589 depth: When 0 (default), groups by entire path. When 1, groups by
565 top-level directory, when 2, groups by top 2 directories, etc. 590 top-level directory, when 2, groups by top 2 directories, etc.
566 fallback: Use this value when no namespace exists. 591 fallback: Use this value when no namespace exists.
567 fallback_to_object_path: When True (default), uses object_path when 592 fallback_to_object_path: When True (default), uses object_path when
568 source_path is missing. 593 source_path is missing.
569 min_count: Miniumum number of symbols for a group. If fewer than this many 594 min_count: Miniumum number of symbols for a group. If fewer than this many
570 symbols end up in a group, they will not be put within a group. 595 symbols end up in a group, they will not be put within a group.
571 Use a negative value to omit symbols entirely rather than 596 Use a negative value to omit symbols entirely rather than
572 include them outside of a group. 597 include them outside of a group.
573 """ 598 """
574 def extract_path(symbol): 599 def extract_path(symbol):
575 path = symbol.source_path 600 path = symbol.source_path
576 if fallback_to_object_path and not path: 601 if fallback_to_object_path and not path:
577 path = symbol.object_path 602 path = symbol.object_path
578 path = path or fallback 603 path = path or fallback
579 return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth) 604 return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth)
580 return self.GroupBy(extract_path, min_count=min_count) 605 return self.GroupBy(extract_path, min_count=min_count)
581 606
582 def GroupByObjectPath(self, depth=0, fallback='{no path}', min_count=0):
583 """Groups by object_path.
584
585 Args:
586 depth: When 0 (default), groups by entire path. When 1, groups by
587 top-level directory, when 2, groups by top 2 directories, etc.
588 fallback: Use this value when no namespace exists.
589 min_count: Miniumum number of symbols for a group. If fewer than this many
590 symbols end up in a group, they will not be put within a group.
591 Use a negative value to omit symbols entirely rather than
592 include them outside of a group.
593 """
594 def extract_path(symbol):
595 path = symbol.object_path or fallback
596 return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth)
597 return self.GroupBy(extract_path, min_count=min_count)
598
599 607
600 class SymbolDiff(SymbolGroup): 608 class SymbolDiff(SymbolGroup):
601 """A SymbolGroup subclass representing a diff of two other SymbolGroups. 609 """A SymbolGroup subclass representing a diff of two other SymbolGroups.
602 610
603 All Symbols contained within have a |size| which is actually the size delta. 611 All Symbols contained within have a |size| which is actually the size delta.
604 Additionally, metadata is kept about which symbols were added / removed / 612 Additionally, metadata is kept about which symbols were added / removed /
605 changed. 613 changed.
606 """ 614 """
607 __slots__ = ( 615 __slots__ = (
608 '_added_ids', 616 '_added_ids',
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 705
698 def _ExtractPrefixBeforeSeparator(string, separator, count=1): 706 def _ExtractPrefixBeforeSeparator(string, separator, count=1):
699 idx = -len(separator) 707 idx = -len(separator)
700 prev_idx = None 708 prev_idx = None
701 for _ in xrange(count): 709 for _ in xrange(count):
702 idx = string.find(separator, idx + len(separator)) 710 idx = string.find(separator, idx + len(separator))
703 if idx < 0: 711 if idx < 0:
704 break 712 break
705 prev_idx = idx 713 prev_idx = idx
706 return string[:prev_idx] 714 return string[:prev_idx]
OLDNEW
« no previous file with comments | « tools/binary_size/libsupersize/integration_test.py ('k') | tools/binary_size/libsupersize/precanned_queries.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698