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
5import json
6import os
7import string
8import subprocess
9import sys
10
11
12BASE_DIR = os.path.dirname(os.path.abspath(__file__))
13
14
15def Run(*args):
16  with open(os.devnull, 'w') as null:
17    subprocess.check_call(args, stdout=null, stderr=null)
18
19
20def FindNode(node, component):
21  for child in node['children']:
22    if child['name'] == component:
23      return child
24  return None
25
26
27def InsertIntoTree(tree, source_name, size):
28  components = source_name.replace(':', '').split('\\')
29  node = tree
30  for index, component in enumerate(components):
31    data = FindNode(node, component)
32    if not data:
33      data = { 'name': component }
34      if index == len(components) - 1:
35        data['size'] = size
36      else:
37        data['children'] = []
38      node['children'].append(data)
39    node = data
40
41
42def main():
43  out_dir = os.path.join(BASE_DIR, '..', '..', '..', 'out', 'Release')
44  jsons = []
45  for dll in ('chrome.dll', 'chrome_child.dll'):
46    dll_path = os.path.normpath(os.path.join(out_dir, dll))
47    if os.path.exists(dll_path):
48      print 'Tallying %s...' % dll_path
49      json_path = dll_path + '.json'
50      Run(os.path.join(BASE_DIR, 'code_tally.exe'),
51          '--input-image=' + dll_path,
52          '--input-pdb=' + dll_path + '.pdb',
53          '--output-file=' + json_path)
54      jsons.append(json_path)
55  if not jsons:
56    print 'Couldn\'t find binaries, looking in', out_dir
57    return 1
58
59  for json_name in jsons:
60    with open(json_name, 'r') as jsonf:
61      all_data = json.load(jsonf)
62    html_path = os.path.splitext(json_name)[0] + '.html'
63    print 'Generating %s...' % html_path
64    by_source = {}
65    for obj_name, obj_data in all_data['objects'].iteritems():
66      for symbol, symbol_data in obj_data.iteritems():
67        size = int(symbol_data['size'])
68        # Sometimes there's symbols with no source file, we just ignore those.
69        if 'contribs' in symbol_data:
70          # There may be more than one file in the list, we just assign to the
71          # first source file that contains the symbol, rather than try to
72          # split or duplicate info.
73          src_index = symbol_data['contribs'][0]
74          source = all_data['sources'][int(src_index)]
75          if source not in by_source:
76            by_source[source] = []
77          by_source[source].append(size)
78    binary_name = all_data['executable']['name']
79    data = {}
80    data['name'] = binary_name
81    data['children'] = []
82    for source, sizes in by_source.iteritems():
83      InsertIntoTree(data, source, sum(sizes))
84    with open(html_path, 'w') as f:
85      with open(os.path.join(BASE_DIR, 'template.html'), 'r') as templatef:
86        template = templatef.read()
87      f.write(string.Template(template).substitute(
88          {'data': json.dumps(data, indent=2),
89           'dllname': binary_name + ' ' + all_data['executable']['version']}))
90
91  return 0
92
93
94if __name__ == '__main__':
95  sys.exit(main())
96