/* $Id: setPerfCountOnSMP.c
 *
 */
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libperfctr.h"
#include "arch.h"

//static struct vperfctr_control control;

static struct gperfctr *gperfctr;
static struct perfctr_info info;
static unsigned int nrcpus;
static unsigned short *cpu_logical_map;
struct gperfctr_state
{				/* no longer defined in or used by the kernel */
  unsigned int nrcpus;
  struct gperfctr_cpu_state cpu_state[2];	/* actually 'nrcpus' */
};
static struct gperfctr_state *state;
static struct gperfctr_state *prev_state;
int counting_mips;
static unsigned long sampling_interval = 10000;	/* XXX: reduce for >4GHz CPUs */
static unsigned int sleep_interval = 5;

/*
 * Data used in the user program
 */

/*
 * Vector of size N of input sizes
int TAB[5] = {512, 1024, 2048, 4096, 8192 };
int TAB[5] = {2048, 2048, 2048, 2048, 2048 };
int TAB[5] = {8192 ,8192 ,8192 ,8192 ,8192 };
int TAB[5] = {65536 ,65536 ,65536 ,65536 ,65536 };
 */

int TAB[5] = { 268435451, 1, 1, 1, 1 };

/*******************************************************/

static unsigned int
hweight32 (unsigned int w)
{
  unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
  res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
  res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
  res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
  return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
}

static void
setup_cpu_logical_map_and_nrcpus (const struct perfctr_cpus_info *cpus_info)
{
  const unsigned int *cpus, *cpus_forbidden;
  unsigned int nrwords, i, cpumask, bitmask;
  unsigned int logical_cpu_nr, kernel_cpu_nr;

  cpus = cpus_info->cpus->mask;
  cpus_forbidden = cpus_info->cpus_forbidden->mask;
  nrwords = cpus_info->cpus->nrwords;

  nrcpus = 0;
  for (i = 0; i < nrwords; ++i)
    nrcpus += hweight32 (cpus[i] & ~cpus_forbidden[i]);

  printf ("nrcpus=%d nrwords=%d\n", nrcpus, nrwords);

  cpu_logical_map = malloc (nrcpus * sizeof (cpu_logical_map[0]));
  if (!cpu_logical_map)
    {
      perror ("malloc");
      exit (1);
    }

  logical_cpu_nr = 0;
  for (i = 0; i < nrwords; ++i)
    {
      cpumask = cpus[i] & ~cpus_forbidden[i];
      kernel_cpu_nr = i * 8 * sizeof (int);
      for (bitmask = 1; cpumask != 0; ++kernel_cpu_nr, bitmask <<= 1)
	{
	  if (cpumask & bitmask)
	    {
	      cpumask &= ~bitmask;
	      cpu_logical_map[logical_cpu_nr] = kernel_cpu_nr;
	      ++logical_cpu_nr;
	    }
	}
    }

  if (logical_cpu_nr != nrcpus)
    abort ();
}

static void
do_init (void)
{
  struct perfctr_cpus_info *cpus_info;
  size_t nbytes;
  unsigned int i;

  gperfctr = gperfctr_open ();
  if (!gperfctr)
    {
      perror ("gperfctr_open");
      exit (1);
    }
  if (gperfctr_info (gperfctr, &info) < 0)
    {
      perror ("gperfctr_info");
      exit (1);
    }
  cpus_info = gperfctr_cpus_info (gperfctr);
  if (!cpus_info)
    {
      perror ("gperfctr_info");
      exit (1);
    }
  printf ("\nPerfCtr Info:\n");
  perfctr_info_print (&info);
  perfctr_cpus_info_print (cpus_info);

  /* use all non-forbidden CPUs */

  setup_cpu_logical_map_and_nrcpus (cpus_info);
  free (cpus_info);

  /* now alloc state memory based on nrcpus */

  nbytes = offsetof (struct gperfctr_state, cpu_state[0])
    + nrcpus * sizeof (state->cpu_state[0]);
  state = malloc (nbytes);
  prev_state = malloc (nbytes);
  if (!state || !prev_state)
    {
      perror ("malloc");
      exit (1);
    }
  memset (state, 0, nbytes);
  memset (prev_state, 0, nbytes);

  /* format state to indicate which CPUs we want to sample */

  for (i = 0; i < nrcpus; ++i)
    {
      state->cpu_state[i].cpu = cpu_logical_map[i];
      prev_state->cpu_state[i].cpu = cpu_logical_map[i];
    }
  state->nrcpus = nrcpus;
  prev_state->nrcpus = nrcpus;

}

static int
do_read ()
{
  unsigned int i;

  for (i = 0; i < state->nrcpus; ++i)
    {
      if (gperfctr_read (gperfctr, &state->cpu_state[i]) < 0)
	{
	  perror ("gperfctr_read error on state");
	  return -1;
	}
    }
  return 0;
}

static void
print_control (const struct perfctr_cpu_control *control)
{
  printf ("\nControl used:\n");
  perfctr_cpu_control_print (control);
}

static void
do_enable ()
{
  struct perfctr_cpu_control cpu_control;
  unsigned int i;

  setup_control (&info, &cpu_control);
  print_control (&cpu_control);

  for (i = 0; i < nrcpus; ++i)
    {
      struct gperfctr_cpu_control control;
      control.cpu = cpu_logical_map[i];
      control.cpu_control = cpu_control;
      if (gperfctr_control (gperfctr, &control) < 0)
	{
	  perror ("gperfctr_control");
	  exit (1);
	}
    }
  if (gperfctr_start (gperfctr, sampling_interval) < 0)
    {
      perror ("gperfctr_start");
      exit (1);
    }
}

void
do_print ()
{
  int i, cpu, ctr;

  printf ("\nFinal Sample(s):\n");
  for (i = 0; i < state->nrcpus; ++i)
    {
      cpu = state->cpu_state[i].cpu;
      printf ("\nCPU %d:\n", cpu);
      if (state->cpu_state[i].cpu_control.tsc_on)
	{
	  printf ("\ttsc\t\t%lld\n", state->cpu_state[i].sum.tsc);
	}
      for (ctr = 0; ctr < state->cpu_state[i].cpu_control.nractrs; ++ctr)
	{
	  printf ("\tpmc[%d]\t\t%lld\n",
		  ctr, state->cpu_state[i].sum.pmc[ctr]);
	}
	if( ctr >= 1 ) {	/* compute and display MFLOP/s or MIP/s */
	    unsigned long long tsc = state->cpu_state[i].sum.tsc;
	    double seconds = state->cpu_state[i].cpu_control.tsc_on
	      ? ((double)tsc * (double)(info.tsc_to_cpu_mult ? : 1) / (double)info.cpu_khz) / 1000.0
		: (double)sleep_interval; /* don't div-by-0 on WinChip ... */
	    printf("\tSECONDS\t\t%.15g\n", seconds);
	}
    }
}


int
main (void)
{
  int i;
  int *t;

  do_init ();
  // next line is optional... but useful to reinitialize the "machine"
  gperfctr_stop (gperfctr);
  /////////////////////////////////////////////////////////
  do_enable ();
  // Put yout code here
  t = (int *) malloc (TAB[0] * sizeof (int));
  for (i = 0; i < TAB[0]; i++)
    t[i] = i;
  for (i = 0; i < TAB[0] - 1; i++)
    t[i] = t[i] + t[i + 1];
  free (t);
  //system("ls");
  // end of your code that we monitor
  do_read ();
  do_print ();

  if (gperfctr)
    {
      printf ("shutting down..\n");
      gperfctr_stop (gperfctr);
    }
  return 0;
}
