#!/bin/sh
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
set -e

CURSOR_SAVE="\033[s"
CURSOR_RESTORE="\033[u"
PAD=".................................................."
AWK_RESULT='$0 ~ /^info  benchmark: processing/ { print $4" "$7" "$10" "$13" "$16; }'
AWK_PROGRESS='$0 ~ /^info  benchmark: progress/ {
	printf "'$CURSOR_RESTORE'%6s %%", $4
	fflush()
}
$0 ~ /^info  benchmark: processing/ {
	printf "'$CURSOR_RESTORE'Passed %13s Mib/s, %18s packets/s\033[K\n", $13, $16
}'

_usage() {
	echo "Usage: $1 [-djkrhvpl] <dir|pcap file>"
	echo "Options:"
	echo "\t-j <thread count>:  Run haka on <thread count> threads"
	echo "\t-o <output dir>:    Output benchmark results to <output dir>"
	echo "\t-d:                 Display debug output"
	echo "\t-k:                 Keep configuration files"
	echo "\t-r <repeat>:        Repeat pcap <repeat> times. Default use last part of pcap file name"
	echo "\t-h:                 Display this help"
	echo "\t-v:                 Profile with valgrind/callgrind"
	echo "\t-p:                 Profile with perf"
	echo "\t-l:                 Profile with LuaJit perf"
}

_error() {
	printf "$@" 1>&2
}

_info() {
	printf "$@"
}

_debug() {
	if $DEBUG; then
		printf "$@"
	fi
}

_bench_dir() {
	for target in $1/*.pcap; do
		_bench_file $target
	done
}

_gen_conf() {
	local conf=`mktemp $1`
	cat > $conf <<EOF
[general]
configuration = "$2"

thread = $4

[packet]
module = "packet/benchmark"
file = "$3"
repeat = $5

[log]
module = "log/syslog"

[alert]
module = "alert/syslog"
EOF
	echo "$conf"
}

_bench_file() {
	local pcap="$1"
	local test_path="${pcap%-*}"
	local name="${test_path##*/}"
	local rule="$test_path.lua"
	if [ -z "$REPEAT" ]; then
		local repeat="${pcap##*-}"
		local repeat="${repeat%.*}"
	else
		local repeat="$REPEAT"
	fi

	local conf=`_gen_conf "$name.XXXXX.conf" $rule $pcap $THREAD $repeat`

	if $BENCHMARK; then
		_do_bench "$name" "$conf"
	fi
	if $VALGRIND; then
		_do_bench "$name-valgrind" "$conf" "`printf "$VALGRIND_WRAPPER" "$RESULT_DIR/valgrind-$name.out"`"
	fi
	if $PERF; then
		_do_bench "$name-perf" "$conf" "`printf "$PERF_WRAPPER" "$RESULT_DIR/perf-$name.out"`"
	fi
	if $LUAJITPERF; then
		_do_bench "$name-luajit-perf" "$conf" $LUAJITPERF_WRAPPER
	fi

	if $RM_CONFIG; then
		rm $conf
	fi
}

_do_bench() {
	local name="$1"
	local conf="$2"
	local wrapper="$3"
	local out="$name.txt"
	local res=""

	_info "%s %0.*s $CURSOR_SAVE  0.00 %%" "$name" `expr ${#PAD} - ${#name}` "$PAD"
	_debug "\n"
	_debug "$wrapper $HAKA -c $conf --pid-file /tmp/haka.pid --ctl-file /tmp/haka.sock --no-daemon\n"
	_debug "output at $out\n"

	set +e
	stdbuf -oL \
	$wrapper $HAKA -c $conf \
		--pid-file /tmp/haka.pid --ctl-file /tmp/haka.sock \
		--no-daemon \
		2>&1 | tee $out | gawk "$AWK_PROGRESS"
	local ret=$?
	set -e

	if [ $ret -ne 0 ]; then
		_info "\033[KFailed\n"
		RESULT="$RESULT$name Failed\n"
		return
	fi

	res=`gawk "$AWK_RESULT" $out`
	RESULT="$RESULT$name $THREAD $res\n"
}

THREAD=`nproc`
DEBUG=false
RM_CONFIG=true
BENCHMARK=true
VALGRIND=false
PERF=false
LUAJITPERF=false
DATE=`date +%FT%T`
RESULT_DIR="hakabench-result-$DATE"
VALGRIND_WRAPPER="valgrind --tool=callgrind --collect-bus=yes --dump-instr=yes --collect-jumps=yes --cache-sim=yes --branch-sim=yes --callgrind-out-file=%s"
PERF_WRAPPER="perf record -e cycles,instructions,cache-misses,branch-misses,cs,migrations,faults -g -v -F 100 --output=%s"
[ -z "$HAKA" ] && HAKA="haka"

while getopts "dkr:hj:o:vpl" OPT; do
	case $OPT in
	d)
		DEBUG=true
	;;
	j)
		THREAD="$OPTARG"
	;;
	k)
		RM_CONFIG=false
	;;
	o)
		RESULT_DIR="$OPTARG"
	;;
	r)
		REPEAT="$OPTARG"
	;;
	h|\?)
		_usage $0
		exit 0
	;;
	v)
		BENCHMARK=false
		VALGRIND=true
	;;
	p)
		BENCHMARK=false
		PERF=true
	;;
	l)
		BENCHMARK=false
		LUAJITPERF=true
	;;
	esac
done

shift $((OPTIND-1))

if [ -z "$1" ]; then
	_error "Missing benchmark directory or file.\n"
	_usage $0
	exit 1
fi

mkdir -p $RESULT_DIR
for TARGET in "$@"; do
	if [ -d $TARGET ]; then
		_bench_dir $TARGET
	elif [ -f $TARGET ]; then
		_bench_file $TARGET
	else
		_error "invalid target $TARGET\n"
	fi
done

# Save result
RESULT_FILE="$RESULT_DIR/summary.txt"
echo -n $RESULT > $RESULT_FILE

_info "\nResults (also in $RESULT_FILE):\n"
echo $RESULT
