blob: 9cd6b52f099505fbd1c935cb8774f456c70f783e [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
15#IGNORE = ['screenc', 'bvprintf']
16IGNORE = ['screenc']
17
18# Find out maximum stack usage for a function
19def 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
36hex_s = r'[0-9a-f]+'
37re_func = re.compile(r'^' + hex_s + r' <(?P<func>.*)>:$')
38re_asm = re.compile(
39 r'^[ ]*(?P<addr>' + hex_s + r'):\t.*\t'
40 r'(?P<insn>[a-z0-9]+ [^<]*)( <(?P<ref>.*)>)?$')
41re_usestack = re.compile(
42 r'^(push.*)|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$')
43
44def 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
113def main():
114 calc()
115
116if __name__ == '__main__':
117 main()