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

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

Issue 2813963002: //tools/binary_size: Consolidate most tools into "supersize" command (Closed)
Patch Set: Fix readme formatting. Make archive's --outoput-file a positional arg 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
(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 """Methods for converting model objects to human-readable formats."""
5
6 import datetime
7 import itertools
8 import time
9
10 import models
11
12
13 def _PrettySize(size):
14 # Arbitrarily chosen cut-off.
15 if abs(size) < 2000:
16 return '%d bytes' % size
17 # Always show 3 digits.
18 size /= 1024.0
19 if abs(size) < 10:
20 return '%.2fkb' % size
21 elif abs(size) < 100:
22 return '%.1fkb' % size
23 elif abs(size) < 1024:
24 return '%dkb' % size
25 size /= 1024.0
26 if abs(size) < 10:
27 return '%.2fmb' % size
28 # We shouldn't be seeing sizes > 100mb.
29 return '%.1fmb' % size
30
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
42 class Describer(object):
43 def __init__(self, verbose=False, recursive=False):
44 self.verbose = verbose
45 self.recursive = recursive
46
47 def _DescribeSectionSizes(self, section_sizes):
48 relevant_names = models.SECTION_TO_SECTION_NAME.values()
49 section_names = sorted(k for k in section_sizes.iterkeys()
50 if k in relevant_names or k.startswith('.data'))
51 total_bytes = sum(v for k, v in section_sizes.iteritems()
52 if k in section_names and k != '.bss')
53 yield ''
54 yield 'Section Sizes (Total={:,} bytes):'.format(total_bytes)
55 for name in section_names:
56 size = section_sizes[name]
57 if name == '.bss':
58 yield ' {}: {:,} bytes (not included in totals)'.format(name, size)
59 else:
60 percent = float(size) / total_bytes if total_bytes else 0
61 yield ' {}: {:,} bytes ({:.1%})'.format(name, size, percent)
62
63 if self.verbose:
64 yield ''
65 yield 'Other section sizes:'
66 section_names = sorted(k for k in section_sizes.iterkeys()
67 if k not in section_names)
68 for name in section_names:
69 yield ' {}: {:,} bytes'.format(name, section_sizes[name])
70
71 def _DescribeSymbol(self, sym):
72 if sym.IsGroup():
73 address = 'Group'
74 else:
75 address = hex(sym.address)
76 if self.verbose:
77 count_part = ' count=%d' % len(sym) if sym.IsGroup() else ''
78 yield '{}@{:<9s} size={} padding={} size_without_padding={}{}'.format(
79 sym.section, address, sym.size, sym.padding, sym.size_without_padding,
80 count_part)
81 yield ' source_path={} \tobject_path={}'.format(
82 sym.source_path, sym.object_path)
83 if sym.name:
84 yield ' is_anonymous={} name={}'.format(
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)
91 else:
92 yield '{}@{:<9s} {:<7} {}'.format(
93 sym.section, address, sym.size,
94 sym.source_path or sym.object_path or '{no path}')
95 if sym.name:
96 count_part = ' (count=%d)' % len(sym) if sym.IsGroup() else ''
97 yield ' {}{}'.format(sym.name, count_part)
98
99 def _DescribeSymbolGroupChildren(self, group, indent=0):
100 running_total = 0
101 sorted_syms = group if group.is_sorted else group.Sorted()
102 is_diff = isinstance(group, models.SymbolDiff)
103
104 indent_prefix = '> ' * indent
105 diff_prefix = ''
106 for s in sorted_syms:
107 if group.IsBss() or not s.IsBss():
108 running_total += s.size
109 for l in self._DescribeSymbol(s):
110 if l[:4].isspace():
111 indent_size = 8 + len(indent_prefix) + len(diff_prefix)
112 yield '{} {}'.format(' ' * indent_size, l)
113 else:
114 if is_diff:
115 diff_prefix = _DiffPrefix(group, s)
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)
142
143 def _DescribeSymbolDiff(self, diff):
144 header_template = ('{} symbols added (+), {} changed (~), {} removed (-), '
145 '{} unchanged ({})')
146 unchanged_msg = '=' if self.verbose else 'not shown'
147 symbol_delta_desc = [header_template.format(
148 diff.added_count, diff.changed_count, diff.removed_count,
149 diff.unchanged_count, unchanged_msg)]
150
151 similar_paths = set()
152 added_paths = set()
153 removed_paths = set()
154 for s in diff:
155 if diff.IsAdded(s):
156 added_paths.add(s.object_path)
157 elif diff.IsRemoved(s):
158 removed_paths.add(s.object_path)
159 else:
160 similar_paths.add(s.object_path)
161 added_paths.difference_update(similar_paths)
162 removed_paths.difference_update(similar_paths)
163 path_delta_desc = ['{} object files added, {} removed'.format(
164 len(added_paths), len(removed_paths))]
165 if self.verbose and len(added_paths):
166 path_delta_desc.append('Added files:')
167 path_delta_desc.extend(' ' + p for p in sorted(added_paths))
168 if self.verbose and len(removed_paths):
169 path_delta_desc.append('Removed files:')
170 path_delta_desc.extend(' ' + p for p in sorted(removed_paths))
171
172 diff = diff if self.verbose else diff.WhereNotUnchanged()
173 group_desc = self._DescribeSymbolGroup(diff)
174 return itertools.chain(symbol_delta_desc, path_delta_desc, ('',),
175 group_desc)
176
177 def _DescribeSizeInfoDiff(self, diff):
178 common_metadata = {k: v for k, v in diff.old_metadata.iteritems()
179 if diff.new_metadata[k] == v}
180 old_metadata = {k: v for k, v in diff.old_metadata.iteritems()
181 if k not in common_metadata}
182 new_metadata = {k: v for k, v in diff.new_metadata.iteritems()
183 if k not in common_metadata}
184 metadata_desc = itertools.chain(
185 ('Common Metadata:',),
186 (' %s' % line for line in DescribeMetadata(common_metadata)),
187 ('Old Metadata:',),
188 (' %s' % line for line in DescribeMetadata(old_metadata)),
189 ('New Metadata:',),
190 (' %s' % line for line in DescribeMetadata(new_metadata)))
191 section_desc = self._DescribeSectionSizes(diff.section_sizes)
192 group_desc = self.GenerateLines(diff.symbols)
193 return itertools.chain(metadata_desc, section_desc, ('',), group_desc)
194
195 def _DescribeSizeInfo(self, size_info):
196 metadata_desc = itertools.chain(
197 ('Metadata:',),
198 (' %s' % line for line in DescribeMetadata(size_info.metadata)))
199 section_desc = self._DescribeSectionSizes(size_info.section_sizes)
200 coverage_desc = ()
201 if self.verbose:
202 coverage_desc = itertools.chain(
203 ('',), DescribeSizeInfoCoverage(size_info))
204 group_desc = self.GenerateLines(size_info.symbols)
205 return itertools.chain(metadata_desc, section_desc, coverage_desc, ('',),
206 group_desc)
207
208 def GenerateLines(self, obj):
209 if isinstance(obj, models.SizeInfoDiff):
210 return self._DescribeSizeInfoDiff(obj)
211 if isinstance(obj, models.SizeInfo):
212 return self._DescribeSizeInfo(obj)
213 if isinstance(obj, models.SymbolDiff):
214 return self._DescribeSymbolDiff(obj)
215 if isinstance(obj, models.SymbolGroup):
216 return self._DescribeSymbolGroup(obj)
217 if isinstance(obj, models.Symbol):
218 return self._DescribeSymbol(obj)
219 return (repr(obj),)
220
221
222 def DescribeSizeInfoCoverage(size_info):
223 """Yields lines describing how accurate |size_info| is."""
224 symbols = models.SymbolGroup(size_info.raw_symbols)
225 for section in models.SECTION_TO_SECTION_NAME:
226 if section == 'd':
227 expected_size = sum(v for k, v in size_info.section_sizes.iteritems()
228 if k.startswith('.data'))
229 else:
230 expected_size = size_info.section_sizes[
231 models.SECTION_TO_SECTION_NAME[section]]
232
233 def one_stat(group):
234 template = ('Section %s has %.1f%% of %d bytes accounted for from '
235 '%d symbols. %d bytes are unaccounted for. Padding '
236 'accounts for %d bytes')
237 actual_size = group.size
238 count = len(group)
239 padding = group.padding
240 size_percent = 100.0 * actual_size / expected_size
241 return (template % (section, size_percent, actual_size, count,
242 expected_size - actual_size, padding))
243
244 in_section = symbols.WhereInSection(section)
245 yield one_stat(in_section)
246
247 star_syms = in_section.WhereNameMatches(r'^\*')
248 attributed_syms = star_syms.Inverted().WhereHasAnyAttribution()
249 anonymous_syms = attributed_syms.Inverted()
250 if star_syms or anonymous_syms:
251 missing_size = star_syms.size + anonymous_syms.size
252 yield ('+ Without %d merge sections and %d anonymous entries ('
253 'accounting for %d bytes):') % (
254 len(star_syms), len(anonymous_syms), missing_size)
255 yield '+ ' + one_stat(attributed_syms)
256
257
258 def _UtcToLocal(utc):
259 epoch = time.mktime(utc.timetuple())
260 offset = (datetime.datetime.fromtimestamp(epoch) -
261 datetime.datetime.utcfromtimestamp(epoch))
262 return utc + offset
263
264
265 def DescribeMetadata(metadata):
266 display_dict = metadata.copy()
267 timestamp = display_dict.get(models.METADATA_ELF_MTIME)
268 if timestamp:
269 timestamp_obj = datetime.datetime.utcfromtimestamp(timestamp)
270 display_dict[models.METADATA_ELF_MTIME] = (
271 _UtcToLocal(timestamp_obj).strftime('%Y-%m-%d %H:%M:%S'))
272 gn_args = display_dict.get(models.METADATA_GN_ARGS)
273 if gn_args:
274 display_dict[models.METADATA_GN_ARGS] = '; '.join(gn_args)
275 return sorted('%s=%s' % t for t in display_dict.iteritems())
276
277
278 def GenerateLines(obj, verbose=False, recursive=False):
279 """Returns an iterable of lines (without \n) that describes |obj|."""
280 return Describer(verbose=verbose, recursive=recursive).GenerateLines(obj)
281
282
283 def WriteLines(lines, func):
284 for l in lines:
285 func(l)
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