diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-12 21:46:41 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-12 21:46:41 +0000 |
commit | 74b2cbcf9f8868fe0b9b7c0ac54ecb33f5d59bbc (patch) | |
tree | 918411406831f9d29276db1423d8432ff03527d8 /tools/unused-symbols-report.py | |
parent | b58dd3e18054fe1f1567f810114e8a84fa144a21 (diff) | |
download | chromium_src-74b2cbcf9f8868fe0b9b7c0ac54ecb33f5d59bbc.zip chromium_src-74b2cbcf9f8868fe0b9b7c0ac54ecb33f5d59bbc.tar.gz chromium_src-74b2cbcf9f8868fe0b9b7c0ac54ecb33f5d59bbc.tar.bz2 |
Check in a script that prints linker-determined unused symbols.
This outputs an HTML report of symbols that the linker claimed
were not used. It includes instructions on how to get the linker
to emit that.
Review URL: http://codereview.chromium.org/3739002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62342 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/unused-symbols-report.py')
-rwxr-xr-x | tools/unused-symbols-report.py | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/tools/unused-symbols-report.py b/tools/unused-symbols-report.py new file mode 100755 index 0000000..12eb053 --- /dev/null +++ b/tools/unused-symbols-report.py @@ -0,0 +1,170 @@ +#!/usr/bin/python +# Copyright (c) 2010 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. + +"""Print 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() |