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 |