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