OLD | NEW |
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 |
11 | 11 |
12 | 12 |
| 13 _DIFF_PREFIX_BY_STATUS = ['= ', '~ ', '+ ', '- '] |
| 14 |
| 15 |
13 def _PrettySize(size): | 16 def _PrettySize(size): |
14 # Arbitrarily chosen cut-off. | 17 # Arbitrarily chosen cut-off. |
15 if abs(size) < 2000: | 18 if abs(size) < 2000: |
16 return '%d bytes' % size | 19 return '%d bytes' % size |
17 # Always show 3 digits. | 20 # Always show 3 digits. |
18 size /= 1024.0 | 21 size /= 1024.0 |
19 if abs(size) < 10: | 22 if abs(size) < 10: |
20 return '%.2fkb' % size | 23 return '%.2fkb' % size |
21 elif abs(size) < 100: | 24 elif abs(size) < 100: |
22 return '%.1fkb' % size | 25 return '%.1fkb' % size |
(...skipping 12 matching lines...) Expand all Loading... |
35 if pss > 10: | 38 if pss > 10: |
36 return str(int(pss)) | 39 return str(int(pss)) |
37 ret = str(round(pss, 1)) | 40 ret = str(round(pss, 1)) |
38 if ret.endswith('.0'): | 41 if ret.endswith('.0'): |
39 ret = ret[:-2] | 42 ret = ret[:-2] |
40 if ret == '0' and pss: | 43 if ret == '0' and pss: |
41 ret = '~0' | 44 ret = '~0' |
42 return ret | 45 return ret |
43 | 46 |
44 | 47 |
45 def _DiffPrefix(diff, sym): | |
46 if diff.IsAdded(sym): | |
47 return '+ ' | |
48 if diff.IsRemoved(sym): | |
49 return '- ' | |
50 if sym.size: | |
51 return '~ ' | |
52 return '= ' | |
53 | |
54 | |
55 def _Divide(a, b): | 48 def _Divide(a, b): |
56 return float(a) / b if b else 0 | 49 return float(a) / b if b else 0 |
57 | 50 |
58 | 51 |
59 class Describer(object): | 52 class Describer(object): |
60 def __init__(self, verbose=False, recursive=False): | 53 def __init__(self, verbose=False, recursive=False): |
61 self.verbose = verbose | 54 self.verbose = verbose |
62 self.recursive = recursive | 55 self.recursive = recursive |
63 | 56 |
64 def _DescribeSectionSizes(self, section_sizes): | 57 def _DescribeSectionSizes(self, section_sizes): |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 for index, s in enumerate(group): | 125 for index, s in enumerate(group): |
133 if group.IsBss() or not s.IsBss(): | 126 if group.IsBss() or not s.IsBss(): |
134 running_total += s.pss | 127 running_total += s.pss |
135 running_percent = _Divide(running_total, total) | 128 running_percent = _Divide(running_total, total) |
136 for l in self._DescribeSymbol(s, single_line=all_groups): | 129 for l in self._DescribeSymbol(s, single_line=all_groups): |
137 if l[:4].isspace(): | 130 if l[:4].isspace(): |
138 indent_size = 8 + len(indent_prefix) + len(diff_prefix) | 131 indent_size = 8 + len(indent_prefix) + len(diff_prefix) |
139 yield '{} {}'.format(' ' * indent_size, l) | 132 yield '{} {}'.format(' ' * indent_size, l) |
140 else: | 133 else: |
141 if is_diff: | 134 if is_diff: |
142 diff_prefix = _DiffPrefix(group, s) | 135 diff_prefix = _DIFF_PREFIX_BY_STATUS[group.DiffStatus(s)] |
143 yield '{}{}{:<4} {:>8} {:7} {}'.format( | 136 yield '{}{}{:<4} {:>8} {:7} {}'.format( |
144 indent_prefix, diff_prefix, str(index) + ')', | 137 indent_prefix, diff_prefix, str(index) + ')', |
145 _FormatPss(running_total), '({:.1%})'.format(running_percent), l) | 138 _FormatPss(running_total), '({:.1%})'.format(running_percent), l) |
146 | 139 |
147 if self.recursive and s.IsGroup(): | 140 if self.recursive and s.IsGroup(): |
148 for l in self._DescribeSymbolGroupChildren(s, indent=indent + 1): | 141 for l in self._DescribeSymbolGroupChildren(s, indent=indent + 1): |
149 yield l | 142 yield l |
150 | 143 |
151 def _DescribeSymbolGroup(self, group): | 144 def _DescribeSymbolGroup(self, group): |
152 total_size = group.pss | 145 total_size = group.pss |
(...skipping 22 matching lines...) Expand all Loading... |
175 _PrettySize(int(data_size)), _PrettySize(int(bss_size)), | 168 _PrettySize(int(data_size)), _PrettySize(int(bss_size)), |
176 _PrettySize(int(total_size))), | 169 _PrettySize(int(total_size))), |
177 'Number of unique paths: {}'.format(len(unique_paths)), | 170 'Number of unique paths: {}'.format(len(unique_paths)), |
178 '', | 171 '', |
179 'Index, Running Total, Section@Address, PSS', | 172 'Index, Running Total, Section@Address, PSS', |
180 '-' * 60 | 173 '-' * 60 |
181 ] | 174 ] |
182 children_desc = self._DescribeSymbolGroupChildren(group) | 175 children_desc = self._DescribeSymbolGroupChildren(group) |
183 return itertools.chain(header_desc, children_desc) | 176 return itertools.chain(header_desc, children_desc) |
184 | 177 |
| 178 def _DescribeDiffObjectPaths(self, diff): |
| 179 paths_by_status = [set(), set(), set(), set()] |
| 180 def helper(group): |
| 181 for s in group: |
| 182 if s.IsGroup(): |
| 183 helper(s) |
| 184 else: |
| 185 status = group.DiffStatus(s) |
| 186 paths_by_status[status].add(s.source_path or s.object_path) |
| 187 helper(diff) |
| 188 # Show only paths that have no changed symbols (pure adds / removes). |
| 189 unchanged, changed, added, removed = paths_by_status |
| 190 added.difference_update(unchanged) |
| 191 added.difference_update(changed) |
| 192 removed.difference_update(unchanged) |
| 193 removed.difference_update(changed) |
| 194 yield '{} paths added, {} removed, {} changed'.format( |
| 195 len(added), len(removed), len(changed)) |
| 196 |
| 197 if self.verbose and len(added): |
| 198 yield 'Added files:' |
| 199 for p in sorted(added): |
| 200 yield ' ' + p |
| 201 if self.verbose and len(removed): |
| 202 yield 'Removed files:' |
| 203 for p in sorted(removed): |
| 204 yield ' ' + p |
| 205 if self.verbose and len(changed): |
| 206 yield 'Changed files:' |
| 207 for p in sorted(changed): |
| 208 yield ' ' + p |
| 209 |
185 def _DescribeSymbolDiff(self, diff): | 210 def _DescribeSymbolDiff(self, diff): |
186 header_template = ('{} symbols added (+), {} changed (~), {} removed (-), ' | 211 header_template = ('{} symbols added (+), {} changed (~), {} removed (-), ' |
187 '{} unchanged ({})') | 212 '{} unchanged ({})') |
188 unchanged_msg = '=' if self.verbose else 'not shown' | 213 unchanged_msg = '=' if self.verbose else 'not shown' |
189 symbol_delta_desc = [header_template.format( | 214 symbol_delta_desc = [header_template.format( |
190 diff.added_count, diff.changed_count, diff.removed_count, | 215 diff.added_count, diff.changed_count, diff.removed_count, |
191 diff.unchanged_count, unchanged_msg)] | 216 diff.unchanged_count, unchanged_msg)] |
192 | 217 path_delta_desc = self._DescribeDiffObjectPaths(diff) |
193 similar_paths = set() | |
194 added_paths = set() | |
195 removed_paths = set() | |
196 for s in diff: | |
197 if diff.IsAdded(s): | |
198 added_paths.add(s.object_path) | |
199 elif diff.IsRemoved(s): | |
200 removed_paths.add(s.object_path) | |
201 else: | |
202 similar_paths.add(s.object_path) | |
203 added_paths.difference_update(similar_paths) | |
204 removed_paths.difference_update(similar_paths) | |
205 path_delta_desc = ['{} object files added, {} removed'.format( | |
206 len(added_paths), len(removed_paths))] | |
207 if self.verbose and len(added_paths): | |
208 path_delta_desc.append('Added files:') | |
209 path_delta_desc.extend(' ' + p for p in sorted(added_paths)) | |
210 if self.verbose and len(removed_paths): | |
211 path_delta_desc.append('Removed files:') | |
212 path_delta_desc.extend(' ' + p for p in sorted(removed_paths)) | |
213 | 218 |
214 diff = diff if self.verbose else diff.WhereNotUnchanged() | 219 diff = diff if self.verbose else diff.WhereNotUnchanged() |
215 group_desc = self._DescribeSymbolGroup(diff) | 220 group_desc = self._DescribeSymbolGroup(diff) |
216 return itertools.chain(symbol_delta_desc, path_delta_desc, ('',), | 221 return itertools.chain(symbol_delta_desc, path_delta_desc, ('',), |
217 group_desc) | 222 group_desc) |
218 | 223 |
219 def _DescribeSizeInfoDiff(self, diff): | 224 def _DescribeSizeInfoDiff(self, diff): |
220 common_metadata = {k: v for k, v in diff.before_metadata.iteritems() | 225 common_metadata = {k: v for k, v in diff.before_metadata.iteritems() |
221 if diff.after_metadata[k] == v} | 226 if diff.after_metadata[k] == v} |
222 before_metadata = {k: v for k, v in diff.before_metadata.iteritems() | 227 before_metadata = {k: v for k, v in diff.before_metadata.iteritems() |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 | 341 |
337 def GenerateLines(obj, verbose=False, recursive=False): | 342 def GenerateLines(obj, verbose=False, recursive=False): |
338 """Returns an iterable of lines (without \n) that describes |obj|.""" | 343 """Returns an iterable of lines (without \n) that describes |obj|.""" |
339 return Describer(verbose=verbose, recursive=recursive).GenerateLines(obj) | 344 return Describer(verbose=verbose, recursive=recursive).GenerateLines(obj) |
340 | 345 |
341 | 346 |
342 def WriteLines(lines, func): | 347 def WriteLines(lines, func): |
343 for l in lines: | 348 for l in lines: |
344 func(l) | 349 func(l) |
345 func('\n') | 350 func('\n') |
OLD | NEW |