/*Just to read F*//* CANO-2005 */
/* Usage: Whats_F */
/* make -f ..makefile */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<sys/time.h>
#include <unistd.h>
#include "CANO_PHASE_DVFS_LKM.h"
#include "perf_event_def.h"

/* THE PARANTEZES IN THE MACROS ARE VITAL!!!*/
#define MAX(a,b) ((a < b) ? b : a)
#define MIN(a,b) ((a < b) ? a : b)
#define ABS(a)   ((a < 0) ? (-1 * (a)) : a)
#define THRESHOLD(a,th)   ((a < th) ? (0) : 1) /*thresholding for phase grouping*/
#define SQUARE(a) ( (a)*(a) )
#define LENGTH(array) (sizeof(array) / sizeof(array[0])  )
#define ROWS(matrix) (sizeof(matrix) / sizeof(matrix[0])  )

#define DO_FREQ_TEST 0


int main(int argc, char *argv[])
{
  int freq[6] = {600, 800, 1000, 1200, 1400, 1500};
  int f,V;
  double clock_f; double delta_t;
  
  
  if(argc<4)
  {
      printf("Usage: %s -D <Sampling_delta_ins[x1000]> <app-n> <args>\n", argv[0]);
      printf("  i.e. %s -D 100000 ../benches/high_low < ../benches/high_low_input \n", argv[0]);
      printf("   or  %s -D 100000 /bin/ls -alf \n", argv[0]);
      printf("   (FOR 100 MILLION delta_ins sampling) \n");

      return -1;
  }
  else if (strcmp(argv[1], "-D") != 0)
  {
      printf("Usage: %s -D <Sampling_delta_ins[x1000]> <app-n> <args>\n", argv[0]);
      return -1;
  }

  delta_ins = -1 * atoi(argv[2]) * 1000;
  printf("Using %.2lf Million delta_ins sampling\n", ((double) (-1*delta_ins)) / 1000000.0);
  sleep(1);

  /* convert the rest to benchmark argv-s*/
  for(i=0;i<argc-3;i++) {
    my_argv[i] = argv[i+3];
    //    printf("[%d] = %s\n",i, my_argv[i]);
  }
  my_argv[i] = '\0';
  
  /******************************************************************************/
  /* DO THE FREQ TEST FIRST */
  /******************************************************************************/
  get_current_f_V(&f,&V);
  fprintf(stderr,"Currently %d MHz %d mV \n", f, V);
#if DO_FREQ_TEST 
  for (i=0; i<LENGTH(freq); i++)
  {
  
     fprintf(stderr,"\n\nTRYING: %d MHz TEST: \n", freq[i]);
     fprintf(stderr,  "===================== \n\n");
     
     set_target_f(freq[i]);
     get_target_f_V(&f,&V);
     fprintf(stderr,"Target (PERF_CTL MSR): %d MHz %d mV \n", f, V);
     gettimeofday(&t1, NULL);
     readpmu(&counters);
     fprintf(stderr, "TSC1      : %lld\n",counters.clock);
     TSC1          = counters.clock;
     
     sleep(1);

     readpmu(&counters);
     gettimeofday(&t2, NULL);
     get_current_f_V(&f,&V);
     fprintf(stderr,"Currently (PERF_STATUS MSR: %d MHz %d mV \n", f, V);
     fprintf(stderr, "TSC2      : %lld\n",counters.clock);
     TSC2          = counters.clock;
     
     delta_t = timediff(t2, t1);
     // compute the clock frequency
     clock_f = ((double)(TSC2 - TSC1)) / delta_t; /* Hz */
     clock_f = clock_f /1000000.0; /* MHz */
     fprintf(stderr, "\n----------------------------\n");
     fprintf(stderr, "OBSERVED FREQ:              %lf MHz\n", clock_f);
     fprintf(stderr, "----------------------------\n\n");
   }
#endif   
  /******************************************************************************/
  /* DONE THE FREQ TEST FIRST */
  /******************************************************************************/
     

  /******************************************************************************/
  /* NOW LET'S START PHASE PREDN/DVFS */
  /******************************************************************************/
  sample_log  = calloc(MAX_SAMPLES, sizeof(unsigned long long *));
  for (i=0; i< MAX_SAMPLES; i++)
  { 
    sample_log[i]  = calloc(NUM_ind, sizeof(unsigned long long));
  }

  select_count_event(0, PMU_event[PMU_Uops_Retired]);
//  select_count_event(1, PMU_event[PMU_Uops_Retired]);
  select_count_event(1, PMU_event[PMU_Mem_Bus_Access]);
  reset_both_event_counters();
  enable_interrupt();
  start_both_event_counters();
  
  /* Write the value to overflow */
  printf("Writing to Counter0 %d (0x%010x)\n",delta_ins, delta_ins);
  wr_msr_32(MSR_P6_PERFCTR0, delta_ins);
  /* also send it to kernel so interrup know the delta */
  set_delta_ins(delta_ins);
  
  readpmu(&counters);
  printf("Counter0 (%s): %lld\n",PMU_event_name[PMU_Uops_Retired], counters.counter0);
//  printf("Counter1 (%s): %lld\n",PMU_event_name[PMU_Uops_Retired],counters.counter1);
//  printf("Counter1 (%s): %lld\n",PMU_event_name[PMU_BUS_DATA_RCV],counters.counter1);
  printf("Counter1 (%s): %lld\n",PMU_event_name[PMU_Mem_Bus_Access],counters.counter1);
  printf("TSC      : %lld\n",counters.clock);

  if((child_pid=fork())==0)
  /* Child does this */
  {
    /* start logging here */
    enable_kernel_PMU_log();
    
    if(execv(my_argv[0], my_argv)==-1)
    { 
      printf("%s ",my_argv[0]);
      perror("[E] Error in execv Function");
    }
    
    fprintf(stdout,"CHILD FINISHES HERE\n");
    return 0;

  }
  else 
  {
  /* PARENT DOES THIS */
    //    printf("Waiting for %d\n",child_pid);
    waitpid(child_pid,NULL,0);

    /* stop logging here */
    disable_kernel_PMU_log();
    stop_both_event_counters(); /* so no more interrupts will come */
    
    /* WHEN CHILD FINISHES, LET'S GET THE LOG */
    copy_sample_log(sample_log, &num_samples);

    fprintf(stdout, "NUM SAMPLES: %d\n\n", num_samples);
    
    output_trace = fopen(filename, "w");
//    fprintf(output_trace, "TSC\tCNTR0\tCNTR1\tUPC0\tBUS_DATA_RCV/CYC\tBUS_DATA_RCV/Uop\n");
    fprintf(output_trace, "TSC\tCNTR0\tCNTR1\tUPC0\tBUS_TRAN_MEM/CYC\tBUS_TRAN_MEM/Uop\tACTUAL_PHASE\tPRED_PHASE\n");
    for (i=0; i<num_samples; i++)
    {
      fprintf(output_trace, "%lld\t%lld\t%lld\t%5.3lf\t%7.5lf\t%7.5lf\t%lld\t%lld\n",
                      sample_log[i][TSC_ind], sample_log[i][CNTR0_ind], sample_log[i][CNTR1_ind],
		      ((double) (-1 * delta_ins)) / ((double) sample_log[i][TSC_ind]),
		      ((double) sample_log[i][CNTR1_ind]) / ((double) sample_log[i][TSC_ind]),
		      ((double) sample_log[i][CNTR1_ind]) / ((double) (-1 * delta_ins)),
		      sample_log[i][real_PHASE_ind], sample_log[i][pred_PHASE_ind]
		      );
    }	      
    fclose(output_trace);
    
    /* COMPUTE CORRECT PHASE PREDN PERCENTAGE: */
    predn_stats = fopen(statname, "w");
    matches = 0.0;
    for (i=0; i<num_samples; i++)
    {
	    if (sample_log[i][real_PHASE_ind] == sample_log[i][pred_PHASE_ind])
	    {
		    matches = matches + 1.0;
	    }
    }
    correct_perc = (matches / (double) num_samples) * 100.0;
    fprintf(predn_stats,"CORRECT_PREDICTION_RATE(%%) [%s]:\t%6.3lf\n", Prediction_Name[PREDICTION], correct_perc);

    /* COMPUTE AVE PRED-N ERROR DISTANCE: (for 6 phases) */
    total_distance = 0.0;
    for (i=0; i<num_samples; i++)
    {
	    total_distance += ((double) ABS((int)sample_log[i][real_PHASE_ind] - (int)sample_log[i][pred_PHASE_ind]) ) / 5.0;
	    //fprintf(stderr, "totd = %4.2lf\n", total_distance);
	    //getchar();
    }
    ave_error_dist = (total_distance / (double) num_samples);
    fprintf(predn_stats,"AVE_PREDN_ERROR_DISTANCE [%s]:\t%5.4lf\n", Prediction_Name[PREDICTION], ave_error_dist);
    fclose(predn_stats);
    
  } /* EO else (PARENT EXECUTES THIS) */
    
  
  fprintf(stdout,"ONLY PARENT FINISHES HERE\n");
  for (i=0; i< MAX_SAMPLES; i++)
  { 
    free(sample_log[i]);
  }
  free(sample_log);
  

  return 0; /* only parent comes here */
  
}
