|
|
1.1 root 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 <[email protected]>
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: if insn == 'movl %esp,%ebp':
82: # Still part of initial header
83: continue
84: cur[1] = stackusage
85: atstart = 0
86:
87: calladdr = m.group('calladdr')
88: if calladdr is None:
89: if insn[:6] == 'lcallw':
90: stackusage += 4
91: calladdr = -1
92: else:
93: # misc instruction - just ignore
94: continue
95: else:
96: # Jump or call insn
97: calladdr = int(calladdr, 16)
98: ref = m.group('ref')
99: if '+' in ref:
100: # Inter-function jump - reset stack usage to
101: # preamble usage
102: stackusage = cur[1]
103: continue
104: if insn[:1] == 'j':
105: # Tail call
106: stackusage = 0
107: elif insn[:5] == 'calll':
108: stackusage += 4
109: else:
110: print "unknown call", ref
111: if (calladdr, stackusage) not in subfuncs:
112: cur[3].append((m.group('insnaddr'), calladdr, stackusage))
113: subfuncs[(calladdr, stackusage)] = 1
114: # Reset stack usage to preamble usage
115: stackusage = cur[1]
116:
117: continue
118:
119: #print "other", repr(line)
120:
121: # Calculate maxstackusage
122: bynames = {}
123: for funcaddr, info in funcs.items():
124: bynames[info[0]] = info
125: if info[2] is not None:
126: continue
127: calcmaxstack(funcs, funcaddr)
128:
129: # Show all functions
130: funcnames = bynames.keys()
131: funcnames.sort()
132: for funcname in funcnames:
133: name, basicusage, maxusage, calls = bynames[funcname]
134: if maxusage == 0:
135: continue
136: print "\n%s[%d,%d]:" % (funcname, basicusage, maxusage)
137: for insnaddr, calladdr, stackusage in calls:
138: callinfo = funcs[calladdr]
139: print " %04s:%-40s [%d+%d,%d]" % (
140: insnaddr, callinfo[0], stackusage, callinfo[1]
141: , stackusage+callinfo[2])
142:
143: def main():
144: calc()
145:
146: if __name__ == '__main__':
147: main()
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.