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.
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
29 """Demangle a C++ symbol by passing it through c++filt."""
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()
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
)
51 def Parse(input, skip_paths
=None, only_paths
=None):
52 """Parse the --print-gc-sections build output.
55 input: iterable over the lines of the build output
58 (target name, path to .o file, demangled symbol)
60 symbol_re
= re
.compile(r
"'\.text\.(\S+)' in file '(\S+)'$")
61 path_re
= re
.compile(r
"^out/[^/]+/[^/]+/([^/]+)/(.*)$")
63 match
= symbol_re
.search(line
)
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
:
71 if only_paths
and only_paths
not in path
:
73 match
= path_re
.match(path
)
75 print >>sys
.stderr
, "Skipping weird path", path
77 target
, path
= match
.groups()
78 yield target
, path
, symbol
81 # HTML header for our output page.
82 TEMPLATE_HEADER
= """<!DOCTYPE html>
86 font-family: sans-serif;
102 text-decoration: none;
106 font-family: WebKitWorkAround, monospace;
114 font-family: WebKitWorkAround, monospace;
121 <h1>chrome symbols deleted at link time</h1>
126 """Print HTML given an iterable of (target, path, symbol) tuples."""
128 for target
, path
, symbol
in iter:
129 entries
= targets
.setdefault(target
, [])
130 entries
.append((symbol
, path
))
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>"
139 for target
in sorted(targets
.keys()):
140 print "<h2>%s" % target
141 print "<a class=permalink href='#%s' name='%s'>#</a>" % (target
, target
)
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
152 parser
= optparse
.OptionParser(usage
='%prog [options] buildoutput\n\n' +
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()
164 iter = Parse(open(args
[0]),
165 skip_paths
=opts
.skip_paths
,
166 only_paths
=opts
.only_paths
)
170 if __name__
== '__main__':