blob: 993e436b0411567a952e18ff91f70bcb122a7caf [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
20import cgi
21import optparse
22import os
23import re
24import subprocess
25import sys
26
27cppfilt_proc = None
28def 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
38def 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, '
Adam Riceff26dcf2017-08-14 05:01:5245 'base::string16_internals::'
46 'string16_char_traits, '
[email protected]74b2cbcf2010-10-12 21:46:4147 'std::allocator<unsigned short> >', 'string16')
48 sym = re.sub(r', std::allocator<\S+\s+>', '', sym)
49 return sym
50
51
52def Parse(input, skip_paths=None, only_paths=None):
53 """Parse the --print-gc-sections build output.
54
55 Args:
56 input: iterable over the lines of the build output
57
58 Yields:
59 (target name, path to .o file, demangled symbol)
60 """
61 symbol_re = re.compile(r"'\.text\.(\S+)' in file '(\S+)'$")
62 path_re = re.compile(r"^out/[^/]+/[^/]+/([^/]+)/(.*)$")
63 for line in input:
64 match = symbol_re.search(line)
65 if not match:
66 continue
67 symbol, path = match.groups()
68 symbol = Unyuck(Demangle(symbol))
69 path = os.path.normpath(path)
70 if skip_paths and skip_paths in path:
71 continue
72 if only_paths and only_paths not in path:
73 continue
74 match = path_re.match(path)
75 if not match:
76 print >>sys.stderr, "Skipping weird path", path
77 continue
78 target, path = match.groups()
79 yield target, path, symbol
80
81
82# HTML header for our output page.
83TEMPLATE_HEADER = """<!DOCTYPE html>
84<head>
85<style>
86body {
87 font-family: sans-serif;
88 font-size: 0.8em;
89}
90h1, h2 {
91 font-weight: normal;
92 margin: 0.5em 0;
93}
94h2 {
95 margin-top: 1em;
96}
97tr:hover {
98 background: #eee;
99}
100.permalink {
101 padding-left: 1ex;
102 font-size: 80%;
103 text-decoration: none;
104 color: #ccc;
105}
106.symbol {
107 font-family: WebKitWorkAround, monospace;
108 margin-left: 4ex;
109 text-indent: -4ex;
110 padding: 0.5ex 1ex;
111}
112.file {
113 padding: 0.5ex 1ex;
114 padding-left: 2ex;
115 font-family: WebKitWorkAround, monospace;
116 font-size: 90%;
117 color: #777;
118}
119</style>
120</head>
121<body>
122<h1>chrome symbols deleted at link time</h1>
123"""
124
125
126def Output(iter):
127 """Print HTML given an iterable of (target, path, symbol) tuples."""
128 targets = {}
129 for target, path, symbol in iter:
130 entries = targets.setdefault(target, [])
131 entries.append((symbol, path))
132
133 print TEMPLATE_HEADER
134 print "<p>jump to target:"
135 print "<select onchange='document.location.hash = this.value'>"
136 for target in sorted(targets.keys()):
137 print "<option>%s</option>" % target
138 print "</select></p>"
139
140 for target in sorted(targets.keys()):
141 print "<h2>%s" % target
142 print "<a class=permalink href='#%s' name='%s'>#</a>" % (target, target)
143 print "</h2>"
144 print "<table width=100% cellspacing=0>"
145 for symbol, path in sorted(targets[target]):
146 htmlsymbol = cgi.escape(symbol).replace('::', '::<wbr>')
147 print "<tr><td><div class=symbol>%s</div></td>" % htmlsymbol
148 print "<td valign=top><div class=file>%s</div></td></tr>" % path
149 print "</table>"
150
151
152def main():
153 parser = optparse.OptionParser(usage='%prog [options] buildoutput\n\n' +
154 __doc__)
155 parser.add_option("--skip-paths", metavar="STR", default="third_party",
156 help="skip paths matching STR [default=%default]")
157 parser.add_option("--only-paths", metavar="STR",
158 help="only include paths matching STR [default=%default]")
159 opts, args = parser.parse_args()
160
161 if len(args) < 1:
162 parser.print_help()
163 sys.exit(1)
164
165 iter = Parse(open(args[0]),
166 skip_paths=opts.skip_paths,
167 only_paths=opts.only_paths)
168 Output(iter)
169
[email protected]cb155a82011-11-29 17:25:34170
[email protected]74b2cbcf2010-10-12 21:46:41171if __name__ == '__main__':
172 main()