/*  -*- Mode: C;  -*- */

/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe is free software; you can redistribute it and/or modify            *
*   it under the terms of the GNU General Public License as published by      *
*   the Free Software Foundation; either version 2 of the License, or         *
*   (at your option) any later version.                                       *
*                                                                             *
*   Nprobe is distributed in the hope that it will be useful,                 *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
*   GNU General Public License for more details.                              *
*                                                                             *
*   You should have received a copy of the GNU General Public License         *
*   along with Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <linux/limits.h>
#include <sys/socket.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#endif
#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#undef __STDC__
#include <netinet/ip.h>

#ifdef __alpha__
#include <netinet/ip_var.h>
#endif

#include <netinet/tcp.h>
#include <netinet/if_ether.h>

#include <assert.h>

#include "basic_defs.h"
#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "flows.h"
#include "service.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "timeouts.h"
#include "if_nprobe.h"
#include "seq.h"
#include "pool.h"
#include "output.h"
#include "arr_his.h"
#include "probe_config.h"

#define MAX(x, y) ((x) > (y) ? (x) : (y))

#define MAXFLOAT FLT_MAX

#ifdef  sampleHistogram_TRACE
#define TRACE(x)  x
#else
#define TRACE(x)
#endif


#define US_LOW  0.0
#define US_HIGH 1000000.0
#define US_WIDE 1000.0

#define STEP_LOW 0.0
#define STEP_HIGH 5000.0
#define STEP_WIDE 10.0 


sampleHistogram *get_his, *fetch_his, *pkts_his, *hw_his, *pw_his;
FILE *get_f, *fetch_f, *pkts_f, *hw_f, *pw_f;

extern char repfnm[];
extern void error(char *msg, char *pmsg);

/*
 * rdtsc: reads the timestamp counter.
 *
 * Returns: the value from the timestamp counter.
 */
static inline unsigned int
rdtsc(void) 
{
    unsigned int h;
    unsigned int l;

    __asm__ __volatile__ ("rdtsc" 
			  : "=a" (l), "=d" (h) /* Outputs. */
	);
    
    return(l);
}




void 
hist_init(void)
{
  char his_fnm[PATH_MAX+1];
  char *cp;

  fprintf(stderr, "\tCreating histograms\n");

  strcpy(his_fnm, repfnm);
  cp = strrchr(his_fnm, '.');


  if ((histflags & HIS_GET))
    {
      get_his = sampleHistogram_create(US_LOW, US_HIGH, US_WIDE);
      *cp = '\0';
      strcat(his_fnm, ".get_hist");
      if ((get_f = fopen(his_fnm, "w")) == NULL)
	error("get_hist_init", "fopen");
    }
  
  if ((histflags & HIS_FETCH))
    {
      fetch_his = sampleHistogram_create(US_LOW, US_HIGH, US_WIDE);
      *cp = '\0';
      strcat(his_fnm, ".fetch_hist");
      if ((fetch_f = fopen(his_fnm, "w")) == NULL)
	error("get_hist_init", "fopen");
    }
  
  if ((histflags & HIS_PKTS))
    {
      pkts_his = sampleHistogram_create(STEP_LOW, STEP_HIGH, STEP_WIDE);
      *cp = '\0';
      strcat(his_fnm, ".pkts_hist");
      if ((pkts_f = fopen(his_fnm, "w")) == NULL)
	error("hw_hist_init", "fopen");
    }
  
  if ((histflags & HIS_HOSTWALK))
    {
      hw_his = sampleHistogram_create(STEP_LOW, STEP_HIGH, STEP_WIDE);
      *cp = '\0';
      strcat(his_fnm, ".hostwalk_hist");
      if ((hw_f = fopen(his_fnm, "w")) == NULL)
	error("hw_hist_init", "fopen");
    }
  
  if ((histflags & HIS_PORTWALK))
    {
      pw_his = sampleHistogram_create(STEP_LOW, STEP_HIGH, STEP_WIDE);
      *cp = '\0';
      strcat(his_fnm, ".portwalk_hist");
      if ((pw_f = fopen(his_fnm, "w")) == NULL)
	error("pw_hist_init", "fopen");
    }

  return;
}
  
void 
get_hist(void)
{
  static unsigned int last;
  unsigned int now;
  static int started = 0;

  now = rdtsc();

  if (started)
    {
      unsigned int ivl = (now - last)/counters.fh.hz;
      sampleHistogram_add(get_his, (double)ivl);
      counters.max_ctrs.int_get = MAX( counters.max_ctrs.int_get, ivl);
    }
  else
    {
      started++;
    }

  last = now;
  
  return;
}
  
unsigned int 
fetch_hist(void)
{
  static unsigned int last;
  unsigned int now;
  static int started = 0;
  unsigned int ivl = 0;

  now = rdtsc();

  if (started)
    {
      ivl = (now - last)/counters.fh.hz;
      if ((histflags & HIS_FETCH))
	sampleHistogram_add(fetch_his, (double)ivl);
      counters.max_ctrs.int_fetch = MAX( counters.max_ctrs.int_fetch, ivl);
    }
  else
    {
      started++;
    }

  last = now;
  
  return ivl;
}

void 
hist_end(void)
{

  printf("hist_end() called\n");

  if ((histflags & HIS_GET))
    sampleHistogram_dump(get_f, get_his, "Get loop times in analysis code");
  if ((histflags & HIS_FETCH))
    sampleHistogram_dump(fetch_f, fetch_his, "Fetch loop intervals in analysis code");
  if ((histflags & HIS_PKTS))
    sampleHistogram_dump(pkts_f, pkts_his, "Pkts fetched per loop");
  if ((histflags & HIS_HOSTWALK))
    sampleHistogram_dump(hw_f, hw_his, "Host hash list steps");
  if ((histflags & HIS_PORTWALK))
    sampleHistogram_dump(pw_f, pw_his, "Port hash list steps");

  return;
}

/*
 * The remainder of this file relies heavily on borrows from SampleHistogram.c
 */


int
sampleHistogram_init(sampleHistogram *s, sampleType low, 
		     sampleType high, sampleType width)
{
  sampleType t = high;
  int i;
  if(! s) {  
    return 0;
  }
  sampleStatistic_reset(& (s->stats));
  if (high < low) {
    high = low;
    low = t;
  }
  s->min = low;
  s->max = high;
  if (width == -1) {
    width = (high - low) / 10;
  }
  if (s->bucketCount && s->howManyBuckets) free((char *)s->bucketCount);
  s->bucketWidth = width;

  s->howManyBuckets =  (int)((high - low) / width) + 3;
  s->bucketCount = (sampleType *)malloc(s->howManyBuckets *
					sizeof(sampleType));
  if(!  (s->bucketCount )) {  
    TRACE (printf("sampleHistogram_init: failed\n"));
    return 0;
  }
  for (i = 0; i < s->howManyBuckets; i++) {
    s->bucketCount[i] = 0;
  }
  TRACE (printf("sampleHistogram_init: %#x %d %d %d\n",s,(int)low,
		(int)high,(int)width));
  return 1;
}

/* ---------------------------------------------------------------------- */

sampleHistogram *
sampleHistogram_create(sampleType low, sampleType high, sampleType width)
{
  sampleHistogram *s  = (sampleHistogram *)malloc(sizeof(sampleHistogram));
  if(! s) {  
    return NULL;
  }
  if(!sampleHistogram_init(s, low, high, width)) {  
    free((char *)s);
    return NULL;
  }
  return s;
}

void
sampleStatistic_reset(sampleStatistic *s)
{
  s->n = 0; s->x = s->x2 = 0;
  s->maxValue = minSample;
  s->minValue = maxSample;
}


/* ---------------------------------------------------------------------- */

void
sampleHistogram_add(sampleHistogram *s, sampleType value)
{ 
  int compare;
  sampleStatistic_add(& (s->stats),value);
  if(value < s->min) {
    s->bucketCount[0]++;
    TRACE (printf("add %E to %d limit %E\n",value,0,s->min));
    return;
  }
  compare = (int)
    (( value - s->min ) / s->bucketWidth);
  if(value > (compare * s->bucketWidth + s->min))
    compare++;
  if(compare >= s->howManyBuckets)
    compare = s->howManyBuckets-1;
  TRACE (printf("add %E to %d limit %E\n",value,compare,
	 (compare * s->bucketWidth + s->min)));
  s->bucketCount[compare]++;
}

void
sampleStatistic_add(sampleStatistic *s, sampleType value)
{
  s->n++;
  s->x += value;
  s->x2 += (value * value);
  if ( s->minValue > value) s->minValue = value;
  if ( s->maxValue < value) s->maxValue = value;
}
void
sampleHistogram_dump(FILE *f, sampleHistogram *s, char * title)
{
  int i;
  sampleType compare = s->min;
  sampleStatistic_dump(f, & s->stats, title);
  fprintf(f, "#%s: buckets %d low: %E high: %E width: %E\n", title,
	  s->howManyBuckets,
	  s->min,s->max,s->bucketWidth);
  if(s->bucketCount[0])
    fprintf(f,"%E, %E\n", s->min, s->bucketCount[0]);
  for(i = 1; i < s->howManyBuckets-1; i++) {  
    compare += s->bucketWidth;
    if(s->bucketCount[i])
      fprintf(f,"%E, %E\n", compare, s->bucketCount[i]);
  }
/* James 3:1:96 - changed this to report over range values as max + 1 */
  if(s->bucketCount[s->howManyBuckets-1])
    fprintf(f,"%E, %E\n", 
	    /* maxSample, */
	    s->max + 1,
	    s->bucketCount[s->howManyBuckets-1]);
} 


void
sampleStatistic_dump(FILE *f, sampleStatistic *s, char *title)
{
  fprintf(f,"#%s: samples %E  minVal %E\n", title,
	    sampleStatistic_samples(s),
	    sampleStatistic_min(s));
  fprintf(f,"#%s: maxVal  %E  sum %E\n", title,
	    sampleStatistic_max(s),
	    sampleStatistic_sum(s));
  fprintf(f,"#%s: mean    %E  var    %E  stdDev %E\n", title,
	    sampleStatistic_mean(s),
	    sampleStatistic_var(s),
	    sampleStatistic_stdDev(s));
}

sampleType
sampleStatistic_mean(sampleStatistic *s)
{
  if ( s->n > 0) {
    return (s->x / s->n);
  }
  else {
    return ( 0 );
  }
}

sampleType
sampleStatistic_samples(sampleStatistic *s)
{
  return(s->n);
}

sampleType
sampleStatistic_min(sampleStatistic *s)
{
  return(s->minValue);
}

sampleType
sampleStatistic_max(sampleStatistic *s)
{
  return(s->maxValue);
}

sampleType
sampleStatistic_sum(sampleStatistic *s)
{
  return(s->x);
}


sampleType
sampleStatistic_var(sampleStatistic *s)
{
  if ( s->n > 1) {
    return(( s->x2 - ((s->x * s->x) /  s->n)) / ( s->n - 1));
  }
  else {
    return ( 0 );
  }
}

sampleType
sampleStatistic_stdDev(sampleStatistic *s)
{
  if ( s->n <= 0 || sampleStatistic_var(s) <= 0) {
    return(0);
  } else {
    return( (sampleType) sqrt( sampleStatistic_var(s) ) );
  }
}
   

