#!/bin/bash
########################################################################
# Marcus Ebert, marcus.ebert@ed.ac.uk, mec6975@gmail.com               #
# Version 1, August 2016                                               #
# Version 2, January 2017, added comments and help output              #
#----------------------------------------------------------------------#
# Parameter list                                                       #
# 1) file that contains filenames including absolute paths to be read  #
# 2) Name that indicates the test                                      #
#    should contain the test disk configuration and file system used   #
# 3) Area used for write tests (should have no compression enabled)    #
########################################################################
helpoutput ()
{
echo
echo "The script needs 3 parameters:"
echo
echo "1: text file that contains the name of all files to be read"
echo "   It must include the absolute path."
echo 
echo "2: A name you want to give your test,e.g. ZFS-12disk-raidz2"
echo
echo "3: the absolute path of the area used to write to"
echo "   This area should not have compression enabled!"
echo
echo "Example: ./testscript.sh files.txt ZFS-12disk-raidz2 /tank-4TB/writes"
echo
}

if [ $(echo $@|grep -c -e "-h\|--help\|-?") -eq 1 ]; 
then
 helpoutput
 exit 0
fi

if [ "$#" -ne 3 ]; 
then
 helpoutput
 exit 1 
fi


filelist=$1  # contains list of files to be read, mix of small and large files

#Output of results goes by default into the homedir
resultarea="$HOME/zfstests"

outputarea="$resultarea/$2"
writearea=$3

mkdir -p $outputarea
mkdir -p $writearea

parallelread=10   # number of files read in parallel

maxlarge=100      #maximum number of files to write in test
parallellarge=5   #maximum parallel writes
largebs=10M       #dd bs
largecount=5120   #dd count

maxsmall=200000   #maximum number of small files to write in test
parallelsmall=20  #maximum parallel writes for small files
smallbs=8k        #dd bs for writing of small files
smallcount=16     #dd count for writing of small files


###################################################################
# (Usually) Nothing to be changed behind this point               #
#                                                                 #
###################################################################
readtest ()
{
emptycache
echo "-------------------------------------------"
echo " starting read test"
date
echo "-------------------------------------------"
local line="  "
local blocksize=$2
while read line
do
 readout $line $blocksize
done < $1
echo "-------------------------------------------"
date
echo " finished read test"
echo "-------------------------------------------"
}

preadtest ()
{
emptycache
echo "-------------------------------------------"
echo " starting parallel read test"
date
echo "-------------------------------------------"
local line="  "
local counter
local counting=0
local endpoint=$3
local blocksize=$2
local pids[0]=0
local filenames=($(cat $1))
local numberoffiles=${#filenames[@]}
while [ $counting -lt $numberoffiles ];
do
 for counter in $(seq 1 $endpoint)
 do
  line=${filenames[counting]}
  readout $line $blocksize &
  pids[$counter]=$!
  counting=$((counting + 1))
 done
 waitpids pids[@]
done 
echo "-------------------------------------------"
date
echo " finished parallel read test"
echo "-------------------------------------------"
}


writetest ()
{
emptycache
echo "-------------------------------------------"
echo " starting write test"
date
echo "-------------------------------------------"
local counter=0
local endpoint=$1
local counts=$2
local bsize=$3
local writeto=$4
for counter in $(seq 1 $endpoint)
do
  writeout $counter $counts $bsize $writeto
done
echo "-------------------------------------------"
date
echo " finished write test"
echo "-------------------------------------------"
}

pwritetest ()
{
emptycache
echo "-------------------------------------------"
echo " starting parallel write test"
date
echo "-------------------------------------------"
local counter=0
local counting=0
local pids[0]=0
local endpoint=$1
local counts=$2
local bsize=$3
local writeto=$4
local parallelwrite=$5
echo "$endpoint   $counts    $bsize    $parallelwrite"
while [ $counting -lt $endpoint ];
do
 for counter in $(seq 1 $parallelwrite)
 do
  writeout $counting $counts $bsize $writeto&
  pids[$counter]=$!
  counting=$((counting + 1))
  if [ $counting -eq $endpoint ];
  then
   break
  fi
 done
 waitpids pids[@]
done
echo "-------------------------------------------"
date
echo " finished parallel write test"
echo "-------------------------------------------"
}



writeout ()
{
local array
local number=$1
local blockcount=$2
local bsize=$3
local writedir=$4
 array=($(/usr/bin/time -f "%P %e %K %I %O" /bin/dd if=/dev/zero of=$writedir/testfile$number bs=$3 count=$2 conv=fsync 2>&1|grep -A 1 "copied"))
 rm $writedir/testfile$1
 echo ${array[*]}
}

readout ()
{
local array
local file=$1
local bsize=$2
 array=($file $(/usr/bin/time -f "%P %e %K %I %O" /bin/dd if=$file of=/dev/null bs=$2 2>&1|grep -A 1 "copied"))
 echo ${array[*]}
}

waitpids ()
{
local number=1
declare -a localarray=("${!1}")
local numberofpids=${#localarray[@]}
while [ $number -le $numberofpids ]
do
 wait ${localarray[$number]}
 number=$((number+1))
done
}

emptycache ()
{
echo 3 > /proc/sys/vm/drop_caches
}


export -f readtest
export -f preadtest
export -f writetest
export -f pwritetest


# Read tests, sequential and parallel
  (time -p readtest $filelist $largebs)  &> $outputarea/readtest.txt
  (time -p preadtest $filelist $largebs $parallelread) &> $outputarea/preadtest.txt

  # Write tests, sequential and parallel for large files (50GB each)
  (time -p writetest $maxlarge $largecount $largebs $writearea) &> $outputarea/largewritetest.txt
  (time -p pwritetest $maxlarge $largecount $largebs $writearea $parallellarge) &> $outputarea/largepwritetest.txt


  # Write tests, sequential and parallel for small files (128k each)
  (time -p writetest $maxsmall $smallcount $smallbs $writearea) &> $outputarea/smallwritetest.txt
  (time -p pwritetest $maxsmall $smallcount $smallbs $writearea $parallelsmall) &> $outputarea/smallpwritetest.txt
