| 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 |