xref: /openbmc/openbmc/poky/scripts/tiny/ksize.py (revision c182c62d)
1#!/usr/bin/env python3
2#
3# Copyright (c) 2011, Intel Corporation.
4#
5# SPDX-License-Identifier: GPL-2.0-or-later
6#
7# Display details of the kernel build size, broken up by built-in.[o,a]. Sort
8# the objects by size. Run from the top level kernel build directory.
9#
10# Author: Darren Hart <dvhart@linux.intel.com>
11#
12
13import sys
14import getopt
15import os
16from subprocess import *
17
18def usage():
19    prog = os.path.basename(sys.argv[0])
20    print('Usage: %s [OPTION]...' % prog)
21    print('  -d,                 display an additional level of drivers detail')
22    print('  -h, --help          display this help and exit')
23    print('')
24    print('Run %s from the top-level Linux kernel build directory.' % prog)
25
26
27class Sizes:
28    def __init__(self, glob):
29        self.title = glob
30        p = Popen("size -t " + str(glob), shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True)
31        output = p.communicate()[0].splitlines()
32        if len(output) > 2:
33            sizes = output[-1].split()[0:4]
34            self.text = int(sizes[0])
35            self.data = int(sizes[1])
36            self.bss = int(sizes[2])
37            self.total = int(sizes[3])
38        else:
39            self.text = self.data = self.bss = self.total = 0
40
41    def show(self, indent=""):
42        print("%-32s %10d | %10d %10d %10d" % \
43              (indent+self.title, self.total, self.text, self.data, self.bss))
44
45
46class Report:
47    def create(filename, title, subglob=None):
48        r = Report(filename, title)
49        path = os.path.dirname(filename)
50
51        p = Popen("ls " + str(path) + "/*.o | grep -v built-in.o",
52                  shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True)
53        glob = ' '.join(p.communicate()[0].splitlines())
54        oreport = Report(glob, str(path) + "/*.o")
55        oreport.sizes.title = str(path) + "/*.o"
56        r.parts.append(oreport)
57
58        if subglob:
59            p = Popen("ls " + subglob, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True)
60            for f in p.communicate()[0].splitlines():
61                path = os.path.dirname(f)
62                r.parts.append(Report.create(f, path, str(path) + "/*/built-in.[o,a]"))
63            r.parts.sort(reverse=True)
64
65        for b in r.parts:
66            r.totals["total"] += b.sizes.total
67            r.totals["text"] += b.sizes.text
68            r.totals["data"] += b.sizes.data
69            r.totals["bss"] += b.sizes.bss
70
71        r.deltas["total"] = r.sizes.total - r.totals["total"]
72        r.deltas["text"] = r.sizes.text - r.totals["text"]
73        r.deltas["data"] = r.sizes.data - r.totals["data"]
74        r.deltas["bss"] = r.sizes.bss - r.totals["bss"]
75        return r
76    create = staticmethod(create)
77
78    def __init__(self, glob, title):
79        self.glob = glob
80        self.title = title
81        self.sizes = Sizes(glob)
82        self.parts = []
83        self.totals = {"total":0, "text":0, "data":0, "bss":0}
84        self.deltas = {"total":0, "text":0, "data":0, "bss":0}
85
86    def show(self, indent=""):
87        rule = str.ljust(indent, 80, '-')
88        print("%-32s %10s | %10s %10s %10s" % \
89              (indent+self.title, "total", "text", "data", "bss"))
90        print(rule)
91        self.sizes.show(indent)
92        print(rule)
93        for p in self.parts:
94            if p.sizes.total > 0:
95                p.sizes.show(indent)
96        print(rule)
97        print("%-32s %10d | %10d %10d %10d" % \
98              (indent+"sum", self.totals["total"], self.totals["text"],
99               self.totals["data"], self.totals["bss"]))
100        print("%-32s %10d | %10d %10d %10d" % \
101              (indent+"delta", self.deltas["total"], self.deltas["text"],
102               self.deltas["data"], self.deltas["bss"]))
103        print("\n")
104
105    def __lt__(this, that):
106        if that is None:
107            return 1
108        if not isinstance(that, Report):
109            raise TypeError
110        return this.sizes.total < that.sizes.total
111
112    def __cmp__(this, that):
113        if that is None:
114            return 1
115        if not isinstance(that, Report):
116            raise TypeError
117        if this.sizes.total < that.sizes.total:
118            return -1
119        if this.sizes.total > that.sizes.total:
120            return 1
121        return 0
122
123
124def main():
125    try:
126        opts, args = getopt.getopt(sys.argv[1:], "dh", ["help"])
127    except getopt.GetoptError as err:
128        print('%s' % str(err))
129        usage()
130        sys.exit(2)
131
132    driver_detail = False
133    for o, a in opts:
134        if o == '-d':
135            driver_detail = True
136        elif o in ('-h', '--help'):
137            usage()
138            sys.exit(0)
139        else:
140            assert False, "unhandled option"
141
142    glob = "arch/*/built-in.[o,a] */built-in.[o,a]"
143    vmlinux = Report.create("vmlinux",  "Linux Kernel", glob)
144
145    vmlinux.show()
146    for b in vmlinux.parts:
147        if b.totals["total"] > 0 and len(b.parts) > 1:
148            b.show()
149        if b.title == "drivers" and driver_detail:
150            for d in b.parts:
151                if d.totals["total"] > 0 and len(d.parts) > 1:
152                    d.show("    ")
153
154
155if __name__ == "__main__":
156    main()
157