Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 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 | |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 15 | # List of functions we can assume are never called. |
| 16 | #IGNORE = ['screenc', 'BX_PANIC', '__dprintf'] |
| 17 | IGNORE = ['screenc', 'BX_PANIC'] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 18 | |
| 19 | # Find out maximum stack usage for a function |
| 20 | def 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'Connor | d43fcd3 | 2008-07-04 15:15:49 -0400 | [diff] [blame] | 28 | info[1] = max |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 29 | 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 | |
| 38 | hex_s = r'[0-9a-f]+' |
| 39 | re_func = re.compile(r'^' + hex_s + r' <(?P<func>.*)>:$') |
| 40 | re_asm = re.compile( |
| 41 | r'^[ ]*(?P<addr>' + hex_s + r'):\t.*\t' |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 42 | r'(addr32 )?(?P<insn>[a-z0-9]+ [^<]*)( <(?P<ref>.*)>)?$') |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 43 | re_usestack = re.compile( |
| 44 | r'^(push.*)|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$') |
| 45 | |
| 46 | def calc(): |
| 47 | # funcs = {funcname: [basicstackusage, maxstackusage |
| 48 | # , [(addr, callfname, stackusage), ...]] } |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 49 | funcs = {'<indirect>': [0, 0, []]} |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 50 | 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'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 63 | subfuncs = {} |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 64 | 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'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 71 | if insn[:5] == 'pushl' or insn[:6] == 'pushfl': |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 72 | stackusage += 4 |
| 73 | continue |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 74 | elif insn[:5] == 'pushw' or insn[:6] == 'pushfw': |
| 75 | stackusage += 2 |
| 76 | continue |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 77 | 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'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 84 | 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'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 102 | # Tail call |
| 103 | stackusage = 0 |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 104 | elif insn[:5] == 'calll': |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 105 | stackusage += 4 |
| 106 | else: |
| 107 | print "unknown call", ref |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 108 | if (ref, stackusage) not in subfuncs: |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 109 | cur[2].append((m.group('addr'), ref, stackusage)) |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 110 | subfuncs[(ref, stackusage)] = 1 |
| 111 | # Reset stack usage to preamble usage |
| 112 | stackusage = cur[0] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 113 | |
| 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'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 134 | print " %04s:%-40s [%d+%d,%d]" % ( |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 135 | addr, callfname, stackusage, callinfo[0], stackusage+callinfo[1]) |
| 136 | |
| 137 | def main(): |
| 138 | calc() |
| 139 | |
| 140 | if __name__ == '__main__': |
| 141 | main() |