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

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

Issue 2870743003: supersize: Add symbol.template_name, and strip <>s from symbol.name (Closed)
Patch Set: canned query 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).
11 * size: The number of bytes this symbol takes up, including padding that comes 11 * size: The number of bytes this symbol takes up, including padding that comes
12 before |address|. 12 before |address|.
13 * num_aliases: The number of symbols with the same address (including self). 13 * num_aliases: The number of symbols with the same address (including self).
14 * pss: size / num_aliases. 14 * pss: size / num_aliases.
15 * padding: The number of bytes of padding before |address| due to this symbol. 15 * padding: The number of bytes of padding before |address| due to this symbol.
16 * name: Symbol names with parameter list removed. 16 * name: Names with templates and parameter list removed.
17 Never None, but will be '' for anonymous symbols. 17 Never None, but will be '' for anonymous symbols.
18 * full_name: Symbols names with parameter list left in. 18 * template_name: Name with parameter list removed (but templates left in).
19 Never None, but will be '' for anonymous symbols, and for symbols that do 19 Never None, but will be '' for anonymous symbols.
20 not contain a parameter list. 20 * full_name: Name with template and parameter list left in.
21 Never None, but will be '' for anonymous symbols.
21 * is_anonymous: True when the symbol exists in an anonymous namespace (which 22 * is_anonymous: True when the symbol exists in an anonymous namespace (which
22 are removed from both full_name and name during normalization). 23 are removed from both full_name and name during normalization).
23 * section_name: E.g. ".text", ".rodata", ".data.rel.local" 24 * section_name: E.g. ".text", ".rodata", ".data.rel.local"
24 * section: The second character of |section_name|. E.g. "t", "r", "d". 25 * section: The second character of |section_name|. E.g. "t", "r", "d".
25 """ 26 """
26 27
27 import collections 28 import collections
28 import logging 29 import logging
29 import os 30 import os
30 import re 31 import re
31 32
32 import cluster_symbols 33 import cluster_symbols
(...skipping 28 matching lines...) Expand all
61 class SizeInfo(object): 62 class SizeInfo(object):
62 """Represents all size information for a single binary. 63 """Represents all size information for a single binary.
63 64
64 Fields: 65 Fields:
65 section_sizes: A dict of section_name -> size. 66 section_sizes: A dict of section_name -> size.
66 symbols: A SymbolGroup containing all symbols, sorted by address. 67 symbols: A SymbolGroup containing all symbols, sorted by address.
67 metadata: A dict. 68 metadata: A dict.
68 """ 69 """
69 __slots__ = ( 70 __slots__ = (
70 'section_sizes', 71 'section_sizes',
72 'raw_symbols',
estevenson 2017/05/09 22:15:12 nit: could update the docs above.
agrieve 2017/05/10 00:51:38 Done.
71 'symbols', 73 'symbols',
72 'metadata', 74 'metadata',
73 ) 75 )
74 76
75 """Root size information.""" 77 """Root size information."""
76 def __init__(self, section_sizes, symbols, metadata=None): 78 def __init__(self, section_sizes, raw_symbols, metadata=None, symbols=None):
77 self.section_sizes = section_sizes # E.g. {'.text': 0} 79 self.section_sizes = section_sizes # E.g. {'.text': 0}
78 self.symbols = symbols 80 self.raw_symbols = raw_symbols
81 self.symbols = symbols or SymbolGroup(raw_symbols)
79 self.metadata = metadata or {} 82 self.metadata = metadata or {}
80 83
81 def Cluster(self): 84 def Clustered(self):
82 """Returns a new SizeInfo with some symbols moved into subgroups. 85 """Returns a new SizeInfo with some symbols moved into subgroups.
83 86
84 See SymbolGroup.Cluster() for more details. 87 See SymbolGroup.Clustered() for more details.
85 """ 88 """
86 return SizeInfo(self.section_sizes, self.symbols.Cluster(), self.metadata) 89 return SizeInfo(self.section_sizes, self.raw_symbols, self.metadata,
90 symbols=self.symbols.Clustered())
87 91
88 92
89 class SizeInfoDiff(object): 93 class SizeInfoDiff(object):
90 """What you get when you Diff() two SizeInfo objects. 94 """What you get when you Diff() two SizeInfo objects.
91 95
92 Fields: 96 Fields:
93 section_sizes: A dict of section_name -> size delta. 97 section_sizes: A dict of section_name -> size delta.
94 symbols: A SymbolDiff with all symbols in it. 98 symbols: A SymbolDiff with all symbols in it.
95 before_metadata: metadata of the "before" SizeInfo. 99 before_metadata: metadata of the "before" SizeInfo.
96 after_metadata: metadata of the "after" SizeInfo. 100 after_metadata: metadata of the "after" SizeInfo.
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
187 191
188 class Symbol(BaseSymbol): 192 class Symbol(BaseSymbol):
189 """Represents a single symbol within a binary. 193 """Represents a single symbol within a binary.
190 194
191 Refer to module docs for field descriptions. 195 Refer to module docs for field descriptions.
192 """ 196 """
193 197
194 __slots__ = ( 198 __slots__ = (
195 'address', 199 'address',
196 'full_name', 200 'full_name',
201 'template_name',
202 'name',
197 'flags', 203 'flags',
198 'object_path', 204 'object_path',
199 'name',
200 'aliases', 205 'aliases',
201 'padding', 206 'padding',
202 'section_name', 207 'section_name',
203 'source_path', 208 'source_path',
204 'size', 209 'size',
205 ) 210 )
206 211
207 def __init__(self, section_name, size_without_padding, address=None, 212 def __init__(self, section_name, size_without_padding, address=None,
208 name=None, source_path=None, object_path=None, full_name=None, 213 full_name=None, template_name=None, name=None, source_path=None,
209 flags=0, aliases=None): 214 object_path=None, flags=0, aliases=None):
210 self.section_name = section_name 215 self.section_name = section_name
211 self.address = address or 0 216 self.address = address or 0
217 self.full_name = full_name or ''
218 self.template_name = template_name or ''
212 self.name = name or '' 219 self.name = name or ''
213 self.full_name = full_name or ''
214 self.source_path = source_path or '' 220 self.source_path = source_path or ''
215 self.object_path = object_path or '' 221 self.object_path = object_path or ''
216 self.size = size_without_padding 222 self.size = size_without_padding
217 self.flags = flags 223 self.flags = flags
218 self.aliases = aliases 224 self.aliases = aliases
219 self.padding = 0 225 self.padding = 0
220 226
221 def __repr__(self): 227 def __repr__(self):
222 template = ('{}@{:x}(size_without_padding={},padding={},name={},' 228 template = ('{}@{:x}(size_without_padding={},padding={},name={},'
223 'object_path={},source_path={},flags={})') 229 'object_path={},source_path={},flags={})')
(...skipping 26 matching lines...) Expand all
250 * unioned = group1 + group2 256 * unioned = group1 + group2
251 """ 257 """
252 258
253 __slots__ = ( 259 __slots__ = (
254 '_padding', 260 '_padding',
255 '_size', 261 '_size',
256 '_pss', 262 '_pss',
257 '_symbols', 263 '_symbols',
258 '_filtered_symbols', 264 '_filtered_symbols',
259 'full_name', 265 'full_name',
266 'template_name',
260 'name', 267 'name',
261 'section_name', 268 'section_name',
262 'is_sorted', 269 'is_sorted',
263 ) 270 )
264 271
265 def __init__(self, symbols, filtered_symbols=None, name=None, 272 # template_name and full_name are useful when clustering symbol clones.
266 full_name=None, section_name=None, is_sorted=False): 273 def __init__(self, symbols, filtered_symbols=None, full_name=None,
274 template_name=None, name='', section_name=None, is_sorted=False):
267 self._padding = None 275 self._padding = None
268 self._size = None 276 self._size = None
269 self._pss = None 277 self._pss = None
270 self._symbols = symbols 278 self._symbols = symbols
271 self._filtered_symbols = filtered_symbols or [] 279 self._filtered_symbols = filtered_symbols or []
280 self.full_name = full_name if full_name is not None else name
281 self.template_name = template_name if template_name is not None else name
272 self.name = name or '' 282 self.name = name or ''
273 self.full_name = full_name
274 self.section_name = section_name or '.*' 283 self.section_name = section_name or '.*'
275 self.is_sorted = is_sorted 284 self.is_sorted = is_sorted
276 285
277 def __repr__(self): 286 def __repr__(self):
278 return 'Group(name=%s,count=%d,size=%d)' % ( 287 return 'Group(name=%s,count=%d,size=%d)' % (
279 self.name, len(self), self.size) 288 self.name, len(self), self.size)
280 289
281 def __iter__(self): 290 def __iter__(self):
282 return iter(self._symbols) 291 return iter(self._symbols)
283 292
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 for s in self: 388 for s in self:
380 if s.IsGroup(): 389 if s.IsGroup():
381 for x in s.IterLeafSymbols(): 390 for x in s.IterLeafSymbols():
382 yield x 391 yield x
383 else: 392 else:
384 yield s 393 yield s
385 394
386 def CountUniqueSymbols(self): 395 def CountUniqueSymbols(self):
387 return sum(1 for s in self.IterUniqueSymbols()) 396 return sum(1 for s in self.IterUniqueSymbols())
388 397
389 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, 398 def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None,
390 full_name=None, section_name=None, is_sorted=None): 399 template_name=None, name=None, section_name=None,
400 is_sorted=None):
391 if is_sorted is None: 401 if is_sorted is None:
392 is_sorted = self.is_sorted 402 is_sorted = self.is_sorted
393 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, 403 return SymbolGroup(symbols, filtered_symbols=filtered_symbols,
394 full_name=full_name, section_name=section_name, 404 full_name=full_name, template_name=template_name,
405 name=name, section_name=section_name,
395 is_sorted=is_sorted) 406 is_sorted=is_sorted)
396 407
397 def Cluster(self): 408 def Clustered(self):
398 """Returns a new SymbolGroup with some symbols moved into subgroups. 409 """Returns a new SymbolGroup with some symbols moved into subgroups.
399 410
400 Subgroups include: 411 Subgroups include:
401 * Symbols that have [clone] in their name (created during inlining). 412 * Symbols that have [clone] in their name (created during inlining).
402 * Star symbols (such as "** merge strings", and "** symbol gap") 413 * Star symbols (such as "** merge strings", and "** symbol gap")
403 414
404 To view created groups: 415 To view created groups:
405 Print(clustered.Filter(lambda s: s.IsGroup()), recursive=True) 416 Print(clustered.Filter(lambda s: s.IsGroup()), recursive=True)
406 """ 417 """
407 return self._CreateTransformed(cluster_symbols.ClusterSymbols(self)) 418 return self._CreateTransformed(cluster_symbols.ClusterSymbols(self))
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
446 457
447 def WhereInSection(self, section): 458 def WhereInSection(self, section):
448 if len(section) == 1: 459 if len(section) == 1:
449 ret = self.Filter(lambda s: s.section == section) 460 ret = self.Filter(lambda s: s.section == section)
450 ret.section_name = SECTION_TO_SECTION_NAME[section] 461 ret.section_name = SECTION_TO_SECTION_NAME[section]
451 else: 462 else:
452 ret = self.Filter(lambda s: s.section_name == section) 463 ret = self.Filter(lambda s: s.section_name == section)
453 ret.section_name = section 464 ret.section_name = section
454 return ret 465 return ret
455 466
467 def WhereIsTemplate(self):
468 return self.Filter(lambda s: s.template_name is not s.name)
469
456 def WhereSourceIsGenerated(self): 470 def WhereSourceIsGenerated(self):
457 return self.Filter(lambda s: s.generated_source) 471 return self.Filter(lambda s: s.generated_source)
458 472
459 def WhereGeneratedByToolchain(self): 473 def WhereGeneratedByToolchain(self):
460 return self.Filter(lambda s: s.IsGeneratedByToolchain()) 474 return self.Filter(lambda s: s.IsGeneratedByToolchain())
461 475
476 def WhereFullNameMatches(self, pattern):
477 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
478 return self.Filter(lambda s: regex.search(s.full_name))
479
480 def WhereTemplateNameMatches(self, pattern):
481 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
482 return self.Filter(lambda s: regex.search(s.template_name))
483
462 def WhereNameMatches(self, pattern): 484 def WhereNameMatches(self, pattern):
463 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 485 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
464 return self.Filter(lambda s: regex.search(s.name)) 486 return self.Filter(lambda s: regex.search(s.name))
465 487
466 def WhereFullNameMatches(self, pattern):
467 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
468 return self.Filter(lambda s: regex.search(s.full_name or s.name))
469
470 def WhereObjectPathMatches(self, pattern): 488 def WhereObjectPathMatches(self, pattern):
471 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 489 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
472 return self.Filter(lambda s: regex.search(s.object_path)) 490 return self.Filter(lambda s: regex.search(s.object_path))
473 491
474 def WhereSourcePathMatches(self, pattern): 492 def WhereSourcePathMatches(self, pattern):
475 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 493 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
476 return self.Filter(lambda s: regex.search(s.source_path)) 494 return self.Filter(lambda s: regex.search(s.source_path))
477 495
478 def WherePathMatches(self, pattern): 496 def WherePathMatches(self, pattern):
479 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 497 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
480 return self.Filter(lambda s: (regex.search(s.source_path) or 498 return self.Filter(lambda s: (regex.search(s.source_path) or
481 regex.search(s.object_path))) 499 regex.search(s.object_path)))
482 500
483 def WhereMatches(self, pattern): 501 def WhereMatches(self, pattern):
484 """Looks for |pattern| within all paths & names.""" 502 """Looks for |pattern| within all paths & names."""
485 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern)) 503 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
486 return self.Filter(lambda s: (regex.search(s.source_path) or 504 return self.Filter(lambda s: (
487 regex.search(s.object_path) or 505 regex.search(s.source_path) or
488 regex.search(s.full_name or '') or 506 regex.search(s.object_path) or
489 regex.search(s.name))) 507 regex.search(s.full_name) or
508 s.full_name is not s.template_name and regex.search(s.template_name) or
509 s.full_name is not s.name and regex.search(s.name)))
490 510
491 def WhereAddressInRange(self, start, end=None): 511 def WhereAddressInRange(self, start, end=None):
492 """Searches for addesses within [start, end). 512 """Searches for addesses within [start, end).
493 513
494 Args may be ints or hex strings. Default value for |end| is |start| + 1. 514 Args may be ints or hex strings. Default value for |end| is |start| + 1.
495 """ 515 """
496 if isinstance(start, basestring): 516 if isinstance(start, basestring):
497 start = int(start, 16) 517 start = int(start, 16)
498 if end is None: 518 if end is None:
499 end = start + 1 519 end = start + 1
500 return self.Filter(lambda s: s.address >= start and s.address < end) 520 return self.Filter(lambda s: s.address >= start and s.address < end)
501 521
502 def WhereHasPath(self): 522 def WhereHasPath(self):
503 return self.Filter(lambda s: s.source_path or s.object_path) 523 return self.Filter(lambda s: s.source_path or s.object_path)
504 524
505 def WhereHasAnyAttribution(self): 525 def WhereHasAnyAttribution(self):
506 return self.Filter(lambda s: s.name or s.source_path or s.object_path) 526 return self.Filter(lambda s: s.full_name or s.source_path or s.object_path)
507 527
508 def Inverted(self): 528 def Inverted(self):
509 """Returns the symbols that were filtered out by the previous filter. 529 """Returns the symbols that were filtered out by the previous filter.
510 530
511 Applies only when the previous call was a filter. 531 Applies only when the previous call was a filter.
512 532
513 Example: 533 Example:
514 # Symbols that do not have "third_party" in their path. 534 # Symbols that do not have "third_party" in their path.
515 symbols.WherePathMatches(r'third_party').Inverted() 535 symbols.WherePathMatches(r'third_party').Inverted()
516 # Symbols within third_party that do not contain the string "foo". 536 # Symbols within third_party that do not contain the string "foo".
517 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted() 537 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted()
518 """ 538 """
519 return self._CreateTransformed( 539 return self._CreateTransformed(
520 self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False) 540 self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False)
521 541
522 def GroupBy(self, func, min_count=0): 542 def GroupedBy(self, func, min_count=0):
523 """Returns a SymbolGroup of SymbolGroups, indexed by |func|. 543 """Returns a SymbolGroup of SymbolGroups, indexed by |func|.
524 544
525 Symbols within each subgroup maintain their relative ordering. 545 Symbols within each subgroup maintain their relative ordering.
526 546
527 Args: 547 Args:
528 func: Grouping function. Passed a symbol and returns a string for the 548 func: Grouping function. Passed a symbol and returns a string for the
529 name of the subgroup to put the symbol in. If None is returned, the 549 name of the subgroup to put the symbol in. If None is returned, the
530 symbol is omitted. 550 symbol is omitted.
531 min_count: Miniumum number of symbols for a group. If fewer than this many 551 min_count: Miniumum number of symbols for a group. If fewer than this many
532 symbols end up in a group, they will not be put within a group. 552 symbols end up in a group, they will not be put within a group.
(...skipping 19 matching lines...) Expand all
552 is_sorted=False)) 572 is_sorted=False))
553 elif include_singles: 573 elif include_singles:
554 after_syms.extend(symbols) 574 after_syms.extend(symbols)
555 else: 575 else:
556 filtered_symbols.extend(symbols) 576 filtered_symbols.extend(symbols)
557 grouped = self._CreateTransformed( 577 grouped = self._CreateTransformed(
558 after_syms, filtered_symbols=filtered_symbols, 578 after_syms, filtered_symbols=filtered_symbols,
559 section_name=self.section_name, is_sorted=False) 579 section_name=self.section_name, is_sorted=False)
560 return grouped 580 return grouped
561 581
562 def GroupBySectionName(self): 582 def GroupedBySectionName(self):
563 return self.GroupBy(lambda s: s.section_name) 583 return self.GroupedBy(lambda s: s.section_name)
564 584
565 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0): 585 def GroupedByName(self, depth=0, min_count=0):
566 """Groups by symbol namespace (as denoted by ::s). 586 """Groups by symbol name, where |depth| controls how many ::s to include.
567 587
568 Does not differentiate between C++ namespaces and C++ classes. 588 Does not differentiate between namespaces/classes/functions.
569 589
570 Args: 590 Args:
571 depth: When 0 (default), groups by entire namespace. When 1, groups by 591 depth: 0 (default): Groups by entire name. Useful for grouping templates.
572 top-level name, when 2, groups by top 2 names, etc. 592 >0: Groups by this many name parts.
573 fallback: Use this value when no namespace exists. 593 Example: 1 -> std::, 2 -> std::map
594 <0: Groups by entire name minus this many name parts
595 Example: -1 -> std::map, -2 -> std::
574 min_count: Miniumum number of symbols for a group. If fewer than this many 596 min_count: Miniumum number of symbols for a group. If fewer than this many
575 symbols end up in a group, they will not be put within a group. 597 symbols end up in a group, they will not be put within a group.
576 Use a negative value to omit symbols entirely rather than 598 Use a negative value to omit symbols entirely rather than
577 include them outside of a group. 599 include them outside of a group.
578 """ 600 """
579 def extract_namespace(symbol): 601 if depth >= 0:
580 # Remove template params. 602 extract_namespace = (
581 name = symbol.name 603 lambda s: _ExtractPrefixBeforeSeparator(s.name, '::', depth))
582 template_idx = name.find('<') 604 else:
583 if template_idx: 605 depth = -depth
584 name = name[:template_idx] 606 extract_namespace = (
607 lambda s: _ExtractSuffixAfterSeparator(s.name, '::', depth))
608 return self.GroupedBy(extract_namespace, min_count=min_count)
585 609
586 # Remove after the final :: (not part of the namespace). 610 def GroupedByPath(self, depth=0, fallback='{no path}',
587 colon_idx = name.rfind('::')
588 if colon_idx == -1:
589 return fallback
590 name = name[:colon_idx]
591
592 return _ExtractPrefixBeforeSeparator(name, '::', depth)
593 return self.GroupBy(extract_namespace, min_count=min_count)
594
595 def GroupByPath(self, depth=0, fallback='{no path}',
596 fallback_to_object_path=True, min_count=0): 611 fallback_to_object_path=True, min_count=0):
597 """Groups by source_path. 612 """Groups by source_path.
598 613
599 Args: 614 Args:
600 depth: When 0 (default), groups by entire path. When 1, groups by 615 depth: When 0 (default), groups by entire path. When 1, groups by
601 top-level directory, when 2, groups by top 2 directories, etc. 616 top-level directory, when 2, groups by top 2 directories, etc.
602 fallback: Use this value when no namespace exists. 617 fallback: Use this value when no namespace exists.
603 fallback_to_object_path: When True (default), uses object_path when 618 fallback_to_object_path: When True (default), uses object_path when
604 source_path is missing. 619 source_path is missing.
605 min_count: Miniumum number of symbols for a group. If fewer than this many 620 min_count: Miniumum number of symbols for a group. If fewer than this many
606 symbols end up in a group, they will not be put within a group. 621 symbols end up in a group, they will not be put within a group.
607 Use a negative value to omit symbols entirely rather than 622 Use a negative value to omit symbols entirely rather than
608 include them outside of a group. 623 include them outside of a group.
609 """ 624 """
610 def extract_path(symbol): 625 def extract_path(symbol):
611 path = symbol.source_path 626 path = symbol.source_path
612 if fallback_to_object_path and not path: 627 if fallback_to_object_path and not path:
613 path = symbol.object_path 628 path = symbol.object_path
614 path = path or fallback 629 path = path or fallback
615 return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth) 630 return _ExtractPrefixBeforeSeparator(path, os.path.sep, depth)
616 return self.GroupBy(extract_path, min_count=min_count) 631 return self.GroupedBy(extract_path, min_count=min_count)
617 632
618 633
619 class SymbolDiff(SymbolGroup): 634 class SymbolDiff(SymbolGroup):
620 """A SymbolGroup subclass representing a diff of two other SymbolGroups. 635 """A SymbolGroup subclass representing a diff of two other SymbolGroups.
621 636
622 All Symbols contained within have a |size| which is actually the size delta. 637 All Symbols contained within have a |size| which is actually the size delta.
623 Additionally, metadata is kept about which symbols were added / removed / 638 Additionally, metadata is kept about which symbols were added / removed /
624 changed. 639 changed.
625 """ 640 """
626 __slots__ = ( 641 __slots__ = (
627 '_added_ids', 642 '_added_ids',
628 '_removed_ids', 643 '_removed_ids',
629 ) 644 )
630 645
631 def __init__(self, added, removed, similar, name=None, full_name=None, 646 def __init__(self, added, removed, similar):
632 section_name=None):
633 self._added_ids = set(id(s) for s in added) 647 self._added_ids = set(id(s) for s in added)
634 self._removed_ids = set(id(s) for s in removed) 648 self._removed_ids = set(id(s) for s in removed)
635 symbols = [] 649 symbols = []
636 symbols.extend(added) 650 symbols.extend(added)
637 symbols.extend(removed) 651 symbols.extend(removed)
638 symbols.extend(similar) 652 symbols.extend(similar)
639 super(SymbolDiff, self).__init__(symbols, name=name, full_name=full_name, 653 super(SymbolDiff, self).__init__(symbols)
640 section_name=section_name)
641 654
642 def __repr__(self): 655 def __repr__(self):
643 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( 656 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % (
644 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, 657 'SymbolGroup', self.added_count, self.removed_count, self.changed_count,
645 self.unchanged_count, self.size) 658 self.unchanged_count, self.size)
646 659
647 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, 660 def _CreateTransformed(self, symbols, filtered_symbols=None, full_name=None,
648 full_name=None, section_name=None, is_sorted=None): 661 template_name=None, name=None, section_name=None,
662 is_sorted=None):
649 # Printing sorts, so short-circuit the same symbols case. 663 # Printing sorts, so short-circuit the same symbols case.
650 if len(symbols) == len(self._symbols): 664 if len(symbols) == len(self._symbols):
651 new_added_ids = self._added_ids 665 new_added_ids = self._added_ids
652 new_removed_ids = self._removed_ids 666 new_removed_ids = self._removed_ids
653 else: 667 else:
654 old_added_ids = self._added_ids 668 old_added_ids = self._added_ids
655 old_removed_ids = self._removed_ids 669 old_removed_ids = self._removed_ids
656 670
657 def get_status(sym): 671 def get_status(sym):
658 obj_id = id(sym) 672 obj_id = id(sym)
(...skipping 13 matching lines...) Expand all
672 status = get_status(sym) 686 status = get_status(sym)
673 if status == 0: 687 if status == 0:
674 new_added_ids.add(id(sym)) 688 new_added_ids.add(id(sym))
675 elif status == 1: 689 elif status == 1:
676 new_removed_ids.add(id(sym)) 690 new_removed_ids.add(id(sym))
677 691
678 ret = SymbolDiff.__new__(SymbolDiff) 692 ret = SymbolDiff.__new__(SymbolDiff)
679 ret._added_ids = new_added_ids 693 ret._added_ids = new_added_ids
680 ret._removed_ids = new_removed_ids 694 ret._removed_ids = new_removed_ids
681 super(SymbolDiff, ret).__init__( 695 super(SymbolDiff, ret).__init__(
682 symbols, filtered_symbols=filtered_symbols, name=name, 696 symbols, filtered_symbols=filtered_symbols, full_name=full_name,
683 full_name=full_name, section_name=section_name, is_sorted=is_sorted) 697 template_name=template_name, name=name, section_name=section_name,
698 is_sorted=is_sorted)
684 return ret 699 return ret
685 700
686 @property 701 @property
687 def added_count(self): 702 def added_count(self):
688 return len(self._added_ids) 703 return len(self._added_ids)
689 704
690 @property 705 @property
691 def removed_count(self): 706 def removed_count(self):
692 return len(self._removed_ids) 707 return len(self._removed_ids)
693 708
(...skipping 13 matching lines...) Expand all
707 key = id(sym) 722 key = id(sym)
708 return key not in self._added_ids and key not in self._removed_ids 723 return key not in self._added_ids and key not in self._removed_ids
709 724
710 def IsRemoved(self, sym): 725 def IsRemoved(self, sym):
711 return id(sym) in self._removed_ids 726 return id(sym) in self._removed_ids
712 727
713 def WhereNotUnchanged(self): 728 def WhereNotUnchanged(self):
714 return self.Filter(lambda s: not self.IsSimilar(s) or s.size) 729 return self.Filter(lambda s: not self.IsSimilar(s) or s.size)
715 730
716 731
717 def _ExtractPrefixBeforeSeparator(string, separator, count=1): 732 def _ExtractPrefixBeforeSeparator(string, separator, count):
718 idx = -len(separator) 733 idx = -len(separator)
719 prev_idx = None 734 prev_idx = None
720 for _ in xrange(count): 735 for _ in xrange(count):
721 idx = string.find(separator, idx + len(separator)) 736 idx = string.find(separator, idx + len(separator))
722 if idx < 0: 737 if idx < 0:
723 break 738 break
724 prev_idx = idx 739 prev_idx = idx
725 return string[:prev_idx] 740 return string[:prev_idx]
741
742
743 def _ExtractSuffixAfterSeparator(string, separator, count):
744 prev_idx = len(string) + 1
745 for _ in xrange(count):
746 idx = string.rfind(separator, 0, prev_idx - 1)
747 if idx < 0:
748 break
749 prev_idx = idx
750 return string[:prev_idx]
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698