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 | 4 |
5 """An interactive console for looking analyzing .size files.""" | 5 """An interactive console for looking analyzing .size files.""" |
6 | 6 |
7 import argparse | 7 import argparse |
8 import atexit | 8 import atexit |
9 import code | 9 import code |
10 import contextlib | 10 import contextlib |
11 import itertools | 11 import itertools |
12 import logging | 12 import logging |
13 import os | 13 import os |
14 import readline | 14 import readline |
15 import subprocess | 15 import subprocess |
16 import sys | 16 import sys |
17 | 17 |
18 import archive | 18 import archive |
19 import describe | 19 import describe |
20 import diff | 20 import diff |
21 import file_format | 21 import file_format |
22 import match_util | 22 import match_util |
23 import models | 23 import models |
24 import paths | 24 import paths |
| 25 import precanned_queries |
25 | 26 |
26 | 27 |
27 # Number of lines before using less for Print(). | 28 # Number of lines before using less for Print(). |
28 _THRESHOLD_FOR_PAGER = 30 | 29 _THRESHOLD_FOR_PAGER = 30 |
29 | 30 |
30 | 31 |
31 @contextlib.contextmanager | 32 @contextlib.contextmanager |
32 def _LessPipe(): | 33 def _LessPipe(): |
33 """Output to `less`. Yields a file object to write to.""" | 34 """Output to `less`. Yields a file object to write to.""" |
34 try: | 35 try: |
(...skipping 26 matching lines...) Expand all Loading... |
61 else: | 62 else: |
62 describe.WriteLines(lines, sys.stdout.write) | 63 describe.WriteLines(lines, sys.stdout.write) |
63 | 64 |
64 | 65 |
65 class _Session(object): | 66 class _Session(object): |
66 _readline_initialized = False | 67 _readline_initialized = False |
67 | 68 |
68 def __init__(self, size_infos, lazy_paths): | 69 def __init__(self, size_infos, lazy_paths): |
69 self._variables = { | 70 self._variables = { |
70 'Print': self._PrintFunc, | 71 'Print': self._PrintFunc, |
71 'Diff': diff.Diff, | 72 'Diff': self._DiffFunc, |
| 73 'CategorizeByChromeComponent': self._CategorizeByChromeComponentFunc, |
| 74 'CategorizeGenerated': self._CategorizeGeneratedFunc, |
72 'Disassemble': self._DisassembleFunc, | 75 'Disassemble': self._DisassembleFunc, |
73 'ExpandRegex': match_util.ExpandRegexIdentifierPlaceholder, | 76 'ExpandRegex': match_util.ExpandRegexIdentifierPlaceholder, |
74 'ShowExamples': self._ShowExamplesFunc, | 77 'ShowExamples': self._ShowExamplesFunc, |
75 } | 78 } |
76 self._lazy_paths = lazy_paths | 79 self._lazy_paths = lazy_paths |
77 self._size_infos = size_infos | 80 self._size_infos = size_infos |
78 | 81 |
79 if len(size_infos) == 1: | 82 if len(size_infos) == 1: |
80 self._variables['size_info'] = size_infos[0] | 83 self._variables['size_info'] = size_infos[0] |
81 else: | 84 else: |
82 for i, size_info in enumerate(size_infos): | 85 for i, size_info in enumerate(size_infos): |
83 self._variables['size_info%d' % (i + 1)] = size_info | 86 self._variables['size_info%d' % (i + 1)] = size_info |
84 | 87 |
85 def _PrintFunc(self, obj, verbose=False, recursive=False, use_pager=None, | 88 def _DiffFunc(before=None, after=None, cluster=True): |
| 89 """Diffs two SizeInfo objects. Returns a SizeInfoDiff. |
| 90 |
| 91 Args: |
| 92 before: Defaults to first size_infos[0]. |
| 93 after: Defaults to second size_infos[1]. |
| 94 cluster: When True, calls SymbolGroup.Cluster() after diffing. This |
| 95 generally reduces noise. |
| 96 """ |
| 97 before = before if before is not None else self._size_infos[0] |
| 98 after = after if after is not None else self._size_infos[1] |
| 99 return diff.Diff(before, after, cluster=cluster) |
| 100 |
| 101 def _PrintFunc(self, obj=None, verbose=False, recursive=False, use_pager=None, |
86 to_file=None): | 102 to_file=None): |
87 """Prints out the given Symbol / SymbolGroup / SymbolDiff / SizeInfo. | 103 """Prints out the given Symbol / SymbolGroup / SymbolDiff / SizeInfo. |
88 | 104 |
89 Args: | 105 Args: |
90 obj: The object to be printed. | 106 obj: The object to be printed. Defaults to size_infos[-1]. |
91 verbose: Show more detailed output. | 107 verbose: Show more detailed output. |
92 recursive: Print children of nested SymbolGroups. | 108 recursive: Print children of nested SymbolGroups. |
93 use_pager: Pipe output through `less`. Ignored when |obj| is a Symbol. | 109 use_pager: Pipe output through `less`. Ignored when |obj| is a Symbol. |
94 default is to automatically pipe when output is long. | 110 default is to automatically pipe when output is long. |
95 to_file: Rather than print to stdio, write to the given file. | 111 to_file: Rather than print to stdio, write to the given file. |
96 """ | 112 """ |
| 113 obj = obj if obj is not None else self._size_infos[-1] |
97 lines = describe.GenerateLines(obj, verbose=verbose, recursive=recursive) | 114 lines = describe.GenerateLines(obj, verbose=verbose, recursive=recursive) |
98 _WriteToStream(lines, use_pager=use_pager, to_file=to_file) | 115 _WriteToStream(lines, use_pager=use_pager, to_file=to_file) |
99 | 116 |
| 117 def _CategorizeByChromeComponentFunc(self, arg=None): |
| 118 """Groups symbols by component using predefined queries. |
| 119 |
| 120 Args: |
| 121 arg: A SizeInfo or SymbolGroup. Defaults to the size_infos[-1]. |
| 122 """ |
| 123 arg = arg if arg is not None else self._size_infos[-1] |
| 124 if isinstance(arg, models.SizeInfo): |
| 125 arg = arg.symbols |
| 126 return precanned_queries.CategorizeByChromeComponent(arg) |
| 127 |
| 128 def _CategorizeGeneratedFunc(self, arg=None): |
| 129 """Categorizes symbols from generated sources. |
| 130 |
| 131 Args: |
| 132 arg: A SizeInfo or SymbolGroup. Defaults to the size_infos[-1]. |
| 133 """ |
| 134 arg = arg if arg is not None else self._size_infos[-1] |
| 135 if isinstance(arg, models.SizeInfo): |
| 136 arg = arg.symbols |
| 137 return precanned_queries.CategorizeGenerated(arg) |
| 138 |
| 139 |
100 def _ElfPathForSymbol(self, symbol): | 140 def _ElfPathForSymbol(self, symbol): |
101 size_info = None | 141 size_info = None |
102 for size_info in self._size_infos: | 142 for size_info in self._size_infos: |
103 if symbol in size_info.symbols: | 143 if symbol in size_info.symbols: |
104 break | 144 break |
105 else: | 145 else: |
106 assert False, 'Symbol does not belong to a size_info.' | 146 assert False, 'Symbol does not belong to a size_info.' |
107 | 147 |
108 filename = size_info.metadata.get(models.METADATA_ELF_FILENAME) | 148 filename = size_info.metadata.get(models.METADATA_ELF_FILENAME) |
109 output_dir = self._lazy_paths.output_directory or '' | 149 output_dir = self._lazy_paths.output_directory or '' |
(...skipping 23 matching lines...) Expand all Loading... |
133 '--start-address=0x%x' % symbol.address, | 173 '--start-address=0x%x' % symbol.address, |
134 '--stop-address=0x%x' % symbol.end_address, elf_path] | 174 '--stop-address=0x%x' % symbol.end_address, elf_path] |
135 proc = subprocess.Popen(args, stdout=subprocess.PIPE) | 175 proc = subprocess.Popen(args, stdout=subprocess.PIPE) |
136 lines = itertools.chain(('Showing disassembly for %r' % symbol, | 176 lines = itertools.chain(('Showing disassembly for %r' % symbol, |
137 'Command: %s' % ' '.join(args)), | 177 'Command: %s' % ' '.join(args)), |
138 (l.rstrip() for l in proc.stdout)) | 178 (l.rstrip() for l in proc.stdout)) |
139 _WriteToStream(lines, use_pager=use_pager, to_file=to_file) | 179 _WriteToStream(lines, use_pager=use_pager, to_file=to_file) |
140 proc.kill() | 180 proc.kill() |
141 | 181 |
142 def _ShowExamplesFunc(self): | 182 def _ShowExamplesFunc(self): |
| 183 print self._CreateBanner() |
143 print '\n'.join([ | 184 print '\n'.join([ |
144 '# Show pydoc for main types:', | 185 '# Show pydoc for main types:', |
145 'import models', | 186 'import models', |
146 'help(models)', | 187 'help(models)', |
147 '', | 188 '', |
148 '# Show all attributes of all symbols & per-section totals:', | 189 '# Show all attributes of all symbols & per-section totals:', |
149 'Print(size_info, verbose=True)', | 190 'Print(size_info, verbose=True)', |
150 '', | 191 '', |
151 '# Show two levels of .text, grouped by first two subdirectories', | 192 '# Show two levels of .text, grouped by first two subdirectories', |
152 'text_syms = size_info.symbols.WhereInSection("t")', | 193 'text_syms = size_info.symbols.WhereInSection("t")', |
153 'by_path = text_syms.GroupBySourcePath(depth=2)', | 194 'by_path = text_syms.GroupByPath(depth=2)', |
154 'Print(by_path.WhereBiggerThan(1024))', | 195 'Print(by_path.WhereBiggerThan(1024))', |
155 '', | 196 '', |
156 '# Show all non-vtable generated symbols', | 197 '# Show all non-vtable generated symbols', |
157 'generated_syms = size_info.symbols.WhereIsGenerated()', | 198 'generated_syms = size_info.symbols.WhereGeneratedByToolchain()', |
158 'Print(generated_syms.WhereNameMatches(r"vtable").Inverted())', | 199 'Print(generated_syms.WhereNameMatches(r"vtable").Inverted().Sorted())', |
159 '', | 200 '', |
160 '# Show all symbols that have "print" in their name or path, except', | 201 '# Show all symbols that have "print" in their name or path, except', |
161 '# those within components/.', | 202 '# those within components/.', |
162 '# Note: Could have also used Inverted(), as above.', | 203 '# Note: Could have also used Inverted(), as above.', |
163 '# Note: Use "help(ExpandRegex)" for more about what {{_print_}} does.', | 204 '# Note: Use "help(ExpandRegex)" for more about what {{_print_}} does.', |
164 'print_syms = size_info.symbols.WhereMatches(r"{{_print_}}")', | 205 'print_syms = size_info.symbols.WhereMatches(r"{{_print_}}")', |
165 'Print(print_syms - print_syms.WherePathMatches(r"^components/"))', | 206 'Print(print_syms - print_syms.WherePathMatches(r"^components/"))', |
166 '', | 207 '', |
167 '# Diff two .size files and save result to a file:', | 208 '# Diff two .size files and save result to a file:', |
168 'Print(Diff(size_info1, size_info2), to_file="output.txt")', | 209 'Print(Diff(size_info1, size_info2), to_file="output.txt")', |
169 '', | 210 '', |
| 211 '# View per-component breakdowns, then inspect the "other" subgroup.', |
| 212 'c = GroupByChromeComponent()', |
| 213 'Print(c)', |
| 214 'Print(c[-1].GroupByPath(depth=2))', |
170 ]) | 215 ]) |
171 | 216 |
172 def _CreateBanner(self): | 217 def _CreateBanner(self): |
173 symbol_info_keys = sorted(m for m in dir(models.SizeInfo) if m[0] != '_') | 218 symbol_info_keys = sorted(m for m in dir(models.SizeInfo) if m[0] != '_') |
174 symbol_keys = sorted(m for m in dir(models.Symbol) if m[0] != '_') | 219 symbol_keys = sorted(m for m in dir(models.Symbol) if m[0] != '_') |
175 symbol_group_keys = [m for m in dir(models.SymbolGroup) if m[0] != '_'] | 220 symbol_group_keys = [m for m in dir(models.SymbolGroup) if m[0] != '_'] |
176 symbol_diff_keys = sorted(m for m in dir(models.SymbolDiff) | 221 symbol_diff_keys = sorted(m for m in dir(models.SymbolDiff) |
177 if m[0] != '_' and m not in symbol_group_keys) | 222 if m[0] != '_' and m not in symbol_group_keys) |
178 symbol_group_keys = sorted(m for m in symbol_group_keys | 223 symbol_group_keys = sorted(m for m in symbol_group_keys |
179 if m not in symbol_keys) | 224 if m not in symbol_keys) |
180 functions = sorted(k for k in self._variables if k[0].isupper()) | 225 functions = sorted(k for k in self._variables if k[0].isupper()) |
181 variables = sorted(k for k in self._variables if k[0].islower()) | 226 variables = sorted(k for k in self._variables if k[0].islower()) |
182 return '\n'.join([ | 227 return '\n'.join([ |
183 '*' * 80, | 228 '*' * 80, |
184 'Entering interactive Python shell. Quick reference:', | 229 'Entering interactive Python shell. Quick reference:', |
185 '', | 230 '', |
186 'SizeInfo: %s' % ', '.join(symbol_info_keys), | 231 'SizeInfo: %s' % ', '.join(symbol_info_keys), |
187 'Symbol: %s' % ', '.join(symbol_keys), | 232 'Symbol: %s' % ', '.join(symbol_keys), |
| 233 '', |
188 'SymbolGroup (extends Symbol): %s' % ', '.join(symbol_group_keys), | 234 'SymbolGroup (extends Symbol): %s' % ', '.join(symbol_group_keys), |
| 235 '', |
189 'SymbolDiff (extends SymbolGroup): %s' % ', '.join(symbol_diff_keys), | 236 'SymbolDiff (extends SymbolGroup): %s' % ', '.join(symbol_diff_keys), |
190 '', | 237 '', |
191 'Functions: %s' % ', '.join('%s()' % f for f in functions), | 238 'Functions: %s' % ', '.join('%s()' % f for f in functions), |
192 'Variables: %s' % ', '.join(variables), | 239 'Variables: %s' % ', '.join(variables), |
193 '*' * 80, | 240 '*' * 80, |
194 ]) | 241 ]) |
195 | 242 |
196 @classmethod | 243 @classmethod |
197 def _InitReadline(cls): | 244 def _InitReadline(cls): |
198 if cls._readline_initialized: | 245 if cls._readline_initialized: |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 output_directory=args.output_directory, | 288 output_directory=args.output_directory, |
242 any_path_within_output_directory=args.inputs[0]) | 289 any_path_within_output_directory=args.inputs[0]) |
243 session = _Session(size_infos, lazy_paths) | 290 session = _Session(size_infos, lazy_paths) |
244 | 291 |
245 if args.query: | 292 if args.query: |
246 logging.info('Running query from command-line.') | 293 logging.info('Running query from command-line.') |
247 session.Eval(args.query) | 294 session.Eval(args.query) |
248 else: | 295 else: |
249 logging.info('Entering interactive console.') | 296 logging.info('Entering interactive console.') |
250 session.GoInteractive() | 297 session.GoInteractive() |
OLD | NEW |