| 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 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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') |
| OLD | NEW |