aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2010-03-09 20:01:35 -0500
committerKevin O'Connor <kevin@koconnor.net>2010-03-09 20:01:35 -0500
commita979c1cd7994cde05a06f066beca8784c9dc75a9 (patch)
tree667c08b98a84a1602e7995552fc91b96600bbfd1
parent190cc622a2f57ca21d8ffc85be94c32fb518b04d (diff)
downloadseabios-a979c1cd7994cde05a06f066beca8784c9dc75a9.tar.gz
Improvements to tools/checkstack.py.
Add explicit tracking of functions that switch stacks. Add new tracking of "yield" points -- areas in the code that appear to hand control to third-party code which may use arbitrary stack space. Try to arrange the output so that functions that call each other are near each other.
-rwxr-xr-xtools/checkstack.py147
1 files changed, 113 insertions, 34 deletions
diff --git a/tools/checkstack.py b/tools/checkstack.py
index 82becdd3..84bd778d 100755
--- a/tools/checkstack.py
+++ b/tools/checkstack.py
@@ -12,28 +12,96 @@
import sys
import re
+# Functions that change stacks
+STACKHOP = ['__send_disk_op']
# List of functions we can assume are never called.
-#IGNORE = ['panic', '__dprintf', '__send_disk_op']
-IGNORE = ['panic', '__send_disk_op']
+#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.
- max = info[1]
- info[2] = max
- for insnaddr, calladdr, usage in info[3]:
+ 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[calladdr]
if callinfo[2] is None:
calcmaxstack(funcs, calladdr)
- if callinfo[0].split('.')[0] in IGNORE:
+ 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 > max:
- max = totusage
- info[2] = max
+ 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(funcnames, availnames, funcs):
+ l = [(availnames[name][5], name)
+ for name in funcnames if name in availnames]
+ l.sort()
+ l.reverse()
+ out = []
+ while l:
+ count, name = l.pop(0)
+ if name not in availnames:
+ continue
+ callnames = [funcs[calls[1]][0] for calls in availnames[name][6]]
+ del availnames[name]
+ out = out + orderfuncs(callnames, availnames, funcs) + [name]
+ 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>.*)>:$')
@@ -46,8 +114,9 @@ re_usestack = re.compile(
def calc():
# funcs[funcaddr] = [funcname, basicstackusage, maxstackusage
- # , [(addr, callfname, stackusage), ...]]
- funcs = {-1: ['<indirect>', 0, 0, []]}
+ # , yieldusage, maxyieldusage, totalcalls
+ # , [(insnaddr, calladdr, stackusage), ...]]
+ funcs = {-1: ['<indirect>', 0, 0, None, None, 0, []]}
cur = None
atstart = 0
stackusage = 0
@@ -58,7 +127,7 @@ def calc():
if m is not None:
# Found function
funcaddr = int(m.group('funcaddr'), 16)
- funcs[funcaddr] = cur = [m.group('func'), 0, None, []]
+ funcs[funcaddr] = cur = [m.group('func'), 0, None, None, None, 0, []]
stackusage = 0
atstart = 1
subfuncs = {}
@@ -84,38 +153,38 @@ def calc():
cur[1] = stackusage
atstart = 0
+ insnaddr = m.group('insnaddr')
calladdr = m.group('calladdr')
if calladdr is None:
if insn[:6] == 'lcallw':
- stackusage += 4
- calladdr = -1
+ noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4)
+ noteYield(cur, stackusage + 4)
+ elif insn[:3] == 'int':
+ noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6)
+ noteYield(cur, stackusage + 6)
+ elif insn[:3] == 'sti':
+ noteYield(cur, stackusage)
else:
- # misc instruction - just ignore
+ # misc instruction
continue
else:
# Jump or call insn
calladdr = int(calladdr, 16)
ref = m.group('ref')
if '+' in ref:
- # Inter-function jump - reset stack usage to
- # preamble usage
- stackusage = cur[1]
- continue
- if insn[:1] == 'j':
+ # Inter-function jump.
+ pass
+ elif insn[:1] == 'j':
# Tail call
- stackusage = 0
+ noteCall(cur, subfuncs, insnaddr, calladdr, 0)
elif insn[:5] == 'calll':
- stackusage += 4
+ noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4)
else:
print "unknown call", ref
- if (calladdr, stackusage) not in subfuncs:
- cur[3].append((m.group('insnaddr'), calladdr, stackusage))
- subfuncs[(calladdr, stackusage)] = 1
+ noteCall(cur, subfuncs, insnaddr, calladdr, stackusage)
# Reset stack usage to preamble usage
stackusage = cur[1]
- continue
-
#print "other", repr(line)
# Calculate maxstackusage
@@ -126,19 +195,29 @@ def calc():
continue
calcmaxstack(funcs, funcaddr)
- # Show all functions
+ # Sort functions for output
funcnames = bynames.keys()
- funcnames.sort()
+ funcnames = orderfuncs(funcnames, bynames.copy(), funcs)
+
+ # Show all functions
+ print OUTPUTDESC
for funcname in funcnames:
- name, basicusage, maxusage, calls = bynames[funcname]
- if maxusage == 0:
+ name, basicusage, maxusage, yieldusage, maxyieldusage, count, calls = \
+ bynames[funcname]
+ if maxusage == 0 and maxyieldusage is None:
continue
- print "\n%s[%d,%d]:" % (funcname, basicusage, maxusage)
+ yieldstr = ""
+ if maxyieldusage is not None:
+ yieldstr = ",%d" % maxyieldusage
+ print "\n%s[%d,%d%s]:" % (funcname, basicusage, maxusage, yieldstr)
for insnaddr, calladdr, stackusage in calls:
callinfo = funcs[calladdr]
- print " %04s:%-40s [%d+%d,%d]" % (
+ 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])
+ , stackusage+callinfo[2], yieldstr)
def main():
calc()