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.
6 """Prints a report of symbols stripped by the linker due to being unused.
8 To use, build with these linker flags:
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.
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
20 from __future__ import print_function
31 """Demangle a C++ symbol by passing it through c++filt."""
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()
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')
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)
53 def Parse(input, skip_paths=None, only_paths=None):
54 """Parse the --print-gc-sections build output.
57 input: iterable over the lines of the build output
60 (target name, path to .o file, demangled symbol)
62 symbol_re = re.compile(r"'\.text\.(\S+)' in file '(\S+)'$")
63 path_re = re.compile(r"^out/[^/]+/[^/]+/([^/]+)/(.*)$")
65 match = symbol_re.search(line)
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:
73 if only_paths and only_paths not in path:
75 match = path_re.match(path)
77 print("Skipping weird path", path, file=sys.stderr)
79 target, path = match.groups()
80 yield target, path, symbol
83 # HTML header for our output page.
84 TEMPLATE_HEADER = """<!DOCTYPE html>
88 font-family: sans-serif;
104 text-decoration: none;
108 font-family: WebKitWorkAround, monospace;
116 font-family: WebKitWorkAround, monospace;
123 <h1>chrome symbols deleted at link time</h1>
128 """Print HTML given an iterable of (target, path, symbol) tuples."""
130 for target, path, symbol in iter:
131 entries = targets.setdefault(target, [])
132 entries.append((symbol, path))
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>")
141 for target in sorted(targets.keys()):
142 print("<h2>%s" % target)
143 print("<a class=permalink href='#%s' name='%s'>#</a>" % (target, target))
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)
154 parser = optparse.OptionParser(usage='%prog [options] buildoutput\n\n' +
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()
166 iter = Parse(open(args[0]),
167 skip_paths=opts.skip_paths,
168 only_paths=opts.only_paths)
172 if __name__ == '__main__':