| 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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 METADATA_GN_ARGS = 'gn_args' | 43 METADATA_GN_ARGS = 'gn_args' |
| 44 METADATA_TOOL_PREFIX = 'tool_prefix' # Path relative to SRC_ROOT. | 44 METADATA_TOOL_PREFIX = 'tool_prefix' # Path relative to SRC_ROOT. |
| 45 | 45 |
| 46 | 46 |
| 47 SECTION_TO_SECTION_NAME = { | 47 SECTION_TO_SECTION_NAME = { |
| 48 'b': '.bss', | 48 'b': '.bss', |
| 49 'd': '.data', | 49 'd': '.data', |
| 50 'r': '.rodata', | 50 'r': '.rodata', |
| 51 't': '.text', | 51 't': '.text', |
| 52 } | 52 } |
| 53 # Used by SymbolGroup when they contain a mix of sections. |
| 54 SECTION_NAME_MULTIPLE = '.*' |
| 53 | 55 |
| 54 FLAG_ANONYMOUS = 1 | 56 FLAG_ANONYMOUS = 1 |
| 55 FLAG_STARTUP = 2 | 57 FLAG_STARTUP = 2 |
| 56 FLAG_UNLIKELY = 4 | 58 FLAG_UNLIKELY = 4 |
| 57 FLAG_REL = 8 | 59 FLAG_REL = 8 |
| 58 FLAG_REL_LOCAL = 16 | 60 FLAG_REL_LOCAL = 16 |
| 59 FLAG_GENERATED_SOURCE = 32 | 61 FLAG_GENERATED_SOURCE = 32 |
| 60 FLAG_CLONE = 64 | 62 FLAG_CLONE = 64 |
| 61 | 63 |
| 62 DIFF_STATUS_UNCHANGED = 0 | 64 DIFF_STATUS_UNCHANGED = 0 |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 304 def __init__(self, symbols, filtered_symbols=None, full_name=None, | 306 def __init__(self, symbols, filtered_symbols=None, full_name=None, |
| 305 template_name=None, name='', section_name=None, is_sorted=False): | 307 template_name=None, name='', section_name=None, is_sorted=False): |
| 306 self._padding = None | 308 self._padding = None |
| 307 self._size = None | 309 self._size = None |
| 308 self._pss = None | 310 self._pss = None |
| 309 self._symbols = symbols | 311 self._symbols = symbols |
| 310 self._filtered_symbols = filtered_symbols or [] | 312 self._filtered_symbols = filtered_symbols or [] |
| 311 self.full_name = full_name if full_name is not None else name | 313 self.full_name = full_name if full_name is not None else name |
| 312 self.template_name = template_name if template_name is not None else name | 314 self.template_name = template_name if template_name is not None else name |
| 313 self.name = name or '' | 315 self.name = name or '' |
| 314 self.section_name = section_name or '.*' | 316 self.section_name = section_name or SECTION_NAME_MULTIPLE |
| 315 self.is_sorted = is_sorted | 317 self.is_sorted = is_sorted |
| 316 | 318 |
| 317 def __repr__(self): | 319 def __repr__(self): |
| 318 return 'Group(full_name=%s,count=%d,size=%d)' % ( | 320 return 'Group(full_name=%s,count=%d,size=%d)' % ( |
| 319 self.full_name, len(self), self.size) | 321 self.full_name, len(self), self.size) |
| 320 | 322 |
| 321 def __iter__(self): | 323 def __iter__(self): |
| 322 return iter(self._symbols) | 324 return iter(self._symbols) |
| 323 | 325 |
| 324 def __len__(self): | 326 def __len__(self): |
| 325 return len(self._symbols) | 327 return len(self._symbols) |
| 326 | 328 |
| 327 def __eq__(self, other): | 329 def __eq__(self, other): |
| 328 return isinstance(other, SymbolGroup) and self._symbols == other._symbols | 330 return isinstance(other, SymbolGroup) and self._symbols == other._symbols |
| 329 | 331 |
| 330 def __getitem__(self, key): | 332 def __getitem__(self, key): |
| 331 """|key| can be an index or an address. | 333 """|key| can be an index or an address. |
| 332 | 334 |
| 333 Raises if multiple symbols map to the address. | 335 Raises if multiple symbols map to the address. |
| 334 """ | 336 """ |
| 335 if isinstance(key, slice): | 337 if isinstance(key, slice): |
| 336 return self._symbols.__getitem__(key) | 338 return self._CreateTransformed(self._symbols.__getitem__(key)) |
| 337 if isinstance(key, basestring) or key > len(self._symbols): | 339 if isinstance(key, basestring) or key > len(self._symbols): |
| 338 found = self.WhereAddressInRange(key) | 340 found = self.WhereAddressInRange(key) |
| 339 if len(found) != 1: | 341 if len(found) != 1: |
| 340 raise KeyError('%d symbols found at address %s.' % (len(found), key)) | 342 raise KeyError('%d symbols found at address %s.' % (len(found), key)) |
| 341 return found[0] | 343 return found[0] |
| 342 return self._symbols[key] | 344 return self._symbols[key] |
| 343 | 345 |
| 344 def __sub__(self, other): | 346 def __sub__(self, other): |
| 345 other_ids = set(id(s) for s in other) | 347 other_ids = set(id(s) for s in other) |
| 346 after_symbols = [s for s in self if id(s) not in other_ids] | 348 after_symbols = [s for s in self if id(s) not in other_ids] |
| 347 return self._CreateTransformed(after_symbols, | 349 return self._CreateTransformed(after_symbols) |
| 348 section_name=self.section_name) | |
| 349 | 350 |
| 350 def __add__(self, other): | 351 def __add__(self, other): |
| 351 self_ids = set(id(s) for s in self) | 352 self_ids = set(id(s) for s in self) |
| 352 after_symbols = self._symbols + [s for s in other if id(s) not in self_ids] | 353 after_symbols = self._symbols + [s for s in other if id(s) not in self_ids] |
| 353 return self._CreateTransformed( | 354 return self._CreateTransformed(after_symbols, is_sorted=False) |
| 354 after_symbols, section_name=self.section_name, is_sorted=False) | |
| 355 | 355 |
| 356 @property | 356 @property |
| 357 def address(self): | 357 def address(self): |
| 358 first = self._symbols[0].address if self else 0 | 358 first = self._symbols[0].address if self else 0 |
| 359 return first if all(s.address == first for s in self._symbols) else 0 | 359 return first if all(s.address == first for s in self._symbols) else 0 |
| 360 | 360 |
| 361 @property | 361 @property |
| 362 def flags(self): | 362 def flags(self): |
| 363 first = self._symbols[0].flags if self else 0 | 363 first = self._symbols[0].flags if self else 0 |
| 364 return first if all(s.flags == first for s in self._symbols) else 0 | 364 return first if all(s.flags == first for s in self._symbols) else 0 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 429 yield s | 429 yield s |
| 430 | 430 |
| 431 def CountUniqueSymbols(self): | 431 def CountUniqueSymbols(self): |
| 432 return sum(1 for s in self.IterUniqueSymbols()) | 432 return sum(1 for s in self.IterUniqueSymbols()) |
| 433 | 433 |
| 434 def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None, | 434 def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None, |
| 435 template_name=None, name=None, section_name=None, | 435 template_name=None, name=None, section_name=None, |
| 436 is_sorted=None): | 436 is_sorted=None): |
| 437 if is_sorted is None: | 437 if is_sorted is None: |
| 438 is_sorted = self.is_sorted | 438 is_sorted = self.is_sorted |
| 439 if section_name is None: |
| 440 section_name = self.section_name |
| 439 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, | 441 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, |
| 440 full_name=full_name, template_name=template_name, | 442 full_name=full_name, template_name=template_name, |
| 441 name=name, section_name=section_name, | 443 name=name, section_name=section_name, |
| 442 is_sorted=is_sorted) | 444 is_sorted=is_sorted) |
| 443 | 445 |
| 444 def Sorted(self, cmp_func=None, key=None, reverse=False): | 446 def Sorted(self, cmp_func=None, key=None, reverse=False): |
| 445 if cmp_func is None and key is None: | 447 if cmp_func is None and key is None: |
| 446 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.pss), a.name), | 448 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.pss), a.name), |
| 447 (b.IsBss(), abs(a.pss), b.name)) | 449 (b.IsBss(), abs(a.pss), b.name)) |
| 448 | 450 |
| 449 after_symbols = sorted(self._symbols, cmp_func, key, reverse) | 451 after_symbols = sorted(self._symbols, cmp_func, key, reverse) |
| 450 return self._CreateTransformed( | 452 return self._CreateTransformed( |
| 451 after_symbols, filtered_symbols=self._filtered_symbols, | 453 after_symbols, filtered_symbols=self._filtered_symbols, |
| 452 section_name=self.section_name, is_sorted=True) | 454 is_sorted=True) |
| 453 | 455 |
| 454 def SortedByName(self, reverse=False): | 456 def SortedByName(self, reverse=False): |
| 455 return self.Sorted(key=(lambda s:s.name), reverse=reverse) | 457 return self.Sorted(key=(lambda s:s.name), reverse=reverse) |
| 456 | 458 |
| 457 def SortedByAddress(self, reverse=False): | 459 def SortedByAddress(self, reverse=False): |
| 458 return self.Sorted(key=(lambda s:(s.address, s.object_path, s.name)), | 460 return self.Sorted(key=(lambda s:(s.address, s.object_path, s.name)), |
| 459 reverse=reverse) | 461 reverse=reverse) |
| 460 | 462 |
| 461 def SortedByCount(self, reverse=False): | 463 def SortedByCount(self, reverse=False): |
| 462 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1), | 464 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1), |
| 463 reverse=not reverse) | 465 reverse=not reverse) |
| 464 | 466 |
| 465 def Filter(self, func): | 467 def Filter(self, func): |
| 466 filtered_and_kept = ([], []) | 468 filtered_and_kept = ([], []) |
| 467 symbol = None | 469 symbol = None |
| 468 try: | 470 try: |
| 469 for symbol in self: | 471 for symbol in self: |
| 470 filtered_and_kept[int(bool(func(symbol)))].append(symbol) | 472 filtered_and_kept[int(bool(func(symbol)))].append(symbol) |
| 471 except: | 473 except: |
| 472 logging.warning('Filter failed on symbol %r', symbol) | 474 logging.warning('Filter failed on symbol %r', symbol) |
| 473 raise | 475 raise |
| 474 | 476 |
| 475 return self._CreateTransformed(filtered_and_kept[1], | 477 return self._CreateTransformed(filtered_and_kept[1], |
| 476 filtered_symbols=filtered_and_kept[0], | 478 filtered_symbols=filtered_and_kept[0]) |
| 477 section_name=self.section_name) | |
| 478 | 479 |
| 479 def WhereIsGroup(self): | 480 def WhereIsGroup(self): |
| 480 return self.Filter(lambda s: s.IsGroup()) | 481 return self.Filter(lambda s: s.IsGroup()) |
| 481 | 482 |
| 482 def WhereSizeBiggerThan(self, min_size): | 483 def WhereSizeBiggerThan(self, min_size): |
| 483 return self.Filter(lambda s: s.size >= min_size) | 484 return self.Filter(lambda s: s.size >= min_size) |
| 484 | 485 |
| 485 def WherePssBiggerThan(self, min_pss): | 486 def WherePssBiggerThan(self, min_pss): |
| 486 return self.Filter(lambda s: s.pss >= min_pss) | 487 return self.Filter(lambda s: s.pss >= min_pss) |
| 487 | 488 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 | 561 |
| 561 Applies only when the previous call was a filter. | 562 Applies only when the previous call was a filter. |
| 562 | 563 |
| 563 Example: | 564 Example: |
| 564 # Symbols that do not have "third_party" in their path. | 565 # Symbols that do not have "third_party" in their path. |
| 565 symbols.WherePathMatches(r'third_party').Inverted() | 566 symbols.WherePathMatches(r'third_party').Inverted() |
| 566 # Symbols within third_party that do not contain the string "foo". | 567 # Symbols within third_party that do not contain the string "foo". |
| 567 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted() | 568 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted() |
| 568 """ | 569 """ |
| 569 return self._CreateTransformed( | 570 return self._CreateTransformed( |
| 570 self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False) | 571 self._filtered_symbols, filtered_symbols=self._symbols, |
| 572 section_name=SECTION_NAME_MULTIPLE, is_sorted=False) |
| 571 | 573 |
| 572 def GroupedBy(self, func, min_count=0, group_factory=None): | 574 def GroupedBy(self, func, min_count=0, group_factory=None): |
| 573 """Returns a SymbolGroup of SymbolGroups, indexed by |func|. | 575 """Returns a SymbolGroup of SymbolGroups, indexed by |func|. |
| 574 | 576 |
| 575 Symbols within each subgroup maintain their relative ordering. | 577 Symbols within each subgroup maintain their relative ordering. |
| 576 | 578 |
| 577 Args: | 579 Args: |
| 578 func: Grouping function. Passed a symbol and returns a string for the | 580 func: Grouping function. Passed a symbol and returns a string for the |
| 579 name of the subgroup to put the symbol in. If None is returned, the | 581 name of the subgroup to put the symbol in. If None is returned, the |
| 580 symbol is omitted. | 582 symbol is omitted. |
| 581 min_count: Miniumum number of symbols for a group. If fewer than this many | 583 min_count: Miniumum number of symbols for a group. If fewer than this many |
| 582 symbols end up in a group, they will not be put within a group. | 584 symbols end up in a group, they will not be put within a group. |
| 583 Use a negative value to omit symbols entirely rather than | 585 Use a negative value to omit symbols entirely rather than |
| 584 include them outside of a group. | 586 include them outside of a group. |
| 585 group_factory: Function to create SymbolGroup from a list of Symbols. | 587 group_factory: Function to create SymbolGroup from a list of Symbols. |
| 586 """ | 588 """ |
| 587 if group_factory is None: | 589 if group_factory is None: |
| 588 group_factory = lambda token, symbols: self._CreateTransformed( | 590 group_factory = lambda token, symbols: self._CreateTransformed( |
| 589 symbols, full_name=token, template_name=token, name=token, | 591 symbols, full_name=token, template_name=token, name=token) |
| 590 section_name=self.section_name) | |
| 591 | 592 |
| 592 after_syms = [] | 593 after_syms = [] |
| 593 filtered_symbols = [] | 594 filtered_symbols = [] |
| 594 symbols_by_token = collections.OrderedDict() | 595 symbols_by_token = collections.OrderedDict() |
| 595 # Index symbols by |func|. | 596 # Index symbols by |func|. |
| 596 for symbol in self: | 597 for symbol in self: |
| 597 token = func(symbol) | 598 token = func(symbol) |
| 598 if token is None: | 599 if token is None: |
| 599 filtered_symbols.append(symbol) | 600 filtered_symbols.append(symbol) |
| 600 else: | 601 else: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 619 symbol_or_list = [symbol_or_list] | 620 symbol_or_list = [symbol_or_list] |
| 620 after_syms.append(group_factory(token, symbol_or_list)) | 621 after_syms.append(group_factory(token, symbol_or_list)) |
| 621 else: | 622 else: |
| 622 target_list = after_syms if include_singles else filtered_symbols | 623 target_list = after_syms if include_singles else filtered_symbols |
| 623 if count == 1: | 624 if count == 1: |
| 624 target_list.append(symbol_or_list) | 625 target_list.append(symbol_or_list) |
| 625 else: | 626 else: |
| 626 target_list.extend(symbol_or_list) | 627 target_list.extend(symbol_or_list) |
| 627 | 628 |
| 628 return self._CreateTransformed( | 629 return self._CreateTransformed( |
| 629 after_syms, filtered_symbols=filtered_symbols, | 630 after_syms, filtered_symbols=filtered_symbols) |
| 630 section_name=self.section_name) | |
| 631 | 631 |
| 632 def _Clustered(self): | 632 def _Clustered(self): |
| 633 """Returns a new SymbolGroup with some symbols moved into subgroups. | 633 """Returns a new SymbolGroup with some symbols moved into subgroups. |
| 634 | 634 |
| 635 Method is private since it only ever makes sense to call it from | 635 Method is private since it only ever makes sense to call it from |
| 636 SizeInfo.symbols. | 636 SizeInfo.symbols. |
| 637 | 637 |
| 638 The main function of clustering is to put symbols that were broken into | 638 The main function of clustering is to put symbols that were broken into |
| 639 multiple parts under a group so that they once again look like a single | 639 multiple parts under a group so that they once again look like a single |
| 640 symbol. It also groups together symbols like "** merge strings". | 640 symbol. It also groups together symbols like "** merge strings". |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 862 | 862 |
| 863 | 863 |
| 864 def _ExtractSuffixAfterSeparator(string, separator, count): | 864 def _ExtractSuffixAfterSeparator(string, separator, count): |
| 865 prev_idx = len(string) + 1 | 865 prev_idx = len(string) + 1 |
| 866 for _ in xrange(count): | 866 for _ in xrange(count): |
| 867 idx = string.rfind(separator, 0, prev_idx - 1) | 867 idx = string.rfind(separator, 0, prev_idx - 1) |
| 868 if idx < 0: | 868 if idx < 0: |
| 869 break | 869 break |
| 870 prev_idx = idx | 870 prev_idx = idx |
| 871 return string[:prev_idx] | 871 return string[:prev_idx] |
| OLD | NEW |