# lwpstacks.ksh
#
# Solaris dbx ksh script for getting stack traces from OpenAFS LWP processes
# usage:
# (dbx) source lwpstacks.ksh
# (dbx) lwpstacks
# (dbx) lwpstacks | mdb /path/to/binary /path/to/core
#
# if you don't have LWP debug symbols, that won't work. instead run:
#
# (dbx) lwpstacks | mdb /path/to/binary /path/to/core
#
# to guess at some structures with hard-coded address offsets. that won't
# always work, and will probably need to be updated if structures change in
# OpenAFS.
#
# always try running lwpstacks without piping through mdb the first time, to
# make sure it runs without errors. we don't exactly have very robust error
# handling here.


# output: sets shell vars $head and $pcb
gethead() {
	echo "!# looking for threads in $1 $2"
	if $nodebug ; then
		if [ "x$2" = x ] ; then
			head="((void**)&$1)[0]"
		else
			head="((void**)&$1)[$2*2]"
		fi
		pcb="$head"
	else
		if [ "x$2" = x ] ; then
			head=$1.head
		else
			head="$1[$2].head"
		fi
		pcb="((struct lwp_pcb*)$head)"
	fi
}

# output: sets shell var $topstack
gettopstack() {
	if $nodebug ; then
		topstack=$[((void**)$1)[21]]
	else
		topstack=$[(void*)$1->context.topstack]
	fi
}

# output: sets shell var $pcb
getnext() {
	if $nodebug ; then
		pcb=$[((void**)$1)[51]]
	else
		pcb=$[$pcb->next]
		pcb="((struct lwp_pcb*)$pcb)"
	fi
}

printstacks() {
	pcb="$1"

	if [ "x$[$pcb==0]" = x0 ] ; then
		first=1
		while [ "x$[$pcb==$head]" = x0 ] || [ "x$first" = x1 ] ; do
			first=0

			gettopstack "$pcb"

			printf "::echo stack pointer for thread %x: %x\n" "$[$pcb]" "$topstack"
			printf "0x%08x::stack 0 ! sed 's/^/  /'\n" "$topstack"
			echo "::echo"

			getnext "$pcb"
		done
	fi
}

lwpstacks() {
	if [ x"$1" = xnodebug ] ; then
		nodebug=true
		echo "!# NOT using debug symbols"
	else
		nodebug=false
		echo "!# using debug symbols"
	fi
	
	for head in blocked qwaiting ; do
		gethead "$head"
		printstacks "$pcb"
	done

	for i in 0 1 2 3 4 ; do
		gethead runnable "$i"
		printstacks "$pcb"
	done

	echo
	echo "!# to get stack traces, pipe this output through: mdb /path/to/binary /path/to/core"
	echo
}
