Annotation of qemu/roms/seabios/tools/checkstack.py, revision 1.1.1.2

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:
1.1.1.2 ! root       10: #   objdump -m i386 -M i8086 -M suffix -d out/rom16.o | tools/checkstack.py
1.1       root       11: 
                     12: import sys
                     13: import re
                     14: 
1.1.1.2 ! root       15: # Functions that change stacks
        !            16: STACKHOP = ['__send_disk_op']
1.1       root       17: # List of functions we can assume are never called.
1.1.1.2 ! root       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: """
1.1       root       28: 
                     29: # Find out maximum stack usage for a function
                     30: def calcmaxstack(funcs, funcaddr):
                     31:     info = funcs[funcaddr]
                     32:     # Find max of all nested calls.
1.1.1.2 ! root       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]:
        !            43:         callinfo = funcs.get(calladdr)
        !            44:         if callinfo is None:
        !            45:             continue
1.1       root       46:         if callinfo[2] is None:
                     47:             calcmaxstack(funcs, calladdr)
1.1.1.2 ! root       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:
1.1       root       53:             # This called function is ignored - don't contribute it to
                     54:             # the max stack.
                     55:             continue
1.1.1.2 ! root       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
1.1       root       64:         totusage = usage + callinfo[2]
1.1.1.2 ! root       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.
        !            79: def orderfuncs(funcaddrs, availfuncs):
        !            80:     l = [(availfuncs[funcaddr][5], availfuncs[funcaddr][0], funcaddr)
        !            81:          for funcaddr in funcaddrs if funcaddr in availfuncs]
        !            82:     l.sort()
        !            83:     l.reverse()
        !            84:     out = []
        !            85:     while l:
        !            86:         count, name, funcaddr = l.pop(0)
        !            87:         if funcaddr not in availfuncs:
        !            88:             continue
        !            89:         calladdrs = [calls[1] for calls in availfuncs[funcaddr][6]]
        !            90:         del availfuncs[funcaddr]
        !            91:         out = out + orderfuncs(calladdrs, availfuncs) + [funcaddr]
        !            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
1.1       root      107: 
                    108: hex_s = r'[0-9a-f]+'
                    109: re_func = re.compile(r'^(?P<funcaddr>' + hex_s + r') <(?P<func>.*)>:$')
                    110: re_asm = re.compile(
                    111:     r'^[ ]*(?P<insnaddr>' + hex_s
                    112:     + r'):\t.*\t(addr32 )?(?P<insn>.+?)[ ]*((?P<calladdr>' + hex_s
                    113:     + r') <(?P<ref>.*)>)?$')
                    114: re_usestack = re.compile(
                    115:     r'^(push[f]?[lw])|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$')
                    116: 
                    117: def calc():
                    118:     # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage
1.1.1.2 ! root      119:     #                    , yieldusage, maxyieldusage, totalcalls
        !           120:     #                    , [(insnaddr, calladdr, stackusage), ...]]
        !           121:     funcs = {-1: ['<indirect>', 0, 0, None, None, 0, []]}
1.1       root      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
                    131:             funcaddr = int(m.group('funcaddr'), 16)
1.1.1.2 ! root      132:             funcs[funcaddr] = cur = [m.group('func'), 0, None, None, None, 0, []]
1.1       root      133:             stackusage = 0
                    134:             atstart = 1
                    135:             subfuncs = {}
                    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:
                    143:                 if insn[:5] == 'pushl' or insn[:6] == 'pushfl':
                    144:                     stackusage += 4
                    145:                     continue
                    146:                 elif insn[:5] == 'pushw' or insn[:6] == 'pushfw':
                    147:                     stackusage += 2
                    148:                     continue
                    149:                 stackusage += int(im.group('num'), 16)
                    150: 
                    151:             if atstart:
                    152:                 if insn == 'movl   %esp,%ebp':
                    153:                     # Still part of initial header
                    154:                     continue
                    155:                 cur[1] = stackusage
                    156:                 atstart = 0
                    157: 
1.1.1.2 ! root      158:             insnaddr = m.group('insnaddr')
1.1       root      159:             calladdr = m.group('calladdr')
                    160:             if calladdr is None:
                    161:                 if insn[:6] == 'lcallw':
1.1.1.2 ! root      162:                     noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4)
        !           163:                     noteYield(cur, stackusage + 4)
        !           164:                 elif insn[:3] == 'int':
        !           165:                     noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6)
        !           166:                     noteYield(cur, stackusage + 6)
        !           167:                 elif insn[:3] == 'sti':
        !           168:                     noteYield(cur, stackusage)
1.1       root      169:                 else:
1.1.1.2 ! root      170:                     # misc instruction
1.1       root      171:                     continue
                    172:             else:
                    173:                 # Jump or call insn
                    174:                 calladdr = int(calladdr, 16)
                    175:                 ref = m.group('ref')
                    176:                 if '+' in ref:
1.1.1.2 ! root      177:                     # Inter-function jump.
        !           178:                     pass
        !           179:                 elif insn[:1] == 'j':
1.1       root      180:                     # Tail call
1.1.1.2 ! root      181:                     noteCall(cur, subfuncs, insnaddr, calladdr, 0)
1.1       root      182:                 elif insn[:5] == 'calll':
1.1.1.2 ! root      183:                     noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4)
1.1       root      184:                 else:
                    185:                     print "unknown call", ref
1.1.1.2 ! root      186:                     noteCall(cur, subfuncs, insnaddr, calladdr, stackusage)
1.1       root      187:             # Reset stack usage to preamble usage
                    188:             stackusage = cur[1]
                    189: 
                    190:         #print "other", repr(line)
                    191: 
                    192:     # Calculate maxstackusage
                    193:     for funcaddr, info in funcs.items():
                    194:         if info[2] is not None:
                    195:             continue
                    196:         calcmaxstack(funcs, funcaddr)
                    197: 
1.1.1.2 ! root      198:     # Sort functions for output
        !           199:     funcaddrs = orderfuncs(funcs.keys(), funcs.copy())
        !           200: 
1.1       root      201:     # Show all functions
1.1.1.2 ! root      202:     print OUTPUTDESC
        !           203:     for funcaddr in funcaddrs:
        !           204:         name, basicusage, maxusage, yieldusage, maxyieldusage, count, calls = \
        !           205:             funcs[funcaddr]
        !           206:         if maxusage == 0 and maxyieldusage is None:
1.1       root      207:             continue
1.1.1.2 ! root      208:         yieldstr = ""
        !           209:         if maxyieldusage is not None:
        !           210:             yieldstr = ",%d" % maxyieldusage
        !           211:         print "\n%s[%d,%d%s]:" % (name, basicusage, maxusage, yieldstr)
1.1       root      212:         for insnaddr, calladdr, stackusage in calls:
1.1.1.2 ! root      213:             callinfo = funcs.get(calladdr, ("<unknown>", 0, 0, 0, None))
        !           214:             yieldstr = ""
        !           215:             if callinfo[4] is not None:
        !           216:                 yieldstr = ",%d" % (stackusage + callinfo[4])
        !           217:             print "    %04s:%-40s [%d+%d,%d%s]" % (
1.1       root      218:                 insnaddr, callinfo[0], stackusage, callinfo[1]
1.1.1.2 ! root      219:                 , stackusage+callinfo[2], yieldstr)
1.1       root      220: 
                    221: def main():
                    222:     calc()
                    223: 
                    224: if __name__ == '__main__':
                    225:     main()

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.