| # Copyright 2013 The Chromium Authors. All rights reserved. |
| # 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 |
| import subprocess |
| import sys |
| |
| |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
| |
| |
| def Run(*args): |
| p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| out, err = p.communicate() |
| if p.returncode != 0: |
| raise SystemExit(out) |
| |
| |
| def FindNode(node, component): |
| for child in node['children']: |
| if child['name'] == component: |
| return child |
| return None |
| |
| |
| def InsertIntoTree(tree, source_name, size): |
| components = source_name[3:].split('\\') |
| node = tree |
| for index, component in enumerate(components): |
| data = FindNode(node, component) |
| if not data: |
| data = { 'name': source_name, 'name': component } |
| if index == len(components) - 1: |
| data['size'] = size |
| else: |
| data['children'] = [] |
| node['children'].append(data) |
| 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(): |
| jsons = [] |
| if len(sys.argv) > 1: |
| dlls = sys.argv[1:] |
| else: |
| out_dir = os.path.join(BASE_DIR, '..', '..', '..', 'out', 'Release') |
| dlls = [os.path.normpath(os.path.join(out_dir, dll)) |
| for dll in ('chrome.dll', 'chrome_child.dll')] |
| for dll_path in dlls: |
| if os.path.exists(dll_path): |
| print 'Tallying %s...' % dll_path |
| json_path = dll_path + '.json' |
| Run(os.path.join(BASE_DIR, '..', '..', '..', 'third_party', 'syzygy', |
| 'binaries', 'exe', 'experimental', 'code_tally.exe'), |
| '--input-image=' + dll_path, |
| '--input-pdb=' + dll_path + '.pdb', |
| '--output-file=' + json_path) |
| jsons.append(json_path) |
| if not jsons: |
| print 'Couldn\'t find dlls.' |
| print 'Pass fully qualified dll name(s) if you want to use something other ' |
| print 'than out\\Release\\chrome.dll and chrome_child.dll.' |
| 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... (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: |
| 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'] = '/' |
| data['children'] = [] |
| 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: |
| 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 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |