Index: tools/win/sizeviewer/sizeviewer.py |
diff --git a/tools/win/sizeviewer/sizeviewer.py b/tools/win/sizeviewer/sizeviewer.py |
index defb00a4b85a5d9cdb60eb2aaf116013eba119b5..3e6d92d03a3956d84e934d382a52bb61f925a396 100644 |
--- a/tools/win/sizeviewer/sizeviewer.py |
+++ b/tools/win/sizeviewer/sizeviewer.py |
@@ -2,6 +2,8 @@ |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
+import base64 |
+import codecs |
import json |
import os |
import string |
@@ -25,12 +27,12 @@ def FindNode(node, component): |
def InsertIntoTree(tree, source_name, size): |
- components = source_name.replace(':', '').split('\\') |
+ components = source_name[3:].split('\\') |
node = tree |
for index, component in enumerate(components): |
data = FindNode(node, component) |
if not data: |
- data = { 'name': component } |
+ data = { 'name': source_name, 'name': component } |
if index == len(components) - 1: |
data['size'] = size |
else: |
@@ -39,6 +41,37 @@ def InsertIntoTree(tree, source_name, size): |
node = data |
+def FlattenTree(tree): |
+ result = [['Path', 'Parent', 'Size', 'Value']] |
+ def Flatten(node, parent): |
+ name = node['name'] |
+ if parent and parent != '/': |
+ name = parent + '/' + name |
+ if 'children' in node: |
+ result.append([name, parent, -1, -1]) |
+ for c in node['children']: |
+ Flatten(c, name) |
+ else: |
+ result.append([name, parent, node['size'], node['size']]) |
+ Flatten(tree, '') |
+ return result |
+ |
+ |
+def GetAsset(filename): |
+ with open(os.path.join(BASE_DIR, filename), 'rb') as f: |
+ return f.read() |
+ |
+ |
+def AppendAsScriptBlock(f, value, var=None): |
+ f.write('<script type="text/javascript">\n') |
+ if var: |
+ f.write('var ' + var + ' = ') |
+ f.write(value) |
+ if var: |
+ f.write(';\n') |
+ f.write('</script>\n') |
+ |
+ |
def main(): |
out_dir = os.path.join(BASE_DIR, '..', '..', '..', 'out', 'Release') |
jsons = [] |
@@ -56,37 +89,99 @@ def main(): |
print 'Couldn\'t find binaries, looking in', out_dir |
return 1 |
+ # Munge the code_tally json format into an easier-to-view format. |
for json_name in jsons: |
with open(json_name, 'r') as jsonf: |
all_data = json.load(jsonf) |
html_path = os.path.splitext(json_name)[0] + '.html' |
- print 'Generating %s...' % html_path |
+ print 'Generating %s... (standlone)' % html_path |
by_source = {} |
+ symbols_index = {} |
+ symbols = [] |
for obj_name, obj_data in all_data['objects'].iteritems(): |
for symbol, symbol_data in obj_data.iteritems(): |
size = int(symbol_data['size']) |
# Sometimes there's symbols with no source file, we just ignore those. |
if 'contribs' in symbol_data: |
- # There may be more than one file in the list, we just assign to the |
- # first source file that contains the symbol, rather than try to |
- # split or duplicate info. |
- src_index = symbol_data['contribs'][0] |
- source = all_data['sources'][int(src_index)] |
- if source not in by_source: |
- by_source[source] = [] |
- by_source[source].append(size) |
+ i = 0 |
+ while i < len(symbol_data['contribs']): |
+ src_index = symbol_data['contribs'][i] |
+ i += 1 |
+ per_line = symbol_data['contribs'][i] |
+ i += 1 |
+ source = all_data['sources'][int(src_index)] |
+ if source not in by_source: |
+ by_source[source] = {'lines': {}, 'total_size': 0} |
+ size = 0 |
+ # per_line is [line, size, line, size, line, size, ...] |
+ for j in range(0, len(per_line), 2): |
+ line_number = per_line[j] |
+ size += per_line[j + 1] |
+ # Save some time/space in JS by using an array here. 0 == size, |
+ # 1 == symbol list. |
+ by_source[source]['lines'].setdefault(line_number, [0, []]) |
+ by_source[source]['lines'][line_number][0] += per_line[j + 1] |
+ if symbol in symbols_index: |
+ symindex = symbols_index[symbol] |
+ else: |
+ symbols.append(symbol) |
+ symbols_index[symbol] = symindex = len(symbols) - 1 |
+ by_source[source]['lines'][line_number][1].append( |
+ symindex) |
+ by_source[source]['total_size'] += size |
binary_name = all_data['executable']['name'] |
data = {} |
- data['name'] = binary_name |
+ data['name'] = '/' |
data['children'] = [] |
- for source, sizes in by_source.iteritems(): |
- InsertIntoTree(data, source, sum(sizes)) |
+ file_contents = {} |
+ line_data = {} |
+ for source, file_data in by_source.iteritems(): |
+ InsertIntoTree(data, source, file_data['total_size']) |
+ |
+ store_as = source[3:].replace('\\', '/') |
+ try: |
+ with codecs.open(source, 'rb', encoding='latin1') as f: |
+ file_contents[store_as] = f.read() |
+ except IOError: |
+ file_contents[store_as] = '// Unable to load source.' |
+ |
+ line_data[store_as] = file_data['lines'] |
+ # code_tally attempts to assign fractional bytes when code is shared |
+ # across multiple symbols. Round off here for display after summing above. |
+ for per_line in line_data[store_as].values(): |
+ per_line[0] = round(per_line[0]) |
+ |
+ flattened = FlattenTree(data) |
+ maxval = 0 |
+ for i in flattened[1:]: |
+ maxval = max(i[2], maxval) |
+ flattened_str = json.dumps(flattened) |
+ |
+ to_write = GetAsset('template.html') |
+ # Save all data and what would normally be external resources into the |
+ # one html so that it's a standalone report. |
with open(html_path, 'w') as f: |
- with open(os.path.join(BASE_DIR, 'template.html'), 'r') as templatef: |
- template = templatef.read() |
- f.write(string.Template(template).substitute( |
- {'data': json.dumps(data, indent=2), |
- 'dllname': binary_name + ' ' + all_data['executable']['version']})) |
+ f.write(to_write) |
+ # These aren't subbed in as a silly workaround for 32-bit python. |
+ # The end result is only ~100M, but while substituting these into a |
+ # template, it otherwise raises a MemoryError, I guess due to |
+ # fragmentation. So instead, we just append them as variables to the file |
+ # and then refer to the variables in the main script. |
+ filedata_str = json.dumps(file_contents).replace( |
+ '</script>', '</scr"+"ipt>') |
+ AppendAsScriptBlock(f, filedata_str, var='g_file_contents') |
+ AppendAsScriptBlock(f, json.dumps(line_data), var='g_line_data') |
+ AppendAsScriptBlock(f, json.dumps(symbols), var='g_symbol_list') |
+ favicon_str = json.dumps(base64.b64encode(GetAsset('favicon.png'))) |
+ AppendAsScriptBlock(f, favicon_str, var='g_favicon') |
+ AppendAsScriptBlock(f, flattened_str, var='g_raw_data') |
+ AppendAsScriptBlock(f, str(maxval), var='g_maxval') |
+ dllname_str = binary_name + ' ' + all_data['executable']['version'] |
+ AppendAsScriptBlock(f, json.dumps(dllname_str), var='g_dllname') |
+ AppendAsScriptBlock(f, GetAsset('codemirror.js')) |
+ AppendAsScriptBlock(f, GetAsset('clike.js')) |
+ AppendAsScriptBlock(f, GetAsset('main.js')) |
+ f.write('</html>') |
return 0 |