summaryrefslogtreecommitdiffstats
path: root/qemu/roms/seabios/scripts/checkstack.py
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/seabios/scripts/checkstack.py')
-rwxr-xr-xqemu/roms/seabios/scripts/checkstack.py227
1 files changed, 227 insertions, 0 deletions
diff --git a/qemu/roms/seabios/scripts/checkstack.py b/qemu/roms/seabios/scripts/checkstack.py
new file mode 100755
index 000000000..b49b6c8cc
--- /dev/null
+++ b/qemu/roms/seabios/scripts/checkstack.py
@@ -0,0 +1,227 @@
+#!/usr/bin/env python
+# Script that tries to find how much stack space each function in an
+# object is using.
+#
+# Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+
+# Usage:
+# objdump -m i386 -M i8086 -M suffix -d out/rom16.o | scripts/checkstack.py
+
+import sys
+import re
+
+# Functions that change stacks
+STACKHOP = ['stack_hop', 'stack_hop_back']
+# List of functions we can assume are never called.
+#IGNORE = ['panic', '__dprintf']
+IGNORE = ['panic']
+
+OUTPUTDESC = """
+#funcname1[preamble_stack_usage,max_usage_with_callers]:
+# insn_addr:called_function [usage_at_call_point+caller_preamble,total_usage]
+#
+#funcname2[p,m,max_usage_to_yield_point]:
+# insn_addr:called_function [u+c,t,usage_to_yield_point]
+"""
+
+# Find out maximum stack usage for a function
+def calcmaxstack(funcs, funcaddr):
+ info = funcs[funcaddr]
+ # Find max of all nested calls.
+ maxusage = info[1]
+ maxyieldusage = doesyield = 0
+ if info[3] is not None:
+ maxyieldusage = info[3]
+ doesyield = 1
+ info[2] = maxusage
+ info[4] = info[3]
+ seenbefore = {}
+ totcalls = 0
+ for insnaddr, calladdr, usage in info[6]:
+ callinfo = funcs.get(calladdr)
+ if callinfo is None:
+ continue
+ if callinfo[2] is None:
+ calcmaxstack(funcs, calladdr)
+ if callinfo[0] not in seenbefore:
+ seenbefore[callinfo[0]] = 1
+ totcalls += 1 + callinfo[5]
+ funcnameroot = callinfo[0].split('.')[0]
+ if funcnameroot in IGNORE:
+ # This called function is ignored - don't contribute it to
+ # the max stack.
+ continue
+ if funcnameroot in STACKHOP:
+ if usage > maxusage:
+ maxusage = usage
+ if callinfo[4] is not None:
+ doesyield = 1
+ if usage > maxyieldusage:
+ maxyieldusage = usage
+ continue
+ totusage = usage + callinfo[2]
+ if totusage > maxusage:
+ maxusage = totusage
+ if callinfo[4] is not None:
+ doesyield = 1
+ totyieldusage = usage + callinfo[4]
+ if totyieldusage > maxyieldusage:
+ maxyieldusage = totyieldusage
+ info[2] = maxusage
+ if doesyield:
+ info[4] = maxyieldusage
+ info[5] = totcalls
+
+# Try to arrange output so that functions that call each other are
+# near each other.
+def orderfuncs(funcaddrs, availfuncs):
+ l = [(availfuncs[funcaddr][5], availfuncs[funcaddr][0], funcaddr)
+ for funcaddr in funcaddrs if funcaddr in availfuncs]
+ l.sort()
+ l.reverse()
+ out = []
+ while l:
+ count, name, funcaddr = l.pop(0)
+ if funcaddr not in availfuncs:
+ continue
+ calladdrs = [calls[1] for calls in availfuncs[funcaddr][6]]
+ del availfuncs[funcaddr]
+ out = out + orderfuncs(calladdrs, availfuncs) + [funcaddr]
+ return out
+
+# Update function info with a found "yield" point.
+def noteYield(info, stackusage):
+ prevyield = info[3]
+ if prevyield is None or prevyield < stackusage:
+ info[3] = stackusage
+
+# Update function info with a found "call" point.
+def noteCall(info, subfuncs, insnaddr, calladdr, stackusage):
+ if (calladdr, stackusage) in subfuncs:
+ # Already noted a nearly identical call - ignore this one.
+ return
+ info[6].append((insnaddr, calladdr, stackusage))
+ subfuncs[(calladdr, stackusage)] = 1
+
+hex_s = r'[0-9a-f]+'
+re_func = re.compile(r'^(?P<funcaddr>' + hex_s + r') <(?P<func>.*)>:$')
+re_asm = re.compile(
+ r'^[ ]*(?P<insnaddr>' + hex_s
+ + r'):\t.*\t(addr32 )?(?P<insn>.+?)[ ]*((?P<calladdr>' + hex_s
+ + r') <(?P<ref>.*)>)?$')
+re_usestack = re.compile(
+ r'^(push[f]?[lw])|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$')
+
+def calc():
+ # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage
+ # , yieldusage, maxyieldusage, totalcalls
+ # , [(insnaddr, calladdr, stackusage), ...]]
+ funcs = {-1: ['<indirect>', 0, 0, None, None, 0, []]}
+ cur = None
+ atstart = 0
+ stackusage = 0
+
+ # Parse input lines
+ for line in sys.stdin.readlines():
+ m = re_func.match(line)
+ if m is not None:
+ # Found function
+ funcaddr = int(m.group('funcaddr'), 16)
+ funcs[funcaddr] = cur = [m.group('func'), 0, None, None, None, 0, []]
+ stackusage = 0
+ atstart = 1
+ subfuncs = {}
+ continue
+ m = re_asm.match(line)
+ if m is not None:
+ insn = m.group('insn')
+
+ im = re_usestack.match(insn)
+ if im is not None:
+ if insn.startswith('pushl') or insn.startswith('pushfl'):
+ stackusage += 4
+ continue
+ elif insn.startswith('pushw') or insn.startswith('pushfw'):
+ stackusage += 2
+ continue
+ stackusage += int(im.group('num'), 16)
+
+ if atstart:
+ if '%esp' in insn or insn.startswith('leal'):
+ # Still part of initial header
+ continue
+ cur[1] = stackusage
+ atstart = 0
+
+ insnaddr = m.group('insnaddr')
+ calladdr = m.group('calladdr')
+ if calladdr is None:
+ if insn.startswith('lcallw'):
+ noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4)
+ noteYield(cur, stackusage + 4)
+ elif insn.startswith('int'):
+ noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6)
+ noteYield(cur, stackusage + 6)
+ elif insn.startswith('sti'):
+ noteYield(cur, stackusage)
+ else:
+ # misc instruction
+ continue
+ else:
+ # Jump or call insn
+ calladdr = int(calladdr, 16)
+ ref = m.group('ref')
+ if '+' in ref:
+ # Inter-function jump.
+ pass
+ elif insn.startswith('j'):
+ # Tail call
+ noteCall(cur, subfuncs, insnaddr, calladdr, 0)
+ elif insn.startswith('calll'):
+ noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4)
+ elif insn.startswith('callw'):
+ noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 2)
+ else:
+ print("unknown call", ref)
+ noteCall(cur, subfuncs, insnaddr, calladdr, stackusage)
+ # Reset stack usage to preamble usage
+ stackusage = cur[1]
+
+ #print("other", repr(line))
+
+ # Calculate maxstackusage
+ for funcaddr, info in funcs.items():
+ if info[2] is not None:
+ continue
+ calcmaxstack(funcs, funcaddr)
+
+ # Sort functions for output
+ funcaddrs = orderfuncs(funcs.keys(), funcs.copy())
+
+ # Show all functions
+ print(OUTPUTDESC)
+ for funcaddr in funcaddrs:
+ name, basicusage, maxusage, yieldusage, maxyieldusage, count, calls = \
+ funcs[funcaddr]
+ if maxusage == 0 and maxyieldusage is None:
+ continue
+ yieldstr = ""
+ if maxyieldusage is not None:
+ yieldstr = ",%d" % maxyieldusage
+ print("\n%s[%d,%d%s]:" % (name, basicusage, maxusage, yieldstr))
+ for insnaddr, calladdr, stackusage in calls:
+ callinfo = funcs.get(calladdr, ("<unknown>", 0, 0, 0, None))
+ yieldstr = ""
+ if callinfo[4] is not None:
+ yieldstr = ",%d" % (stackusage + callinfo[4])
+ print(" %04s:%-40s [%d+%d,%d%s]" % (
+ insnaddr, callinfo[0], stackusage, callinfo[1]
+ , stackusage+callinfo[2], yieldstr))
+
+def main():
+ calc()
+
+if __name__ == '__main__':
+ main()