1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import print_function 6 7import base64 8import codecs 9import json 10import os 11import string 12import subprocess 13import sys 14 15 16BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 17 18 19def Run(*args): 20 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 21 out, err = p.communicate() 22 if p.returncode != 0: 23 raise SystemExit(out) 24 25 26def FindNode(node, component): 27 for child in node['children']: 28 if child['name'] == component: 29 return child 30 return None 31 32 33def InsertIntoTree(tree, source_name, size): 34 components = source_name[3:].split('\\') 35 node = tree 36 for index, component in enumerate(components): 37 data = FindNode(node, component) 38 if not data: 39 data = { 'name': source_name, 'name': component } 40 if index == len(components) - 1: 41 data['size'] = size 42 else: 43 data['children'] = [] 44 node['children'].append(data) 45 node = data 46 47 48def FlattenTree(tree): 49 result = [['Path', 'Parent', 'Size', 'Value']] 50 def Flatten(node, parent): 51 name = node['name'] 52 if parent and parent != '/': 53 name = parent + '/' + name 54 if 'children' in node: 55 result.append([name, parent, -1, -1]) 56 for c in node['children']: 57 Flatten(c, name) 58 else: 59 result.append([name, parent, node['size'], node['size']]) 60 Flatten(tree, '') 61 return result 62 63 64def GetAsset(filename): 65 with open(os.path.join(BASE_DIR, filename), 'rb') as f: 66 return f.read() 67 68 69def AppendAsScriptBlock(f, value, var=None): 70 f.write('<script type="text/javascript">\n') 71 if var: 72 f.write('var ' + var + ' = ') 73 f.write(value) 74 if var: 75 f.write(';\n') 76 f.write('</script>\n') 77 78 79def main(): 80 jsons = [] 81 if len(sys.argv) > 1: 82 dlls = sys.argv[1:] 83 else: 84 out_dir = os.path.join(BASE_DIR, '..', '..', '..', 'out', 'Release') 85 dlls = [os.path.normpath(os.path.join(out_dir, dll)) 86 for dll in ('chrome.dll', 'chrome_child.dll')] 87 for dll_path in dlls: 88 if os.path.exists(dll_path): 89 print('Tallying %s...' % dll_path) 90 json_path = dll_path + '.json' 91 Run(os.path.join(BASE_DIR, '..', '..', '..', 'third_party', 'syzygy', 92 'binaries', 'exe', 'experimental', 'code_tally.exe'), 93 '--input-image=' + dll_path, 94 '--input-pdb=' + dll_path + '.pdb', 95 '--output-file=' + json_path) 96 jsons.append(json_path) 97 if not jsons: 98 print('Couldn\'t find dlls.') 99 print( 100 'Pass fully qualified dll name(s) if you want to use something other ') 101 print('than out\\Release\\chrome.dll and chrome_child.dll.') 102 return 1 103 104 # Munge the code_tally json format into an easier-to-view format. 105 for json_name in jsons: 106 with open(json_name, 'r') as jsonf: 107 all_data = json.load(jsonf) 108 html_path = os.path.splitext(json_name)[0] + '.html' 109 print('Generating %s... (standlone)' % html_path) 110 by_source = {} 111 symbols_index = {} 112 symbols = [] 113 for obj_name, obj_data in all_data['objects'].iteritems(): 114 for symbol, symbol_data in obj_data.iteritems(): 115 size = int(symbol_data['size']) 116 # Sometimes there's symbols with no source file, we just ignore those. 117 if 'contribs' in symbol_data: 118 i = 0 119 while i < len(symbol_data['contribs']): 120 src_index = symbol_data['contribs'][i] 121 i += 1 122 per_line = symbol_data['contribs'][i] 123 i += 1 124 source = all_data['sources'][int(src_index)] 125 if source not in by_source: 126 by_source[source] = {'lines': {}, 'total_size': 0} 127 size = 0 128 # per_line is [line, size, line, size, line, size, ...] 129 for j in range(0, len(per_line), 2): 130 line_number = per_line[j] 131 size += per_line[j + 1] 132 # Save some time/space in JS by using an array here. 0 == size, 133 # 1 == symbol list. 134 by_source[source]['lines'].setdefault(line_number, [0, []]) 135 by_source[source]['lines'][line_number][0] += per_line[j + 1] 136 if symbol in symbols_index: 137 symindex = symbols_index[symbol] 138 else: 139 symbols.append(symbol) 140 symbols_index[symbol] = symindex = len(symbols) - 1 141 by_source[source]['lines'][line_number][1].append( 142 symindex) 143 by_source[source]['total_size'] += size 144 binary_name = all_data['executable']['name'] 145 data = {} 146 data['name'] = '/' 147 data['children'] = [] 148 file_contents = {} 149 line_data = {} 150 for source, file_data in by_source.iteritems(): 151 InsertIntoTree(data, source, file_data['total_size']) 152 153 store_as = source[3:].replace('\\', '/') 154 try: 155 with codecs.open(source, 'rb', encoding='latin1') as f: 156 file_contents[store_as] = f.read() 157 except IOError: 158 file_contents[store_as] = '// Unable to load source.' 159 160 line_data[store_as] = file_data['lines'] 161 # code_tally attempts to assign fractional bytes when code is shared 162 # across multiple symbols. Round off here for display after summing above. 163 for per_line in line_data[store_as].values(): 164 per_line[0] = round(per_line[0]) 165 166 flattened = FlattenTree(data) 167 maxval = 0 168 for i in flattened[1:]: 169 maxval = max(i[2], maxval) 170 flattened_str = json.dumps(flattened) 171 172 to_write = GetAsset('template.html') 173 # Save all data and what would normally be external resources into the 174 # one html so that it's a standalone report. 175 with open(html_path, 'w') as f: 176 f.write(to_write) 177 # These aren't subbed in as a silly workaround for 32-bit python. 178 # The end result is only ~100M, but while substituting these into a 179 # template, it otherwise raises a MemoryError, I guess due to 180 # fragmentation. So instead, we just append them as variables to the file 181 # and then refer to the variables in the main script. 182 filedata_str = json.dumps(file_contents).replace( 183 '</script>', '</scr"+"ipt>') 184 AppendAsScriptBlock(f, filedata_str, var='g_file_contents') 185 AppendAsScriptBlock(f, json.dumps(line_data), var='g_line_data') 186 AppendAsScriptBlock(f, json.dumps(symbols), var='g_symbol_list') 187 favicon_str = json.dumps(base64.b64encode(GetAsset('favicon.png'))) 188 AppendAsScriptBlock(f, favicon_str, var='g_favicon') 189 AppendAsScriptBlock(f, flattened_str, var='g_raw_data') 190 AppendAsScriptBlock(f, str(maxval), var='g_maxval') 191 dllname_str = binary_name + ' ' + all_data['executable']['version'] 192 AppendAsScriptBlock(f, json.dumps(dllname_str), var='g_dllname') 193 AppendAsScriptBlock(f, GetAsset('codemirror.js')) 194 AppendAsScriptBlock(f, GetAsset('clike.js')) 195 AppendAsScriptBlock(f, GetAsset('main.js')) 196 f.write('</html>') 197 198 return 0 199 200 201if __name__ == '__main__': 202 sys.exit(main()) 203