#!/bin/sh
#
# kft-trace - helper program to execute traces on a target board
#
# Copyright 2008 Sony Corporation
#
# GPL version 2.0 only applies.
#

VERSION="1.0"

trace_dir=/tmp

# uncomment next line to debug script
#set -x

function usage
{
	echo "Usage: $1 [options] <trace-name> [-l <l1> <l2>...]"
	echo "Execute a KFT trace, and store the results in <trace-name>.log"
	echo "Options:"
	echo " -h         Show this usage help"
	echo " -f <func>  Trace a single function <func> in the kernel"
	echo " -fs <func> Start trace on entry to function <func>"
	echo " -fe <func> End trace on exit from function <func>"
	echo " -ts <u>    Start trace at time <u> microseconds after kernel boot"
	echo " -te <u>    End trace at time <u> microseconds after trace start"
	echo " -m <u>     Filter out functions that last less then <u> micro-seconds"
	echo "            (Default is 100)"
	echo " -n         Filter out functions that occur in interrupt context"
	echo " -l <f1> <f2> ...  Get duration times for functions in the specified list"
	echo "            (If used, this option MUST! be specified last on the command line)"
	echo " -r         Autorepeat the trace"
	echo " -c <cmd>   Execute a command during the trace"
	echo " -p <n>     Stop this script after specified phase"
	echo "            1=config, 2=prime and start trace, 3=run command"
	echo " -k <trace-name> Stop the currently running trace, and store the results."
	echo " -v         Be verbose"
	echo
	exit 0
}

# make sure kernel has kft
if [ ! -f /proc/kft ]; then
	echo "Error: kernel does not support KFT"
	echo "Please recompile kernel with KFT support and reinstall"
	exit 1
fi

# parse command-line args
if [ $# -lt 1 ]; then
	usage `basename $0`
fi

func=None
kill=0
verbose=0
mintime="filter mintime 100"
trig_start_line=""
trig_stop_line=""
noints=""
trace_name=kftrace
func_line=""
autorepeat_line=""
cmd=None
phase=all
while [ $# -ge 1 ]; do
	#echo arg 1 = $1
	case $1 in
	-h)
		usage $(basename $0)
		;;
	-f)
		trig_start_line="trigger start entry $2"
		trig_stop_line="trigger stop exit $2"
		shift
		;;
	-fs)
		trig_start_line="trigger start entry $2"
		shift
		;;
	-fe)
		trig_start_line="trigger start entry $2"
		shift
		;;
	-k)
		kill=1
		;;
	-m)
		mintime="filter mintime $2"
		shift
		;;
	-n)
		noints="filter noints"
		shift
		;;
	-c)
		cmd=$2
		shift
		;;
	-v)
		verbose=1
		;;
	-ts)
		trig_start_line="trigger start time $2"
		shift
		;;
	-te)
		trig_stop_line="trigger stop time $2"
		shift
		;;
	-r)
		autorepeat_line="autorepeat"
		;;
	-p)
		phase=$2
		shift
		;;
	-l)
		shift
		func_line="filter funclist"
		while [ $# -ge 1 ]; do
			func_line="$func_line $1"
			shift
		done
		func_line="$func_line fend"
		;;
	-*)
		echo "Unknown option '$1'"
		usage $(basename $0)
		;;
	*)
		trace_name=$1
		;;
	esac
	shift
done

if [ "$kill" = "1" ]; then
	echo stop >/proc/kft
	cat /proc/kft >${trace_dir}/${trace_name}.log
	cat /proc/kft_data >>${trace_dir}/${trace_name}.log

	echo "Trace done.  Log file is at: ${trace_dir}/${trace_name}.log"
	exit 0
fi

if [ "$verbose" = "1" ]; then
	echo "kill=$kill ; phase=$phase"
	echo "mintime=$mintime ; noints=$noints"
	echo "cmd=$cmd ; trace_name=$trace_name"
fi

# build config for this
conf_file=/tmp/${trace_name}.conf
cat <<CONF >$conf_file
new
begin
$trig_start_line
$trig_stop_line
$mintime
$noints
$autorepeat_line
$func_line
end
CONF

if [ "$verbose" = "1" ]; then
	echo "Configuration is:"
	cat $conf_file
fi

# find System.map file
if [ -f /tmp/System.map ]; then
	mapfile=/tmp/System.map
fi
if [ -f /boot/System.map ]; then
	mapfile=/boot/System.map
fi
if [ -f ./System.map ]; then
	mapfile=./System.map
fi
if [ -f ../System.map ]; then
	mapfile=../System.map
fi

if [ "$verbose" = "1" ]; then
	echo "Using map file $mapfile"
fi

# convert symbols to addresses
echo -n >${conf_file}.mapped
cat $conf_file | while read line
do
        for word in $line ; do
                case $word in
                        new|begin|trigger|start|stop|entry|exit|time|filter|mintime|maxtime|noints|onlyints|0|funclist|fend)
                                ;;
                        *)
                                addr=`grep " ${word}$" $mapfile | cut -d " " -f1`
                                if [ -n "$addr" ]; then
                                        word="0x$addr"
                                fi
                                ;;
                esac
                echo -n "$word " >>${conf_file}.mapped
        done
       	echo >>${conf_file}.mapped
done

# apply configuration
cat ${conf_file}.mapped >/proc/kft

if [ "$verbose" = "1" ]; then
	echo "Loaded configuration:"
	cat /proc/kft
fi

if [ "$phase" = "1" ]; then
	echo "Stopped after building and installing the trace configuration"
	exit 0
fi

echo prime >/proc/kft

if [ -z "$trig_start_line" ]; then
	echo start >/proc/kft
fi

if [ "$phase" = "2" ]; then
	echo "Stopped after priming and starting trace"
	exit 0
fi

if [ $cmd != "None" ]; then
	$cmd
fi

if [ "$phase" = "3" ]; then
	echo "Stopped after executing command"
	exit 0
fi

#set -x
echo "Waiting for trace to complete"
sleep 2
while grep -q "not complete" /proc/kft ; do
	if [ "$verbose" = "1" ]; then
		head -1 /proc/kft
	fi
	sleep 1
done
#set +x

cat /proc/kft >${trace_dir}/${trace_name}.log
cat /proc/kft_data >>${trace_dir}/${trace_name}.log

echo "Trace done.  Log file is at: ${trace_dir}/${trace_name}.log"
