[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | 74b2cbcf | 2010-10-12 21:46:41 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 6 | """Prints a report of symbols stripped by the linker due to being unused. |
[email protected] | 74b2cbcf | 2010-10-12 21:46:41 | [diff] [blame] | 7 | |
| 8 | To use, build with these linker flags: |
| 9 | -Wl,--gc-sections |
| 10 | -Wl,--print-gc-sections |
| 11 | the first one is the default in Release; search build/common.gypi for it |
| 12 | and to see where to add the other. |
| 13 | |
| 14 | Then build, saving the output into a file: |
| 15 | make chrome 2>&1 | tee buildlog |
| 16 | and run this script on it: |
| 17 | ./tools/unused-symbols-report.py buildlog > report.html |
| 18 | """ |
| 19 | |
| 20 | import cgi |
| 21 | import optparse |
| 22 | import os |
| 23 | import re |
| 24 | import subprocess |
| 25 | import sys |
| 26 | |
| 27 | cppfilt_proc = None |
| 28 | def 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 | |
| 38 | def 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, ' |
| 45 | 'base::string16_char_traits, ' |
| 46 | 'std::allocator<unsigned short> >', 'string16') |
| 47 | sym = re.sub(r', std::allocator<\S+\s+>', '', sym) |
| 48 | return sym |
| 49 | |
| 50 | |
| 51 | def Parse(input, skip_paths=None, only_paths=None): |
| 52 | """Parse the --print-gc-sections build output. |
| 53 | |
| 54 | Args: |
| 55 | input: iterable over the lines of the build output |
| 56 | |
| 57 | Yields: |
| 58 | (target name, path to .o file, demangled symbol) |
| 59 | """ |
| 60 | symbol_re = re.compile(r"'\.text\.(\S+)' in file '(\S+)'$") |
| 61 | path_re = re.compile(r"^out/[^/]+/[^/]+/([^/]+)/(.*)$") |
| 62 | for line in input: |
| 63 | match = symbol_re.search(line) |
| 64 | if not match: |
| 65 | continue |
| 66 | symbol, path = match.groups() |
| 67 | symbol = Unyuck(Demangle(symbol)) |
| 68 | path = os.path.normpath(path) |
| 69 | if skip_paths and skip_paths in path: |
| 70 | continue |
| 71 | if only_paths and only_paths not in path: |
| 72 | continue |
| 73 | match = path_re.match(path) |
| 74 | if not match: |
| 75 | print >>sys.stderr, "Skipping weird path", path |
| 76 | continue |
| 77 | target, path = match.groups() |
| 78 | yield target, path, symbol |
| 79 | |
| 80 | |
| 81 | # HTML header for our output page. |
| 82 | TEMPLATE_HEADER = """<!DOCTYPE html> |
| 83 | <head> |
| 84 | <style> |
| 85 | body { |
| 86 | font-family: sans-serif; |
| 87 | font-size: 0.8em; |
| 88 | } |
| 89 | h1, h2 { |
| 90 | font-weight: normal; |
| 91 | margin: 0.5em 0; |
| 92 | } |
| 93 | h2 { |
| 94 | margin-top: 1em; |
| 95 | } |
| 96 | tr:hover { |
| 97 | background: #eee; |
| 98 | } |
| 99 | .permalink { |
| 100 | padding-left: 1ex; |
| 101 | font-size: 80%; |
| 102 | text-decoration: none; |
| 103 | color: #ccc; |
| 104 | } |
| 105 | .symbol { |
| 106 | font-family: WebKitWorkAround, monospace; |
| 107 | margin-left: 4ex; |
| 108 | text-indent: -4ex; |
| 109 | padding: 0.5ex 1ex; |
| 110 | } |
| 111 | .file { |
| 112 | padding: 0.5ex 1ex; |
| 113 | padding-left: 2ex; |
| 114 | font-family: WebKitWorkAround, monospace; |
| 115 | font-size: 90%; |
| 116 | color: #777; |
| 117 | } |
| 118 | </style> |
| 119 | </head> |
| 120 | <body> |
| 121 | <h1>chrome symbols deleted at link time</h1> |
| 122 | """ |
| 123 | |
| 124 | |
| 125 | def Output(iter): |
| 126 | """Print HTML given an iterable of (target, path, symbol) tuples.""" |
| 127 | targets = {} |
| 128 | for target, path, symbol in iter: |
| 129 | entries = targets.setdefault(target, []) |
| 130 | entries.append((symbol, path)) |
| 131 | |
| 132 | print TEMPLATE_HEADER |
| 133 | print "<p>jump to target:" |
| 134 | print "<select onchange='document.location.hash = this.value'>" |
| 135 | for target in sorted(targets.keys()): |
| 136 | print "<option>%s</option>" % target |
| 137 | print "</select></p>" |
| 138 | |
| 139 | for target in sorted(targets.keys()): |
| 140 | print "<h2>%s" % target |
| 141 | print "<a class=permalink href='#%s' name='%s'>#</a>" % (target, target) |
| 142 | print "</h2>" |
| 143 | print "<table width=100% cellspacing=0>" |
| 144 | for symbol, path in sorted(targets[target]): |
| 145 | htmlsymbol = cgi.escape(symbol).replace('::', '::<wbr>') |
| 146 | print "<tr><td><div class=symbol>%s</div></td>" % htmlsymbol |
| 147 | print "<td valign=top><div class=file>%s</div></td></tr>" % path |
| 148 | print "</table>" |
| 149 | |
| 150 | |
| 151 | def main(): |
| 152 | parser = optparse.OptionParser(usage='%prog [options] buildoutput\n\n' + |
| 153 | __doc__) |
| 154 | parser.add_option("--skip-paths", metavar="STR", default="third_party", |
| 155 | help="skip paths matching STR [default=%default]") |
| 156 | parser.add_option("--only-paths", metavar="STR", |
| 157 | help="only include paths matching STR [default=%default]") |
| 158 | opts, args = parser.parse_args() |
| 159 | |
| 160 | if len(args) < 1: |
| 161 | parser.print_help() |
| 162 | sys.exit(1) |
| 163 | |
| 164 | iter = Parse(open(args[0]), |
| 165 | skip_paths=opts.skip_paths, |
| 166 | only_paths=opts.only_paths) |
| 167 | Output(iter) |
| 168 | |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 169 | |
[email protected] | 74b2cbcf | 2010-10-12 21:46:41 | [diff] [blame] | 170 | if __name__ == '__main__': |
| 171 | main() |