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

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

Issue 2809043003: //tools/binary_size: Group [clone] and ** symbols (Closed)
Patch Set: review comments 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/create_html_breakdown.py ('k') | tools/binary_size/file_format.py » ('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 """Methods for converting model objects to human-readable formats.""" 4 """Methods for converting model objects to human-readable formats."""
5 5
6 import datetime 6 import datetime
7 import itertools 7 import itertools
8 import time 8 import time
9 9
10 import models 10 import models
(...skipping 11 matching lines...) Expand all
22 return '%.1fkb' % size 22 return '%.1fkb' % size
23 elif abs(size) < 1024: 23 elif abs(size) < 1024:
24 return '%dkb' % size 24 return '%dkb' % size
25 size /= 1024.0 25 size /= 1024.0
26 if abs(size) < 10: 26 if abs(size) < 10:
27 return '%.2fmb' % size 27 return '%.2fmb' % size
28 # We shouldn't be seeing sizes > 100mb. 28 # We shouldn't be seeing sizes > 100mb.
29 return '%.1fmb' % size 29 return '%.1fmb' % size
30 30
31 31
32 def _DiffPrefix(diff, sym):
33 if diff.IsAdded(sym):
34 return '+ '
35 if diff.IsRemoved(sym):
36 return '- '
37 if sym.size:
38 return '~ '
39 return '= '
40
41
32 class Describer(object): 42 class Describer(object):
33 def __init__(self, verbose=False): 43 def __init__(self, verbose=False, recursive=False):
34 self.verbose = verbose 44 self.verbose = verbose
45 self.recursive = recursive
35 46
36 def _DescribeSectionSizes(self, section_sizes): 47 def _DescribeSectionSizes(self, section_sizes):
37 relevant_names = models.SECTION_TO_SECTION_NAME.values() 48 relevant_names = models.SECTION_TO_SECTION_NAME.values()
38 section_names = sorted(k for k in section_sizes.iterkeys() 49 section_names = sorted(k for k in section_sizes.iterkeys()
39 if k in relevant_names or k.startswith('.data')) 50 if k in relevant_names or k.startswith('.data'))
40 total_bytes = sum(v for k, v in section_sizes.iteritems() 51 total_bytes = sum(v for k, v in section_sizes.iteritems()
41 if k in section_names and k != '.bss') 52 if k in section_names and k != '.bss')
42 yield '' 53 yield ''
43 yield 'Section Sizes (Total={:,} bytes):'.format(total_bytes) 54 yield 'Section Sizes (Total={:,} bytes):'.format(total_bytes)
44 for name in section_names: 55 for name in section_names:
45 size = section_sizes[name] 56 size = section_sizes[name]
46 if name == '.bss': 57 if name == '.bss':
47 yield ' {}: {:,} bytes (not included in totals)'.format(name, size) 58 yield ' {}: {:,} bytes (not included in totals)'.format(name, size)
48 else: 59 else:
49 percent = float(size) / total_bytes if total_bytes else 0 60 percent = float(size) / total_bytes if total_bytes else 0
50 yield ' {}: {:,} bytes ({:.1%})'.format(name, size, percent) 61 yield ' {}: {:,} bytes ({:.1%})'.format(name, size, percent)
51 62
52 if self.verbose: 63 if self.verbose:
53 yield '' 64 yield ''
54 yield 'Other section sizes:' 65 yield 'Other section sizes:'
55 section_names = sorted(k for k in section_sizes.iterkeys() 66 section_names = sorted(k for k in section_sizes.iterkeys()
56 if k not in section_names) 67 if k not in section_names)
57 for name in section_names: 68 for name in section_names:
58 yield ' {}: {:,} bytes'.format(name, section_sizes[name]) 69 yield ' {}: {:,} bytes'.format(name, section_sizes[name])
59 70
60 def _DescribeSymbol(self, sym): 71 def _DescribeSymbol(self, sym):
61 # SymbolGroups are passed here when we don't want to expand them.
62 if sym.IsGroup(): 72 if sym.IsGroup():
63 if self.verbose: 73 address = 'Group'
64 yield ('{} {:<8} {} (count={}) padding={} ' 74 else:
65 'size_without_padding={}').format( 75 address = hex(sym.address)
66 sym.section, sym.size, sym.name, len(sym), sym.padding,
67 sym.size_without_padding)
68 else:
69 yield '{} {:<8} {} (count={})'.format(sym.section, sym.size, sym.name,
70 len(sym))
71 return
72
73 if self.verbose: 76 if self.verbose:
74 yield '{}@0x{:<8x} size={} padding={} size_without_padding={}'.format( 77 count_part = ' count=%d' % len(sym) if sym.IsGroup() else ''
75 sym.section, sym.address, sym.size, sym.padding, 78 yield '{}@{:<9s} size={} padding={} size_without_padding={}{}'.format(
76 sym.size_without_padding) 79 sym.section, address, sym.size, sym.padding, sym.size_without_padding,
80 count_part)
77 yield ' source_path={} \tobject_path={}'.format( 81 yield ' source_path={} \tobject_path={}'.format(
78 sym.source_path, sym.object_path) 82 sym.source_path, sym.object_path)
79 if sym.full_name:
80 yield ' is_anonymous={} full_name={}'.format(
81 int(sym.is_anonymous), sym.full_name)
82 if sym.name: 83 if sym.name:
83 yield ' is_anonymous={} name={}'.format( 84 yield ' is_anonymous={} name={}'.format(
84 int(sym.is_anonymous), sym.name) 85 int(sym.is_anonymous), sym.name)
86 if sym.full_name:
87 yield ' full_name={}'.format(sym.full_name)
88 elif sym.full_name:
89 yield ' is_anonymous={} full_name={}'.format(
90 int(sym.is_anonymous), sym.full_name)
85 else: 91 else:
86 yield '{}@0x{:<8x} {:<7} {}'.format( 92 yield '{}@{:<9s} {:<7} {}'.format(
87 sym.section, sym.address, sym.size, 93 sym.section, address, sym.size,
88 sym.source_path or sym.object_path or '{no path}') 94 sym.source_path or sym.object_path or '{no path}')
89 if sym.name: 95 if sym.name:
90 yield ' {}'.format(sym.name) 96 count_part = ' (count=%d)' % len(sym) if sym.IsGroup() else ''
97 yield ' {}{}'.format(sym.name, count_part)
91 98
92 def _DescribeSymbolGroup(self, group, prefix_func=None): 99 def _DescribeSymbolGroupChildren(self, group, indent=0):
93 total_size = group.size 100 running_total = 0
94 yield 'Showing {:,} symbols with total size: {} bytes'.format( 101 sorted_syms = group if group.is_sorted else group.Sorted()
95 len(group), total_size) 102 is_diff = isinstance(group, models.SymbolDiff)
96 code_syms = group.WhereInSection('t')
97 code_size = code_syms.size
98 ro_size = code_syms.Inverted().WhereInSection('r').size
99 yield '.text={:<10} .rodata={:<10} other={:<10} total={}'.format(
100 _PrettySize(code_size), _PrettySize(ro_size),
101 _PrettySize(total_size - code_size - ro_size),
102 _PrettySize(total_size))
103 unique_paths = set(s.object_path for s in group)
104 yield 'Number of object files: {}'.format(len(unique_paths))
105 yield ''
106 yield 'First columns are: running total, type, size'
107 103
108 running_total = 0 104 indent_prefix = '> ' * indent
109 prefix = '' 105 diff_prefix = ''
110 sorted_syms = group if group.is_sorted else group.Sorted()
111
112 prefix = ''
113 for s in sorted_syms: 106 for s in sorted_syms:
114 if group.IsBss() or not s.IsBss(): 107 if group.IsBss() or not s.IsBss():
115 running_total += s.size 108 running_total += s.size
116 for l in self._DescribeSymbol(s): 109 for l in self._DescribeSymbol(s):
117 if l[:4].isspace(): 110 if l[:4].isspace():
118 yield '{} {}'.format(' ' * (8 + len(prefix)), l) 111 indent_size = 8 + len(indent_prefix) + len(diff_prefix)
112 yield '{} {}'.format(' ' * indent_size, l)
119 else: 113 else:
120 if prefix_func: 114 if is_diff:
121 prefix = prefix_func(s) 115 diff_prefix = _DiffPrefix(group, s)
122 yield '{}{:8} {}'.format(prefix, running_total, l) 116 yield '{}{}{:8} {}'.format(indent_prefix, diff_prefix,
117 running_total, l)
118
119 if self.recursive and s.IsGroup():
120 for l in self._DescribeSymbolGroupChildren(s, indent=indent + 1):
121 yield l
122
123 def _DescribeSymbolGroup(self, group):
124 total_size = group.size
125 code_syms = group.WhereInSection('t')
126 code_size = code_syms.size
127 ro_size = code_syms.Inverted().WhereInSection('r').size
128 unique_paths = set(s.object_path for s in group)
129 header_desc = [
130 'Showing {:,} symbols with total size: {} bytes'.format(
131 len(group), total_size),
132 '.text={:<10} .rodata={:<10} other={:<10} total={}'.format(
133 _PrettySize(code_size), _PrettySize(ro_size),
134 _PrettySize(total_size - code_size - ro_size),
135 _PrettySize(total_size)),
136 'Number of object files: {}'.format(len(unique_paths)),
137 '',
138 'First columns are: running total, type, size',
139 ]
140 children_desc = self._DescribeSymbolGroupChildren(group)
141 return itertools.chain(header_desc, children_desc)
123 142
124 def _DescribeSymbolDiff(self, diff): 143 def _DescribeSymbolDiff(self, diff):
125 header_template = ('{} symbols added (+), {} changed (~), {} removed (-), ' 144 header_template = ('{} symbols added (+), {} changed (~), {} removed (-), '
126 '{} unchanged ({})') 145 '{} unchanged ({})')
127 unchanged_msg = '=' if self.verbose else 'not shown' 146 unchanged_msg = '=' if self.verbose else 'not shown'
128 symbol_delta_desc = [header_template.format( 147 symbol_delta_desc = [header_template.format(
129 diff.added_count, diff.changed_count, diff.removed_count, 148 diff.added_count, diff.changed_count, diff.removed_count,
130 diff.unchanged_count, unchanged_msg)] 149 diff.unchanged_count, unchanged_msg)]
131 150
132 similar_paths = set() 151 similar_paths = set()
(...skipping 10 matching lines...) Expand all
143 removed_paths.difference_update(similar_paths) 162 removed_paths.difference_update(similar_paths)
144 path_delta_desc = ['{} object files added, {} removed'.format( 163 path_delta_desc = ['{} object files added, {} removed'.format(
145 len(added_paths), len(removed_paths))] 164 len(added_paths), len(removed_paths))]
146 if self.verbose and len(added_paths): 165 if self.verbose and len(added_paths):
147 path_delta_desc.append('Added files:') 166 path_delta_desc.append('Added files:')
148 path_delta_desc.extend(' ' + p for p in sorted(added_paths)) 167 path_delta_desc.extend(' ' + p for p in sorted(added_paths))
149 if self.verbose and len(removed_paths): 168 if self.verbose and len(removed_paths):
150 path_delta_desc.append('Removed files:') 169 path_delta_desc.append('Removed files:')
151 path_delta_desc.extend(' ' + p for p in sorted(removed_paths)) 170 path_delta_desc.extend(' ' + p for p in sorted(removed_paths))
152 171
153 def prefix_func(sym):
154 if diff.IsAdded(sym):
155 return '+ '
156 if diff.IsRemoved(sym):
157 return '- '
158 if sym.size:
159 return '~ '
160 return '= '
161
162 diff = diff if self.verbose else diff.WhereNotUnchanged() 172 diff = diff if self.verbose else diff.WhereNotUnchanged()
163 group_desc = self._DescribeSymbolGroup(diff, prefix_func=prefix_func) 173 group_desc = self._DescribeSymbolGroup(diff)
164 return itertools.chain(symbol_delta_desc, path_delta_desc, ('',), 174 return itertools.chain(symbol_delta_desc, path_delta_desc, ('',),
165 group_desc) 175 group_desc)
166 176
167 def _DescribeSizeInfoDiff(self, diff): 177 def _DescribeSizeInfoDiff(self, diff):
168 common_metadata = {k: v for k, v in diff.old_metadata.iteritems() 178 common_metadata = {k: v for k, v in diff.old_metadata.iteritems()
169 if diff.new_metadata[k] == v} 179 if diff.new_metadata[k] == v}
170 old_metadata = {k: v for k, v in diff.old_metadata.iteritems() 180 old_metadata = {k: v for k, v in diff.old_metadata.iteritems()
171 if k not in common_metadata} 181 if k not in common_metadata}
172 new_metadata = {k: v for k, v in diff.new_metadata.iteritems() 182 new_metadata = {k: v for k, v in diff.new_metadata.iteritems()
173 if k not in common_metadata} 183 if k not in common_metadata}
(...skipping 30 matching lines...) Expand all
204 return self._DescribeSymbolDiff(obj) 214 return self._DescribeSymbolDiff(obj)
205 if isinstance(obj, models.SymbolGroup): 215 if isinstance(obj, models.SymbolGroup):
206 return self._DescribeSymbolGroup(obj) 216 return self._DescribeSymbolGroup(obj)
207 if isinstance(obj, models.Symbol): 217 if isinstance(obj, models.Symbol):
208 return self._DescribeSymbol(obj) 218 return self._DescribeSymbol(obj)
209 return (repr(obj),) 219 return (repr(obj),)
210 220
211 221
212 def DescribeSizeInfoCoverage(size_info): 222 def DescribeSizeInfoCoverage(size_info):
213 """Yields lines describing how accurate |size_info| is.""" 223 """Yields lines describing how accurate |size_info| is."""
224 symbols = models.SymbolGroup(size_info.raw_symbols)
214 for section in models.SECTION_TO_SECTION_NAME: 225 for section in models.SECTION_TO_SECTION_NAME:
215 if section == 'd': 226 if section == 'd':
216 expected_size = sum(v for k, v in size_info.section_sizes.iteritems() 227 expected_size = sum(v for k, v in size_info.section_sizes.iteritems()
217 if k.startswith('.data')) 228 if k.startswith('.data'))
218 else: 229 else:
219 expected_size = size_info.section_sizes[ 230 expected_size = size_info.section_sizes[
220 models.SECTION_TO_SECTION_NAME[section]] 231 models.SECTION_TO_SECTION_NAME[section]]
221 232
222 def one_stat(group): 233 def one_stat(group):
223 template = ('Section %s has %.1f%% of %d bytes accounted for from ' 234 template = ('Section %s has %.1f%% of %d bytes accounted for from '
224 '%d symbols. %d bytes are unaccounted for. Padding ' 235 '%d symbols. %d bytes are unaccounted for. Padding '
225 'accounts for %d bytes') 236 'accounts for %d bytes')
226 actual_size = group.size 237 actual_size = group.size
227 count = len(group) 238 count = len(group)
228 padding = group.padding 239 padding = group.padding
229 size_percent = 100.0 * actual_size / expected_size 240 size_percent = 100.0 * actual_size / expected_size
230 return (template % (section, size_percent, actual_size, count, 241 return (template % (section, size_percent, actual_size, count,
231 expected_size - actual_size, padding)) 242 expected_size - actual_size, padding))
232 243
233 in_section = size_info.symbols.WhereInSection(section) 244 in_section = symbols.WhereInSection(section)
234 yield one_stat(in_section) 245 yield one_stat(in_section)
235 246
236 star_syms = in_section.WhereNameMatches(r'^\*') 247 star_syms = in_section.WhereNameMatches(r'^\*')
237 attributed_syms = star_syms.Inverted().WhereHasAnyAttribution() 248 attributed_syms = star_syms.Inverted().WhereHasAnyAttribution()
238 anonymous_syms = attributed_syms.Inverted() 249 anonymous_syms = attributed_syms.Inverted()
239 if star_syms or anonymous_syms: 250 if star_syms or anonymous_syms:
240 missing_size = star_syms.size + anonymous_syms.size 251 missing_size = star_syms.size + anonymous_syms.size
241 yield ('+ Without %d merge sections and %d anonymous entries (' 252 yield ('+ Without %d merge sections and %d anonymous entries ('
242 'accounting for %d bytes):') % ( 253 'accounting for %d bytes):') % (
243 len(star_syms), len(anonymous_syms), missing_size) 254 len(star_syms), len(anonymous_syms), missing_size)
(...skipping 13 matching lines...) Expand all
257 if timestamp: 268 if timestamp:
258 timestamp_obj = datetime.datetime.utcfromtimestamp(timestamp) 269 timestamp_obj = datetime.datetime.utcfromtimestamp(timestamp)
259 display_dict[models.METADATA_ELF_MTIME] = ( 270 display_dict[models.METADATA_ELF_MTIME] = (
260 _UtcToLocal(timestamp_obj).strftime('%Y-%m-%d %H:%M:%S')) 271 _UtcToLocal(timestamp_obj).strftime('%Y-%m-%d %H:%M:%S'))
261 gn_args = display_dict.get(models.METADATA_GN_ARGS) 272 gn_args = display_dict.get(models.METADATA_GN_ARGS)
262 if gn_args: 273 if gn_args:
263 display_dict[models.METADATA_GN_ARGS] = '; '.join(gn_args) 274 display_dict[models.METADATA_GN_ARGS] = '; '.join(gn_args)
264 return sorted('%s=%s' % t for t in display_dict.iteritems()) 275 return sorted('%s=%s' % t for t in display_dict.iteritems())
265 276
266 277
267 def GenerateLines(obj, verbose=False): 278 def GenerateLines(obj, verbose=False, recursive=False):
268 return Describer(verbose).GenerateLines(obj) 279 """Returns an iterable of lines (without \n) that describes |obj|."""
280 return Describer(verbose=verbose, recursive=recursive).GenerateLines(obj)
269 281
270 282
271 def WriteLines(lines, func): 283 def WriteLines(lines, func):
272 for l in lines: 284 for l in lines:
273 func(l) 285 func(l)
274 func('\n') 286 func('\n')
OLDNEW
« no previous file with comments | « tools/binary_size/create_html_breakdown.py ('k') | tools/binary_size/file_format.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698