summaryrefslogtreecommitdiffhomepage
path: root/scripts/bloat-o-meter
blob: 0d3ff7f5e6f9cd0a28920af4b5522e8f78929d21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env python
#
# Copyright 2004 Matt Mackall <mpm@selenic.com>
#
# Inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

import sys, os#, re

def usage():
    sys.stderr.write("usage: %s [-t] file1 file2\n" % sys.argv[0])
    sys.exit(-1)

f1, f2 = (None, None)
flag_timing, dashes = (False, False)

for f in sys.argv[1:]:
    if f.startswith("-"):
        if f == "--": # sym_args
            dashes = True
            break
        if f == "-t": # timings
            flag_timing = True
    else:
        if not os.path.exists(f):
            sys.stderr.write("Error: file '%s' does not exist\n" % f)
            usage()
        if f1 is None:
            f1 = f
        elif f2 is None:
            f2 = f
if flag_timing:
    import time
if f1 is None or f2 is None:
    usage()

sym_args = " ".join(sys.argv[3 + flag_timing + dashes:])
def getsizes(file):
    sym, alias, lut = {}, {}, {}
    #dynsym_filter = re.compile("^\d+:\s+[\dA-Fa-f]+\s+\d+\s+\w+\s+\w+\s+\w+\s+\w+\s+\w+$")
    for l in os.popen("readelf -W -s %s %s" % (sym_args, file)).readlines():
        if True:
            l = l.strip()
            if not (len(l) and l[0].isdigit() and len(l.split()) == 8):
                continue
            num, value, size, typ, bind, vis, ndx, name = l.split()
            if ndx == "UND": continue # skip undefined
            if typ in ["SECTION", "FILES"]: continue # skip sections and files
        #else:
        #    l = l.strip()
        #    match = dynsym_filter.match(l)
        #    if not match: continue
        #    x, value, size, typ, bind, x, ndx, name = l.split()
        #    if ndx == "UND": continue # skip undefined
        #    if typ in ["SECTION", "FILES"]: continue # skip sections and files
        if "." in name: name = "static." + name.split(".")[0]
        value = int(value, 16)
        size = int(size)
        if vis != "DEFAULT" and bind != "GLOBAL": # see if it is an alias
            alias[(value, size)] = {"name" : name}
        else:
            sym[name] = {"addr" : value, "size":  size}
            lut[(value, size)] = 0
    for addr, sz in iter(alias.keys()):
        # If the non-GLOBAL sym has an implementation elsewhere then
        # it's an alias, disregard it.
        if not (addr, sz) in lut:
            # If this non-GLOBAL sym does not have an implementation at
            # another address, then treat it as a normal symbol.
            sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz}
    for l in os.popen("readelf -W -S " + file).readlines():
        x = l.split()
        if len(x)<6: continue
        # Should take these into account too!
        #if x[1] not in [".text", ".rodata", ".symtab", ".strtab"]: continue
        if x[1] not in [".rodata"]: continue
        sym[x[1]] = {"addr" : int(x[3], 16), "size" : int(x[5], 16)}
    return sym

if flag_timing:
    start_t1 = int(time.time() * 1e9)
old = getsizes(f1)
if flag_timing:
    end_t1 = int(time.time() * 1e9)
    start_t2 = int(time.time() * 1e9)
new = getsizes(f2)
if flag_timing:
    end_t2 = int(time.time() * 1e9)
    start_t3 = int(time.time() * 1e9)
grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
delta, common = [], {}

for name in iter(old.keys()):
    if name in new:
        common[name] = 1

for name in old:
    if name not in common:
        remove += 1
        sz = old[name]["size"]
        down += sz
        delta.append((-sz, name))

for name in new:
    if name not in common:
        add += 1
        sz = new[name]["size"]
        up += sz
        delta.append((sz, name))

for name in common:
        d = new[name].get("size", 0) - old[name].get("size", 0)
        if d>0: grow, up = grow+1, up+d
        elif d<0: shrink, down = shrink+1, down-d
        else:
            continue
        delta.append((d, name))

delta.sort()
delta.reverse()
if flag_timing:
    end_t3 = int(time.time() * 1e9)

print("%-48s %7s %7s %+7s" % ("function", "old", "new", "delta"))
for d, n in delta:
    if d:
        old_sz = old.get(n, {}).get("size", "-")
        new_sz = new.get(n, {}).get("size", "-")
        print("%-48s %7s %7s %+7d" % (n, old_sz, new_sz, d))
print("-"*78)
total="(add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s)%%sTotal: %s bytes"\
    % (add, remove, grow, shrink, up, -down, up-down)
print(total % (" "*(80-len(total))))
if flag_timing:
    print("\n%d/%d; %d Parse origin/new; processing nsecs" %
        (end_t1-start_t1, end_t2-start_t2, end_t3-start_t3))
    print("total nsecs: %d" % (end_t3-start_t1))