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