From 74b2cbcf9f8868fe0b9b7c0ac54ecb33f5d59bbc Mon Sep 17 00:00:00 2001 From: "evan@chromium.org" Date: Tue, 12 Oct 2010 21:46:41 +0000 Subject: 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 --- tools/unused-symbols-report.py | 170 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100755 tools/unused-symbols-report.py 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, ' + 'std::allocator >', 'std::string') + sym = sym.replace('std::basic_string, ' + 'std::allocator >', 'std::wstring') + sym = sym.replace('std::basic_string >', '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 = """ + + + + +

chrome symbols deleted at link time

+""" + + +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 "

jump to target:" + print "

" + + for target in sorted(targets.keys()): + print "

%s" % target + print "" % (target, target) + print "

" + print "" + for symbol, path in sorted(targets[target]): + htmlsymbol = cgi.escape(symbol).replace('::', '::') + print "" % htmlsymbol + print "" % path + print "
%s
%s
" + + +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() -- cgit v1.1