| Index: third_party/chrome/tools/unused-symbols-report.py
|
| diff --git a/third_party/chrome/tools/unused-symbols-report.py b/third_party/chrome/tools/unused-symbols-report.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..900bf16e120b1b0bd8b94647ed5e2464badb933e
|
| --- /dev/null
|
| +++ b/third_party/chrome/tools/unused-symbols-report.py
|
| @@ -0,0 +1,171 @@
|
| +#!/usr/bin/env python
|
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""Prints a report of symbols stripped by the linker due to being unused.
|
| +
|
| +To use, build with these linker flags:
|
| + -Wl,--gc-sections
|
| + -Wl,--print-gc-sections
|
| +the first one is the default in Release; search build/common.gypi for it
|
| +and to see where to add the other.
|
| +
|
| +Then build, saving the output into a file:
|
| + make chrome 2>&1 | tee buildlog
|
| +and run this script on it:
|
| + ./tools/unused-symbols-report.py buildlog > report.html
|
| +"""
|
| +
|
| +import cgi
|
| +import optparse
|
| +import os
|
| +import re
|
| +import subprocess
|
| +import sys
|
| +
|
| +cppfilt_proc = None
|
| +def Demangle(sym):
|
| + """Demangle a C++ symbol by passing it through c++filt."""
|
| + global cppfilt_proc
|
| + if cppfilt_proc is None:
|
| + cppfilt_proc = subprocess.Popen(['c++filt'], stdin=subprocess.PIPE,
|
| + stdout=subprocess.PIPE)
|
| + print >>cppfilt_proc.stdin, sym
|
| + return cppfilt_proc.stdout.readline().strip()
|
| +
|
| +
|
| +def Unyuck(sym):
|
| + """Attempt to prettify a C++ symbol by some basic heuristics."""
|
| + sym = sym.replace('std::basic_string<char, std::char_traits<char>, '
|
| + 'std::allocator<char> >', 'std::string')
|
| + sym = sym.replace('std::basic_string<wchar_t, std::char_traits<wchar_t>, '
|
| + 'std::allocator<wchar_t> >', 'std::wstring')
|
| + sym = sym.replace('std::basic_string<unsigned short, '
|
| + 'base::string16_char_traits, '
|
| + 'std::allocator<unsigned short> >', 'string16')
|
| + sym = re.sub(r', std::allocator<\S+\s+>', '', sym)
|
| + return sym
|
| +
|
| +
|
| +def Parse(input, skip_paths=None, only_paths=None):
|
| + """Parse the --print-gc-sections build output.
|
| +
|
| + Args:
|
| + input: iterable over the lines of the build output
|
| +
|
| + Yields:
|
| + (target name, path to .o file, demangled symbol)
|
| + """
|
| + symbol_re = re.compile(r"'\.text\.(\S+)' in file '(\S+)'$")
|
| + path_re = re.compile(r"^out/[^/]+/[^/]+/([^/]+)/(.*)$")
|
| + for line in input:
|
| + match = symbol_re.search(line)
|
| + if not match:
|
| + continue
|
| + symbol, path = match.groups()
|
| + symbol = Unyuck(Demangle(symbol))
|
| + path = os.path.normpath(path)
|
| + if skip_paths and skip_paths in path:
|
| + continue
|
| + if only_paths and only_paths not in path:
|
| + continue
|
| + match = path_re.match(path)
|
| + if not match:
|
| + print >>sys.stderr, "Skipping weird path", path
|
| + continue
|
| + target, path = match.groups()
|
| + yield target, path, symbol
|
| +
|
| +
|
| +# HTML header for our output page.
|
| +TEMPLATE_HEADER = """<!DOCTYPE html>
|
| +<head>
|
| +<style>
|
| +body {
|
| + font-family: sans-serif;
|
| + font-size: 0.8em;
|
| +}
|
| +h1, h2 {
|
| + font-weight: normal;
|
| + margin: 0.5em 0;
|
| +}
|
| +h2 {
|
| + margin-top: 1em;
|
| +}
|
| +tr:hover {
|
| + background: #eee;
|
| +}
|
| +.permalink {
|
| + padding-left: 1ex;
|
| + font-size: 80%;
|
| + text-decoration: none;
|
| + color: #ccc;
|
| +}
|
| +.symbol {
|
| + font-family: WebKitWorkAround, monospace;
|
| + margin-left: 4ex;
|
| + text-indent: -4ex;
|
| + padding: 0.5ex 1ex;
|
| +}
|
| +.file {
|
| + padding: 0.5ex 1ex;
|
| + padding-left: 2ex;
|
| + font-family: WebKitWorkAround, monospace;
|
| + font-size: 90%;
|
| + color: #777;
|
| +}
|
| +</style>
|
| +</head>
|
| +<body>
|
| +<h1>chrome symbols deleted at link time</h1>
|
| +"""
|
| +
|
| +
|
| +def Output(iter):
|
| + """Print HTML given an iterable of (target, path, symbol) tuples."""
|
| + targets = {}
|
| + for target, path, symbol in iter:
|
| + entries = targets.setdefault(target, [])
|
| + entries.append((symbol, path))
|
| +
|
| + print TEMPLATE_HEADER
|
| + print "<p>jump to target:"
|
| + print "<select onchange='document.location.hash = this.value'>"
|
| + for target in sorted(targets.keys()):
|
| + print "<option>%s</option>" % target
|
| + print "</select></p>"
|
| +
|
| + for target in sorted(targets.keys()):
|
| + print "<h2>%s" % target
|
| + print "<a class=permalink href='#%s' name='%s'>#</a>" % (target, target)
|
| + print "</h2>"
|
| + print "<table width=100% cellspacing=0>"
|
| + for symbol, path in sorted(targets[target]):
|
| + htmlsymbol = cgi.escape(symbol).replace('::', '::<wbr>')
|
| + print "<tr><td><div class=symbol>%s</div></td>" % htmlsymbol
|
| + print "<td valign=top><div class=file>%s</div></td></tr>" % path
|
| + print "</table>"
|
| +
|
| +
|
| +def main():
|
| + parser = optparse.OptionParser(usage='%prog [options] buildoutput\n\n' +
|
| + __doc__)
|
| + parser.add_option("--skip-paths", metavar="STR", default="third_party",
|
| + help="skip paths matching STR [default=%default]")
|
| + parser.add_option("--only-paths", metavar="STR",
|
| + help="only include paths matching STR [default=%default]")
|
| + opts, args = parser.parse_args()
|
| +
|
| + if len(args) < 1:
|
| + parser.print_help()
|
| + sys.exit(1)
|
| +
|
| + iter = Parse(open(args[0]),
|
| + skip_paths=opts.skip_paths,
|
| + only_paths=opts.only_paths)
|
| + Output(iter)
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + main()
|
|
|