#!/usr/bin/python
# Copyright (c) 2012 Veselin Georgiev
# Feel free to use this tool for your own projects, to adapt and distribute it.
# Licensed as Creative Commons by-sa 3.0

import sys, re

def fromhex(s):
	if s[:2] not in ['0x', '0X']:
		s = "0x" + s
	return int(s, 16)

USAGE = """
Usage: funcsize.py [options] <file.lst>"

Options:
	-a = sort functions by name
	-s = sort by size (default is unsorted)"""

def main(args):
	if len(args) == 1 or "-h" in args or "--help" in args:
		print USAGE
		return -1
	sort_order = -1
	fn = None
	for arg in args[1:]:
		if arg == '-a':
			sort_order = 0
		elif arg == '-s':
			sort_order = 1
		else:
			assert fn is None, "Only one file can be given on the command line"
			fn = arg
	if fn is None:
		print "File name must be given"
		print USAGE
		return -1
	lines = map(lambda s: s.strip(), open(fn, "r").readlines())
	funcList = []
	
	funcsig = re.compile(r";; \*\*\*\*\*\** function ([a-zA-Z0-9_@]+) \*\*\*\*\*\**")
	startre = re.compile(" *([0-9A-F]+) *([a-zA-Z0-9_@]+):")
	endre =   re.compile(" *([0-9A-F]+)[ \t]*__end_of([a-zA-Z0-9_@]+):")
	prefix = re.compile(" *([0-9]+) (.*)")
	current_func = None
	startaddr = -1
	endaddr = -1
	for l in lines:
		orig_l = l
		if prefix.match(l):
			l = prefix.findall(l)[0][1]
		if current_func == None and funcsig.match(l):
			current_func = funcsig.findall(l)[0]
			startaddr = -1
			endaddr = -1
		if current_func and startaddr == -1 and startre.match(l):
			cfunc = startre.findall(l)[0][1]
			assert cfunc == current_func, "Syntax error at line [%s]" % orig_l
			startaddr = fromhex(startre.findall(l)[0][0])
		if current_func and startaddr != -1 and endre.match(l):
			cfunc = endre.findall(l)[0][1]
			assert cfunc == current_func, "Syntax error at line [%s]" % orig_l
			endaddr = fromhex(endre.findall(l)[0][0])
			funcList.append((current_func, endaddr - startaddr))
			current_func = None
	total_bytes = 0
	longest_func_name = 0
	for (fn, size) in funcList:
		total_bytes += size
		longest_func_name = max(longest_func_name, len(fn))
	if sort_order == 0:
		funcList = sorted(funcList, lambda x, y: cmp(x[0], y[0]))
	elif sort_order == 1:
		funcList = sorted(funcList, lambda x, y: cmp(y[1], x[1]))
	fmt_string = "%%%ds: %%d words (%%.1lf%%%% of all code)" % longest_func_name
	for (fn, size) in funcList:
		print fmt_string % (fn[1:], size, size * 100.0 / total_bytes)

if __name__ == "__main__":
	main(sys.argv)
