Index: tools/binary_size/run_binary_size_analysis.py |
diff --git a/tools/binary_size/run_binary_size_analysis.py b/tools/binary_size/run_binary_size_analysis.py |
index f4dad625868b64b765115205ac8c36a1543fa69a..f06884f8dafe1f7ba54c8586d18882c0e0ec5227 100755 |
--- a/tools/binary_size/run_binary_size_analysis.py |
+++ b/tools/binary_size/run_binary_size_analysis.py |
@@ -39,6 +39,25 @@ sys.path.append(elf_symbolizer_path) |
import symbols.elf_symbolizer as elf_symbolizer # pylint: disable=F0401 |
+# Node dictionary keys. These are output in json read by the webapp so |
+# keep them short to save file size. |
+# Note: If these change, the webapp must also change. |
+NODE_TYPE_KEY = 'k' |
+NODE_NAME_KEY = 'n' |
+NODE_CHILDREN_KEY = 'children' |
+NODE_SYMBOL_TYPE_KEY = 't' |
+NODE_SYMBOL_SIZE_KEY = 'value' |
+NODE_MAX_DEPTH_KEY = 'maxDepth' |
+NODE_LAST_PATH_ELEMENT_KEY = 'lastPathElement' |
+ |
+# The display name of the bucket where we put symbols without path. |
+NAME_NO_PATH_BUCKET = '(No Path)' |
+ |
+# Try to keep data buckets smaller than this to avoid killing the |
+# graphing lib. |
+BIG_BUCKET_LIMIT = 3000 |
+ |
+ |
# TODO(andrewhayden): Only used for legacy reports. Delete. |
def FormatBytes(byte_count): |
"""Pretty-print a number of bytes.""" |
@@ -63,41 +82,101 @@ def SymbolTypeToHuman(symbol_type): |
def _MkChild(node, name): |
- child = node['children'].get(name) |
+ child = node[NODE_CHILDREN_KEY].get(name) |
if child is None: |
- child = {'n': name, 'children': {}} |
- node['children'][name] = child |
+ child = {NODE_NAME_KEY: name, |
+ NODE_CHILDREN_KEY: {}} |
+ node[NODE_CHILDREN_KEY][name] = child |
return child |
+ |
+def SplitNoPathBucket(node): |
+ """NAME_NO_PATH_BUCKET can be too large for the graphing lib to |
+ handle. Split it into sub-buckets in that case.""" |
+ root_children = node[NODE_CHILDREN_KEY] |
+ if NAME_NO_PATH_BUCKET in root_children: |
+ no_path_bucket = root_children[NAME_NO_PATH_BUCKET] |
+ old_children = no_path_bucket[NODE_CHILDREN_KEY] |
+ count = 0 |
+ for symbol_type, symbol_bucket in old_children.iteritems(): |
+ count += len(symbol_bucket[NODE_CHILDREN_KEY]) |
+ if count > BIG_BUCKET_LIMIT: |
+ new_children = {} |
+ no_path_bucket[NODE_CHILDREN_KEY] = new_children |
+ current_bucket = None |
+ index = 0 |
+ for symbol_type, symbol_bucket in old_children.iteritems(): |
+ for symbol_name, value in symbol_bucket[NODE_CHILDREN_KEY].iteritems(): |
+ if index % BIG_BUCKET_LIMIT == 0: |
+ group_no = (index / BIG_BUCKET_LIMIT) + 1 |
+ current_bucket = _MkChild(no_path_bucket, |
+ '%s subgroup %d' % (NAME_NO_PATH_BUCKET, |
+ group_no)) |
+ assert not NODE_TYPE_KEY in node or node[NODE_TYPE_KEY] == 'p' |
+ node[NODE_TYPE_KEY] = 'p' # p for path |
+ index += 1 |
+ symbol_size = value[NODE_SYMBOL_SIZE_KEY] |
+ AddSymbolIntoFileNode(current_bucket, symbol_type, |
+ symbol_name, symbol_size) |
+ |
+ |
def MakeChildrenDictsIntoLists(node): |
largest_list_len = 0 |
- if 'children' in node: |
- largest_list_len = len(node['children']) |
+ if NODE_CHILDREN_KEY in node: |
+ largest_list_len = len(node[NODE_CHILDREN_KEY]) |
child_list = [] |
- for child in node['children'].itervalues(): |
+ for child in node[NODE_CHILDREN_KEY].itervalues(): |
child_largest_list_len = MakeChildrenDictsIntoLists(child) |
if child_largest_list_len > largest_list_len: |
largest_list_len = child_largest_list_len |
child_list.append(child) |
- node['children'] = child_list |
+ node[NODE_CHILDREN_KEY] = child_list |
return largest_list_len |
+def AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size): |
+ """Puts symbol into the file path node |node|. |
+ Returns the number of added levels in tree. I.e. returns 2.""" |
+ |
+ # 'node' is the file node and first step is to find its symbol-type bucket. |
+ node[NODE_LAST_PATH_ELEMENT_KEY] = True |
+ node = _MkChild(node, symbol_type) |
+ assert not NODE_TYPE_KEY in node or node[NODE_TYPE_KEY] == 'b' |
+ node[NODE_SYMBOL_TYPE_KEY] = symbol_type |
+ node[NODE_TYPE_KEY] = 'b' # b for bucket |
+ |
+ # 'node' is now the symbol-type bucket. Make the child entry. |
+ node = _MkChild(node, symbol_name) |
+ if NODE_CHILDREN_KEY in node: |
+ if node[NODE_CHILDREN_KEY]: |
+ logging.warning('A container node used as symbol for %s.' % symbol_name) |
+ # This is going to be used as a leaf so no use for child list. |
+ del node[NODE_CHILDREN_KEY] |
+ node[NODE_SYMBOL_SIZE_KEY] = symbol_size |
+ node[NODE_SYMBOL_TYPE_KEY] = symbol_type |
+ node[NODE_TYPE_KEY] = 's' # s for symbol |
+ |
+ return 2 # Depth of the added subtree. |
+ |
+ |
def MakeCompactTree(symbols): |
- result = {'n': '/', 'children': {}, 'k': 'p', 'maxDepth': 0} |
+ result = {NODE_NAME_KEY: '/', |
+ NODE_CHILDREN_KEY: {}, |
+ NODE_TYPE_KEY: 'p', |
+ NODE_MAX_DEPTH_KEY: 0} |
seen_symbol_with_path = False |
for symbol_name, symbol_type, symbol_size, file_path in symbols: |
if 'vtable for ' in symbol_name: |
- symbol_type = '@' # hack to categorize these separately |
+ symbol_type = '@' # hack to categorize these separately |
# Take path like '/foo/bar/baz', convert to ['foo', 'bar', 'baz'] |
if file_path: |
file_path = os.path.normpath(file_path) |
seen_symbol_with_path = True |
else: |
- file_path = '(No Path)' |
+ file_path = NAME_NO_PATH_BUCKET |
if file_path.startswith('/'): |
file_path = file_path[1:] |
@@ -112,36 +191,22 @@ def MakeCompactTree(symbols): |
continue |
depth += 1 |
node = _MkChild(node, path_part) |
- assert not 'k' in node or node['k'] == 'p' |
- node['k'] = 'p' # p for path |
- |
- # 'node' is now the file node. Find the symbol-type bucket. |
- node['lastPathElement'] = True |
- node = _MkChild(node, symbol_type) |
- assert not 'k' in node or node['k'] == 'b' |
- node['t'] = symbol_type |
- node['k'] = 'b' # b for bucket |
- depth += 1 |
- |
- # 'node' is now the symbol-type bucket. Make the child entry. |
- node = _MkChild(node, symbol_name) |
- if 'children' in node: |
- if node['children']: |
- logging.warning('A container node used as symbol for %s.' % symbol_name) |
- # This is going to be used as a leaf so no use for child list. |
- del node['children'] |
- node['value'] = symbol_size |
- node['t'] = symbol_type |
- node['k'] = 's' # s for symbol |
- depth += 1 |
- result['maxDepth'] = max(result['maxDepth'], depth) |
+ assert not NODE_TYPE_KEY in node or node[NODE_TYPE_KEY] == 'p' |
+ node[NODE_TYPE_KEY] = 'p' # p for path |
+ |
+ depth += AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size) |
+ result[NODE_MAX_DEPTH_KEY] = max(result[NODE_MAX_DEPTH_KEY], depth) |
if not seen_symbol_with_path: |
logging.warning('Symbols lack paths. Data will not be structured.') |
+ # The (no path) bucket can be extremely large if we failed to get |
+ # path information. Split it into subgroups if needed. |
+ SplitNoPathBucket(result) |
+ |
largest_list_len = MakeChildrenDictsIntoLists(result) |
- if largest_list_len > 1000: |
+ if largest_list_len > BIG_BUCKET_LIMIT: |
logging.warning('There are sections with %d nodes. ' |
'Results might be unusable.' % largest_list_len) |
return result |