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

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

Issue 2795593005: //tools/binary_size: Various enhancements to console.py (Closed)
Patch Set: Review fixes 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/README.md ('k') | tools/binary_size/describe.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2017 The Chromium Authors. All rights reserved. 2 # Copyright 2017 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Tool for analyzing binary size of executables using nm or linker map files. 6 """Tool for analyzing binary size of executables using nm or linker map files.
7 7
8 Map files can be created by passing "-Map Foo.map" to the linker. If a map file 8 Map files can be created by passing "-Map Foo.map" to the linker. If a map file
9 is unavailable, this tool can also be pointed at an unstripped executable, but 9 is unavailable, this tool can also be pointed at an unstripped executable, but
10 the information does not seem to be as accurate in this case. 10 the information does not seem to be as accurate in this case.
(...skipping 10 matching lines...) Expand all
21 import logging 21 import logging
22 import os 22 import os
23 import readline 23 import readline
24 import subprocess 24 import subprocess
25 import sys 25 import sys
26 26
27 import describe 27 import describe
28 import file_format 28 import file_format
29 import helpers 29 import helpers
30 import map2size 30 import map2size
31 import match_util
31 import models 32 import models
32 33
33 34
34 # Number of lines before using less for Print(). 35 # Number of lines before using less for Print().
35 _THRESHOLD_FOR_PAGER = 30 36 _THRESHOLD_FOR_PAGER = 30
36 37
37 38
38 @contextlib.contextmanager 39 @contextlib.contextmanager
39 def _LessPipe(): 40 def _LessPipe():
40 """Output to `less`. Yields a file object to write to.""" 41 """Output to `less`. Yields a file object to write to."""
(...skipping 10 matching lines...) Expand all
51 52
52 53
53 class _Session(object): 54 class _Session(object):
54 _readline_initialized = False 55 _readline_initialized = False
55 56
56 def __init__(self, extra_vars): 57 def __init__(self, extra_vars):
57 self._variables = { 58 self._variables = {
58 'Print': self._PrintFunc, 59 'Print': self._PrintFunc,
59 'Write': self._WriteFunc, 60 'Write': self._WriteFunc,
60 'Diff': models.Diff, 61 'Diff': models.Diff,
62 'ExpandRegex': match_util.ExpandRegexIdentifierPlaceholder,
61 } 63 }
62 self._variables.update(extra_vars) 64 self._variables.update(extra_vars)
63 65
64 def _PrintFunc(self, obj, verbose=False, use_pager=None): 66 def _PrintFunc(self, obj, verbose=False, use_pager=None):
65 """Prints out the given Symbol / SymbolGroup / SymbolDiff / SizeInfo. 67 """Prints out the given Symbol / SymbolGroup / SymbolDiff / SizeInfo.
66 68
67 Args: 69 Args:
68 obj: The object to be printed. 70 obj: The object to be printed.
69 use_pager: Whether to pipe output through `less`. Ignored when |obj| is a 71 use_pager: Whether to pipe output through `less`. Ignored when |obj| is a
70 Symbol. 72 Symbol.
71 """ 73 """
72 lines = describe.GenerateLines(obj, verbose=verbose) 74 lines = describe.GenerateLines(obj, verbose=verbose)
73 if use_pager is None and sys.stdout.isatty(): 75 if use_pager is None and sys.stdout.isatty():
74 # Does not take into account line-wrapping... Oh well. 76 # Does not take into account line-wrapping... Oh well.
75 first_lines = list(itertools.islice(lines, _THRESHOLD_FOR_PAGER)) 77 first_lines = list(itertools.islice(lines, _THRESHOLD_FOR_PAGER))
76 if len(first_lines) == _THRESHOLD_FOR_PAGER: 78 if len(first_lines) == _THRESHOLD_FOR_PAGER:
77 use_pager = True 79 use_pager = True
78 lines = itertools.chain(first_lines, lines) 80 lines = itertools.chain(first_lines, lines)
79 81
80 if use_pager: 82 if use_pager:
81 with _LessPipe() as stdin: 83 with _LessPipe() as stdin:
82 describe.WriteLines(lines, stdin.write) 84 describe.WriteLines(lines, stdin.write)
83 else: 85 else:
84 describe.WriteLines(lines, sys.stdout.write) 86 describe.WriteLines(lines, sys.stdout.write)
85 87
86
87 def _WriteFunc(self, obj, path, verbose=False): 88 def _WriteFunc(self, obj, path, verbose=False):
88 """Same as Print(), but writes to a file. 89 """Same as Print(), but writes to a file.
89 90
90 Example: Write(Diff(size_info2, size_info1), 'output.txt') 91 Example: Write(Diff(size_info2, size_info1), 'output.txt')
91 """ 92 """
92 parent_dir = os.path.dirname(path) 93 parent_dir = os.path.dirname(path)
93 if parent_dir and not os.path.exists(parent_dir): 94 if parent_dir and not os.path.exists(parent_dir):
94 os.makedirs(parent_dir) 95 os.makedirs(parent_dir)
95 with file_format.OpenMaybeGz(path, 'w') as file_obj: 96 with file_format.OpenMaybeGz(path, 'w') as file_obj:
96 lines = describe.GenerateLines(obj, verbose=verbose) 97 lines = describe.GenerateLines(obj, verbose=verbose)
97 describe.WriteLines(lines, file_obj.write) 98 describe.WriteLines(lines, file_obj.write)
98 99
99
100 def _CreateBanner(self): 100 def _CreateBanner(self):
101 symbol_info_keys = sorted(m for m in dir(models.SizeInfo) if m[0] != '_') 101 symbol_info_keys = sorted(m for m in dir(models.SizeInfo) if m[0] != '_')
102 symbol_group_keys = sorted(m for m in dir(models.SymbolGroup) 102 symbol_keys = sorted(m for m in dir(models.Symbol) if m[0] != '_')
103 if m[0] != '_') 103 symbol_group_keys = [m for m in dir(models.SymbolGroup) if m[0] != '_']
104 symbol_diff_keys = sorted(m for m in dir(models.SymbolDiff) 104 symbol_diff_keys = sorted(m for m in dir(models.SymbolDiff)
105 if m[0] != '_' and m not in symbol_group_keys) 105 if m[0] != '_' and m not in symbol_group_keys)
106 symbol_group_keys = sorted(m for m in symbol_group_keys
107 if m not in symbol_keys)
106 functions = sorted(k for k in self._variables if k[0].isupper()) 108 functions = sorted(k for k in self._variables if k[0].isupper())
107 variables = sorted(k for k in self._variables if k[0].islower()) 109 variables = sorted(k for k in self._variables if k[0].islower())
108 return '\n'.join([ 110 return '\n'.join([
109 '*' * 80, 111 '*' * 80,
110 'Entering interactive Python shell. Here is some inspiration:', 112 'Entering interactive Python shell. Here is some inspiration:',
111 '', 113 '',
112 '# Show pydoc for main types:', 114 '# Show pydoc for main types:',
113 'import models', 115 'import models',
114 'help(models)', 116 'help(models)',
115 '', 117 '',
118 '# Show all attributes of all symbols & per-section totals:',
119 'Print(size_info, verbose=True)',
120 '',
116 '# Show two levels of .text, grouped by first two subdirectories', 121 '# Show two levels of .text, grouped by first two subdirectories',
117 'text_syms = size_info1.symbols.WhereInSection("t")', 122 'text_syms = symbols.WhereInSection("t")',
118 'by_path = text_syms.GroupBySourcePath(depth=2)', 123 'by_path = text_syms.GroupBySourcePath(depth=2)',
119 'Print(by_path.WhereBiggerThan(1024))', 124 'Print(by_path.WhereBiggerThan(1024))',
120 '', 125 '',
121 '# Show all non-vtable generated symbols', 126 '# Show all non-vtable generated symbols',
122 'generated_syms = size_info1.symbols.WhereIsGenerated()', 127 'generated_syms = symbols.WhereIsGenerated()',
123 'Print(generated_syms.WhereNameMatches("vtable").Inverted())', 128 'Print(generated_syms.WhereNameMatches(r"vtable").Inverted())',
129 '',
130 '# Show all symbols that have "print" in their name or path, except',
131 '# those within components/.',
132 '# Note: Could have also used Inverted(), as above.',
133 '# Note: Use "help(ExpandRegex)" for more about what {{_print_}} does.',
134 'print_syms = symbols.WhereMatches(r"{{_print_}}")',
135 'Print(print_syms - print_syms.WherePathMatches(r"^components/"))',
136 '',
137 '# Diff two .size files:',
138 'Print(Diff(size_info1, size_info2))',
124 '', 139 '',
125 '*' * 80, 140 '*' * 80,
126 'Here is some quick reference:', 141 'Here is some quick reference:',
127 '', 142 '',
128 'SizeInfo: %s' % ', '.join(symbol_info_keys), 143 'SizeInfo: %s' % ', '.join(symbol_info_keys),
129 'SymbolGroup: %s' % ', '.join(symbol_group_keys), 144 'Symbol: %s' % ', '.join(symbol_keys),
145 'SymbolGroup (extends Symbol): %s' % ', '.join(symbol_group_keys),
130 'SymbolDiff (extends SymbolGroup): %s' % ', '.join(symbol_diff_keys), 146 'SymbolDiff (extends SymbolGroup): %s' % ', '.join(symbol_diff_keys),
131 '', 147 '',
132 'Functions: %s' % ', '.join('%s()' % f for f in functions), 148 'Functions: %s' % ', '.join('%s()' % f for f in functions),
133 'Variables: %s' % ', '.join(variables), 149 'Variables: %s' % ', '.join(variables),
134 '', 150 '',
135 ]) 151 ])
136 152
137 @classmethod 153 @classmethod
138 def _InitReadline(cls): 154 def _InitReadline(cls):
139 if cls._readline_initialized: 155 if cls._readline_initialized:
(...skipping 13 matching lines...) Expand all
153 self._PrintFunc(eval_result) 169 self._PrintFunc(eval_result)
154 170
155 def GoInteractive(self): 171 def GoInteractive(self):
156 _Session._InitReadline() 172 _Session._InitReadline()
157 code.InteractiveConsole(self._variables).interact(self._CreateBanner()) 173 code.InteractiveConsole(self._variables).interact(self._CreateBanner())
158 174
159 175
160 def main(argv): 176 def main(argv):
161 parser = argparse.ArgumentParser() 177 parser = argparse.ArgumentParser()
162 parser.add_argument('inputs', nargs='*', 178 parser.add_argument('inputs', nargs='*',
163 help='Input .size/.map files to load. They will be ' 179 help='Input .size/.map files to load. For a single file, '
164 'mapped to variables as: size_info1, size_info2,' 180 'it will be mapped to variables as: size_info & '
165 ' etc.') 181 'symbols (where symbols = size_info.symbols). For '
182 'multiple inputs, the names will be size_info1, '
183 'symbols1, etc.')
166 parser.add_argument('--query', 184 parser.add_argument('--query',
167 help='Print the result of the given snippet. Example: ' 185 help='Print the result of the given snippet. Example: '
168 'size_info1.symbols.WhereInSection("d").' 186 'symbols.WhereInSection("d").'
169 'WhereBiggerThan(100)') 187 'WhereBiggerThan(100)')
170 map2size.AddOptions(parser) 188 map2size.AddOptions(parser)
171 args = helpers.AddCommonOptionsAndParseArgs(parser, argv) 189 args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
172 190
173 info_variables = {} 191 variables = {}
174 for i, path in enumerate(args.inputs): 192 for i, path in enumerate(args.inputs):
175 size_info = map2size.AnalyzeWithArgs(args, path) 193 size_info = map2size.AnalyzeWithArgs(args, path)
176 info_variables['size_info%d' % (i + 1)] = size_info 194 if len(args.inputs) == 1:
195 variables['size_info'] = size_info
196 variables['symbols'] = size_info.symbols
197 else:
198 variables['size_info%d' % (i + 1)] = size_info
199 variables['symbols%d' % (i + 1)] = size_info.symbols
177 200
178 session = _Session(info_variables) 201 session = _Session(variables)
179 202
180 if args.query: 203 if args.query:
181 logging.info('Running query from command-line.') 204 logging.info('Running query from command-line.')
182 session.Eval(args.query) 205 session.Eval(args.query)
183 else: 206 else:
184 logging.info('Entering interactive console.') 207 logging.info('Entering interactive console.')
185 session.GoInteractive() 208 session.GoInteractive()
186 209
187 210
188 if __name__ == '__main__': 211 if __name__ == '__main__':
189 sys.exit(main(sys.argv)) 212 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « tools/binary_size/README.md ('k') | tools/binary_size/describe.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698