blob: 84635005973b61502c9f579c80a9a8ef120e52a1 [file] [log] [blame]
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -04001#!/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
12import sys
13import re
14
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040015# List of functions we can assume are never called.
16#IGNORE = ['screenc', 'BX_PANIC', '__dprintf']
17IGNORE = ['screenc', 'BX_PANIC']
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040018
19# Find out maximum stack usage for a function
20def calcmaxstack(funcs, func):
21 info = funcs[func]
22 if func.split('.')[0] in IGNORE:
23 # Function is hardcoded to report 0 stack usage
24 info[1] = 0
25 return
26 # Find max of all nested calls.
27 max = info[0]
Kevin O'Connord43fcd32008-07-04 15:15:49 -040028 info[1] = max
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040029 for addr, callfname, usage in info[2]:
30 callinfo = funcs[callfname]
31 if callinfo[1] is None:
32 calcmaxstack(funcs, callfname)
33 totusage = usage + callinfo[1]
34 if totusage > max:
35 max = totusage
36 info[1] = max
37
38hex_s = r'[0-9a-f]+'
39re_func = re.compile(r'^' + hex_s + r' <(?P<func>.*)>:$')
40re_asm = re.compile(
41 r'^[ ]*(?P<addr>' + hex_s + r'):\t.*\t'
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040042 r'(addr32 )?(?P<insn>[a-z0-9]+ [^<]*)( <(?P<ref>.*)>)?$')
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040043re_usestack = re.compile(
44 r'^(push.*)|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$')
45
46def calc():
47 # funcs = {funcname: [basicstackusage, maxstackusage
48 # , [(addr, callfname, stackusage), ...]] }
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040049 funcs = {'<indirect>': [0, 0, []]}
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040050 cur = None
51 atstart = 0
52 stackusage = 0
53
54 # Parse input lines
55 for line in sys.stdin.readlines():
56 m = re_func.match(line)
57 if m is not None:
58 # Found function
59 cur = [0, None, []]
60 funcs[m.group('func')] = cur
61 stackusage = 0
62 atstart = 1
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040063 subfuncs = {}
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040064 continue
65 m = re_asm.match(line)
66 if m is not None:
67 insn = m.group('insn')
68
69 im = re_usestack.match(insn)
70 if im is not None:
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040071 if insn[:5] == 'pushl' or insn[:6] == 'pushfl':
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040072 stackusage += 4
73 continue
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040074 elif insn[:5] == 'pushw' or insn[:6] == 'pushfw':
75 stackusage += 2
76 continue
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -040077 stackusage += int(im.group('num'), 16)
78
79 if atstart:
80 cur[0] = stackusage
81 atstart = 0
82
83 ref = m.group('ref')
Kevin O'Connorf5cb0792008-06-07 10:11:36 -040084 if ref is None:
85 if insn[:6] == 'lcallw':
86 stackusage += 4
87 ref = '<indirect>'
88 else:
89 # misc instruction - just ignore
90 continue
91 else:
92 # Jump or call insn
93 if '+' in ref:
94 # Inter-function jump - reset stack usage to
95 # preamble usage
96 stackusage = cur[0]
97 continue
98 if ref.split('.')[0] in IGNORE:
99 # Call ignored - list only for informational purposes
100 stackusage = 0
101 elif insn[:1] == 'j':
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -0400102 # Tail call
103 stackusage = 0
Kevin O'Connorf5cb0792008-06-07 10:11:36 -0400104 elif insn[:5] == 'calll':
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -0400105 stackusage += 4
106 else:
107 print "unknown call", ref
Kevin O'Connorf5cb0792008-06-07 10:11:36 -0400108 if (ref, stackusage) not in subfuncs:
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -0400109 cur[2].append((m.group('addr'), ref, stackusage))
Kevin O'Connorf5cb0792008-06-07 10:11:36 -0400110 subfuncs[(ref, stackusage)] = 1
111 # Reset stack usage to preamble usage
112 stackusage = cur[0]
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -0400113
114 continue
115
116 #print "other", repr(line)
117
118 # Calculate maxstackusage
119 for func, info in funcs.items():
120 if info[1] is not None:
121 continue
122 calcmaxstack(funcs, func)
123
124 # Show all functions
125 funcnames = funcs.keys()
126 funcnames.sort()
127 for func in funcnames:
128 basicusage, maxusage, calls = funcs[func]
129 if maxusage == 0:
130 continue
131 print "\n%s[%d,%d]:" % (func, basicusage, maxusage)
132 for addr, callfname, stackusage in calls:
133 callinfo = funcs[callfname]
Kevin O'Connorf5cb0792008-06-07 10:11:36 -0400134 print " %04s:%-40s [%d+%d,%d]" % (
Kevin O'Connor5c4a8c62008-05-12 23:50:16 -0400135 addr, callfname, stackusage, callinfo[0], stackusage+callinfo[1])
136
137def main():
138 calc()
139
140if __name__ == '__main__':
141 main()