/*
 * Copyright (C) 2008 The Trustees of Columbia University in the City of New York. 
 * All rights reserved.
 *
 * See the file LICENSE included in this distribution for details.
 */

#include "cpuutil.h"

#include <stdio.h>
#include <string.h>

CpuUtil::CpuUtil() {  
  int status;
  
  m_isFlag=0;   //flag used for reading proc stat for the first time

  //expire timer
  m_uiRTexp=5;  //recompute CPU utilization after 5 seconds

#ifdef WIN32
  status=0;
#else
  gettimeofday(&m_sStartTime, NULL);
  status=getrusage(RUSAGE_SELF, &m_sPrevUsage);
#endif

  m_fCpuUsage=0.0; 
  m_fTCpuUsage=0.0;


}//CpuUtil::CpuUtil



CpuUtil* CpuUtil::GetInstance() {

  if(!m_pObj) {
    m_pObj = new CpuUtil;
  }
  
  return m_pObj;
}//CpuUtil::GetInstance


void CpuUtil::Recompute() {

  if(0==m_isFlag) {
#ifndef WIN32
    read_proc_stats(&idle, &uptime);
#endif

    m_isFlag=1;
  }//end if


  if(0==isRTexpired() ) {
    return;
  } else {
    setRTTimer();
  }

#ifdef WIN32

#else
  int status;
  long double utime1, stime1, ttime1, utime2, stime2, ttime2;
  long double starttime, endtime, timepassed;

  utime1 = (m_sPrevUsage.ru_utime.tv_sec)+
    (m_sPrevUsage.ru_utime.tv_usec*0.000001);
  stime1 = (m_sPrevUsage.ru_stime.tv_sec)+
    (m_sPrevUsage.ru_stime.tv_usec*0.000001);
  ttime1 = utime1 + stime1;

  gettimeofday(&m_sCurrTime, NULL);
  status=getrusage(RUSAGE_SELF, &m_sUsage);
  utime2 = (m_sUsage.ru_utime.tv_sec)+(m_sUsage.ru_utime.tv_usec*0.000001);
  stime2 = (m_sUsage.ru_stime.tv_sec)+(m_sUsage.ru_stime.tv_usec*0.000001);
  ttime2 = utime2 + stime2;

  starttime = (m_sStartTime.tv_sec) + m_sStartTime.tv_usec*0.000001;
  endtime = (m_sCurrTime.tv_sec) + m_sCurrTime.tv_usec*0.000001;
  timepassed = endtime - starttime;
  
  m_fCpuUsage = ( (ttime2 - ttime1) *100 )/ timepassed;

  //for next iteration
  m_sPrevUsage = m_sUsage;
  gettimeofday(&m_sStartTime, NULL);

  //machine CPU utilization
  //FIXME: what happens when there are multiple processors?
  m_fTCpuUsage = get_cpu_stats();
#endif
  
  
}//CpuUtil::Recompute


long double CpuUtil::GetCpuUsage() {
  return m_fCpuUsage;
}//CpuUtil::GetCpuUsage


long double CpuUtil::GetTCpuUsage() {
  return m_fTCpuUsage;
}//CpuUtil::GetTCpuUsage


int CpuUtil::isRTexpired() {
  struct timeval cur_time, diff_time;

  gettimeofday(&cur_time, NULL);
  diff_time=difftimeval(m_stRT, cur_time);
  
  //timer has expired
  if(0==diff_time.tv_sec && 0 == diff_time.tv_usec) {
    return 1;
  }

  return 0;
}//isRTexpired

void CpuUtil::setRTTimer() {
  gettimeofday(&m_stRT, NULL);
  m_stRT=addtimeval(m_stRT, m_uiRTexp, 0);
}//setRTTimer


#ifndef WIN32
int CpuUtil::read_proc_stats(unsigned long long *idle, unsigned long long *uptime) {
  static char line[80];
  FILE *fp = NULL;
  unsigned long long cc_user, cc_nice, cc_system, cc_hardirq, cc_softirq;
  unsigned long long cc_idle, cc_iowait, cc_steal;
  unsigned long long cc_uptime = 0;

  if ((fp = fopen("/proc/stat", "r")) == NULL) {
    //dbgprint(D_ERR,"Could not open the /proc/stat");
    return -1;
  }

  if(fgets(line, 80, fp) != NULL) {

    if (!strncmp(line, "cpu ", 4)) {
        cc_hardirq = cc_softirq = cc_steal = 0;
        /* CPU counters became unsigned long long with kernel 2.6.5 */
        sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu",
               &cc_user, &cc_nice, &cc_system, &cc_idle, &cc_iowait, &cc_hardirq, &cc_softirq, &cc_steal);
        cc_uptime = cc_user + cc_nice + cc_system + cc_idle + cc_iowait + cc_hardirq +  cc_softirq + cc_steal;
    }
  } else {
    return -1;
  }

  if(fp) {
    fclose(fp);
  }

  *idle = cc_idle;
  *uptime = cc_uptime;

  return 1;
}//read_proc_stats


#define HZ (1024)
#define S_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * HZ)

/* new define to normalize to %; HZ is 1024 on IA64 and % should be normalized to 100 */
#define SP_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * 100)


double ll_sp_value(unsigned long long value1, unsigned long long value2,
                   unsigned long long itv) {
  if ((value2 < value1) && (value1 <= 0xffffffff)) {
    /* Counter's type was unsigned long and has overflown */
    return ((double) ((value2 - value1) & 0xffffffff)) / itv * 100;
  } else {
    return SP_VALUE(value1, value2, itv);
  }

}//ll_sp_value

double CpuUtil::get_cpu_stats () {
  unsigned long long uptime1;
  unsigned long long idle1;
  unsigned long long itv;
  double cpu_utils;
  
  //read stats from proc
  read_proc_stats( &idle1, &uptime1);

  itv = (uptime1 - uptime) & 0xffffffff;

  if(idle1 < idle) {
    cpu_utils=0.0;
  } else {
    cpu_utils=ll_sp_value( idle, idle1, itv);
  }

  //printf("IDLE %llu %llu uptime %llu %llu cpuutil: %f\n", idle, idle1, uptime, uptime1, cpu_utils);

  cpu_utils = 100 - cpu_utils;

  read_proc_stats( &idle, &uptime);

  return cpu_utils;
}//get_cpu_stats
#endif

CpuUtil* CpuUtil::m_pObj;
CpuUtil *gCpuUtilization=CpuUtil::GetInstance();



