Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / tools / unused-symbols-report.py
1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Prints a report of symbols stripped by the linker due to being unused.
7
8 To use, build with these linker flags:
9   -Wl,--gc-sections
10   -Wl,--print-gc-sections
11 the first one is the default in Release; search build/common.gypi for it
12 and to see where to add the other.
13
14 Then build, saving the output into a file:
15   make chrome 2>&1 | tee buildlog
16 and run this script on it:
17   ./tools/unused-symbols-report.py buildlog > report.html
18 """
19
20 import cgi
21 import optparse
22 import os
23 import re
24 import subprocess
25 import sys
26
27 cppfilt_proc = None
28 def Demangle(sym):
29   """Demangle a C++ symbol by passing it through c++filt."""
30   global cppfilt_proc
31   if cppfilt_proc is None:
32     cppfilt_proc = subprocess.Popen(['c++filt'], stdin=subprocess.PIPE,
33                                     stdout=subprocess.PIPE)
34   print >>cppfilt_proc.stdin, sym
35   return cppfilt_proc.stdout.readline().strip()
36
37
38 def Unyuck(sym):
39   """Attempt to prettify a C++ symbol by some basic heuristics."""
40   sym = sym.replace('std::basic_string<char, std::char_traits<char>, '
41                     'std::allocator<char> >', 'std::string')
42   sym = sym.replace('std::basic_string<wchar_t, std::char_traits<wchar_t>, '
43                     'std::allocator<wchar_t> >', 'std::wstring')
44   sym = sym.replace('std::basic_string<unsigned short, '
45                     'base::string16_char_traits, '
46                     'std::allocator<unsigned short> >', 'string16')
47   sym = re.sub(r', std::allocator<\S+\s+>', '', sym)
48   return sym
49
50
51 def Parse(input, skip_paths=None, only_paths=None):
52   """Parse the --print-gc-sections build output.
53
54   Args:
55     input: iterable over the lines of the build output
56
57   Yields:
58     (target name, path to .o file, demangled symbol)
59   """
60   symbol_re = re.compile(r"'\.text\.(\S+)' in file '(\S+)'$")
61   path_re = re.compile(r"^out/[^/]+/[^/]+/([^/]+)/(.*)$")
62   for line in input:
63     match = symbol_re.search(line)
64     if not match:
65       continue
66     symbol, path = match.groups()
67     symbol = Unyuck(Demangle(symbol))
68     path = os.path.normpath(path)
69     if skip_paths and skip_paths in path:
70       continue
71     if only_paths and only_paths not in path:
72       continue
73     match = path_re.match(path)
74     if not match:
75       print >>sys.stderr, "Skipping weird path", path
76       continue
77     target, path = match.groups()
78     yield target, path, symbol
79
80
81 # HTML header for our output page.
82 TEMPLATE_HEADER = """<!DOCTYPE html>
83 <head>
84 <style>
85 body {
86   font-family: sans-serif;
87   font-size: 0.8em;
88 }
89 h1, h2 {
90   font-weight: normal;
91   margin: 0.5em 0;
92 }
93 h2 {
94   margin-top: 1em;
95 }
96 tr:hover {
97   background: #eee;
98 }
99 .permalink {
100   padding-left: 1ex;
101   font-size: 80%;
102   text-decoration: none;
103   color: #ccc;
104 }
105 .symbol {
106   font-family: WebKitWorkAround, monospace;
107   margin-left: 4ex;
108   text-indent: -4ex;
109   padding: 0.5ex 1ex;
110 }
111 .file {
112   padding: 0.5ex 1ex;
113   padding-left: 2ex;
114   font-family: WebKitWorkAround, monospace;
115   font-size: 90%;
116   color: #777;
117 }
118 </style>
119 </head>
120 <body>
121 <h1>chrome symbols deleted at link time</h1>
122 """
123
124
125 def Output(iter):
126   """Print HTML given an iterable of (target, path, symbol) tuples."""
127   targets = {}
128   for target, path, symbol in iter:
129     entries = targets.setdefault(target, [])
130     entries.append((symbol, path))
131
132   print TEMPLATE_HEADER
133   print "<p>jump to target:"
134   print "<select onchange='document.location.hash = this.value'>"
135   for target in sorted(targets.keys()):
136     print "<option>%s</option>" % target
137   print "</select></p>"
138
139   for target in sorted(targets.keys()):
140     print "<h2>%s" % target
141     print "<a class=permalink href='#%s' name='%s'>#</a>" % (target, target)
142     print "</h2>"
143     print "<table width=100% cellspacing=0>"
144     for symbol, path in sorted(targets[target]):
145       htmlsymbol = cgi.escape(symbol).replace('::', '::<wbr>')
146       print "<tr><td><div class=symbol>%s</div></td>" % htmlsymbol
147       print "<td valign=top><div class=file>%s</div></td></tr>" % path
148     print "</table>"
149
150
151 def main():
152   parser = optparse.OptionParser(usage='%prog [options] buildoutput\n\n' +
153                                  __doc__)
154   parser.add_option("--skip-paths", metavar="STR", default="third_party",
155                     help="skip paths matching STR [default=%default]")
156   parser.add_option("--only-paths", metavar="STR",
157                     help="only include paths matching STR [default=%default]")
158   opts, args = parser.parse_args()
159
160   if len(args) < 1:
161     parser.print_help()
162     sys.exit(1)
163
164   iter = Parse(open(args[0]),
165                skip_paths=opts.skip_paths,
166                only_paths=opts.only_paths)
167   Output(iter)
168
169
170 if __name__ == '__main__':
171   main()