#!/bin/bash
#
##repobench - generate or render reposurgeon profiling results
##
## usage: repobench [-h] [-c cmd] [-p datafiles] [-o datafiles] [-s runtime|heap|highwater] [dumpfile min step max]

command=""
datfile=""
display=no
style=runtime
while getopts c:hops: opt
do
    case $opt in
	c) command="$OPTARG";;
	o) display=yes; svg=yes;;
	p) display=yes; svg=no;;
	s) style=$OPTARG;;
	*) grep '^##' "${0}" | sed -e 's/##//'
	   exit 0;;
    esac
done
shift $((OPTIND - 1))

reposurgeon="$(dirname "${0}")/reposurgeon"

function alphaonly {
    echo -n "${*}" | tr -cs '[:lower:]' '_'
}

if [ $# = 4 ] && [ $display = no ]
then
    function run {
	datfile="${1}"	# The output benchmark data
	logfile="${2}"	# The logfile for the conversion
	dump="${3}"		# The dump to be read in
	readlimit="${4}"	# The nmber of revisions to read
	"${reposurgeon}" "logfile ${logfile}" \
			 "readlimit ${readlimit}" \
			 "${command}" \
			 "read <${dump}" \
			 "profile bench" >>"${datfile}"
    }

    dump="${1}"
    min="${2}"
    step="${3}"
    max="${4}"

    # This really shouldn't be run outside of the reposurgeon
    # repository, but we set up a fallback just in case
    rev="$(git describe HEAD 2>/dev/null)"
    if [ "$rev" = "" ]
    then
	# shellcheck disable=SC2046
	set -- $(reposurgeon --version)
	rev=$2
    fi

    datfile="$(basename "${dump}")_${rev}.dat"
    for readlimit in $(seq "${min}" "${step}" "${max}"); do
	logfile="$(basename "${dump}")_${rev}_${readlimit}.log"
	# Flush all disk VM caches so the data file has to be reread
	sudo sh -c 'echo 3 >/proc/sys/vm/drop_caches'
	run "${datfile}" "${logfile}" "${dump}" "${readlimit}"
	if [ $? == 1 ]	# In this context, means we got EOF before readlimit
	then
	    break
	fi
    done
    echo "Results are in ${datfile}"
    exit 0
fi

if [ $display = yes ]
then
    case $style in
	runtime) idx=2; title="running time";;
	heap) idx=3; title="heap size";;
	highwater) idx=4; title="total memory";;
	*)
	    echo "Unknown plot type $style" >&2
	    exit 1
	    ;;
    esac
fi

if [ $display = yes ] && [ "$2" = "" ]
then
    datfile=${datfile:-$1}
    if [ ! -f "$datfile" ]
    then
	echo "File ${datfile} is missing." >&2
	exit 1
    fi

    cat >/tmp/plotme$$ <<EOF
set key top lmargin
set xlabel "number of revisions read in by reposurgeon"
set ylabel "$style"
f(x) = a*x + b
g(x) = c*x**2 + d*x + e
fit f(x) file using 1:$idx via a,b
fit g(x) file using 1:$idx via c,d,e
plot file u 1:$idx title "$title" lw 2, \
     f(x) title "linear fit" lw 2, \
     g(x) title "quadratic fit" lw 2
EOF

    # The actual plotting
    if [ $svg = yes ]
    then
	# You could use the non-SVG mode to view the graph, and then
	# use the viewer to save an svg file. However, that has some
	# drawbacks; in particular the size of the graph will be fixed
	# rather than dynamic, which means that it won't resize to
	# your browser window.
	name="$(alphaonly "${title}")_from_$(alphaonly "${datfile}")"
	gnuplot --persist -e "file='${datfile}'; set terminal svg size 1920,1080 dynamic mouse standalone name '${name}'" /tmp/plotme$$
    else
	gnuplot --persist -e "file='${datfile}'" /tmp/plotme$$ -
    fi
    rm -f /tmp/plotme$$
    exit 0
fi

if [ $display = yes ] && [ "$2" != "" ]
then
    # We have: Multiple benchmark-data files corresponding to different
    # revisions.  We want: A merged plot extracting a selected statistic
    # from all of them for comparative graphing.
    cat >/tmp/plotme$$ <<EOF
set key top lmargin
set title "comparative ${title}"
set xlabel "number of revisions read in by reposurgeon"
set ylabel "${style}"
plot for [i=1:words(files)] word(files, i) using 1:${idx} title word(files, i) noenhanced lw 2
EOF
    # The actual plotting
    if [ $svg = yes ]
    then
	name="comparative_$(alphaonly "${title}")"
	gnuplot --persist -e "files='$*'; set terminal svg size 1920,1080 dynamic mouse standalone name '${name}'" /tmp/plotme$$
    else
	gnuplot --persist -e "files='$*'" /tmp/plotme$$ -
    fi
    rm -f /tmp/plotme$$
    exit 0
fi

# Fall through to usage description
grep '^##' "${0}" | sed -e 's/##//'

# end
