xref: /aosp_15_r20/external/swiftshader/third_party/subzero/bloat/bloat.py (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker#!/usr/bin/python
2*03ce13f7SAndroid Build Coastguard Worker#
3*03ce13f7SAndroid Build Coastguard Worker# Copyright 2013 Google Inc. All Rights Reserved.
4*03ce13f7SAndroid Build Coastguard Worker#
5*03ce13f7SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*03ce13f7SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*03ce13f7SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*03ce13f7SAndroid Build Coastguard Worker#
9*03ce13f7SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*03ce13f7SAndroid Build Coastguard Worker#
11*03ce13f7SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*03ce13f7SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*03ce13f7SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*03ce13f7SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*03ce13f7SAndroid Build Coastguard Worker# limitations under the License.
16*03ce13f7SAndroid Build Coastguard Worker
17*03ce13f7SAndroid Build Coastguard Workerimport fileinput
18*03ce13f7SAndroid Build Coastguard Workerimport operator
19*03ce13f7SAndroid Build Coastguard Workerimport optparse
20*03ce13f7SAndroid Build Coastguard Workerimport os
21*03ce13f7SAndroid Build Coastguard Workerimport pprint
22*03ce13f7SAndroid Build Coastguard Workerimport re
23*03ce13f7SAndroid Build Coastguard Workerimport subprocess
24*03ce13f7SAndroid Build Coastguard Workerimport sys
25*03ce13f7SAndroid Build Coastguard Workerimport json
26*03ce13f7SAndroid Build Coastguard Worker
27*03ce13f7SAndroid Build Coastguard Workerdef format_bytes(bytes):
28*03ce13f7SAndroid Build Coastguard Worker    """Pretty-print a number of bytes."""
29*03ce13f7SAndroid Build Coastguard Worker    if bytes > 1e6:
30*03ce13f7SAndroid Build Coastguard Worker        bytes = bytes / 1.0e6
31*03ce13f7SAndroid Build Coastguard Worker        return '%.1fm' % bytes
32*03ce13f7SAndroid Build Coastguard Worker    if bytes > 1e3:
33*03ce13f7SAndroid Build Coastguard Worker        bytes = bytes / 1.0e3
34*03ce13f7SAndroid Build Coastguard Worker        return '%.1fk' % bytes
35*03ce13f7SAndroid Build Coastguard Worker    return str(bytes)
36*03ce13f7SAndroid Build Coastguard Worker
37*03ce13f7SAndroid Build Coastguard Worker
38*03ce13f7SAndroid Build Coastguard Workerdef symbol_type_to_human(type):
39*03ce13f7SAndroid Build Coastguard Worker    """Convert a symbol type as printed by nm into a human-readable name."""
40*03ce13f7SAndroid Build Coastguard Worker    return {
41*03ce13f7SAndroid Build Coastguard Worker        'b': 'bss',
42*03ce13f7SAndroid Build Coastguard Worker        'd': 'data',
43*03ce13f7SAndroid Build Coastguard Worker        'r': 'read-only data',
44*03ce13f7SAndroid Build Coastguard Worker        't': 'code',
45*03ce13f7SAndroid Build Coastguard Worker        'u': 'weak symbol', # Unique global.
46*03ce13f7SAndroid Build Coastguard Worker        'w': 'weak symbol',
47*03ce13f7SAndroid Build Coastguard Worker        'v': 'weak symbol'
48*03ce13f7SAndroid Build Coastguard Worker        }[type]
49*03ce13f7SAndroid Build Coastguard Worker
50*03ce13f7SAndroid Build Coastguard Worker
51*03ce13f7SAndroid Build Coastguard Workerdef parse_nm(input):
52*03ce13f7SAndroid Build Coastguard Worker    """Parse nm output.
53*03ce13f7SAndroid Build Coastguard Worker
54*03ce13f7SAndroid Build Coastguard Worker    Argument: an iterable over lines of nm output.
55*03ce13f7SAndroid Build Coastguard Worker
56*03ce13f7SAndroid Build Coastguard Worker    Yields: (symbol name, symbol type, symbol size, source file path).
57*03ce13f7SAndroid Build Coastguard Worker    Path may be None if nm couldn't figure out the source file.
58*03ce13f7SAndroid Build Coastguard Worker    """
59*03ce13f7SAndroid Build Coastguard Worker
60*03ce13f7SAndroid Build Coastguard Worker    # Match lines with size + symbol + optional filename.
61*03ce13f7SAndroid Build Coastguard Worker    sym_re = re.compile(r'^[0-9a-f]+ ([0-9a-f]+) (.) ([^\t]+)(?:\t(.*):\d+)?$')
62*03ce13f7SAndroid Build Coastguard Worker
63*03ce13f7SAndroid Build Coastguard Worker    # Match lines with addr but no size.
64*03ce13f7SAndroid Build Coastguard Worker    addr_re = re.compile(r'^[0-9a-f]+ (.) ([^\t]+)(?:\t.*)?$')
65*03ce13f7SAndroid Build Coastguard Worker    # Match lines that don't have an address at all -- typically external symbols.
66*03ce13f7SAndroid Build Coastguard Worker    noaddr_re = re.compile(r'^ + (.) (.*)$')
67*03ce13f7SAndroid Build Coastguard Worker
68*03ce13f7SAndroid Build Coastguard Worker    for line in input:
69*03ce13f7SAndroid Build Coastguard Worker        line = line.rstrip()
70*03ce13f7SAndroid Build Coastguard Worker        match = sym_re.match(line)
71*03ce13f7SAndroid Build Coastguard Worker        if match:
72*03ce13f7SAndroid Build Coastguard Worker            size, type, sym = match.groups()[0:3]
73*03ce13f7SAndroid Build Coastguard Worker            size = int(size, 16)
74*03ce13f7SAndroid Build Coastguard Worker            type = type.lower()
75*03ce13f7SAndroid Build Coastguard Worker            if type in ['u', 'v']:
76*03ce13f7SAndroid Build Coastguard Worker                type = 'w'  # just call them all weak
77*03ce13f7SAndroid Build Coastguard Worker            if type == 'b':
78*03ce13f7SAndroid Build Coastguard Worker                continue  # skip all BSS for now
79*03ce13f7SAndroid Build Coastguard Worker            path = match.group(4)
80*03ce13f7SAndroid Build Coastguard Worker            yield sym, type, size, path
81*03ce13f7SAndroid Build Coastguard Worker            continue
82*03ce13f7SAndroid Build Coastguard Worker        match = addr_re.match(line)
83*03ce13f7SAndroid Build Coastguard Worker        if match:
84*03ce13f7SAndroid Build Coastguard Worker            type, sym = match.groups()[0:2]
85*03ce13f7SAndroid Build Coastguard Worker            # No size == we don't care.
86*03ce13f7SAndroid Build Coastguard Worker            continue
87*03ce13f7SAndroid Build Coastguard Worker        match = noaddr_re.match(line)
88*03ce13f7SAndroid Build Coastguard Worker        if match:
89*03ce13f7SAndroid Build Coastguard Worker            type, sym = match.groups()
90*03ce13f7SAndroid Build Coastguard Worker            if type in ('U', 'w'):
91*03ce13f7SAndroid Build Coastguard Worker                # external or weak symbol
92*03ce13f7SAndroid Build Coastguard Worker                continue
93*03ce13f7SAndroid Build Coastguard Worker
94*03ce13f7SAndroid Build Coastguard Worker        print >>sys.stderr, 'unparsed:', repr(line)
95*03ce13f7SAndroid Build Coastguard Worker
96*03ce13f7SAndroid Build Coastguard Workerdef demangle(ident, cppfilt):
97*03ce13f7SAndroid Build Coastguard Worker    if cppfilt and ident.startswith('_Z'):
98*03ce13f7SAndroid Build Coastguard Worker        # Demangle names when possible. Mangled names all start with _Z.
99*03ce13f7SAndroid Build Coastguard Worker        ident = subprocess.check_output([cppfilt, ident]).strip()
100*03ce13f7SAndroid Build Coastguard Worker    return ident
101*03ce13f7SAndroid Build Coastguard Worker
102*03ce13f7SAndroid Build Coastguard Worker
103*03ce13f7SAndroid Build Coastguard Workerclass Suffix:
104*03ce13f7SAndroid Build Coastguard Worker    def __init__(self, suffix, replacement):
105*03ce13f7SAndroid Build Coastguard Worker        self.pattern = '^(.*)' + suffix + '(.*)$'
106*03ce13f7SAndroid Build Coastguard Worker        self.re = re.compile(self.pattern)
107*03ce13f7SAndroid Build Coastguard Worker        self.replacement = replacement
108*03ce13f7SAndroid Build Coastguard Worker
109*03ce13f7SAndroid Build Coastguard Workerclass SuffixCleanup:
110*03ce13f7SAndroid Build Coastguard Worker    """Pre-compile suffix regular expressions."""
111*03ce13f7SAndroid Build Coastguard Worker    def __init__(self):
112*03ce13f7SAndroid Build Coastguard Worker        self.suffixes = [
113*03ce13f7SAndroid Build Coastguard Worker            Suffix('\.part\.([0-9]+)',      'part'),
114*03ce13f7SAndroid Build Coastguard Worker            Suffix('\.constprop\.([0-9]+)', 'constprop'),
115*03ce13f7SAndroid Build Coastguard Worker            Suffix('\.isra\.([0-9]+)',      'isra'),
116*03ce13f7SAndroid Build Coastguard Worker        ]
117*03ce13f7SAndroid Build Coastguard Worker    def cleanup(self, ident, cppfilt):
118*03ce13f7SAndroid Build Coastguard Worker        """Cleanup identifiers that have suffixes preventing demangling,
119*03ce13f7SAndroid Build Coastguard Worker           and demangle if possible."""
120*03ce13f7SAndroid Build Coastguard Worker        to_append = []
121*03ce13f7SAndroid Build Coastguard Worker        for s in self.suffixes:
122*03ce13f7SAndroid Build Coastguard Worker            found = s.re.match(ident)
123*03ce13f7SAndroid Build Coastguard Worker            if not found:
124*03ce13f7SAndroid Build Coastguard Worker                continue
125*03ce13f7SAndroid Build Coastguard Worker            to_append += [' [' + s.replacement + '.' + found.group(2) + ']']
126*03ce13f7SAndroid Build Coastguard Worker            ident = found.group(1) + found.group(3)
127*03ce13f7SAndroid Build Coastguard Worker        if len(to_append) > 0:
128*03ce13f7SAndroid Build Coastguard Worker            # Only try to demangle if there were suffixes.
129*03ce13f7SAndroid Build Coastguard Worker            ident = demangle(ident, cppfilt)
130*03ce13f7SAndroid Build Coastguard Worker        for s in to_append:
131*03ce13f7SAndroid Build Coastguard Worker            ident += s
132*03ce13f7SAndroid Build Coastguard Worker        return ident
133*03ce13f7SAndroid Build Coastguard Worker
134*03ce13f7SAndroid Build Coastguard Workersuffix_cleanup = SuffixCleanup()
135*03ce13f7SAndroid Build Coastguard Worker
136*03ce13f7SAndroid Build Coastguard Workerdef parse_cpp_name(name, cppfilt):
137*03ce13f7SAndroid Build Coastguard Worker    name = suffix_cleanup.cleanup(name, cppfilt)
138*03ce13f7SAndroid Build Coastguard Worker
139*03ce13f7SAndroid Build Coastguard Worker    # Turn prefixes into suffixes so namespacing works.
140*03ce13f7SAndroid Build Coastguard Worker    prefixes = [
141*03ce13f7SAndroid Build Coastguard Worker        ['bool ',                         ''],
142*03ce13f7SAndroid Build Coastguard Worker        ['construction vtable for ',      ' [construction vtable]'],
143*03ce13f7SAndroid Build Coastguard Worker        ['global constructors keyed to ', ' [global constructors]'],
144*03ce13f7SAndroid Build Coastguard Worker        ['guard variable for ',           ' [guard variable]'],
145*03ce13f7SAndroid Build Coastguard Worker        ['int ',                          ''],
146*03ce13f7SAndroid Build Coastguard Worker        ['non-virtual thunk to ',         ' [non-virtual thunk]'],
147*03ce13f7SAndroid Build Coastguard Worker        ['typeinfo for ',                 ' [typeinfo]'],
148*03ce13f7SAndroid Build Coastguard Worker        ['typeinfo name for ',            ' [typeinfo name]'],
149*03ce13f7SAndroid Build Coastguard Worker        ['virtual thunk to ',             ' [virtual thunk]'],
150*03ce13f7SAndroid Build Coastguard Worker        ['void ',                         ''],
151*03ce13f7SAndroid Build Coastguard Worker        ['vtable for ',                   ' [vtable]'],
152*03ce13f7SAndroid Build Coastguard Worker        ['VTT for ',                      ' [VTT]'],
153*03ce13f7SAndroid Build Coastguard Worker    ]
154*03ce13f7SAndroid Build Coastguard Worker    for prefix, replacement in prefixes:
155*03ce13f7SAndroid Build Coastguard Worker        if name.startswith(prefix):
156*03ce13f7SAndroid Build Coastguard Worker            name = name[len(prefix):] + replacement
157*03ce13f7SAndroid Build Coastguard Worker    # Simplify parenthesis parsing.
158*03ce13f7SAndroid Build Coastguard Worker    replacements = [
159*03ce13f7SAndroid Build Coastguard Worker        ['(anonymous namespace)', '[anonymous namespace]'],
160*03ce13f7SAndroid Build Coastguard Worker    ]
161*03ce13f7SAndroid Build Coastguard Worker    for value, replacement in replacements:
162*03ce13f7SAndroid Build Coastguard Worker        name = name.replace(value, replacement)
163*03ce13f7SAndroid Build Coastguard Worker
164*03ce13f7SAndroid Build Coastguard Worker    def parse_one(val):
165*03ce13f7SAndroid Build Coastguard Worker        """Returns (leftmost-part, remaining)."""
166*03ce13f7SAndroid Build Coastguard Worker        if (val.startswith('operator') and
167*03ce13f7SAndroid Build Coastguard Worker            not (val[8].isalnum() or val[8] == '_')):
168*03ce13f7SAndroid Build Coastguard Worker            # Operator overload function, terminate.
169*03ce13f7SAndroid Build Coastguard Worker            return (val, '')
170*03ce13f7SAndroid Build Coastguard Worker        co = val.find('::')
171*03ce13f7SAndroid Build Coastguard Worker        lt = val.find('<')
172*03ce13f7SAndroid Build Coastguard Worker        pa = val.find('(')
173*03ce13f7SAndroid Build Coastguard Worker        co = len(val) if co == -1 else co
174*03ce13f7SAndroid Build Coastguard Worker        lt = len(val) if lt == -1 else lt
175*03ce13f7SAndroid Build Coastguard Worker        pa = len(val) if pa == -1 else pa
176*03ce13f7SAndroid Build Coastguard Worker        if co < lt and co < pa:
177*03ce13f7SAndroid Build Coastguard Worker            # Namespace or type name.
178*03ce13f7SAndroid Build Coastguard Worker            return (val[:co], val[co+2:])
179*03ce13f7SAndroid Build Coastguard Worker        if lt < pa:
180*03ce13f7SAndroid Build Coastguard Worker            # Template. Make sure we capture nested templates too.
181*03ce13f7SAndroid Build Coastguard Worker            open_tmpl = 1
182*03ce13f7SAndroid Build Coastguard Worker            gt = lt
183*03ce13f7SAndroid Build Coastguard Worker            while val[gt] != '>' or open_tmpl != 0:
184*03ce13f7SAndroid Build Coastguard Worker                gt = gt + 1
185*03ce13f7SAndroid Build Coastguard Worker                if val[gt] == '<':
186*03ce13f7SAndroid Build Coastguard Worker                    open_tmpl = open_tmpl + 1
187*03ce13f7SAndroid Build Coastguard Worker                if val[gt] == '>':
188*03ce13f7SAndroid Build Coastguard Worker                    open_tmpl = open_tmpl - 1
189*03ce13f7SAndroid Build Coastguard Worker            ret = val[gt+1:]
190*03ce13f7SAndroid Build Coastguard Worker            if ret.startswith('::'):
191*03ce13f7SAndroid Build Coastguard Worker                ret = ret[2:]
192*03ce13f7SAndroid Build Coastguard Worker            if ret.startswith('('):
193*03ce13f7SAndroid Build Coastguard Worker                # Template function, terminate.
194*03ce13f7SAndroid Build Coastguard Worker                return (val, '')
195*03ce13f7SAndroid Build Coastguard Worker            return (val[:gt+1], ret)
196*03ce13f7SAndroid Build Coastguard Worker        # Terminate with any function name, identifier, or unmangled name.
197*03ce13f7SAndroid Build Coastguard Worker        return (val, '')
198*03ce13f7SAndroid Build Coastguard Worker
199*03ce13f7SAndroid Build Coastguard Worker    parts = []
200*03ce13f7SAndroid Build Coastguard Worker    while len(name) > 0:
201*03ce13f7SAndroid Build Coastguard Worker        (part, name) = parse_one(name)
202*03ce13f7SAndroid Build Coastguard Worker        assert len(part) > 0
203*03ce13f7SAndroid Build Coastguard Worker        parts.append(part)
204*03ce13f7SAndroid Build Coastguard Worker    return parts
205*03ce13f7SAndroid Build Coastguard Worker
206*03ce13f7SAndroid Build Coastguard Worker
207*03ce13f7SAndroid Build Coastguard Workerdef treeify_syms(symbols, strip_prefix=None, cppfilt=None):
208*03ce13f7SAndroid Build Coastguard Worker    dirs = {}
209*03ce13f7SAndroid Build Coastguard Worker    for sym, type, size, path in symbols:
210*03ce13f7SAndroid Build Coastguard Worker        if path:
211*03ce13f7SAndroid Build Coastguard Worker            path = os.path.normpath(path)
212*03ce13f7SAndroid Build Coastguard Worker            if strip_prefix and path.startswith(strip_prefix):
213*03ce13f7SAndroid Build Coastguard Worker                path = path[len(strip_prefix):]
214*03ce13f7SAndroid Build Coastguard Worker            elif path.startswith('/'):
215*03ce13f7SAndroid Build Coastguard Worker                path = path[1:]
216*03ce13f7SAndroid Build Coastguard Worker            path = ['[path]'] + path.split('/')
217*03ce13f7SAndroid Build Coastguard Worker
218*03ce13f7SAndroid Build Coastguard Worker        parts = parse_cpp_name(sym, cppfilt)
219*03ce13f7SAndroid Build Coastguard Worker        if len(parts) == 1:
220*03ce13f7SAndroid Build Coastguard Worker          if path:
221*03ce13f7SAndroid Build Coastguard Worker            # No namespaces, group with path.
222*03ce13f7SAndroid Build Coastguard Worker            parts = path + parts
223*03ce13f7SAndroid Build Coastguard Worker          else:
224*03ce13f7SAndroid Build Coastguard Worker            new_prefix = ['[ungrouped]']
225*03ce13f7SAndroid Build Coastguard Worker            regroups = [
226*03ce13f7SAndroid Build Coastguard Worker                ['.L.str',                 '[str]'],
227*03ce13f7SAndroid Build Coastguard Worker                ['.L__PRETTY_FUNCTION__.', '[__PRETTY_FUNCTION__]'],
228*03ce13f7SAndroid Build Coastguard Worker                ['.L__func__.',            '[__func__]'],
229*03ce13f7SAndroid Build Coastguard Worker                ['.Lswitch.table',         '[switch table]'],
230*03ce13f7SAndroid Build Coastguard Worker            ]
231*03ce13f7SAndroid Build Coastguard Worker            for prefix, group in regroups:
232*03ce13f7SAndroid Build Coastguard Worker                if parts[0].startswith(prefix):
233*03ce13f7SAndroid Build Coastguard Worker                    parts[0] = parts[0][len(prefix):]
234*03ce13f7SAndroid Build Coastguard Worker                    parts[0] = demangle(parts[0], cppfilt)
235*03ce13f7SAndroid Build Coastguard Worker                    new_prefix += [group]
236*03ce13f7SAndroid Build Coastguard Worker                    break
237*03ce13f7SAndroid Build Coastguard Worker            parts = new_prefix + parts
238*03ce13f7SAndroid Build Coastguard Worker
239*03ce13f7SAndroid Build Coastguard Worker        key = parts.pop()
240*03ce13f7SAndroid Build Coastguard Worker        tree = dirs
241*03ce13f7SAndroid Build Coastguard Worker        try:
242*03ce13f7SAndroid Build Coastguard Worker            depth = 0
243*03ce13f7SAndroid Build Coastguard Worker            for part in parts:
244*03ce13f7SAndroid Build Coastguard Worker                depth = depth + 1
245*03ce13f7SAndroid Build Coastguard Worker                assert part != '', path
246*03ce13f7SAndroid Build Coastguard Worker                if part not in tree:
247*03ce13f7SAndroid Build Coastguard Worker                    tree[part] = {'$bloat_symbols':{}}
248*03ce13f7SAndroid Build Coastguard Worker                if type not in tree[part]['$bloat_symbols']:
249*03ce13f7SAndroid Build Coastguard Worker                    tree[part]['$bloat_symbols'][type] = 0
250*03ce13f7SAndroid Build Coastguard Worker                tree[part]['$bloat_symbols'][type] += 1
251*03ce13f7SAndroid Build Coastguard Worker                tree = tree[part]
252*03ce13f7SAndroid Build Coastguard Worker            old_size, old_symbols = tree.get(key, (0, {}))
253*03ce13f7SAndroid Build Coastguard Worker            if type not in old_symbols:
254*03ce13f7SAndroid Build Coastguard Worker                old_symbols[type] = 0
255*03ce13f7SAndroid Build Coastguard Worker            old_symbols[type] += 1
256*03ce13f7SAndroid Build Coastguard Worker            tree[key] = (old_size + size, old_symbols)
257*03ce13f7SAndroid Build Coastguard Worker        except:
258*03ce13f7SAndroid Build Coastguard Worker            print >>sys.stderr, 'sym `%s`\tparts `%s`\tkey `%s`' % (sym, parts, key)
259*03ce13f7SAndroid Build Coastguard Worker            raise
260*03ce13f7SAndroid Build Coastguard Worker    return dirs
261*03ce13f7SAndroid Build Coastguard Worker
262*03ce13f7SAndroid Build Coastguard Worker
263*03ce13f7SAndroid Build Coastguard Workerdef jsonify_tree(tree, name):
264*03ce13f7SAndroid Build Coastguard Worker    children = []
265*03ce13f7SAndroid Build Coastguard Worker    total = 0
266*03ce13f7SAndroid Build Coastguard Worker    files = 0
267*03ce13f7SAndroid Build Coastguard Worker
268*03ce13f7SAndroid Build Coastguard Worker    for key, val in tree.iteritems():
269*03ce13f7SAndroid Build Coastguard Worker        if key == '$bloat_symbols':
270*03ce13f7SAndroid Build Coastguard Worker            continue
271*03ce13f7SAndroid Build Coastguard Worker        if isinstance(val, dict):
272*03ce13f7SAndroid Build Coastguard Worker            subtree = jsonify_tree(val, key)
273*03ce13f7SAndroid Build Coastguard Worker            total += subtree['data']['$area']
274*03ce13f7SAndroid Build Coastguard Worker            children.append(subtree)
275*03ce13f7SAndroid Build Coastguard Worker        else:
276*03ce13f7SAndroid Build Coastguard Worker            (size, symbols) = val
277*03ce13f7SAndroid Build Coastguard Worker            total += size
278*03ce13f7SAndroid Build Coastguard Worker            assert len(symbols) == 1, symbols.values()[0] == 1
279*03ce13f7SAndroid Build Coastguard Worker            symbol = symbol_type_to_human(symbols.keys()[0])
280*03ce13f7SAndroid Build Coastguard Worker            children.append({
281*03ce13f7SAndroid Build Coastguard Worker                    'name': key + ' ' + format_bytes(size),
282*03ce13f7SAndroid Build Coastguard Worker                    'data': {
283*03ce13f7SAndroid Build Coastguard Worker                        '$area': size,
284*03ce13f7SAndroid Build Coastguard Worker                        '$symbol': symbol,
285*03ce13f7SAndroid Build Coastguard Worker                    }
286*03ce13f7SAndroid Build Coastguard Worker            })
287*03ce13f7SAndroid Build Coastguard Worker
288*03ce13f7SAndroid Build Coastguard Worker    children.sort(key=lambda child: -child['data']['$area'])
289*03ce13f7SAndroid Build Coastguard Worker    dominant_symbol = ''
290*03ce13f7SAndroid Build Coastguard Worker    if '$bloat_symbols' in tree:
291*03ce13f7SAndroid Build Coastguard Worker        dominant_symbol = symbol_type_to_human(
292*03ce13f7SAndroid Build Coastguard Worker            max(tree['$bloat_symbols'].iteritems(),
293*03ce13f7SAndroid Build Coastguard Worker                key=operator.itemgetter(1))[0])
294*03ce13f7SAndroid Build Coastguard Worker    return {
295*03ce13f7SAndroid Build Coastguard Worker        'name': name + ' ' + format_bytes(total),
296*03ce13f7SAndroid Build Coastguard Worker        'data': {
297*03ce13f7SAndroid Build Coastguard Worker            '$area': total,
298*03ce13f7SAndroid Build Coastguard Worker            '$dominant_symbol': dominant_symbol,
299*03ce13f7SAndroid Build Coastguard Worker            },
300*03ce13f7SAndroid Build Coastguard Worker        'children': children,
301*03ce13f7SAndroid Build Coastguard Worker        }
302*03ce13f7SAndroid Build Coastguard Worker
303*03ce13f7SAndroid Build Coastguard Worker
304*03ce13f7SAndroid Build Coastguard Workerdef dump_nm(nmfile, strip_prefix, cppfilt):
305*03ce13f7SAndroid Build Coastguard Worker    dirs = treeify_syms(parse_nm(nmfile), strip_prefix, cppfilt)
306*03ce13f7SAndroid Build Coastguard Worker    print ('var kTree = ' +
307*03ce13f7SAndroid Build Coastguard Worker           json.dumps(jsonify_tree(dirs, '[everything]'), indent=2))
308*03ce13f7SAndroid Build Coastguard Worker
309*03ce13f7SAndroid Build Coastguard Worker
310*03ce13f7SAndroid Build Coastguard Workerdef parse_objdump(input):
311*03ce13f7SAndroid Build Coastguard Worker    """Parse objdump -h output."""
312*03ce13f7SAndroid Build Coastguard Worker    sec_re = re.compile('^\d+ (\S+) +([0-9a-z]+)')
313*03ce13f7SAndroid Build Coastguard Worker    sections = []
314*03ce13f7SAndroid Build Coastguard Worker    debug_sections = []
315*03ce13f7SAndroid Build Coastguard Worker
316*03ce13f7SAndroid Build Coastguard Worker    for line in input:
317*03ce13f7SAndroid Build Coastguard Worker        line = line.strip()
318*03ce13f7SAndroid Build Coastguard Worker        match = sec_re.match(line)
319*03ce13f7SAndroid Build Coastguard Worker        if match:
320*03ce13f7SAndroid Build Coastguard Worker            name, size = match.groups()
321*03ce13f7SAndroid Build Coastguard Worker            if name.startswith('.'):
322*03ce13f7SAndroid Build Coastguard Worker                name = name[1:]
323*03ce13f7SAndroid Build Coastguard Worker            if name.startswith('debug_'):
324*03ce13f7SAndroid Build Coastguard Worker                name = name[len('debug_'):]
325*03ce13f7SAndroid Build Coastguard Worker                debug_sections.append((name, int(size, 16)))
326*03ce13f7SAndroid Build Coastguard Worker            else:
327*03ce13f7SAndroid Build Coastguard Worker                sections.append((name, int(size, 16)))
328*03ce13f7SAndroid Build Coastguard Worker            continue
329*03ce13f7SAndroid Build Coastguard Worker    return sections, debug_sections
330*03ce13f7SAndroid Build Coastguard Worker
331*03ce13f7SAndroid Build Coastguard Worker
332*03ce13f7SAndroid Build Coastguard Workerdef jsonify_sections(name, sections):
333*03ce13f7SAndroid Build Coastguard Worker    children = []
334*03ce13f7SAndroid Build Coastguard Worker    total = 0
335*03ce13f7SAndroid Build Coastguard Worker    for section, size in sections:
336*03ce13f7SAndroid Build Coastguard Worker        children.append({
337*03ce13f7SAndroid Build Coastguard Worker                'name': section + ' ' + format_bytes(size),
338*03ce13f7SAndroid Build Coastguard Worker                'data': { '$area': size }
339*03ce13f7SAndroid Build Coastguard Worker                })
340*03ce13f7SAndroid Build Coastguard Worker        total += size
341*03ce13f7SAndroid Build Coastguard Worker
342*03ce13f7SAndroid Build Coastguard Worker    children.sort(key=lambda child: -child['data']['$area'])
343*03ce13f7SAndroid Build Coastguard Worker
344*03ce13f7SAndroid Build Coastguard Worker    return {
345*03ce13f7SAndroid Build Coastguard Worker        'name': name + ' ' + format_bytes(total),
346*03ce13f7SAndroid Build Coastguard Worker        'data': { '$area': total },
347*03ce13f7SAndroid Build Coastguard Worker        'children': children
348*03ce13f7SAndroid Build Coastguard Worker        }
349*03ce13f7SAndroid Build Coastguard Worker
350*03ce13f7SAndroid Build Coastguard Worker
351*03ce13f7SAndroid Build Coastguard Workerdef dump_sections(objdump):
352*03ce13f7SAndroid Build Coastguard Worker    sections, debug_sections = parse_objdump(objdump)
353*03ce13f7SAndroid Build Coastguard Worker    sections = jsonify_sections('sections', sections)
354*03ce13f7SAndroid Build Coastguard Worker    debug_sections = jsonify_sections('debug', debug_sections)
355*03ce13f7SAndroid Build Coastguard Worker    size = sections['data']['$area'] + debug_sections['data']['$area']
356*03ce13f7SAndroid Build Coastguard Worker    print 'var kTree = ' + json.dumps({
357*03ce13f7SAndroid Build Coastguard Worker            'name': 'top ' + format_bytes(size),
358*03ce13f7SAndroid Build Coastguard Worker            'data': { '$area': size },
359*03ce13f7SAndroid Build Coastguard Worker            'children': [ debug_sections, sections ]})
360*03ce13f7SAndroid Build Coastguard Worker
361*03ce13f7SAndroid Build Coastguard Worker
362*03ce13f7SAndroid Build Coastguard Workerusage="""%prog [options] MODE
363*03ce13f7SAndroid Build Coastguard Worker
364*03ce13f7SAndroid Build Coastguard WorkerModes are:
365*03ce13f7SAndroid Build Coastguard Worker  syms: output symbols json suitable for a treemap
366*03ce13f7SAndroid Build Coastguard Worker  dump: print symbols sorted by size (pipe to head for best output)
367*03ce13f7SAndroid Build Coastguard Worker  sections: output binary sections json suitable for a treemap
368*03ce13f7SAndroid Build Coastguard Worker
369*03ce13f7SAndroid Build Coastguard Workernm output passed to --nm-output should from running a command
370*03ce13f7SAndroid Build Coastguard Workerlike the following (note, can take a long time -- 30 minutes):
371*03ce13f7SAndroid Build Coastguard Worker  nm -C -S -l /path/to/binary > nm.out
372*03ce13f7SAndroid Build Coastguard Worker
373*03ce13f7SAndroid Build Coastguard Workerobjdump output passed to --objdump-output should be from a command
374*03ce13f7SAndroid Build Coastguard Workerlike:
375*03ce13f7SAndroid Build Coastguard Worker  objdump -h /path/to/binary > objdump.out"""
376*03ce13f7SAndroid Build Coastguard Workerparser = optparse.OptionParser(usage=usage)
377*03ce13f7SAndroid Build Coastguard Workerparser.add_option('--nm-output', action='store', dest='nmpath',
378*03ce13f7SAndroid Build Coastguard Worker                  metavar='PATH', default='nm.out',
379*03ce13f7SAndroid Build Coastguard Worker                  help='path to nm output [default=nm.out]')
380*03ce13f7SAndroid Build Coastguard Workerparser.add_option('--objdump-output', action='store', dest='objdumppath',
381*03ce13f7SAndroid Build Coastguard Worker                  metavar='PATH', default='objdump.out',
382*03ce13f7SAndroid Build Coastguard Worker                  help='path to objdump output [default=objdump.out]')
383*03ce13f7SAndroid Build Coastguard Workerparser.add_option('--strip-prefix', metavar='PATH', action='store',
384*03ce13f7SAndroid Build Coastguard Worker                  help='strip PATH prefix from paths; e.g. /path/to/src/root')
385*03ce13f7SAndroid Build Coastguard Workerparser.add_option('--filter', action='store',
386*03ce13f7SAndroid Build Coastguard Worker                  help='include only symbols/files matching FILTER')
387*03ce13f7SAndroid Build Coastguard Workerparser.add_option('--c++filt', action='store', metavar='PATH', dest='cppfilt',
388*03ce13f7SAndroid Build Coastguard Worker                  default='c++filt', help="Path to c++filt, used to demangle "
389*03ce13f7SAndroid Build Coastguard Worker                  "symbols that weren't handled by nm. Set to an invalid path "
390*03ce13f7SAndroid Build Coastguard Worker                  "to disable.")
391*03ce13f7SAndroid Build Coastguard Workeropts, args = parser.parse_args()
392*03ce13f7SAndroid Build Coastguard Worker
393*03ce13f7SAndroid Build Coastguard Workerif len(args) != 1:
394*03ce13f7SAndroid Build Coastguard Worker    parser.print_usage()
395*03ce13f7SAndroid Build Coastguard Worker    sys.exit(1)
396*03ce13f7SAndroid Build Coastguard Worker
397*03ce13f7SAndroid Build Coastguard Workermode = args[0]
398*03ce13f7SAndroid Build Coastguard Workerif mode == 'syms':
399*03ce13f7SAndroid Build Coastguard Worker    nmfile = open(opts.nmpath, 'r')
400*03ce13f7SAndroid Build Coastguard Worker    try:
401*03ce13f7SAndroid Build Coastguard Worker        res = subprocess.check_output([opts.cppfilt, 'main'])
402*03ce13f7SAndroid Build Coastguard Worker        if res.strip() != 'main':
403*03ce13f7SAndroid Build Coastguard Worker            print >>sys.stderr, ("%s failed demangling, "
404*03ce13f7SAndroid Build Coastguard Worker                                 "output won't be demangled." % opt.cppfilt)
405*03ce13f7SAndroid Build Coastguard Worker            opts.cppfilt = None
406*03ce13f7SAndroid Build Coastguard Worker    except:
407*03ce13f7SAndroid Build Coastguard Worker        print >>sys.stderr, ("Could not find c++filt at %s, "
408*03ce13f7SAndroid Build Coastguard Worker                             "output won't be demangled." % opt.cppfilt)
409*03ce13f7SAndroid Build Coastguard Worker        opts.cppfilt = None
410*03ce13f7SAndroid Build Coastguard Worker    dump_nm(nmfile, strip_prefix=opts.strip_prefix, cppfilt=opts.cppfilt)
411*03ce13f7SAndroid Build Coastguard Workerelif mode == 'sections':
412*03ce13f7SAndroid Build Coastguard Worker    objdumpfile = open(opts.objdumppath, 'r')
413*03ce13f7SAndroid Build Coastguard Worker    dump_sections(objdumpfile)
414*03ce13f7SAndroid Build Coastguard Workerelif mode == 'dump':
415*03ce13f7SAndroid Build Coastguard Worker    nmfile = open(opts.nmpath, 'r')
416*03ce13f7SAndroid Build Coastguard Worker    syms = list(parse_nm(nmfile))
417*03ce13f7SAndroid Build Coastguard Worker    # a list of (sym, type, size, path); sort by size.
418*03ce13f7SAndroid Build Coastguard Worker    syms.sort(key=lambda x: -x[2])
419*03ce13f7SAndroid Build Coastguard Worker    total = 0
420*03ce13f7SAndroid Build Coastguard Worker    for sym, type, size, path in syms:
421*03ce13f7SAndroid Build Coastguard Worker        if type in ('b', 'w'):
422*03ce13f7SAndroid Build Coastguard Worker            continue  # skip bss and weak symbols
423*03ce13f7SAndroid Build Coastguard Worker        if path is None:
424*03ce13f7SAndroid Build Coastguard Worker            path = ''
425*03ce13f7SAndroid Build Coastguard Worker        if opts.filter and not (opts.filter in sym or opts.filter in path):
426*03ce13f7SAndroid Build Coastguard Worker            continue
427*03ce13f7SAndroid Build Coastguard Worker        print '%6s %s (%s) %s' % (format_bytes(size), sym,
428*03ce13f7SAndroid Build Coastguard Worker                                  symbol_type_to_human(type), path)
429*03ce13f7SAndroid Build Coastguard Worker        total += size
430*03ce13f7SAndroid Build Coastguard Worker    print '%6s %s' % (format_bytes(total), 'total'),
431*03ce13f7SAndroid Build Coastguard Workerelse:
432*03ce13f7SAndroid Build Coastguard Worker    print 'unknown mode'
433*03ce13f7SAndroid Build Coastguard Worker    parser.print_usage()
434