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