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 | 9ba1dea | 2010-05-01 09:50:13 -0400 | [diff] [blame] | 10 | # objdump -m i386 -M i8086 -M suffix -d out/rom16.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 | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 15 | # Functions that change stacks |
Kevin O'Connor | ecdc655 | 2012-05-28 14:25:15 -0400 | [diff] [blame] | 16 | STACKHOP = ['stack_hop', 'stack_hop_back'] |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 17 | # List of functions we can assume are never called. |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 18 | #IGNORE = ['panic', '__dprintf'] |
| 19 | IGNORE = ['panic'] |
| 20 | |
| 21 | OUTPUTDESC = """ |
| 22 | #funcname1[preamble_stack_usage,max_usage_with_callers]: |
| 23 | # insn_addr:called_function [usage_at_call_point+caller_preamble,total_usage] |
| 24 | # |
| 25 | #funcname2[p,m,max_usage_to_yield_point]: |
| 26 | # insn_addr:called_function [u+c,t,usage_to_yield_point] |
| 27 | """ |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 28 | |
| 29 | # Find out maximum stack usage for a function |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 30 | def calcmaxstack(funcs, funcaddr): |
| 31 | info = funcs[funcaddr] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 32 | # Find max of all nested calls. |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 33 | maxusage = info[1] |
| 34 | maxyieldusage = doesyield = 0 |
| 35 | if info[3] is not None: |
| 36 | maxyieldusage = info[3] |
| 37 | doesyield = 1 |
| 38 | info[2] = maxusage |
| 39 | info[4] = info[3] |
| 40 | seenbefore = {} |
| 41 | totcalls = 0 |
| 42 | for insnaddr, calladdr, usage in info[6]: |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 43 | callinfo = funcs.get(calladdr) |
| 44 | if callinfo is None: |
| 45 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 46 | if callinfo[2] is None: |
| 47 | calcmaxstack(funcs, calladdr) |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 48 | if callinfo[0] not in seenbefore: |
| 49 | seenbefore[callinfo[0]] = 1 |
| 50 | totcalls += 1 + callinfo[5] |
| 51 | funcnameroot = callinfo[0].split('.')[0] |
| 52 | if funcnameroot in IGNORE: |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 53 | # This called function is ignored - don't contribute it to |
| 54 | # the max stack. |
| 55 | continue |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 56 | if funcnameroot in STACKHOP: |
| 57 | if usage > maxusage: |
| 58 | maxusage = usage |
| 59 | if callinfo[4] is not None: |
| 60 | doesyield = 1 |
| 61 | if usage > maxyieldusage: |
| 62 | maxyieldusage = usage |
| 63 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 64 | totusage = usage + callinfo[2] |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 65 | if totusage > maxusage: |
| 66 | maxusage = totusage |
| 67 | if callinfo[4] is not None: |
| 68 | doesyield = 1 |
| 69 | totyieldusage = usage + callinfo[4] |
| 70 | if totyieldusage > maxyieldusage: |
| 71 | maxyieldusage = totyieldusage |
| 72 | info[2] = maxusage |
| 73 | if doesyield: |
| 74 | info[4] = maxyieldusage |
| 75 | info[5] = totcalls |
| 76 | |
| 77 | # Try to arrange output so that functions that call each other are |
| 78 | # near each other. |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 79 | def orderfuncs(funcaddrs, availfuncs): |
| 80 | l = [(availfuncs[funcaddr][5], availfuncs[funcaddr][0], funcaddr) |
| 81 | for funcaddr in funcaddrs if funcaddr in availfuncs] |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 82 | l.sort() |
| 83 | l.reverse() |
| 84 | out = [] |
| 85 | while l: |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 86 | count, name, funcaddr = l.pop(0) |
| 87 | if funcaddr not in availfuncs: |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 88 | continue |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 89 | calladdrs = [calls[1] for calls in availfuncs[funcaddr][6]] |
| 90 | del availfuncs[funcaddr] |
| 91 | out = out + orderfuncs(calladdrs, availfuncs) + [funcaddr] |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 92 | return out |
| 93 | |
| 94 | # Update function info with a found "yield" point. |
| 95 | def noteYield(info, stackusage): |
| 96 | prevyield = info[3] |
| 97 | if prevyield is None or prevyield < stackusage: |
| 98 | info[3] = stackusage |
| 99 | |
| 100 | # Update function info with a found "call" point. |
| 101 | def noteCall(info, subfuncs, insnaddr, calladdr, stackusage): |
| 102 | if (calladdr, stackusage) in subfuncs: |
| 103 | # Already noted a nearly identical call - ignore this one. |
| 104 | return |
| 105 | info[6].append((insnaddr, calladdr, stackusage)) |
| 106 | subfuncs[(calladdr, stackusage)] = 1 |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 107 | |
| 108 | hex_s = r'[0-9a-f]+' |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 109 | 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] | 110 | re_asm = re.compile( |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 111 | r'^[ ]*(?P<insnaddr>' + hex_s |
| 112 | + r'):\t.*\t(addr32 )?(?P<insn>.+?)[ ]*((?P<calladdr>' + hex_s |
| 113 | + r') <(?P<ref>.*)>)?$') |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 114 | re_usestack = re.compile( |
Kevin O'Connor | 0b04b78 | 2009-06-10 21:40:26 -0400 | [diff] [blame] | 115 | 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] | 116 | |
| 117 | def calc(): |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 118 | # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 119 | # , yieldusage, maxyieldusage, totalcalls |
| 120 | # , [(insnaddr, calladdr, stackusage), ...]] |
| 121 | funcs = {-1: ['<indirect>', 0, 0, None, None, 0, []]} |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 122 | cur = None |
| 123 | atstart = 0 |
| 124 | stackusage = 0 |
| 125 | |
| 126 | # Parse input lines |
| 127 | for line in sys.stdin.readlines(): |
| 128 | m = re_func.match(line) |
| 129 | if m is not None: |
| 130 | # Found function |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 131 | funcaddr = int(m.group('funcaddr'), 16) |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 132 | funcs[funcaddr] = cur = [m.group('func'), 0, None, None, None, 0, []] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 133 | stackusage = 0 |
| 134 | atstart = 1 |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 135 | subfuncs = {} |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 136 | continue |
| 137 | m = re_asm.match(line) |
| 138 | if m is not None: |
| 139 | insn = m.group('insn') |
| 140 | |
| 141 | im = re_usestack.match(insn) |
| 142 | if im is not None: |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 143 | if insn.startswith('pushl') or insn.startswith('pushfl'): |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 144 | stackusage += 4 |
| 145 | continue |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 146 | elif insn.startswith('pushw') or insn.startswith('pushfw'): |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 147 | stackusage += 2 |
| 148 | continue |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 149 | stackusage += int(im.group('num'), 16) |
| 150 | |
| 151 | if atstart: |
Kevin O'Connor | 6ee837b | 2012-02-13 20:09:02 -0500 | [diff] [blame] | 152 | if '%esp' in insn or insn.startswith('leal'): |
Kevin O'Connor | dbbb7cf | 2009-08-09 13:10:47 -0400 | [diff] [blame] | 153 | # Still part of initial header |
| 154 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 155 | cur[1] = stackusage |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 156 | atstart = 0 |
| 157 | |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 158 | insnaddr = m.group('insnaddr') |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 159 | calladdr = m.group('calladdr') |
| 160 | if calladdr is None: |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 161 | if insn.startswith('lcallw'): |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 162 | noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4) |
| 163 | noteYield(cur, stackusage + 4) |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 164 | elif insn.startswith('int'): |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 165 | noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6) |
| 166 | noteYield(cur, stackusage + 6) |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 167 | elif insn.startswith('sti'): |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 168 | noteYield(cur, stackusage) |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 169 | else: |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 170 | # misc instruction |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 171 | continue |
| 172 | else: |
| 173 | # Jump or call insn |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 174 | calladdr = int(calladdr, 16) |
| 175 | ref = m.group('ref') |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 176 | if '+' in ref: |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 177 | # Inter-function jump. |
| 178 | pass |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 179 | elif insn.startswith('j'): |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 180 | # Tail call |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 181 | noteCall(cur, subfuncs, insnaddr, calladdr, 0) |
Kevin O'Connor | 6c2e781 | 2010-09-13 18:04:02 -0400 | [diff] [blame] | 182 | elif insn.startswith('calll'): |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 183 | noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4) |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 184 | else: |
| 185 | print "unknown call", ref |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 186 | noteCall(cur, subfuncs, insnaddr, calladdr, stackusage) |
Kevin O'Connor | f5cb079 | 2008-06-07 10:11:36 -0400 | [diff] [blame] | 187 | # Reset stack usage to preamble usage |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 188 | stackusage = cur[1] |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 189 | |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 190 | #print "other", repr(line) |
| 191 | |
| 192 | # Calculate maxstackusage |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 193 | for funcaddr, info in funcs.items(): |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 194 | if info[2] is not None: |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 195 | continue |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 196 | calcmaxstack(funcs, funcaddr) |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 197 | |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 198 | # Sort functions for output |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 199 | funcaddrs = orderfuncs(funcs.keys(), funcs.copy()) |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 200 | |
| 201 | # Show all functions |
| 202 | print OUTPUTDESC |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 203 | for funcaddr in funcaddrs: |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 204 | name, basicusage, maxusage, yieldusage, maxyieldusage, count, calls = \ |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 205 | funcs[funcaddr] |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 206 | if maxusage == 0 and maxyieldusage is None: |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 207 | continue |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 208 | yieldstr = "" |
| 209 | if maxyieldusage is not None: |
| 210 | yieldstr = ",%d" % maxyieldusage |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 211 | print "\n%s[%d,%d%s]:" % (name, basicusage, maxusage, yieldstr) |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 212 | for insnaddr, calladdr, stackusage in calls: |
Kevin O'Connor | f59b5ac | 2010-05-01 11:03:10 -0400 | [diff] [blame] | 213 | callinfo = funcs.get(calladdr, ("<unknown>", 0, 0, 0, None)) |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 214 | yieldstr = "" |
| 215 | if callinfo[4] is not None: |
| 216 | yieldstr = ",%d" % (stackusage + callinfo[4]) |
| 217 | print " %04s:%-40s [%d+%d,%d%s]" % ( |
Kevin O'Connor | 95cde27 | 2008-07-13 10:59:11 -0400 | [diff] [blame] | 218 | insnaddr, callinfo[0], stackusage, callinfo[1] |
Kevin O'Connor | a979c1c | 2010-03-09 20:01:35 -0500 | [diff] [blame] | 219 | , stackusage+callinfo[2], yieldstr) |
Kevin O'Connor | 5c4a8c6 | 2008-05-12 23:50:16 -0400 | [diff] [blame] | 220 | |
| 221 | def main(): |
| 222 | calc() |
| 223 | |
| 224 | if __name__ == '__main__': |
| 225 | main() |