/* 0440949 Andreas van Cranenburgh Sun Sep  7 13:07:43 CEST 2008
 * Operating Systems & Netwerken, assignment 1
 * Measure wall clock time, user cpu time and system cpu time.  */

#include "interval.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>

struct intervalData {
	double wallclockTime;
	double userTime;
	double systemTime;
};

/* the function newInterval creates and initialises a new
   intervalData struct and returns its address */
interval newInterval(void) {
	static interval intervalPtr;
	double w, u, s;
	intervalPtr = malloc(sizeof(struct intervalData));
	if (intervalPtr == NULL)
		return NULL;

	intervalPtr->wallclockTime = intervalPtr->userTime = 
		intervalPtr->systemTime = 0;
	
	/* use timeInterval to obtain current times and store results. */
	timeInterval(intervalPtr, &w, &u, &s);
	intervalPtr->wallclockTime = w;
	intervalPtr->userTime = u;
	intervalPtr->systemTime = s;

	return intervalPtr;
}

/* The function delInterval frees the memory for an interval
   variable and NULLs its value to prevent accidental reuse.
   It returns zero on success, -1 when an invalid pointer
   is passed as an argument. */
int delInterval(interval *intervalPtr) {
	if (intervalPtr == NULL)
		return -1;

	free(*intervalPtr);
	intervalPtr = NULL;

	return 0;
}


/* the function timeInterval returns the wall clock time,
   user CPU time and system CPU time for the calling process
   and its children consumed since the previous call for the
   specified interval.
   It returns zero on success, -1 when an invalid pointer
   is passed as an argument. */
int timeInterval(interval id, double *wallclockTime,
	double *userTime, double *systemTime) {
	struct timeval t;

	/* get wall clock time, ignore timezone: */
	gettimeofday(&t, NULL);
	*wallclockTime = (double)t.tv_sec + ((double)t.tv_usec / 1e6);
	*wallclockTime -= id->wallclockTime;

	/* get user and system cpu time for current process */
	struct rusage r;
	if (!getrusage(RUSAGE_SELF, &r) == 0)
		return -1;

	*userTime = (double)r.ru_utime.tv_sec
		+ ((double)r.ru_utime.tv_usec / 1e6) - id->userTime;

	*systemTime = (double)r.ru_stime.tv_sec
		+ ((double)r.ru_stime.tv_usec / 1e6) - id->systemTime;

	return 0;
}

#ifdef STAND_ALONE_TEST

/* Demonstrate functionality. */
int main() {
	int a, b;
	double w, u, s;

	interval i, foo;
	i = newInterval();
	foo = newInterval();
	
	printf("Calling timeInterval 100000 times\n");
	/* Measure overhead of timeInterval  */
	for (a = 1; a < 100000; a++)
		timeInterval(foo, &w, &u, &s);

	if (timeInterval(i, &w, &u, &s) == 0) {
		w /= 100000; u /= 100000; s /= 100000;
		printf("Time/interation (ie., overhead): "); 
		printf("%.6es real, %.6efs user, %.6es sys\n", w, u, s);
	}
	
	delInterval(&i);

	/* 1e-14 is close to maximum precision of doubles. */
	/* iteratively approach minimal time measurement: */
	w = 0;	
	printf("\nLooking for smallest measurable interval\n");
	for (a = 1; w < 1e-14; a++) {
		i = newInterval();
		for (b = 0; b < a; b++)
			;
		timeInterval(i, &w, &u, &s);
	}

	printf("Smallest measurable wall clock time = %.6es, ", w);
	printf("after %d iterations\n", a);

	u = 0;
	for (a = 1; u < 1e-14; a++) {
		i = newInterval();
		for (b = 0; b < a; b++)
			;
		timeInterval(i, &w, &u, &s);
	}

	printf("Smallest measurable user cpu time = %.6es, ", u);
	printf("after %d iterations\n", a);
	
	delInterval(&foo);
	return delInterval(&i) == 0;
}

#endif
