#!/bin/sh
# NOTE: considers realloc(p, n) == malloc(n)

MPRAWK=${MPRAWK-awk}

chainlen=999999		# this may be too low for NT ;^)
chainline=0
width=${COLUMNS-80}
showpct=true
showmax=

for arg in "$@"; do
	case "$1" in
	-m)	showmax=true;;
	-n)	showpct=;;
	-w ) 	width=$2; shift;;
	-w*)	width=`echo $1 | sed 's/-w//'`;;
	-c ) 	chainlen=$2; shift;;
	-c*)	chainlen=`echo $1 | sed 's/-c//'`;;
	-C )	chainline=$2; shift;;
	-C*)	chainline=`echo $1 | sed 's/-C//'`;;
	--)	shift; break;;
	*)	break;;
	esac
	shift
done

case "$#" in
0|1)	;;
*)	echo "usage: mprchain [-m] [-n] [-c N] [-C N] [-w N] [log]" >&2
	exit 1;;
esac

case "$chainline" in
0)	chainline=$chainlen;;
esac

MPRFILTER=`mprfilter mprchain "$@"` || exit 1

trap 'exit 0' 13

$MPRFILTER |

$MPRAWK -F: -v chainlen=$chainlen -v showmax=$showmax '
	function addchain(n,	chain, addr, size) {
		addr = $NF
		size = $n
		if (!addr || !size)	# memory was not allocated
			return
		if (n == 2)		# no frame pointers => empty chains
			chain = "???"
		else {
			chain = $2
			for (i=1; i<chainlen && i<n-2; i++)
				chain = chain":"$(2+i)
		}
		chainnum[chain]++
		if (showmax != "") {
			addrsize[addr] = size
			addrchain[addr] = chain
			chaintotal[chain] += size
			if (chaintotal[chain] >= chainmax[chain])
				chainmax[chain] = chaintotal[chain]
		}
		else
			chaintotal[chain] += size
	}
	function delchain(addr,		chain, size) {
		size = addrsize[addr]
		chain = addrchain[addr]
		if (!size || chain == "???")
			return
		chaintotal[chain] -= addrsize[addr]
		delete addrsize[addr]
		delete addrchain[addr]
	}
	/^m/ {	addchain(NF-1) }
	/^r/ {	if (showmax != "") delchain($(NF-1)); addchain(NF-2) }
	/^f/ {  if (showmax != "") delchain($NF) }
	END  {
		total=0
		if (showmax != "")
			for (chain in chainmax)
				total += chainmax[chain]
		else
			for (chain in chaintotal)
				total += chaintotal[chain]
		print total
		if (showmax != "")
			for (chain in chainmax)
				printf "%s\t%d\t%f\n", chain, chainnum[chain], chainmax[chain]
		else
			for (chain in chaintotal)
				printf "%s\t%d\t%f\n", chain, chainnum[chain], chaintotal[chain]
	} ' |

$MPRAWK '
	NR==1	{ print }
	NR>1	{ print |"sort -t\"	\" -n +2" }' |

$MPRAWK -F"	" -v width=$width -v showpct=$showpct -v chainline=$chainline '
	BEGIN {
		w=width-22		# 22=11(num)+11(amt)
		if (showpct != "")
			w=width-29	# 29=22+7(showpct)
		getline tot
	}
	function printchain(chain, first)
	{
		printf "%-*s", w, chain
		if (first) {
			printf "%-11d", $2
			if ($3 <= 2147483647.0)		# 2^31-1
				printf "%-11d", $3
			else
				printf "%-11.5g", $3
			if (showpct != "")
				printf "%.02f%%", (100.0*$3)/tot
		}
		printf "\n"
	}
	{
		n=split($1, arr, /:/)
		chain=""; nchain=0
		first=1
		for (i=1; i<=n; i++) {
			if (chain != "") {
				if ((nchain >= chainline) ||
				    (length(chain)+length(arr[i]) >= w-2)) {
					printchain(chain":", first)
					chain=""; nchain=0
					first=0
				}
			}
			if (chain != "")
				chain=chain":"arr[i]
			else
				chain=arr[i]
			nchain++
		}
		if (chain != "")
			printchain(chain, first)
		printf "--\n"
	}
'

exit 0
