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.
14 BASE_DIR
= os
.path
.dirname(os
.path
.abspath(__file__
))
18 with
open(os
.devnull
, 'w') as null
:
19 subprocess
.check_call(args
, stdout
=null
, stderr
=null
)
22 def FindNode(node
, component
):
23 for child
in node
['children']:
24 if child
['name'] == component
:
29 def InsertIntoTree(tree
, source_name
, size
):
30 components
= source_name
[3:].split('\\')
32 for index
, component
in enumerate(components
):
33 data
= FindNode(node
, component
)
35 data
= { 'name': source_name
, 'name': component
}
36 if index
== len(components
) - 1:
40 node
['children'].append(data
)
44 def FlattenTree(tree
):
45 result
= [['Path', 'Parent', 'Size', 'Value']]
46 def Flatten(node
, parent
):
48 if parent
and parent
!= '/':
49 name
= parent
+ '/' + name
50 if 'children' in node
:
51 result
.append([name
, parent
, -1, -1])
52 for c
in node
['children']:
55 result
.append([name
, parent
, node
['size'], node
['size']])
60 def GetAsset(filename
):
61 with
open(os
.path
.join(BASE_DIR
, filename
), 'rb') as f
:
65 def AppendAsScriptBlock(f
, value
, var
=None):
66 f
.write('<script type="text/javascript">\n')
68 f
.write('var ' + var
+ ' = ')
72 f
.write('</script>\n')
76 out_dir
= os
.path
.join(BASE_DIR
, '..', '..', '..', 'out', 'Release')
78 for dll
in ('chrome.dll', 'chrome_child.dll'):
79 dll_path
= os
.path
.normpath(os
.path
.join(out_dir
, dll
))
80 if os
.path
.exists(dll_path
):
81 print 'Tallying %s...' % dll_path
82 json_path
= dll_path
+ '.json'
83 Run(os
.path
.join(BASE_DIR
, 'code_tally.exe'),
84 '--input-image=' + dll_path
,
85 '--input-pdb=' + dll_path
+ '.pdb',
86 '--output-file=' + json_path
)
87 jsons
.append(json_path
)
89 print 'Couldn\'t find binaries, looking in', out_dir
92 # Munge the code_tally json format into an easier-to-view format.
93 for json_name
in jsons
:
94 with
open(json_name
, 'r') as jsonf
:
95 all_data
= json
.load(jsonf
)
96 html_path
= os
.path
.splitext(json_name
)[0] + '.html'
97 print 'Generating %s... (standlone)' % html_path
101 for obj_name
, obj_data
in all_data
['objects'].iteritems():
102 for symbol
, symbol_data
in obj_data
.iteritems():
103 size
= int(symbol_data
['size'])
104 # Sometimes there's symbols with no source file, we just ignore those.
105 if 'contribs' in symbol_data
:
107 while i
< len(symbol_data
['contribs']):
108 src_index
= symbol_data
['contribs'][i
]
110 per_line
= symbol_data
['contribs'][i
]
112 source
= all_data
['sources'][int(src_index
)]
113 if source
not in by_source
:
114 by_source
[source
] = {'lines': {}, 'total_size': 0}
116 # per_line is [line, size, line, size, line, size, ...]
117 for j
in range(0, len(per_line
), 2):
118 line_number
= per_line
[j
]
119 size
+= per_line
[j
+ 1]
120 # Save some time/space in JS by using an array here. 0 == size,
122 by_source
[source
]['lines'].setdefault(line_number
, [0, []])
123 by_source
[source
]['lines'][line_number
][0] += per_line
[j
+ 1]
124 if symbol
in symbols_index
:
125 symindex
= symbols_index
[symbol
]
127 symbols
.append(symbol
)
128 symbols_index
[symbol
] = symindex
= len(symbols
) - 1
129 by_source
[source
]['lines'][line_number
][1].append(
131 by_source
[source
]['total_size'] += size
132 binary_name
= all_data
['executable']['name']
135 data
['children'] = []
138 for source
, file_data
in by_source
.iteritems():
139 InsertIntoTree(data
, source
, file_data
['total_size'])
141 store_as
= source
[3:].replace('\\', '/')
143 with codecs
.open(source
, 'rb', encoding
='latin1') as f
:
144 file_contents
[store_as
] = f
.read()
146 file_contents
[store_as
] = '// Unable to load source.'
148 line_data
[store_as
] = file_data
['lines']
149 # code_tally attempts to assign fractional bytes when code is shared
150 # across multiple symbols. Round off here for display after summing above.
151 for per_line
in line_data
[store_as
].values():
152 per_line
[0] = round(per_line
[0])
154 flattened
= FlattenTree(data
)
156 for i
in flattened
[1:]:
157 maxval
= max(i
[2], maxval
)
158 flattened_str
= json
.dumps(flattened
)
160 to_write
= GetAsset('template.html')
161 # Save all data and what would normally be external resources into the
162 # one html so that it's a standalone report.
163 with
open(html_path
, 'w') as f
:
165 # These aren't subbed in as a silly workaround for 32-bit python.
166 # The end result is only ~100M, but while substituting these into a
167 # template, it otherwise raises a MemoryError, I guess due to
168 # fragmentation. So instead, we just append them as variables to the file
169 # and then refer to the variables in the main script.
170 filedata_str
= json
.dumps(file_contents
).replace(
171 '</script>', '</scr"+"ipt>')
172 AppendAsScriptBlock(f
, filedata_str
, var
='g_file_contents')
173 AppendAsScriptBlock(f
, json
.dumps(line_data
), var
='g_line_data')
174 AppendAsScriptBlock(f
, json
.dumps(symbols
), var
='g_symbol_list')
175 favicon_str
= json
.dumps(base64
.b64encode(GetAsset('favicon.png')))
176 AppendAsScriptBlock(f
, favicon_str
, var
='g_favicon')
177 AppendAsScriptBlock(f
, flattened_str
, var
='g_raw_data')
178 AppendAsScriptBlock(f
, str(maxval
), var
='g_maxval')
179 dllname_str
= binary_name
+ ' ' + all_data
['executable']['version']
180 AppendAsScriptBlock(f
, json
.dumps(dllname_str
), var
='g_dllname')
181 AppendAsScriptBlock(f
, GetAsset('codemirror.js'))
182 AppendAsScriptBlock(f
, GetAsset('clike.js'))
183 AppendAsScriptBlock(f
, GetAsset('main.js'))
189 if __name__
== '__main__':