checkstack.py should not match pushaw on stack usage check.
[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.reloc.o | tools/checkstack.py
11
12 import sys
13 import re
14
15 # List of functions we can assume are never called.
16 #IGNORE = ['panic', '__dprintf', '__send_disk_op']
17 IGNORE = ['panic', '__send_disk_op']
18
19 # Find out maximum stack usage for a function
20 def calcmaxstack(funcs, funcaddr):
21     info = funcs[funcaddr]
22     # Find max of all nested calls.
23     max = info[1]
24     info[2] = max
25     for insnaddr, calladdr, usage in info[3]:
26         callinfo = funcs[calladdr]
27         if callinfo[2] is None:
28             calcmaxstack(funcs, calladdr)
29         if callinfo[0].split('.')[0] in IGNORE:
30             # This called function is ignored - don't contribute it to
31             # the max stack.
32             continue
33         totusage = usage + callinfo[2]
34         if totusage > max:
35             max = totusage
36     info[2] = max
37
38 hex_s = r'[0-9a-f]+'
39 re_func = re.compile(r'^(?P<funcaddr>' + hex_s + r') <(?P<func>.*)>:$')
40 re_asm = re.compile(
41     r'^[ ]*(?P<insnaddr>' + hex_s
42     + r'):\t.*\t(addr32 )?(?P<insn>.+?)[ ]*((?P<calladdr>' + hex_s
43     + r') <(?P<ref>.*)>)?$')
44 re_usestack = re.compile(
45     r'^(push[f]?[lw])|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$')
46
47 def calc():
48     # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage
49     #                    , [(addr, callfname, stackusage), ...]]
50     funcs = {-1: ['<indirect>', 0, 0, []]}
51     cur = None
52     atstart = 0
53     stackusage = 0
54
55     # Parse input lines
56     for line in sys.stdin.readlines():
57         m = re_func.match(line)
58         if m is not None:
59             # Found function
60             funcaddr = int(m.group('funcaddr'), 16)
61             funcs[funcaddr] = cur = [m.group('func'), 0, None, []]
62             stackusage = 0
63             atstart = 1
64             subfuncs = {}
65             continue
66         m = re_asm.match(line)
67         if m is not None:
68             insn = m.group('insn')
69
70             im = re_usestack.match(insn)
71             if im is not None:
72                 if insn[:5] == 'pushl' or insn[:6] == 'pushfl':
73                     stackusage += 4
74                     continue
75                 elif insn[:5] == 'pushw' or insn[:6] == 'pushfw':
76                     stackusage += 2
77                     continue
78                 stackusage += int(im.group('num'), 16)
79
80             if atstart:
81                 cur[1] = stackusage
82                 atstart = 0
83
84             calladdr = m.group('calladdr')
85             if calladdr is None:
86                 if insn[:6] == 'lcallw':
87                     stackusage += 4
88                     calladdr = -1
89                 else:
90                     # misc instruction - just ignore
91                     continue
92             else:
93                 # Jump or call insn
94                 calladdr = int(calladdr, 16)
95                 ref = m.group('ref')
96                 if '+' in ref:
97                     # Inter-function jump - reset stack usage to
98                     # preamble usage
99                     stackusage = cur[1]
100                     continue
101                 if insn[:1] == 'j':
102                     # Tail call
103                     stackusage = 0
104                 elif insn[:5] == 'calll':
105                     stackusage += 4
106                 else:
107                     print "unknown call", ref
108             if (calladdr, stackusage) not in subfuncs:
109                 cur[3].append((m.group('insnaddr'), calladdr, stackusage))
110                 subfuncs[(calladdr, stackusage)] = 1
111             # Reset stack usage to preamble usage
112             stackusage = cur[1]
113
114             continue
115
116         #print "other", repr(line)
117
118     # Calculate maxstackusage
119     bynames = {}
120     for funcaddr, info in funcs.items():
121         bynames[info[0]] = info
122         if info[2] is not None:
123             continue
124         calcmaxstack(funcs, funcaddr)
125
126     # Show all functions
127     funcnames = bynames.keys()
128     funcnames.sort()
129     for funcname in funcnames:
130         name, basicusage, maxusage, calls = bynames[funcname]
131         if maxusage == 0:
132             continue
133         print "\n%s[%d,%d]:" % (funcname, basicusage, maxusage)
134         for insnaddr, calladdr, stackusage in calls:
135             callinfo = funcs[calladdr]
136             print "    %04s:%-40s [%d+%d,%d]" % (
137                 insnaddr, callinfo[0], stackusage, callinfo[1]
138                 , stackusage+callinfo[2])
139
140 def main():
141     calc()
142
143 if __name__ == '__main__':
144     main()