123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- #!/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
- """
- from __future__ import print_function
- 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(sym, file=cppfilt_proc.stdin)
- 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<char16_t, std::char_traits<char16_t>, '
- 'std::allocator<char16_t> >', 'std::u16string')
- 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("Skipping weird path", path, file=sys.stderr)
- 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()
|