#!/bin/bash
# This benchmark is intended to test how long it takes to munlock()
# a single buffer allocated with MAP_LOCKED.

###SHELLPACK preamble timedmunlock-bench 0

ALLOC_GB=0
ITERATIONS=10

###SHELLPACK parseargBegin
###SHELLPACK parseargParam --alloc-gb TIMEDMUNLOCK_ALLOC_GB
###SHELLPACK parseargParam --iterations TIMEDMUNLOCK_ITERATIONS
###SHELLPACK parseargEnd
###SHELLPACK monitor_hooks

ALLOC_GB=${TIMEDMUNLOCK_ALLOC_GB}
ITERATIONS=${TIMEDMUNLOCK_ITERATIONS-ITERATIONS}

# Build the munlock measuing program
echo Building munlock program
TEMPFILE=`mktemp`
LINECOUNT=`wc -l $0 | awk '{print $1}'`
CSTART=`grep -n "BEGIN C FILE" $0 | tail -1 | awk -F : '{print $1}'`
tail -$(($LINECOUNT-$CSTART)) $0 | grep -v "^###" > $TEMPFILE.c
gcc -O2 -lrt -DALLOC_GB=$ALLOC_GB $TEMPFILE.c -o $TEMPFILE || exit -1

READY_SIGNAL_FILE="$LOGDIR_RESULTS/ready-for-signal"
MUNLOCK_COMPLETE_FILE="$LOGDIR_RESULTS/munlock-complete"

set_mmtests_numactl 1 1

for i in `seq 1 $ITERATIONS`; do
        echo "Starting program, iteration $i/$ITERATIONS"
        echo $MMTESTS_NUMACTL $TEMPFILE $READY_SIGNAL_FILE $MUNLOCK_COMPLETE_FILE
        $MMTESTS_NUMACTL $TEMPFILE $READY_SIGNAL_FILE $MUNLOCK_COMPLETE_FILE \
              >> $LOGDIR_RESULTS/timedmunlock.time &
        PROG_PID=$!
        echo "Program PID: ${PROG_PID}"
        while [[ ! -f $READY_SIGNAL_FILE ]]; do
                jobs 1 > /dev/null || die "timedmunlock failed prematurely"
                sleep 1
        done
        rm -f $READY_SIGNAL_FILE

        monitor_pre_hook $LOGDIR_RESULTS timedmunlock $PROG_PID
        echo "Sending munlock signal"
        kill -SIGUSR1 $PROG_PID
        while [[ ! -f $MUNLOCK_COMPLETE_FILE ]]; do
                jobs 1 > /dev/null || die "timedmunlock failed prematurely"
                sleep 1
        done
        monitor_post_hook $LOGDIR_RESULTS timedmunlock
        rm -f $MUNLOCK_COMPLETE_FILE

        echo "Sending exit signal"
        kill -SIGUSR2 $PROG_PID
        wait $PROG_PID
done

# Cleanup
rm $TEMPFILE $TEMPFILE.c

exit $SHELLPACK_SUCCESS
==== BEGIN C FILE ====

#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>

#include <time.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static void *area;
static size_t memsize;
static char *munlockCompleteFile;

unsigned long gettimestamp()
{
	struct timespec tms;

	clock_gettime(CLOCK_MONOTONIC, &tms);

	return ((unsigned long) tms.tv_sec * 1000000000 + tms.tv_nsec);
}

void sighandler_munlock(int sig)
{
	unsigned long t_munlock_pre;
	unsigned long t_munlock_post;

        fprintf(stderr, "SIGUSR1 received, attempting munlock\n");

        t_munlock_pre = gettimestamp();
	if (munlock(area, memsize)) {
		perror("munlock");
		exit(1);
	}
	t_munlock_post = gettimestamp();

	fprintf(stderr, "munlock finished in: %lu\n",
			t_munlock_post - t_munlock_pre);
	printf ("%lu\n", t_munlock_post - t_munlock_pre);

        if (creat(munlockCompleteFile, S_IRUSR | S_IWUSR) == -1) {
	    perror(munlockCompleteFile);
	    exit(1);
	}
}

void sighandler_exit(int sig)
{
	fprintf (stderr, "SIGUSR2 received, exiting\n");
	exit(0);
}

int main(int argc, char **argv)
{
	char * readyForSignalFile;
	unsigned long t_mmap_pre;
	unsigned long t_mmap_post;

	if (argc < 3) {
		fprintf(stderr, "missing parameters\n");
		exit(1);
	}

	readyForSignalFile = argv[1];
	munlockCompleteFile = argv[2];

	memsize = (size_t) ALLOC_GB * 1024 * 1024 * 1024;

	fprintf(stderr, "attempting mmap(%zu, MAP_LOCKED | MA_POPULATE)\n",
			memsize);
	t_mmap_pre = gettimestamp();
	area = mmap(NULL, memsize, PROT_READ | PROT_WRITE,
		MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED | MAP_POPULATE, -1, 0);
	t_mmap_post = gettimestamp();

	if (area == MAP_FAILED) {
		perror("mmap");
		exit(1);
	}
	fprintf(stderr, "mmap() time: %lu\n", t_mmap_post - t_mmap_pre);

	if (signal(SIGUSR1, sighandler_munlock) == SIG_ERR) {
		perror("signal(SIGUSR1)");
		exit(1);
	}

	if (signal(SIGUSR2, sighandler_exit) == SIG_ERR) {
		perror("signal(SIGUSR2)");
		exit(1);
	}

	fprintf(stderr, "ready, creating %s\n", readyForSignalFile);
	if (creat(readyForSignalFile, S_IRUSR | S_IWUSR) == -1) {
		perror(readyForSignalFile);
		exit(1);
	}

	while (1)
		sleep(5);

	return (0);
}
