Messages in this thread Patch in this message | | | Date | Tue, 18 Oct 2011 16:31:50 -0700 | From | Tim Bird <> | Subject | [PATCH 2/3] ARM: Add static kernel function stack size analyzer, for ARM |
| |
This tool can be used to analyze the function stack size for ARM kernels, statically, based on a disassembly of vmlinux (or an individual .o file
Signed-off-by: Tim Bird <tim.bird@am.sony.com> --- scripts/stack_size | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 289 insertions(+), 0 deletions(-) create mode 100755 scripts/stack_size
diff --git a/scripts/stack_size b/scripts/stack_size new file mode 100755 index 0000000..af9fdac --- /dev/null +++ b/scripts/stack_size @@ -0,0 +1,289 @@ +#!/usr/bin/python +# stack_size - a program to parse ARM assembly files and produce +# a listing of stack sizes for the functions encountered. +# +# To use: compile your kernel as normal, then run this program on vmlinux: +# $ scripts/stac_size vmlinux +# +# If you have cross-compiled, then your CROSS_COMPILE environment variable +# should be set to the cross prefix for your toolchain +# +# Author: Tim Bird <tim dot bird ~(at)~ am dot sony dot com> +# Copyright 2011 Sony Network Entertainment, Inc +# +# GPL version 2.0 applies. +# + +import os, sys +version = "1.0" + +debug = 0 + +def dprint(msg): + global debug + if debug: + print msg + +def usage(): + print """Usage: %s [options] <filename> + +Show stack size for functions in a program or object file. +Generate and parse the ARM assembly for the indicated filename, +and print out the size of the stack for each function. + +If you used a cross-compiler, please set the cross-compiler prefix +in the environment variable 'CROSS_COMPILE', before using this tool. + +Options: + -h show this usage help + -t <n> show the top n functions with the largest stacks + (default is to show top 10 functions with largest stacks) + -f show full function list + -g show histogram of stack depths + -V or --version show version information and exit + +Notes: This leaves the assembly file in the same directory as the source +filename, called '<filename>.S' This program currently only supports +ARM EABI. + +""" % os.path.basename(sys.argv[0]) + sys.exit(0) + +class function_class(): + def __init__(self, name, addr): + self.name = name + self.addr = addr + self.depth = 0 + + def __repr__(self): + return "function: %s with depth %d" % (self.name, self.depth) + +def open_assembly(filename): + base = filename + if base.endswith(".o"): + base = base[:-2] + + assembly_filename = "%s.S" % base + + try: + fd = open(assembly_filename) + except: + print "Could not find %s - trying to generate from %s\n" % \ + (assembly_filename, filename) + print "This might take a few minutes..." + try: + cross = os.environ["CROSS_COMPILE"] + except: + cross = "" + cmd = "%sobjdump -d %s >%s" % \ + (cross, filename, assembly_filename) + print "Using command: '%s'" % cmd + os.system(cmd) + + try: + fd = open(assembly_filename) + except: + print "Could not find %s, and conversion didn't work\n" % \ + (assembly_filename) + sys.exit(1) + + return fd; + +def do_show_histogram(functions): + depth_count = {} + + max_depth_bucket = 0 + for funcname in functions.keys(): + depth = functions[funcname].depth + depth_bucket = depth/10 + if depth_bucket > max_depth_bucket: + max_depth_bucket = depth_bucket + + try: + depth_count[depth_bucket] += 1 + except: + depth_count[depth_bucket] = 1 + + print "=========== HISTOGRAM ==============" + for i in range(0,max_depth_bucket+1): + try: + count = depth_count[i] + except: + count = 0 + + # use numbers for bars that are too long + if count<72: + bar = count*"*" + else: + bar = (72-8)*"*" + "(%d)*" % count + print "%3d: %s" % (i*10,bar) + +def cmp_depth(f1, f2): + return cmp(f1.depth, f2.depth) + +def main(): + global debug + + full_list = 0 + show_histogram = 0 + debug = 0 + top_count = 10 + + if "-h" in sys.argv: + usage() + + if "-f" in sys.argv: + full_list = 1 + sys.argv.remove("-f") + + if "-g" in sys.argv: + show_histogram = 1 + sys.argv.remove("-g") + + if "-t" in sys.argv: + top_count = sys.argv[sys.argv.index("-t")+1] + sys.argv.remove(top_count) + sys.argv.remove("-t") + top_count = int(top_count) + + if "--debug" in sys.argv: + debug = 1 + sys.argv.remove("--debug") + + if "--version" in sys.argv or "-V" in sys.argv: + print "%s version %s" % (os.path.basename(sys.argv[0]),version) + sys.exit(0) + + try: + filename = sys.argv[1] + except: + print "Error: missing filename to scan" + usage() + + fd = open_assembly(filename) + + functions = {} + func = None + + max_cur_depth = 0 + depth = 0 + func_line_count = 0 + PROGRAM_ENTRY_MAXLINES = 4 + + for line in fd.xreadlines(): + # parse lines + + # find function starts + try: + if line[8:10]==" <": + func_line = line + (func_addr_s,rest) = func_line.split(" ",1) + funcname = rest[1:-3] + #print line, + func_addr = int(func_addr_s, 16) + #print "func_addr=%x, func=%s" % (func_addr, func) + # record depth of last function + if func: + func.depth = max_cur_depth + dprint("stack depth: %d" % max_cur_depth) + + # start new function + dprint("function: %s" % funcname) + func = function_class(funcname, func_addr) + functions[funcname] = func + depth = 0 + max_cur_depth = 0 + func_line_count = 0 + + except: + pass + + func_line_count += 1 + + # calculate stack depth + # pattern is: initial register push (with "push {...} + # reservation of local stack space + if line.find("push\t") != -1 and \ + func_line_count<PROGRAM_ENTRY_MAXLINES: + # parse push + (before, args) = line.split("push",1) + dprint("push args=%s" % args) + regs = line.split(",") + depth += 4 * len(regs) + if depth>max_cur_depth: + max_cur_depth = depth + + dprint("depth=%d" % depth) + + if line.find("sub\tsp, sp, #") != -1 and \ + func_line_count<PROGRAM_ENTRY_MAXLINES: + # parse sub + (before, args) = line.split("sub",1) + if debug: + print "sub args=%s" % args + (sp1,sp2,imm) = line.split(",",2) + # next line splits: ' #84\t ; 0x54\n' + # into ['#84',';','0x54'] + # take [0]th element, and strip leading '#' + #print "imm='%s'" % imm + depth += int(imm.split()[0][1:]) + if depth>max_cur_depth: + max_cur_depth = depth + + dprint("depth=%d" % depth) + + # disable check for pops (used for debugging) + if None and line.find("pop\t") != -1: + # parse pop + (before, args) = line.split("pop",1) + dprint("pop args=%s" % args) + regs = line.split(",") + depth -= 4 * len(regs) + if depth<0: + print "Parser Error: function: %s, depth=%d" % \ + (funcname, depth) + dprint("depth=%d" % depth) + + # record depth of last function + if func: + func.depth = max_cur_depth + dprint("max stack depth: %d" % max_cur_depth) + + fd.close() + + if not functions: + print "No functions found. Done." + return + + # calculate results + max_depth = 0 + maxfunc = func + for func in functions.values(): + if func.depth > max_depth: + max_depth = func.depth + maxfunc = func + + print "============ RESULTS ===============" + print "number of functions = %d" % len(functions) + print "max function stack depth= %d" % maxfunc.depth + print "function with max depth = %s\n" % maxfunc.name + + if full_list or top_count: + funclist = functions.values() + funclist.sort(cmp_depth) + print "%-32s %s" % ("Function Name ","Stack Depth") + print "%-32s %s" % ("=====================","===========") + + if full_list: + top_count = len(funclist) + + start = len(funclist)-top_count + for i in range(start, start+top_count): + func = funclist[i] + print "%-32s %4d" % (func.name, func.depth) + + if show_histogram: + do_show_histogram(functions) + +if __name__=="__main__": + main() -- 1.6.6
| |