Add tool to check stack usage of 16bit code.
[seabios.git] / tools / checkstack.py
1 #!/usr/bin/env python
2 # Script that tries to find how much stack space each function in an
3 # object is using.
4 #
5 # Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
6 #
7 # This file may be distributed under the terms of the GNU GPLv3 license.
8
9 # Usage:
10 #   objdump -m i386 -M i8086 -M suffix -d out/rom16.o | tools/checkstack.py
11
12 import sys
13 import re
14
15 #IGNORE = ['screenc', 'bvprintf']
16 IGNORE = ['screenc']
17
18 # Find out maximum stack usage for a function
19 def calcmaxstack(funcs, func):
20     info = funcs[func]
21     if func.split('.')[0] in IGNORE:
22         # Function is hardcoded to report 0 stack usage
23         info[1] = 0
24         return
25     # Find max of all nested calls.
26     max = info[0]
27     for addr, callfname, usage in info[2]:
28         callinfo = funcs[callfname]
29         if callinfo[1] is None:
30             calcmaxstack(funcs, callfname)
31         totusage = usage + callinfo[1]
32         if totusage > max:
33             max = totusage
34     info[1] = max
35
36 hex_s = r'[0-9a-f]+'
37 re_func = re.compile(r'^' + hex_s + r' <(?P<func>.*)>:$')
38 re_asm = re.compile(
39     r'^[ ]*(?P<addr>' + hex_s + r'):\t.*\t'
40     r'(?P<insn>[a-z0-9]+ [^<]*)( <(?P<ref>.*)>)?$')
41 re_usestack = re.compile(
42     r'^(push.*)|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$')
43
44 def calc():
45     # funcs = {funcname: [basicstackusage, maxstackusage
46     #                     , [(addr, callfname, stackusage), ...]] }
47     funcs = {}
48     cur = None
49     atstart = 0
50     stackusage = 0
51
52     # Parse input lines
53     for line in sys.stdin.readlines():
54         m = re_func.match(line)
55         if m is not None:
56             # Found function
57             cur = [0, None, []]
58             funcs[m.group('func')] = cur
59             stackusage = 0
60             atstart = 1
61             continue
62         m = re_asm.match(line)
63         if m is not None:
64             insn = m.group('insn')
65
66             im = re_usestack.match(insn)
67             if im is not None:
68                 if insn[:4] == 'push':
69                     stackusage += 4
70                     continue
71                 stackusage += int(im.group('num'), 16)
72
73             if atstart:
74                 cur[0] = stackusage
75                 atstart = 0
76
77             ref = m.group('ref')
78             if ref is not None and '+' not in ref:
79                 if insn[:1] == 'j':
80                     # Tail call
81                     stackusage = 0
82                 elif insn[:4] == 'call':
83                     stackusage += 4
84                 else:
85                     print "unknown call", ref
86                 cur[2].append((m.group('addr'), ref, stackusage))
87                 # Reset stack usage to preamble usage
88                 stackusage = cur[0]
89
90             continue
91
92         #print "other", repr(line)
93
94     # Calculate maxstackusage
95     for func, info in funcs.items():
96         if info[1] is not None:
97             continue
98         calcmaxstack(funcs, func)
99
100     # Show all functions
101     funcnames = funcs.keys()
102     funcnames.sort()
103     for func in funcnames:
104         basicusage, maxusage, calls = funcs[func]
105         if maxusage == 0:
106             continue
107         print "\n%s[%d,%d]:" % (func, basicusage, maxusage)
108         for addr, callfname, stackusage in calls:
109             callinfo = funcs[callfname]
110             print "    %04s:%-30s[%d+%d,%d]" % (
111                 addr, callfname, stackusage, callinfo[0], stackusage+callinfo[1])
112
113 def main():
114     calc()
115
116 if __name__ == '__main__':
117     main()