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: |
Kevin O'Connor | 7173f6f | 2009-05-27 22:23:33 -0400 | [diff] [blame] | 10 | # objdump -m i386 -M i8086 -M suffix -d out/rom16.reloc.o | tools/checkstack.py |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 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. |
Kevin O'Connor | e07e18e | 2009-02-08 17:07:29 -0500 | [diff] [blame] | 16 | #IGNORE = ['panic', '__dprintf', '__send_disk_op'] |
| 17 | IGNORE = ['panic', '__send_disk_op'] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 18 | |
| 19 | # Find out maximum stack usage for a function |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 20 | def calcmaxstack(funcs, funcaddr): |
| 21 | info = funcs[funcaddr] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 22 | # Find max of all nested calls. |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 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] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 34 | if totusage > max: |
| 35 | max = totusage |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 36 | info[2] = max |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 37 | |
| 38 | hex_s = r'[0-9a-f]+' |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 39 | re_func = re.compile(r'^(?P<funcaddr>' + hex_s + r') <(?P<func>.*)>:$') |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 40 | re_asm = re.compile( |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 41 | r'^[ ]*(?P<insnaddr>' + hex_s |
| 42 | + r'):\t.*\t(addr32 )?(?P<insn>.+?)[ ]*((?P<calladdr>' + hex_s |
| 43 | + r') <(?P<ref>.*)>)?$') |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 44 | re_usestack = re.compile( |
Kevin O'Connor | 0b04b78 | 2009-06-10 21:40:26 -0400 | [diff] [blame] | 45 | r'^(push[f]?[lw])|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$') |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 46 | |
| 47 | def calc(): |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 48 | # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage |
| 49 | # , [(addr, callfname, stackusage), ...]] |
| 50 | funcs = {-1: ['<indirect>', 0, 0, []]} |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 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 |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 60 | funcaddr = int(m.group('funcaddr'), 16) |
| 61 | funcs[funcaddr] = cur = [m.group('func'), 0, None, []] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 62 | stackusage = 0 |
| 63 | atstart = 1 |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 64 | subfuncs = {} |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 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: |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 72 | if insn[:5] == 'pushl' or insn[:6] == 'pushfl': |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 73 | stackusage += 4 |
| 74 | continue |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 75 | elif insn[:5] == 'pushw' or insn[:6] == 'pushfw': |
| 76 | stackusage += 2 |
| 77 | continue |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 78 | stackusage += int(im.group('num'), 16) |
| 79 | |
| 80 | if atstart: |
Kevin O'Connor | dbbb7cf | 2009-08-09 13:10:47 -0400 | [diff] [blame] | 81 | if insn == 'movl %esp,%ebp': |
| 82 | # Still part of initial header |
| 83 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 84 | cur[1] = stackusage |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 85 | atstart = 0 |
| 86 | |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 87 | calladdr = m.group('calladdr') |
| 88 | if calladdr is None: |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 89 | if insn[:6] == 'lcallw': |
| 90 | stackusage += 4 |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 91 | calladdr = -1 |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 92 | else: |
| 93 | # misc instruction - just ignore |
| 94 | continue |
| 95 | else: |
| 96 | # Jump or call insn |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 97 | calladdr = int(calladdr, 16) |
| 98 | ref = m.group('ref') |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 99 | if '+' in ref: |
| 100 | # Inter-function jump - reset stack usage to |
| 101 | # preamble usage |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 102 | stackusage = cur[1] |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 103 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 104 | if insn[:1] == 'j': |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 105 | # Tail call |
| 106 | stackusage = 0 |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 107 | elif insn[:5] == 'calll': |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 108 | stackusage += 4 |
| 109 | else: |
| 110 | print "unknown call", ref |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 111 | if (calladdr, stackusage) not in subfuncs: |
| 112 | cur[3].append((m.group('insnaddr'), calladdr, stackusage)) |
| 113 | subfuncs[(calladdr, stackusage)] = 1 |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 114 | # Reset stack usage to preamble usage |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 115 | stackusage = cur[1] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 116 | |
| 117 | continue |
| 118 | |
| 119 | #print "other", repr(line) |
| 120 | |
| 121 | # Calculate maxstackusage |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 122 | bynames = {} |
| 123 | for funcaddr, info in funcs.items(): |
| 124 | bynames[info[0]] = info |
| 125 | if info[2] is not None: |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 126 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 127 | calcmaxstack(funcs, funcaddr) |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 128 | |
| 129 | # Show all functions |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 130 | funcnames = bynames.keys() |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 131 | funcnames.sort() |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 132 | for funcname in funcnames: |
| 133 | name, basicusage, maxusage, calls = bynames[funcname] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 134 | if maxusage == 0: |
| 135 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 136 | print "\n%s[%d,%d]:" % (funcname, basicusage, maxusage) |
| 137 | for insnaddr, calladdr, stackusage in calls: |
| 138 | callinfo = funcs[calladdr] |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 139 | print " %04s:%-40s [%d+%d,%d]" % ( |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 140 | insnaddr, callinfo[0], stackusage, callinfo[1] |
| 141 | , stackusage+callinfo[2]) |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 142 | |
| 143 | def main(): |
| 144 | calc() |
| 145 | |
| 146 | if __name__ == '__main__': |
| 147 | main() |