Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 # found in the LICENSE file. | |
| 4 """Logic for diffing two SizeInfo objects.""" | |
| 5 | |
| 6 import collections | |
| 7 | |
| 8 import models | |
| 9 | |
| 10 | |
| 11 def _CloneSymbol(sym, size): | |
| 12 """Returns a copy of |sym| with an updated |size|. | |
| 13 | |
| 14 Padding and aliases are not copied. | |
| 15 """ | |
| 16 return models.Symbol( | |
| 17 sym.section_name, size, address=sym.address, name=sym.name, | |
| 18 source_path=sym.source_path, object_path=sym.object_path, | |
| 19 full_name=sym.full_name, flags=sym.flags) | |
| 20 | |
| 21 | |
| 22 def _CloneAlias(sym, diffed_alias): | |
| 23 """Returns a copy of |sym| and making it an alias of |diffed_alias|.""" | |
| 24 ret = _CloneSymbol(sym, diffed_alias.size_without_padding) | |
| 25 ret.padding = diffed_alias.padding | |
| 26 ret.aliases = diffed_alias.aliases | |
| 27 ret.aliases.append(ret) | |
| 28 return ret | |
| 29 | |
| 30 | |
| 31 def _DiffSymbol(before_sym, after_sym, diffed_symbol_by_after_aliases, | |
| 32 padding_by_section_name): | |
| 33 diffed_alias = None | |
| 34 if after_sym.aliases: | |
| 35 diffed_alias = diffed_symbol_by_after_aliases.get(id(after_sym.aliases)) | |
| 36 | |
| 37 if diffed_alias: | |
| 38 ret = _CloneAlias(after_sym, diffed_alias) | |
| 39 else: | |
| 40 size_diff = (after_sym.size_without_padding - | |
| 41 before_sym.size_without_padding) | |
| 42 ret = _CloneSymbol(after_sym, size_diff) | |
| 43 # Diffs are more stable when comparing size without padding, except when | |
| 44 # the symbol is a padding-only symbol. | |
| 45 if after_sym.size_without_padding == 0 and size_diff == 0: | |
| 46 ret.padding = after_sym.padding - before_sym.padding | |
| 47 else: | |
| 48 padding_diff = after_sym.padding - before_sym.padding | |
| 49 padding_by_section_name[ret.section_name] += padding_diff | |
| 50 | |
| 51 if after_sym.aliases: | |
| 52 ret.aliases = [ret] | |
|
estevenson
2017/04/28 17:06:11
Already forgot - what does this mean again?
agrieve
2017/04/28 19:26:59
Comment added.
| |
| 53 diffed_symbol_by_after_aliases[id(after_sym.aliases)] = ret | |
| 54 return ret | |
| 55 | |
| 56 | |
| 57 def _CloneUnmatched(after_symbols, diffed_symbol_by_after_aliases): | |
| 58 ret = [] | |
| 59 for sym in after_symbols: | |
| 60 cloned = sym | |
| 61 if sym.IsGroup(): | |
| 62 cloned = models.SymbolDiff( | |
| 63 [], _CloneUnmatched(sym, diffed_symbol_by_after_aliases), [], | |
| 64 name=sym.name, full_name=sym.full_name, section_name=sym.section_name) | |
| 65 elif sym.aliases: | |
| 66 diffed_alias = diffed_symbol_by_after_aliases.get(id(sym.aliases)) | |
| 67 if diffed_alias: | |
| 68 # At least one alias was diffed. | |
| 69 cloned = _CloneAlias(sym, diffed_alias) | |
| 70 ret.append(cloned) | |
| 71 return ret | |
| 72 | |
| 73 | |
| 74 def _NegateAndClone(before_symbols, matched_before_aliases, | |
| 75 negated_symbol_by_before_aliases): | |
| 76 ret = [] | |
| 77 for sym in before_symbols: | |
| 78 if sym.IsGroup(): | |
| 79 cloned = models.SymbolDiff( | |
| 80 [], _NegateAndClone(sym, matched_before_aliases, | |
| 81 negated_symbol_by_before_aliases), [], | |
| 82 name=sym.name, full_name=sym.full_name, section_name=sym.section_name) | |
| 83 else: | |
| 84 negated_alias = None | |
| 85 if sym.aliases: | |
| 86 negated_alias = negated_symbol_by_before_aliases.get(id(sym.aliases)) | |
| 87 if negated_alias: | |
| 88 cloned = _CloneAlias(sym, negated_alias) | |
| 89 else: | |
| 90 all_aliases_removed = id(sym.aliases) not in matched_before_aliases | |
| 91 if all_aliases_removed: | |
| 92 cloned = _CloneSymbol(sym, -sym.size_without_padding) | |
| 93 cloned.padding = -sym.padding | |
| 94 else: | |
| 95 cloned = _CloneSymbol(sym, 0) | |
| 96 cloned.aliases = [cloned] | |
| 97 negated_symbol_by_before_aliases[id(sym.aliases)] = cloned | |
| 98 else: | |
| 99 cloned = _CloneSymbol(sym, -sym.size_without_padding) | |
| 100 cloned.padding = -sym.padding | |
| 101 ret.append(cloned) | |
| 102 return ret | |
| 103 | |
| 104 | |
| 105 # TODO(agrieve): Diff logic does not work correctly when aliased symbols span | |
| 106 # mulitple groups. We should simplify by not allowing recursion (allow diffs | |
| 107 # only on SizeInfo, apply logic to raw_symbols, re-cluster after-the-fact. | |
| 108 def _DiffSymbolGroups(before, after): | |
| 109 before_symbols_by_key = collections.defaultdict(list) | |
| 110 for s in before: | |
| 111 before_symbols_by_key[s._Key()].append(s) | |
| 112 | |
| 113 similar = [] | |
| 114 diffed_symbol_by_after_aliases = {} | |
| 115 matched_before_aliases = set() | |
| 116 unmatched_after_syms = [] | |
| 117 # For similar symbols, padding is zeroed out. In order to not lose the | |
| 118 # information entirely, store it in aggregate. | |
| 119 padding_by_section_name = collections.defaultdict(int) | |
| 120 | |
| 121 # Step 1: Create all delta symbols and record unmatched symbols. | |
| 122 for after_sym in after: | |
| 123 matching_syms = before_symbols_by_key.get(after_sym._Key()) | |
| 124 if matching_syms: | |
| 125 before_sym = matching_syms.pop(0) | |
| 126 if before_sym.IsGroup() and after_sym.IsGroup(): | |
| 127 similar.append(_DiffSymbolGroups(before_sym, after_sym)) | |
| 128 else: | |
| 129 if before_sym.aliases: | |
| 130 matched_before_aliases.add(id(before_sym.aliases)) | |
| 131 similar.append( | |
| 132 _DiffSymbol(before_sym, after_sym, diffed_symbol_by_after_aliases, | |
| 133 padding_by_section_name)) | |
| 134 else: | |
| 135 unmatched_after_syms.append(after_sym) | |
| 136 continue | |
| 137 | |
| 138 # Step 2: Copy symbols only in "after" (being careful with aliases). | |
| 139 added = _CloneUnmatched(unmatched_after_syms, diffed_symbol_by_after_aliases) | |
| 140 | |
| 141 # Step 3: Negate symbols only in "before" (being careful with aliases). | |
| 142 removed = [] | |
| 143 negated_symbol_by_before_aliases = {} | |
| 144 for remaining_syms in before_symbols_by_key.itervalues(): | |
| 145 removed.extend(_NegateAndClone(remaining_syms, matched_before_aliases, | |
| 146 negated_symbol_by_before_aliases)) | |
| 147 | |
| 148 # Step 4: Create ** symbols to represent padding differences. | |
| 149 for section_name, padding in padding_by_section_name.iteritems(): | |
| 150 if padding != 0: | |
| 151 similar.append(models.Symbol( | |
| 152 section_name, padding, | |
| 153 name="** aggregate padding of diff'ed symbols")) | |
| 154 return models.SymbolDiff( | |
| 155 added, removed, similar, name=after.name, full_name=after.full_name, | |
| 156 section_name=after.section_name) | |
| 157 | |
| 158 | |
| 159 def Diff(before, after): | |
| 160 """Diffs two SizeInfo or SymbolGroup objects. | |
| 161 | |
| 162 When diffing SizeInfos, a SizeInfoDiff is returned. | |
| 163 When diffing SymbolGroups, a SymbolDiff is returned. | |
| 164 | |
| 165 Returns: | |
| 166 Returns a SizeInfo when args are of type SizeInfo. | |
| 167 Returns a SymbolDiff when args are of type SymbolGroup. | |
| 168 """ | |
| 169 if isinstance(after, models.SizeInfo): | |
| 170 assert isinstance(before, models.SizeInfo) | |
| 171 section_sizes = {k: after.section_sizes[k] - v | |
| 172 for k, v in before.section_sizes.iteritems()} | |
| 173 symbol_diff = _DiffSymbolGroups(before.symbols, after.symbols) | |
| 174 return models.SizeInfoDiff(section_sizes, symbol_diff, before.metadata, | |
| 175 after.metadata) | |
| 176 | |
| 177 assert (isinstance(after, models.SymbolGroup) and | |
| 178 isinstance(before, models.SymbolGroup)) | |
| 179 return _DiffSymbolGroups(before, after) | |
| 180 | |
| 181 | |
| OLD | NEW |