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

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

Issue 2795593005: //tools/binary_size: Various enhancements to console.py (Closed)
Patch Set: Review fixes Created 3 years, 8 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
« no previous file with comments | « tools/binary_size/match_util_test.py ('k') | tools/binary_size/testdata/ActualDiff.golden » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import collections 6 import collections
7 import copy 7 import copy
8 import os 8 import os
9 import re 9 import re
10 10
11 import match_util
12
11 13
12 SECTION_TO_SECTION_NAME = { 14 SECTION_TO_SECTION_NAME = {
13 'b': '.bss', 15 'b': '.bss',
14 'd': '.data', 16 'd': '.data',
15 'r': '.rodata', 17 'r': '.rodata',
16 't': '.text', 18 't': '.text',
17 } 19 }
18 20
19 21
20 class SizeInfo(object): 22 class SizeInfo(object):
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 84
83 class Symbol(BaseSymbol): 85 class Symbol(BaseSymbol):
84 """Represents a single symbol within a binary.""" 86 """Represents a single symbol within a binary."""
85 87
86 __slots__ = ( 88 __slots__ = (
87 'address', 89 'address',
88 'full_name', 90 'full_name',
89 'is_anonymous', 91 'is_anonymous',
90 'object_path', 92 'object_path',
91 'name', 93 'name',
92 'flags',
93 'padding', 94 'padding',
94 'section_name', 95 'section_name',
95 'source_path', 96 'source_path',
96 'size', 97 'size',
97 ) 98 )
98 99
99 def __init__(self, section_name, size_without_padding, address=None, 100 def __init__(self, section_name, size_without_padding, address=None,
100 name=None, source_path=None, object_path=None, 101 name=None, source_path=None, object_path=None,
101 full_name=None, is_anonymous=False): 102 full_name=None, is_anonymous=False):
102 self.section_name = section_name 103 self.section_name = section_name
(...skipping 13 matching lines...) Expand all
116 self.section_name, self.address, self.size_without_padding, 117 self.section_name, self.address, self.size_without_padding,
117 self.padding, self.name, self.source_path or self.object_path, 118 self.padding, self.name, self.source_path or self.object_path,
118 int(self.is_anonymous)) 119 int(self.is_anonymous))
119 120
120 121
121 class SymbolGroup(BaseSymbol): 122 class SymbolGroup(BaseSymbol):
122 """Represents a group of symbols using the same interface as Symbol. 123 """Represents a group of symbols using the same interface as Symbol.
123 124
124 SymbolGroups are immutable. All filtering / sorting will return new 125 SymbolGroups are immutable. All filtering / sorting will return new
125 SymbolGroups objects. 126 SymbolGroups objects.
127
128 Overrides many __functions__. E.g. the following are all valid:
129 * len(group)
130 * iter(group)
131 * group[0]
132 * group['0x1234'] # By symbol address
133 * without_group2 = group1 - group2
134 * unioned = group1 + group2
126 """ 135 """
127 136
128 __slots__ = ( 137 __slots__ = (
129 'symbols', 138 '_padding',
130 'filtered_symbols', 139 '_size',
140 '_symbols',
141 '_filtered_symbols',
131 'name', 142 'name',
132 'section_name', 143 'section_name',
144 'is_sorted',
133 ) 145 )
134 146
135 def __init__(self, symbols, filtered_symbols=None, name=None, 147 def __init__(self, symbols, filtered_symbols=None, name=None,
136 section_name=None): 148 section_name=None, is_sorted=False):
137 self.symbols = symbols 149 self._padding = None
138 self.filtered_symbols = filtered_symbols or [] 150 self._size = None
151 self._symbols = symbols
152 self._filtered_symbols = filtered_symbols or []
139 self.name = name or '' 153 self.name = name or ''
140 self.section_name = section_name or '.*' 154 self.section_name = section_name or '.*'
155 self.is_sorted = is_sorted
141 156
142 def __repr__(self): 157 def __repr__(self):
143 return 'Group(name=%s,count=%d,size=%d)' % ( 158 return 'Group(name=%s,count=%d,size=%d)' % (
144 self.name, len(self), self.size) 159 self.name, len(self), self.size)
145 160
146 def __iter__(self): 161 def __iter__(self):
147 return iter(self.symbols) 162 return iter(self._symbols)
148 163
149 def __len__(self): 164 def __len__(self):
150 return len(self.symbols) 165 return len(self._symbols)
151 166
152 def __getitem__(self, index): 167 def __eq__(self, other):
153 return self.symbols[index] 168 return self._symbols == other._symbols
169
170 def __getitem__(self, key):
171 """|key| can be an index or an address.
172
173 Raises if multiple symbols map to the address.
174 """
175 if isinstance(key, slice):
176 return self._symbols.__getitem__(key)
177 if isinstance(key, basestring) or key > len(self._symbols):
178 found = self.WhereAddressInRange(key)
179 if len(found) != 1:
180 raise KeyError('%d symbols found at address %s.' % (len(found), key))
181 return found[0]
182 return self._symbols[key]
154 183
155 def __sub__(self, other): 184 def __sub__(self, other):
156 other_ids = set(id(s) for s in other) 185 if other.IsGroup():
186 other_ids = set(id(s) for s in other)
187 else:
188 other_ids = set((id(other),))
157 new_symbols = [s for s in self if id(s) not in other_ids] 189 new_symbols = [s for s in self if id(s) not in other_ids]
158 return self._CreateTransformed(new_symbols, section_name=self.section_name) 190 return self._CreateTransformed(new_symbols, section_name=self.section_name)
159 191
160 def __add__(self, other): 192 def __add__(self, other):
161 self_ids = set(id(s) for s in self) 193 self_ids = set(id(s) for s in self)
162 new_symbols = self.symbols + [s for s in other if id(s) not in self_ids] 194 new_symbols = self._symbols + [s for s in other if id(s) not in self_ids]
163 return self._CreateTransformed(new_symbols, section_name=self.section_name) 195 return self._CreateTransformed(new_symbols, section_name=self.section_name,
196 is_sorted=False)
164 197
165 @property 198 @property
166 def address(self): 199 def address(self):
167 return 0 200 return 0
168 201
169 @property 202 @property
170 def full_name(self): 203 def full_name(self):
171 return None 204 return None
172 205
173 @property 206 @property
174 def is_anonymous(self): 207 def is_anonymous(self):
175 return False 208 return False
176 209
177 @property 210 @property
178 def source_path(self): 211 def source_path(self):
179 return None 212 return None
180 213
181 @property 214 @property
182 def size(self): 215 def size(self):
183 if self.IsBss(): 216 if self._size is None:
184 return sum(s.size for s in self) 217 if self.IsBss():
185 return sum(s.size for s in self if not s.IsBss()) 218 self._size = sum(s.size for s in self)
219 self._size = sum(s.size for s in self if not s.IsBss())
220 return self._size
186 221
187 @property 222 @property
188 def padding(self): 223 def padding(self):
189 return sum(s.padding for s in self) 224 if self._padding is None:
225 self._padding = sum(s.padding for s in self)
226 return self._padding
190 227
191 def IsGroup(self): 228 def IsGroup(self):
192 return True 229 return True
193 230
194 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, 231 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None,
195 section_name=None): 232 section_name=None, is_sorted=None):
233 if is_sorted is None:
234 is_sorted = self.is_sorted
196 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name, 235 return SymbolGroup(symbols, filtered_symbols=filtered_symbols, name=name,
197 section_name=section_name) 236 section_name=section_name, is_sorted=is_sorted)
198 237
199 def Sorted(self, cmp_func=None, key=None, reverse=False): 238 def Sorted(self, cmp_func=None, key=None, reverse=False):
200 # Default to sorting by abs(size) then name. 239 # Default to sorting by abs(size) then name.
201 if cmp_func is None and key is None: 240 if cmp_func is None and key is None:
202 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name), 241 cmp_func = lambda a, b: cmp((a.IsBss(), abs(b.size), a.name),
203 (b.IsBss(), abs(a.size), b.name)) 242 (b.IsBss(), abs(a.size), b.name))
204 243
205 new_symbols = sorted(self.symbols, cmp_func, key, reverse) 244 new_symbols = sorted(self._symbols, cmp_func, key, reverse)
206 return self._CreateTransformed(new_symbols, 245 return self._CreateTransformed(
207 filtered_symbols=self.filtered_symbols, 246 new_symbols, filtered_symbols=self._filtered_symbols,
208 section_name=self.section_name) 247 section_name=self.section_name, is_sorted=True)
209 248
210 def SortedByName(self, reverse=False): 249 def SortedByName(self, reverse=False):
211 return self.Sorted(key=(lambda s:s.name), reverse=reverse) 250 return self.Sorted(key=(lambda s:s.name), reverse=reverse)
212 251
213 def SortedByAddress(self, reverse=False): 252 def SortedByAddress(self, reverse=False):
214 return self.Sorted(key=(lambda s:s.address), reverse=reverse) 253 return self.Sorted(key=(lambda s:s.address), reverse=reverse)
215 254
216 def SortedByCount(self, reverse=False): 255 def SortedByCount(self, reverse=False):
217 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1), 256 return self.Sorted(key=(lambda s:len(s) if s.IsGroup() else 1),
218 reverse=not reverse) 257 reverse=not reverse)
(...skipping 15 matching lines...) Expand all
234 ret.section_name = SECTION_TO_SECTION_NAME[section] 273 ret.section_name = SECTION_TO_SECTION_NAME[section]
235 else: 274 else:
236 ret = self.Filter(lambda s: s.section_name == section) 275 ret = self.Filter(lambda s: s.section_name == section)
237 ret.section_name = section 276 ret.section_name = section
238 return ret 277 return ret
239 278
240 def WhereIsGenerated(self): 279 def WhereIsGenerated(self):
241 return self.Filter(lambda s: s.IsGenerated()) 280 return self.Filter(lambda s: s.IsGenerated())
242 281
243 def WhereNameMatches(self, pattern): 282 def WhereNameMatches(self, pattern):
244 regex = re.compile(pattern) 283 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
245 return self.Filter(lambda s: regex.search(s.name)) 284 return self.Filter(lambda s: regex.search(s.name))
246 285
247 def WhereObjectPathMatches(self, pattern): 286 def WhereObjectPathMatches(self, pattern):
248 regex = re.compile(pattern) 287 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
249 return self.Filter(lambda s: regex.search(s.object_path)) 288 return self.Filter(lambda s: regex.search(s.object_path))
250 289
251 def WhereSourcePathMatches(self, pattern): 290 def WhereSourcePathMatches(self, pattern):
252 regex = re.compile(pattern) 291 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
253 return self.Filter(lambda s: regex.search(s.source_path)) 292 return self.Filter(lambda s: regex.search(s.source_path))
254 293
255 def WherePathMatches(self, pattern): 294 def WherePathMatches(self, pattern):
256 regex = re.compile(pattern) 295 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
257 return self.Filter(lambda s: regex.search(s.source_path or s.object_path)) 296 return self.Filter(lambda s: (regex.search(s.source_path) or
297 regex.search(s.object_path)))
258 298
259 def WhereAddressInRange(self, start, end): 299 def WhereMatches(self, pattern):
260 return self.Filter(lambda s: s.address >= start and s.address <= end) 300 """Looks for |pattern| within all paths & names."""
301 regex = re.compile(match_util.ExpandRegexIdentifierPlaceholder(pattern))
302 return self.Filter(lambda s: (regex.search(s.source_path) or
303 regex.search(s.object_path) or
304 regex.search(s.full_name or '') or
305 regex.search(s.name)))
306
307 def WhereAddressInRange(self, start, end=None):
308 """Searches for addesses within [start, end).
309
310 Args may be ints or hex strings. Default value for |end| is |start| + 1.
311 """
312 if isinstance(start, basestring):
313 start = int(start, 16)
314 if end is None:
315 end = start + 1
316 return self.Filter(lambda s: s.address >= start and s.address < end)
261 317
262 def WhereHasAnyAttribution(self): 318 def WhereHasAnyAttribution(self):
263 return self.Filter(lambda s: s.name or s.source_path or s.object_path) 319 return self.Filter(lambda s: s.name or s.source_path or s.object_path)
264 320
265 def Inverted(self): 321 def Inverted(self):
266 return self._CreateTransformed(self.filtered_symbols, 322 """Returns the symbols that were filtered out by the previous filter.
267 filtered_symbols=self.symbols) 323
324 Applies only when the previous call was a filter.
325
326 Example:
327 # Symbols that do not have "third_party" in their path.
328 symbols.WherePathMatches(r'third_party').Inverted()
329 # Symbols within third_party that do not contain the string "foo".
330 symbols.WherePathMatches(r'third_party').WhereMatches('foo').Inverted()
331 """
332 return self._CreateTransformed(
333 self._filtered_symbols, filtered_symbols=self._symbols, is_sorted=False)
268 334
269 def GroupBy(self, func, min_count=0): 335 def GroupBy(self, func, min_count=0):
270 """Returns a SymbolGroup of SymbolGroups, indexed by |func|. 336 """Returns a SymbolGroup of SymbolGroups, indexed by |func|.
271 337
272 Args: 338 Args:
273 func: Grouping function. Passed a symbol and returns a string for the 339 func: Grouping function. Passed a symbol and returns a string for the
274 name of the subgroup to put the symbol in. If None is returned, the 340 name of the subgroup to put the symbol in. If None is returned, the
275 symbol is omitted. 341 symbol is omitted.
276 min_count: Miniumum number of symbols for a group. If fewer than this many 342 min_count: Miniumum number of symbols for a group. If fewer than this many
277 symbols end up in a group, they will not be put within a group. 343 symbols end up in a group, they will not be put within a group.
278 Use a negative value to omit symbols entirely rather than 344 Use a negative value to omit symbols entirely rather than
279 include them outside of a group. 345 include them outside of a group.
280 """ 346 """
281 new_syms = [] 347 new_syms = []
282 filtered_symbols = [] 348 filtered_symbols = []
283 symbols_by_token = collections.defaultdict(list) 349 symbols_by_token = collections.defaultdict(list)
284 # Index symbols by |func|. 350 # Index symbols by |func|.
285 for symbol in self: 351 for symbol in self:
286 token = func(symbol) 352 token = func(symbol)
287 if token is None: 353 if token is None:
288 filtered_symbols.append(symbol) 354 filtered_symbols.append(symbol)
289 symbols_by_token[token].append(symbol) 355 symbols_by_token[token].append(symbol)
290 # Create the subgroups. 356 # Create the subgroups.
291 include_singles = min_count >= 0 357 include_singles = min_count >= 0
292 min_count = abs(min_count) 358 min_count = abs(min_count)
293 for token, symbols in symbols_by_token.iteritems(): 359 for token, symbols in symbols_by_token.iteritems():
294 if len(symbols) >= min_count: 360 if len(symbols) >= min_count:
295 new_syms.append(self._CreateTransformed(symbols, name=token, 361 new_syms.append(self._CreateTransformed(
296 section_name=self.section_name)) 362 symbols, name=token, section_name=self.section_name,
363 is_sorted=False))
297 elif include_singles: 364 elif include_singles:
298 new_syms.extend(symbols) 365 new_syms.extend(symbols)
299 else: 366 else:
300 filtered_symbols.extend(symbols) 367 filtered_symbols.extend(symbols)
301 return self._CreateTransformed(new_syms, filtered_symbols=filtered_symbols, 368 return self._CreateTransformed(
302 section_name=self.section_name) 369 new_syms, filtered_symbols=filtered_symbols,
370 section_name=self.section_name, is_sorted=False)
303 371
304 def GroupBySectionName(self): 372 def GroupBySectionName(self):
305 return self.GroupBy(lambda s: s.section_name) 373 return self.GroupBy(lambda s: s.section_name)
306 374
307 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0): 375 def GroupByNamespace(self, depth=0, fallback='{global}', min_count=0):
308 """Groups by symbol namespace (as denoted by ::s). 376 """Groups by symbol namespace (as denoted by ::s).
309 377
310 Does not differentiate between C++ namespaces and C++ classes. 378 Does not differentiate between C++ namespaces and C++ classes.
311 379
312 Args: 380 Args:
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 symbols.extend(removed) 463 symbols.extend(removed)
396 symbols.extend(similar) 464 symbols.extend(similar)
397 super(SymbolDiff, self).__init__(symbols) 465 super(SymbolDiff, self).__init__(symbols)
398 466
399 def __repr__(self): 467 def __repr__(self):
400 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % ( 468 return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % (
401 'SymbolGroup', self.added_count, self.removed_count, self.changed_count, 469 'SymbolGroup', self.added_count, self.removed_count, self.changed_count,
402 self.unchanged_count, self.size) 470 self.unchanged_count, self.size)
403 471
404 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None, 472 def _CreateTransformed(self, symbols, filtered_symbols=None, name=None,
405 section_name=None): 473 section_name=None, is_sorted=None):
406 ret = SymbolDiff.__new__(SymbolDiff) 474 ret = SymbolDiff.__new__(SymbolDiff)
407 # Printing sorts, so fast-path the same symbols case. 475 # Printing sorts, so fast-path the same symbols case.
408 if len(symbols) == len(self.symbols): 476 if len(symbols) == len(self._symbols):
409 ret._added_ids = self._added_ids 477 ret._added_ids = self._added_ids
410 ret._removed_ids = self._removed_ids 478 ret._removed_ids = self._removed_ids
411 else: 479 else:
412 ret._added_ids = set(id(s) for s in symbols if self.IsAdded(s)) 480 ret._added_ids = set(id(s) for s in symbols if self.IsAdded(s))
413 ret._removed_ids = set(id(s) for s in symbols if self.IsRemoved(s)) 481 ret._removed_ids = set(id(s) for s in symbols if self.IsRemoved(s))
414 super(SymbolDiff, ret).__init__(symbols, filtered_symbols=filtered_symbols, 482 super(SymbolDiff, ret).__init__(
415 name=name, section_name=section_name) 483 symbols, filtered_symbols=filtered_symbols, name=name,
416 484 section_name=section_name, is_sorted=is_sorted)
417 return ret 485 return ret
418 486
419 @property 487 @property
420 def added_count(self): 488 def added_count(self):
421 return len(self._added_ids) 489 return len(self._added_ids)
422 490
423 @property 491 @property
424 def removed_count(self): 492 def removed_count(self):
425 return len(self._removed_ids) 493 return len(self._removed_ids)
426 494
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
511 579
512 def _ExtractPrefixBeforeSeparator(string, separator, count=1): 580 def _ExtractPrefixBeforeSeparator(string, separator, count=1):
513 idx = -len(separator) 581 idx = -len(separator)
514 prev_idx = None 582 prev_idx = None
515 for _ in xrange(count): 583 for _ in xrange(count):
516 idx = string.find(separator, idx + len(separator)) 584 idx = string.find(separator, idx + len(separator))
517 if idx < 0: 585 if idx < 0:
518 break 586 break
519 prev_idx = idx 587 prev_idx = idx
520 return string[:prev_idx] 588 return string[:prev_idx]
OLDNEW
« no previous file with comments | « tools/binary_size/match_util_test.py ('k') | tools/binary_size/testdata/ActualDiff.golden » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698