/*----------------------------------------------------------------------------
Name: fiblogger.c (fibonacci time logger)
Author: Christopher Huyler
Date: March 20, 2002
Description:
Creates three children who execute the fibonacci sequence.  Program uses
real, virtual, and prof timers along with signal handlers to record time
spent overall/cpu/kernel/user, then outputs the statistics.
To run, execute program with 1 or 3 parameters:
	"shell> fiblogger 30"			each child executes fib(30)
	"shell> fiblogger 20 30 36"		children execute different numbers

Issues:
If a process uses less than 10ms of time, the timers are never started.
My solution is to run the fibonacci function right after the timers are started
then record the elapsed time into temp values.  Before printing the times to
the screen, the process must subtract this time from the overall time.
----------------------------------------------------------------------------*/
#include <stdio.h>
#include <ctype.h>
#include "realtimer.c"
#include "virttimer.c"
#include "proftimer.c"

// function prototypes
long unsigned int fibonacci(unsigned int n);
void reset_timers(void);
struct timeval subtract_timevals(struct timeval,struct timeval);

// global variables
static struct timeval p_realt, c1_realt, c2_realt, c3_realt, temp_realt;
static struct timeval p_virtt, c1_virtt, c2_virtt, c3_virtt, temp_virtt;
static struct timeval p_proft, c1_proft, c2_proft, c3_proft, temp_proft;
static struct timeval p_kernt, c1_kernt, c2_kernt, c3_kernt;

int main(int argc, char **argv)
{
	int fib1,fib2,fib3,		// input/output fibonacci numbers
		pid1,pid2,pid3;		// process ids of children

	// check for valid input
	if(argc < 2)
	{	// set default values
		printf("usage: fiblogger [int] [int] [int]\n");
		printf("using defaults: 20 30 36\n");
		fib1 = 20;
		fib2 = 30;
		fib3 = 36;
	}
	else
	{ 	// get command line arguments
		if(argc < 4) {
			fib1 =  atoi(argv[1]);
			fib2 =  fib1;
			fib3 =  fib1;
		} else {
			fib1 =  atoi(argv[1]);
			fib2 =  atoi(argv[2]);
			fib3 =  atoi(argv[3]);
		}
	}

	// initialize timers for parent
	reset_timers();

	pid1 = fork();
	if(pid1 == 0)
	{
		// initialize timers for child
		reset_timers();

		// execute fibonacci
		fib1 = fibonacci(fib1);

		// get the elapsed time
		c1_realt = get_real();
		c1_virtt = get_virt();
		c1_proft = get_prof();

		// subtract temp time
		c1_realt = subtract_timevals(c1_realt,temp_realt);
		c1_virtt = subtract_timevals(c1_virtt,temp_virtt);
		c1_proft = subtract_timevals(c1_proft,temp_proft);

		// calculate kernel time from prof and virt
		c1_kernt = subtract_timevals(c1_proft,c1_virtt);

		// print output
		printf("\nChild 1 fib = %ld, real time = %ld sec, %ld msec\n",
			fib1, c1_realt.tv_sec, c1_realt.tv_usec/1000);
		printf("Child 1 fib = %ld, cpu time = %ld sec, %ld msec\n",
			fib1, c1_proft.tv_sec, c1_proft.tv_usec/1000);
		printf("Child 1 fib = %ld, user time = %ld sec, %ld msec\n",
			fib1, c1_virtt.tv_sec, c1_virtt.tv_usec/1000);
		printf("Child 1 fib = %ld, kernel time = %ld sec, %ld msec\n",
			fib1, c1_kernt.tv_sec, c1_kernt.tv_usec/1000);

		fflush(stdout);
		exit(0);
	}
	else {
		pid2 = fork();
		if(pid2 == 0)
		{
			// initialize timers for child
			reset_timers();

			// execute fibonacci
			fib2 = fibonacci(fib2);

			// get the elapsed time
			c2_realt = get_real();
			c2_virtt = get_virt();
			c2_proft = get_prof();

			// subtract temp time
			c2_realt = subtract_timevals(c2_realt,temp_realt);
			c2_virtt = subtract_timevals(c2_virtt,temp_virtt);
			c2_proft = subtract_timevals(c2_proft,temp_proft);

			// calculate kernel time from prof and virt
			c2_kernt = subtract_timevals(c2_proft,c2_virtt);

			// print output
			printf("\nChild 2 fib = %ld, real time = %ld sec, %ld msec\n",
				fib2, c2_realt.tv_sec, c2_realt.tv_usec/1000);
			printf("Child 2 fib = %ld, cpu time = %ld sec, %ld msec\n",
				fib2, c2_proft.tv_sec, c2_proft.tv_usec/1000);
			printf("Child 2 fib = %ld, user time = %ld sec, %ld msec\n",
				fib2, c2_virtt.tv_sec, c2_virtt.tv_usec/1000);
			printf("Child 2 fib = %ld, kernel time = %ld sec, %ld msec\n",
				fib2, c2_kernt.tv_sec, c2_kernt.tv_usec/1000);

			fflush(stdout);
			exit(0);
		}
		else {
			pid3 = fork();
			if(pid3 == 0)
			{
				// initialize timers for child
				reset_timers();

				// execute fibonacci
				fib3 = fibonacci(fib3);

				// get the elapsed time
				c3_realt = get_real();
				c3_virtt = get_virt();
				c3_proft = get_prof();

				// subtract temp time

				c3_realt = subtract_timevals(c3_realt,temp_realt);
				c3_virtt = subtract_timevals(c3_virtt,temp_virtt);
				c3_proft = subtract_timevals(c3_proft,temp_proft);

				// calculate kernel time from prof and virt
				c3_kernt = subtract_timevals(c3_proft,c3_virtt);

				// print output
				printf("\nChild 3 fib = %ld, real time = %ld sec, %ld msec\n",
					fib3, c3_realt.tv_sec, c3_realt.tv_usec/1000);
				printf("Child 3 fib = %ld, cpu time = %ld sec, %ld msec\n",
					fib3, c3_proft.tv_sec, c3_proft.tv_usec/1000);
				printf("Child 3 fib = %ld, user time = %ld sec, %ld msec\n",
					fib3, c3_virtt.tv_sec, c3_virtt.tv_usec/1000);
				printf("Child 3 fib = %ld, kernel time = %ld sec, %ld msec\n",
					fib3, c3_kernt.tv_sec, c3_kernt.tv_usec/1000);

				fflush(stdout);
				exit(0);
			}
			wait();
		}
		wait();
	}
	wait();

	// get the elapsed time
	p_realt = get_real();
	p_virtt = get_virt();
	p_proft = get_prof();

	// subtract temp time
	p_realt = subtract_timevals(p_realt,temp_realt);
	p_virtt = subtract_timevals(p_virtt,temp_virtt);
	p_proft = subtract_timevals(p_proft,temp_proft);

	// calculate kernel time from prof and virt
	p_kernt = subtract_timevals(p_proft,p_virtt);

	// print output
	printf("\nParent , real time = %ld sec, %ld msec\n",
		p_realt.tv_sec, p_realt.tv_usec/1000);
	printf("Parent , cpu time = %ld sec, %ld msec\n",
		p_proft.tv_sec, p_proft.tv_usec/1000);
	printf("Parent , user time = %ld sec, %ld msec\n",
		p_virtt.tv_sec, p_virtt.tv_usec/1000);
	printf("Parent , kernel time = %ld sec, %ld msec\n\n",
		p_kernt.tv_sec, p_kernt.tv_usec/1000);

	fflush(stdout);
	exit(0);
}


/* fibonacci calculator ------------------------------------------------------
	input		- integer to calculate nth fib number
	output		- nth fib number
	syscalls	- none */
long unsigned int fibonacci(unsigned int n)
{
	switch(n) {
		case 0:  return 0;
		case 1:  return 1;
		case 2:  return 1;
		default: getitimer(ITIMER_REAL,NULL); // add some kernel time to function
				 return(fibonacci(n-1)+fibonacci(n-2));
				 break;
	}
}

/* reset kernel process timers ---------------------------------------------*/
void reset_timers()
{
	reset_real();
	start_real();
	reset_virt();
	start_virt();
	reset_prof();
	start_prof();

	// run a fibonacci sequence to kill time, save elapsed time in temp
	fibonacci(25);
	temp_realt = get_real();
	temp_virtt = get_virt();
	temp_proft = get_prof();
}

/* subtract two timevals and return result ---------------------------------*/
struct timeval subtract_timevals(struct timeval a, struct timeval b) {
	struct timeval c;
	c.tv_sec  = a.tv_sec - b.tv_sec;
	c.tv_usec = a.tv_usec - b.tv_usec;
	if(c.tv_usec < 0) {
		c.tv_sec--;
		c.tv_usec += 1000000;
	}
	return c;
}

