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

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

Issue 2785483002: Reland of V2 of //tools/binary_size rewrite (diffs). (Closed)
Patch Set: add missing name= 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/binary_size_utils.py ('k') | tools/binary_size/create_html_breakdown.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
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
4 # found in the LICENSE file.
5
6 """Tool for analyzing binary size of executables using nm or linker map files.
7
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
10 the information does not seem to be as accurate in this case.
11
12 Inspired by SymbolSort for Windows:
13 https://github.com/adrianstone55/SymbolSort
14 """
15
16 import argparse
17 import atexit
18 import code
19 import contextlib
20 import itertools
21 import logging
22 import os
23 import readline
24 import subprocess
25 import sys
26
27 import describe
28 import file_format
29 import helpers
30 import map2size
31 import models
32
33
34 # Number of lines before using less for Print().
35 _THRESHOLD_FOR_PAGER = 30
36
37
38 @contextlib.contextmanager
39 def _LessPipe():
40 """Output to `less`. Yields a file object to write to."""
41 try:
42 proc = subprocess.Popen(['less'], stdin=subprocess.PIPE, stdout=sys.stdout)
43 yield proc.stdin
44 proc.stdin.close()
45
46 proc.wait()
47 except IOError:
48 pass # Happens when less is quit before all data is written.
49 except KeyboardInterrupt:
50 pass # Assume used to break out of less.
51
52
53 class _Session(object):
54 _readline_initialized = False
55
56 def __init__(self, extra_vars):
57 self._variables = {
58 'Print': self._PrintFunc,
59 'Write': self._WriteFunc,
60 'Diff': models.Diff,
61 }
62 self._variables.update(extra_vars)
63
64 def _PrintFunc(self, obj, verbose=False, use_pager=None):
65 """Prints out the given Symbol / SymbolGroup / SymbolDiff / SizeInfo.
66
67 Args:
68 obj: The object to be printed.
69 use_pager: Whether to pipe output through `less`. Ignored when |obj| is a
70 Symbol.
71 """
72 lines = describe.GenerateLines(obj, verbose=verbose)
73 if use_pager is None and sys.stdout.isatty():
74 # Does not take into account line-wrapping... Oh well.
75 first_lines = list(itertools.islice(lines, _THRESHOLD_FOR_PAGER))
76 if len(first_lines) == _THRESHOLD_FOR_PAGER:
77 use_pager = True
78 lines = itertools.chain(first_lines, lines)
79
80 if use_pager:
81 with _LessPipe() as stdin:
82 describe.WriteLines(lines, stdin.write)
83 else:
84 describe.WriteLines(lines, sys.stdout.write)
85
86
87 def _WriteFunc(self, obj, path, verbose=False):
88 """Same as Print(), but writes to a file.
89
90 Example: Write(Diff(size_info2, size_info1), 'output.txt')
91 """
92 parent_dir = os.path.dirname(path)
93 if parent_dir and not os.path.exists(parent_dir):
94 os.makedirs(parent_dir)
95 with file_format.OpenMaybeGz(path, 'w') as file_obj:
96 lines = describe.GenerateLines(obj, verbose=verbose)
97 describe.WriteLines(lines, file_obj.write)
98
99
100 def _CreateBanner(self):
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)
103 if m[0] != '_')
104 symbol_diff_keys = sorted(m for m in dir(models.SymbolDiff)
105 if m[0] != '_' and m not in symbol_group_keys)
106 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())
108 return '\n'.join([
109 '*' * 80,
110 'Entering interactive Python shell. Here is some inspiration:',
111 '',
112 '# Show pydoc for main types:',
113 'import models',
114 'help(models)',
115 '',
116 '# Show two levels of .text, grouped by first two subdirectories',
117 'text_syms = size_info1.symbols.WhereInSection("t")',
118 'by_path = text_syms.GroupByPath(depth=2)',
119 'Print(by_path.WhereBiggerThan(1024))',
120 '',
121 '# Show all non-vtable generated symbols',
122 'generated_syms = size_info1.symbols.WhereIsGenerated()',
123 'Print(generated_syms.WhereNameMatches("vtable").Inverted())',
124 '',
125 '*' * 80,
126 'Here is some quick reference:',
127 '',
128 'SizeInfo: %s' % ', '.join(symbol_info_keys),
129 'SymbolGroup: %s' % ', '.join(symbol_group_keys),
130 'SymbolDiff (extends SymbolGroup): %s' % ', '.join(symbol_diff_keys),
131 '',
132 'Functions: %s' % ', '.join('%s()' % f for f in functions),
133 'Variables: %s' % ', '.join(variables),
134 '',
135 ])
136
137 @classmethod
138 def _InitReadline(cls):
139 if cls._readline_initialized:
140 return
141 cls._readline_initialized = True
142 # Without initializing readline, arrow keys don't even work!
143 readline.parse_and_bind('tab: complete')
144 history_file = os.path.join(os.path.expanduser('~'),
145 '.binary_size_query_history')
146 if os.path.exists(history_file):
147 readline.read_history_file(history_file)
148 atexit.register(lambda: readline.write_history_file(history_file))
149
150 def Eval(self, query):
151 eval_result = eval(query, self._variables)
152 if eval_result:
153 self._PrintFunc(eval_result)
154
155 def GoInteractive(self):
156 _Session._InitReadline()
157 code.InteractiveConsole(self._variables).interact(self._CreateBanner())
158
159
160 def main(argv):
161 parser = argparse.ArgumentParser()
162 parser.add_argument('inputs', nargs='*',
163 help='Input .size/.map files to load. They will be '
164 'mapped to variables as: size_info1, size_info2,'
165 ' etc.')
166 parser.add_argument('--query',
167 help='Print the result of the given snippet. Example: '
168 'size_info1.symbols.WhereInSection("d").'
169 'WhereBiggerThan(100)')
170 map2size.AddOptions(parser)
171 args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
172
173 info_variables = {}
174 for i, path in enumerate(args.inputs):
175 size_info = map2size.AnalyzeWithArgs(args, path)
176 info_variables['size_info%d' % (i + 1)] = size_info
177
178 session = _Session(info_variables)
179
180 if args.query:
181 logging.info('Running query from command-line.')
182 session.Eval(args.query)
183 else:
184 logging.info('Entering interactive console.')
185 session.GoInteractive()
186
187
188 if __name__ == '__main__':
189 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « tools/binary_size/binary_size_utils.py ('k') | tools/binary_size/create_html_breakdown.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698