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

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

Issue 2813963002: //tools/binary_size: Consolidate most tools into "supersize" command (Closed)
Patch Set: Fix readme formatting. Make archive's --outoput-file a positional arg 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/console.py ('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
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2014 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 """Creates an html report that allows you to view binary size by component."""
7
8 import argparse
9 import json
10 import logging
11 import os
12 import shutil
13 import sys
14
15 import helpers
16 import map2size
17
18
19 # Node dictionary keys. These are output in json read by the webapp so
20 # keep them short to save file size.
21 # Note: If these change, the webapp must also change.
22 _NODE_TYPE_KEY = 'k'
23 _NODE_TYPE_BUCKET = 'b'
24 _NODE_TYPE_PATH = 'p'
25 _NODE_TYPE_SYMBOL = 's'
26 _NODE_NAME_KEY = 'n'
27 _NODE_CHILDREN_KEY = 'children'
28 _NODE_SYMBOL_TYPE_KEY = 't'
29 _NODE_SYMBOL_TYPE_VTABLE = 'v'
30 _NODE_SYMBOL_TYPE_GENERATED = '*'
31 _NODE_SYMBOL_SIZE_KEY = 'value'
32 _NODE_MAX_DEPTH_KEY = 'maxDepth'
33 _NODE_LAST_PATH_ELEMENT_KEY = 'lastPathElement'
34
35 # The display name of the bucket where we put symbols without path.
36 _NAME_NO_PATH_BUCKET = '(No Path)'
37
38 # Try to keep data buckets smaller than this to avoid killing the
39 # graphing lib.
40 _BIG_BUCKET_LIMIT = 3000
41
42
43 def _GetOrMakeChildNode(node, node_type, name):
44 child = node[_NODE_CHILDREN_KEY].get(name)
45 if child is None:
46 child = {
47 _NODE_TYPE_KEY: node_type,
48 _NODE_NAME_KEY: name,
49 }
50 if node_type != _NODE_TYPE_SYMBOL:
51 child[_NODE_CHILDREN_KEY] = {}
52 node[_NODE_CHILDREN_KEY][name] = child
53 else:
54 assert child[_NODE_TYPE_KEY] == node_type
55 return child
56
57
58 def _SplitLargeBucket(bucket):
59 """Split the given node into sub-buckets when it's too big."""
60 old_children = bucket[_NODE_CHILDREN_KEY]
61 count = 0
62 for symbol_type, symbol_bucket in old_children.iteritems():
63 count += len(symbol_bucket[_NODE_CHILDREN_KEY])
64 if count > _BIG_BUCKET_LIMIT:
65 new_children = {}
66 bucket[_NODE_CHILDREN_KEY] = new_children
67 current_bucket = None
68 index = 0
69 for symbol_type, symbol_bucket in old_children.iteritems():
70 for symbol_name, value in symbol_bucket[_NODE_CHILDREN_KEY].iteritems():
71 if index % _BIG_BUCKET_LIMIT == 0:
72 group_no = (index / _BIG_BUCKET_LIMIT) + 1
73 node_name = '%s subgroup %d' % (_NAME_NO_PATH_BUCKET, group_no)
74 current_bucket = _GetOrMakeChildNode(
75 bucket, _NODE_TYPE_PATH, node_name)
76 index += 1
77 symbol_size = value[_NODE_SYMBOL_SIZE_KEY]
78 _AddSymbolIntoFileNode(current_bucket, symbol_type, symbol_name,
79 symbol_size, True)
80
81
82 def _MakeChildrenDictsIntoLists(node):
83 """Recursively converts all children from dicts -> lists."""
84 children = node.get(_NODE_CHILDREN_KEY)
85 if children:
86 children = children.values() # Convert dict -> list.
87 node[_NODE_CHILDREN_KEY] = children
88 for child in children:
89 _MakeChildrenDictsIntoLists(child)
90 if len(children) > _BIG_BUCKET_LIMIT:
91 logging.warning('Bucket found with %d entries. Might be unusable.',
92 len(children))
93
94
95 def _AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size,
96 include_symbols):
97 """Puts symbol into the file path node |node|."""
98 node[_NODE_LAST_PATH_ELEMENT_KEY] = True
99 # Don't bother with buckets when not including symbols.
100 if include_symbols:
101 node = _GetOrMakeChildNode(node, _NODE_TYPE_BUCKET, symbol_type)
102 node[_NODE_SYMBOL_TYPE_KEY] = symbol_type
103
104 # 'node' is now the symbol-type bucket. Make the child entry.
105 if include_symbols or not symbol_name:
106 node_name = symbol_name or '[Anonymous]'
107 elif symbol_name.startswith('*'):
108 node_name = symbol_name
109 else:
110 node_name = symbol_type
111 node = _GetOrMakeChildNode(node, _NODE_TYPE_SYMBOL, node_name)
112 node[_NODE_SYMBOL_SIZE_KEY] = node.get(_NODE_SYMBOL_SIZE_KEY, 0) + symbol_size
113 node[_NODE_SYMBOL_TYPE_KEY] = symbol_type
114
115
116 def _MakeCompactTree(symbols, include_symbols):
117 result = {
118 _NODE_NAME_KEY: '/',
119 _NODE_CHILDREN_KEY: {},
120 _NODE_TYPE_KEY: 'p',
121 _NODE_MAX_DEPTH_KEY: 0,
122 }
123 for symbol in symbols:
124 file_path = symbol.source_path or symbol.object_path or _NAME_NO_PATH_BUCKET
125 node = result
126 depth = 0
127 for path_part in file_path.split(os.path.sep):
128 if not path_part:
129 continue
130 depth += 1
131 node = _GetOrMakeChildNode(node, _NODE_TYPE_PATH, path_part)
132
133 symbol_type = symbol.section
134 if symbol.name.endswith('[vtable]'):
135 symbol_type = _NODE_SYMBOL_TYPE_VTABLE
136 elif symbol.name.endswith(']'):
137 symbol_type = _NODE_SYMBOL_TYPE_GENERATED
138 _AddSymbolIntoFileNode(node, symbol_type, symbol.name, symbol.size,
139 include_symbols)
140 depth += 2
141 result[_NODE_MAX_DEPTH_KEY] = max(result[_NODE_MAX_DEPTH_KEY], depth)
142
143 # The (no path) bucket can be extremely large if we failed to get
144 # path information. Split it into subgroups if needed.
145 no_path_bucket = result[_NODE_CHILDREN_KEY].get(_NAME_NO_PATH_BUCKET)
146 if no_path_bucket and include_symbols:
147 _SplitLargeBucket(no_path_bucket)
148
149 _MakeChildrenDictsIntoLists(result)
150
151 return result
152
153
154 def _CopyTemplateFiles(dest_dir):
155 d3_out = os.path.join(dest_dir, 'd3')
156 if not os.path.exists(d3_out):
157 os.makedirs(d3_out, 0755)
158 d3_src = os.path.join(helpers.SRC_ROOT, 'third_party', 'd3', 'src')
159 template_src = os.path.join(os.path.dirname(__file__), 'template')
160 shutil.copy(os.path.join(d3_src, 'LICENSE'), d3_out)
161 shutil.copy(os.path.join(d3_src, 'd3.js'), d3_out)
162 shutil.copy(os.path.join(template_src, 'index.html'), dest_dir)
163 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), dest_dir)
164
165
166 def main(argv):
167 parser = argparse.ArgumentParser()
168 parser.add_argument('input_file',
169 help='Path to input .size file.')
170 parser.add_argument('--report-dir', metavar='PATH', required=True,
171 help='Write output to the specified directory. An HTML '
172 'report is generated here.')
173 parser.add_argument('--include-bss', action='store_true',
174 help='Include symbols from .bss (which consume no real '
175 'space)')
176 parser.add_argument('--include-symbols', action='store_true',
177 help='Use per-symbol granularity rather than per-file.')
178 args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
179
180 size_info = map2size.LoadAndPostProcessSizeInfo(args.input_file)
181 symbols = size_info.symbols
182 if not args.include_bss:
183 symbols = symbols.WhereInSection('b').Inverted()
184 symbols = symbols.WhereBiggerThan(0)
185
186 # Copy report boilerplate into output directory. This also proves that the
187 # output directory is safe for writing, so there should be no problems writing
188 # the nm.out file later.
189 _CopyTemplateFiles(args.report_dir)
190
191 logging.info('Creating JSON objects')
192 tree_root = _MakeCompactTree(symbols, args.include_symbols)
193
194 logging.info('Serializing')
195 with open(os.path.join(args.report_dir, 'data.js'), 'w') as out_file:
196 out_file.write('var tree_data=')
197 # Use separators without whitespace to get a smaller file.
198 json.dump(tree_root, out_file, ensure_ascii=False, check_circular=False,
199 separators=(',', ':'))
200
201 print 'Report saved to ' + args.report_dir + '/index.html'
202
203
204 if __name__ == '__main__':
205 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « tools/binary_size/console.py ('k') | tools/binary_size/describe.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698