Chromium Code Reviews| 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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
|
estevenson
2017/05/05 19:14:58
probably obvious but what is the "if self" for?
agrieve
2017/05/06 00:37:29
it checks for the case of a SymbolGroup with no ch
| |
| 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 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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): | |
|
estevenson
2017/05/05 19:14:58
Does it make sense to just add this to WhereNameMa
agrieve
2017/05/06 00:37:29
This is certainly a place where I'm not sure what
| |
| 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 Loading... | |
| 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. |
| 482 symbols.WherePathMatches(r'third_party').Inverted() | 505 symbols.WherePathMatches(r'third_party').Inverted() |
| 483 # Symbols within third_party that do not contain the string "foo". | 506 # Symbols within third_party that do not contain the string "foo". |
| 484 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted() | 507 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted() |
| 485 """ | 508 """ |
| 486 return self._CreateTransformed( | 509 return self._CreateTransformed( |
| 487 self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False) | 510 self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False) |
| 488 | 511 |
| 489 def GroupBy(self, func, min_count=0): | 512 def GroupBy(self, func, min_count=0): |
| 490 """Returns a SymbolGroup of SymbolGroups, indexed by |func|. | 513 """Returns a SymbolGroup of SymbolGroups, indexed by |func|. |
| 491 | 514 |
| 515 Symbols within each subgroup maintain their relative ordering. | |
| 516 | |
| 492 Args: | 517 Args: |
| 493 func: Grouping function. Passed a symbol and returns a string for the | 518 func: Grouping function. Passed a symbol and returns a string for the |
| 494 name of the subgroup to put the symbol in. If None is returned, the | 519 name of the subgroup to put the symbol in. If None is returned, the |
| 495 symbol is omitted. | 520 symbol is omitted. |
| 496 min_count: Miniumum number of symbols for a group. If fewer than this many | 521 min_count: Miniumum number of symbols for a group. If fewer than this many |
| 497 symbols end up in a group, they will not be put within a group. | 522 symbols end up in a group, they will not be put within a group. |
| 498 Use a negative value to omit symbols entirely rather than | 523 Use a negative value to omit symbols entirely rather than |
| 499 include them outside of a group. | 524 include them outside of a group. |
| 500 """ | 525 """ |
| 501 after_syms = [] | 526 after_syms = [] |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 697 | 722 |
| 698 def _ExtractPrefixBeforeSeparator(string, separator, count=1): | 723 def _ExtractPrefixBeforeSeparator(string, separator, count=1): |
| 699 idx = -len(separator) | 724 idx = -len(separator) |
| 700 prev_idx = None | 725 prev_idx = None |
| 701 for _ in xrange(count): | 726 for _ in xrange(count): |
| 702 idx = string.find(separator, idx + len(separator)) | 727 idx = string.find(separator, idx + len(separator)) |
| 703 if idx < 0: | 728 if idx < 0: |
| 704 break | 729 break |
| 705 prev_idx = idx | 730 prev_idx = idx |
| 706 return string[:prev_idx] | 731 return string[:prev_idx] |
| OLD | NEW |