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

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

Issue 302443006: binary_size: Avoid creating nodes with many thousand children. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 6 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 | « no previous file | no next file » | 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 2014 The Chromium Authors. All rights reserved. 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 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 """Generate a spatial analysis against an arbitrary library. 6 """Generate a spatial analysis against an arbitrary library.
7 7
8 To use, build the 'binary_size_tool' target. Then run this tool, passing 8 To use, build the 'binary_size_tool' target. Then run this tool, passing
9 in the location of the library to be analyzed along with any other options 9 in the location of the library to be analyzed along with any other options
10 you desire. 10 you desire.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 # TODO(andrewhayden): Only used for legacy reports. Delete. 54 # TODO(andrewhayden): Only used for legacy reports. Delete.
55 def SymbolTypeToHuman(symbol_type): 55 def SymbolTypeToHuman(symbol_type):
56 """Convert a symbol type as printed by nm into a human-readable name.""" 56 """Convert a symbol type as printed by nm into a human-readable name."""
57 return {'b': 'bss', 57 return {'b': 'bss',
58 'd': 'data', 58 'd': 'data',
59 'r': 'read-only data', 59 'r': 'read-only data',
60 't': 'code', 60 't': 'code',
61 'w': 'weak symbol', 61 'w': 'weak symbol',
62 'v': 'weak symbol'}[symbol_type] 62 'v': 'weak symbol'}[symbol_type]
63 63
64 # Node dictionary keys. These are output in json read by the webapp so
65 # keep them short to save file size.
66 # Note: If these change, the webapp must also change.
67 NODE_TYPE_KEY = 'k'
Primiano Tucci (use gerrit) 2014/05/27 09:24:17 Globals and constatns should go between imports an
Daniel Bratell 2014/06/04 13:04:45 Done.
68 NODE_NAME_KEY = 'n'
69 NODE_CHILDREN_KEY = 'children'
70 NODE_SYMBOL_TYPE_KEY = 't'
71 NODE_SYMBOL_SIZE_KEY = 'value'
72 NODE_MAX_DEPTH_KEY = 'maxDepth'
73 NODE_LAST_PATH_ELEMENT_KEY = 'lastPathElement'
64 74
65 def _MkChild(node, name): 75 def _MkChild(node, name):
66 child = node['children'].get(name) 76 child = node[NODE_CHILDREN_KEY].get(name)
Primiano Tucci (use gerrit) 2014/05/27 09:24:17 To be honest, sounds like your nodes should start
Daniel Bratell 2014/06/04 13:04:45 I agree, but I'd prefer to postpone that change to
67 if child is None: 77 if child is None:
68 child = {'n': name, 'children': {}} 78 child = {NODE_NAME_KEY: name,
69 node['children'][name] = child 79 NODE_CHILDREN_KEY: {}}
80 node[NODE_CHILDREN_KEY][name] = child
70 return child 81 return child
71 82
72 83
84 BIG_BUCKET_LIMIT = 2000
Primiano Tucci (use gerrit) 2014/05/27 09:24:17 Ditto. Globals should live all together happily in
Andrew Hayden (chromium.org) 2014/05/27 10:04:11 Minor nit, the review says 3k and this constant is
Daniel Bratell 2014/06/04 13:04:45 Done.
Daniel Bratell 2014/06/04 13:04:45 Done.
85
86
87 def SplitNoPathBucket(node):
88 """(No Path) can be too large for the graphing lib to handle. Split
89 it into sub-buckets in that case."""
90 root_children = node[NODE_CHILDREN_KEY]
91 if '(No Path)' in root_children:
Andrew Hayden (chromium.org) 2014/05/27 10:04:11 We should probably extract the string "(No Path)"
Daniel Bratell 2014/06/04 13:04:45 Done.
92 no_path_bucket = root_children['(No Path)']
93 old_children = no_path_bucket[NODE_CHILDREN_KEY]
94 count = 0
95 for symbol_type, symbol_bucket in old_children.iteritems():
96 count += len(symbol_bucket[NODE_CHILDREN_KEY])
97 if count > BIG_BUCKET_LIMIT:
98 new_children = {}
99 no_path_bucket[NODE_CHILDREN_KEY] = new_children
100 current_bucket = None
101 index = 0
102 for symbol_type, symbol_bucket in old_children.iteritems():
103 for symbol_name, value in symbol_bucket[NODE_CHILDREN_KEY].iteritems():
104 if index % BIG_BUCKET_LIMIT == 0:
105 group_no = (index / BIG_BUCKET_LIMIT) + 1
106 current_bucket = _MkChild(no_path_bucket,
107 '(No Path) subgroup %d' % group_no)
108 assert not NODE_TYPE_KEY in node or node[NODE_TYPE_KEY] == 'p'
109 node[NODE_TYPE_KEY] = 'p' # p for path
110 index += 1
111 symbol_size = value[NODE_SYMBOL_SIZE_KEY]
112 AddSymbolIntoFileNode(current_bucket, symbol_type,
113 symbol_name, symbol_size)
114 no_path_bucket[NODE_CHILDREN_KEY] = new_children
Primiano Tucci (use gerrit) 2014/05/27 09:24:17 Uh? Looks like you already did this on line 99? Or
Daniel Bratell 2014/06/04 13:04:45 Yes, duplicate assignments. Fixed.
115
116
73 def MakeChildrenDictsIntoLists(node): 117 def MakeChildrenDictsIntoLists(node):
74 largest_list_len = 0 118 largest_list_len = 0
75 if 'children' in node: 119 if NODE_CHILDREN_KEY in node:
76 largest_list_len = len(node['children']) 120 largest_list_len = len(node[NODE_CHILDREN_KEY])
77 child_list = [] 121 child_list = []
78 for child in node['children'].itervalues(): 122 for child in node[NODE_CHILDREN_KEY].itervalues():
79 child_largest_list_len = MakeChildrenDictsIntoLists(child) 123 child_largest_list_len = MakeChildrenDictsIntoLists(child)
80 if child_largest_list_len > largest_list_len: 124 if child_largest_list_len > largest_list_len:
81 largest_list_len = child_largest_list_len 125 largest_list_len = child_largest_list_len
82 child_list.append(child) 126 child_list.append(child)
83 node['children'] = child_list 127 node[NODE_CHILDREN_KEY] = child_list
84 128
85 return largest_list_len 129 return largest_list_len
86 130
87 131
132 def AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size):
133 """Puts symbol into the file path node |node|. Returns the number of
Primiano Tucci (use gerrit) 2014/05/27 09:24:17 Nit: the first line of a docstring should fit in o
Daniel Bratell 2014/06/04 13:04:45 They are always of height 2. :-) This is extracte
134 added levels in tree. I.e. returns 2."""
135
136 depth = 0
Andrew Hayden (chromium.org) 2014/05/27 10:04:11 From a clarity standpoint I'd prefer if we avoid t
Daniel Bratell 2014/06/04 13:04:45 Done.
137
138 # 'node' is now the file node. Find the symbol-type bucket.
Andrew Hayden (chromium.org) 2014/05/27 10:04:11 The first part of this comment made sense in-situ,
Daniel Bratell 2014/06/04 13:04:45 Done.
139 node[NODE_LAST_PATH_ELEMENT_KEY] = True
140 node = _MkChild(node, symbol_type)
141 assert not NODE_TYPE_KEY in node or node[NODE_TYPE_KEY] == 'b'
142 node[NODE_SYMBOL_TYPE_KEY] = symbol_type
143 node[NODE_TYPE_KEY] = 'b' # b for bucket
144 depth += 1
145
146 # 'node' is now the symbol-type bucket. Make the child entry.
147 node = _MkChild(node, symbol_name)
148 if NODE_CHILDREN_KEY in node:
149 if node[NODE_CHILDREN_KEY]:
150 logging.warning('A container node used as symbol for %s.' % symbol_name)
151 # This is going to be used as a leaf so no use for child list.
152 del node[NODE_CHILDREN_KEY]
153 node[NODE_SYMBOL_SIZE_KEY] = symbol_size
154 node[NODE_SYMBOL_TYPE_KEY] = symbol_type
155 node[NODE_TYPE_KEY] = 's' # s for symbol
156 depth += 1
157 return depth
158
159
88 def MakeCompactTree(symbols): 160 def MakeCompactTree(symbols):
89 result = {'n': '/', 'children': {}, 'k': 'p', 'maxDepth': 0} 161 result = {NODE_NAME_KEY: '/',
162 NODE_CHILDREN_KEY: {},
163 NODE_TYPE_KEY: 'p',
164 NODE_MAX_DEPTH_KEY: 0}
90 seen_symbol_with_path = False 165 seen_symbol_with_path = False
91 for symbol_name, symbol_type, symbol_size, file_path in symbols: 166 for symbol_name, symbol_type, symbol_size, file_path in symbols:
92 167
93 if 'vtable for ' in symbol_name: 168 if 'vtable for ' in symbol_name:
94 symbol_type = '@' # hack to categorize these separately 169 symbol_type = '@' # hack to categorize these separately
95 # Take path like '/foo/bar/baz', convert to ['foo', 'bar', 'baz'] 170 # Take path like '/foo/bar/baz', convert to ['foo', 'bar', 'baz']
96 if file_path: 171 if file_path:
97 file_path = os.path.normpath(file_path) 172 file_path = os.path.normpath(file_path)
98 seen_symbol_with_path = True 173 seen_symbol_with_path = True
99 else: 174 else:
100 file_path = '(No Path)' 175 file_path = '(No Path)'
101 176
102 if file_path.startswith('/'): 177 if file_path.startswith('/'):
103 file_path = file_path[1:] 178 file_path = file_path[1:]
104 path_parts = file_path.split('/') 179 path_parts = file_path.split('/')
105 180
106 # Find pre-existing node in tree, or update if it already exists 181 # Find pre-existing node in tree, or update if it already exists
107 node = result 182 node = result
108 depth = 0 183 depth = 0
109 while len(path_parts) > 0: 184 while len(path_parts) > 0:
110 path_part = path_parts.pop(0) 185 path_part = path_parts.pop(0)
111 if len(path_part) == 0: 186 if len(path_part) == 0:
112 continue 187 continue
113 depth += 1 188 depth += 1
114 node = _MkChild(node, path_part) 189 node = _MkChild(node, path_part)
115 assert not 'k' in node or node['k'] == 'p' 190 assert not NODE_TYPE_KEY in node or node[NODE_TYPE_KEY] == 'p'
116 node['k'] = 'p' # p for path 191 node[NODE_TYPE_KEY] = 'p' # p for path
Primiano Tucci (use gerrit) 2014/05/27 09:24:17 Nit: add an extra space before #
117 192
118 # 'node' is now the file node. Find the symbol-type bucket. 193 depth += AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size)
119 node['lastPathElement'] = True 194 result[NODE_MAX_DEPTH_KEY] = max(result[NODE_MAX_DEPTH_KEY], depth)
120 node = _MkChild(node, symbol_type)
121 assert not 'k' in node or node['k'] == 'b'
122 node['t'] = symbol_type
123 node['k'] = 'b' # b for bucket
124 depth += 1
125
126 # 'node' is now the symbol-type bucket. Make the child entry.
127 node = _MkChild(node, symbol_name)
128 if 'children' in node:
129 if node['children']:
130 logging.warning('A container node used as symbol for %s.' % symbol_name)
131 # This is going to be used as a leaf so no use for child list.
132 del node['children']
133 node['value'] = symbol_size
134 node['t'] = symbol_type
135 node['k'] = 's' # s for symbol
136 depth += 1
137 result['maxDepth'] = max(result['maxDepth'], depth)
138 195
139 if not seen_symbol_with_path: 196 if not seen_symbol_with_path:
140 logging.warning('Symbols lack paths. Data will not be structured.') 197 logging.warning('Symbols lack paths. Data will not be structured.')
141 198
199 # The (no path) bucket can be extremely large if we failed to get
200 # path information. Split it into subgroups if needed.
201 SplitNoPathBucket(result)
202
142 largest_list_len = MakeChildrenDictsIntoLists(result) 203 largest_list_len = MakeChildrenDictsIntoLists(result)
143 204
144 if largest_list_len > 1000: 205 if largest_list_len > BIG_BUCKET_LIMIT:
145 logging.warning('There are sections with %d nodes. ' 206 logging.warning('There are sections with %d nodes. '
146 'Results might be unusable.' % largest_list_len) 207 'Results might be unusable.' % largest_list_len)
147 return result 208 return result
148 209
149 210
150 # TODO(andrewhayden): Only used for legacy reports. Delete. 211 # TODO(andrewhayden): Only used for legacy reports. Delete.
151 def TreeifySymbols(symbols): 212 def TreeifySymbols(symbols):
152 """Convert symbols into a path-based tree, calculating size information 213 """Convert symbols into a path-based tree, calculating size information
153 along the way. 214 along the way.
154 215
(...skipping 526 matching lines...) Expand 10 before | Expand all | Expand 10 after
681 print('Copying index.html') 742 print('Copying index.html')
682 shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir) 743 shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir)
683 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir) 744 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir)
684 745
685 if opts.verbose: 746 if opts.verbose:
686 print 'Report saved to ' + opts.destdir + '/index.html' 747 print 'Report saved to ' + opts.destdir + '/index.html'
687 748
688 749
689 if __name__ == '__main__': 750 if __name__ == '__main__':
690 sys.exit(main()) 751 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698