All of lore.kernel.org
 help / color / mirror / Atom feed
From: tim.bird@am.sony.com (Tim Bird)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/3] ARM: Add static kernel function stack size analyzer, for ARM
Date: Tue, 18 Oct 2011 16:31:50 -0700	[thread overview]
Message-ID: <4E9E0C66.3020405@am.sony.com> (raw)
In-Reply-To: <4E9E0B71.9020708@am.sony.com>

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

WARNING: multiple messages have this Message-ID (diff)
From: Tim Bird <tim.bird@am.sony.com>
To: Russell King <rmk@arm.linux.org.uk>,
	"linux-arm-kernel@lists.infradead.org" 
	<linux-arm-kernel@lists.infradead.org>,
	Arnd Bergmann <arnd@arndb.de>, Andi Kleen <andi@firstfloor.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	linux kernel <linux-kernel@vger.kernel.org>
Subject: [PATCH 2/3] ARM: Add static kernel function stack size analyzer, for ARM
Date: Tue, 18 Oct 2011 16:31:50 -0700	[thread overview]
Message-ID: <4E9E0C66.3020405@am.sony.com> (raw)
In-Reply-To: <4E9E0B71.9020708@am.sony.com>

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


  parent reply	other threads:[~2011-10-18 23:31 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-10-18 23:27 [PATCH 0/3] ARM 4Kstacks: introduction Tim Bird
2011-10-18 23:27 ` Tim Bird
2011-10-18 23:29 ` [PATCH 1/3] ARM 4Kstacks: Add support for 4K kernel stacks to ARM Tim Bird
2011-10-18 23:29   ` Tim Bird
2011-10-18 23:31 ` Tim Bird [this message]
2011-10-18 23:31   ` [PATCH 2/3] ARM: Add static kernel function stack size analyzer, for ARM Tim Bird
2011-10-19 10:45   ` Arnd Bergmann
2011-10-19 10:45     ` Arnd Bergmann
2011-10-21 17:03     ` Andi Kleen
2011-10-21 17:03       ` Andi Kleen
2011-10-18 23:34 ` [PATCH 3/3] ARM 4Kstacks: Decrease poll and select stack usage, when using 4K stacks Tim Bird
2011-10-18 23:34   ` Tim Bird
2011-10-20 12:21   ` Arnd Bergmann
2011-10-20 12:21     ` Arnd Bergmann
2011-10-19  0:14 ` [PATCH 0/3] ARM 4Kstacks: introduction Joe Perches
2011-10-19  0:14   ` Joe Perches
2011-10-19  0:26   ` Tim Bird
2011-10-19  0:26     ` Tim Bird
2011-10-19  0:31     ` Joe Perches
2011-10-19  0:31       ` Joe Perches
2011-10-19 23:43       ` Tim Bird
2011-10-19 23:43         ` Tim Bird
2011-10-19  4:54     ` Dave Chinner
2011-10-19  4:54       ` Dave Chinner
2011-10-19  5:02       ` Andi Kleen
2011-10-19  5:02         ` Andi Kleen
2011-10-19 10:51     ` Arnd Bergmann
2011-10-19 10:51       ` Arnd Bergmann
2011-10-22  8:50       ` Ming Lei
2011-10-22  8:50         ` Ming Lei
2011-10-22 13:13         ` Måns Rullgård
2011-10-22 13:13           ` Måns Rullgård
2011-10-22 14:27           ` Andi Kleen
2011-10-22 14:27             ` Andi Kleen
2011-10-22 13:36         ` Russell King - ARM Linux
2011-10-22 13:36           ` Russell King - ARM Linux
2011-10-23 19:25           ` Tim Bird
2011-10-23 19:25             ` Tim Bird
2011-10-23 20:11             ` Russell King - ARM Linux
2011-10-23 20:11               ` Russell King - ARM Linux
2011-10-24 10:36           ` Ming Lei
2011-10-24 10:36             ` Ming Lei
2011-10-23 14:06       ` Bernd Petrovitsch
2011-10-23 14:06         ` Bernd Petrovitsch
2011-10-19  4:33 ` Dave Chinner
2011-10-19  4:33   ` Dave Chinner
2011-10-19  7:35 ` Russell King - ARM Linux
2011-10-19  7:35   ` Russell King - ARM Linux
2011-10-19 23:36   ` Tim Bird
2011-10-19 23:36     ` Tim Bird
2011-10-20  0:13     ` Måns Rullgård
2011-10-20  0:13       ` Måns Rullgård
2011-10-20  1:08       ` Tim Bird
2011-10-20  1:08         ` Tim Bird
2011-10-20  1:55         ` Måns Rullgård
2011-10-20  1:55           ` Måns Rullgård
2011-10-20 22:21     ` Dave Chinner
2011-10-20 22:21       ` Dave Chinner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E9E0C66.3020405@am.sony.com \
    --to=tim.bird@am.sony.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.