#include "sopnamsp.h"
#include "resusage.h"

#include <typeinfo>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <fstream>
#include <stdio.h>
#include <string>
/*!
  \class SOPHYA::ResourceUsage
  \ingroup SysTools
  This class gives acces to various system resource usage 
  (CPU, memory, ...).
  All returned values are in kilobytes for memory consumptions
  and in milli-seconds fo CPU and elapsed times.
  The information should be updated through the call to the 
  Update() method. 
  Note: The information is obtained through \c getrusage()   
  and \c getrlimit() system calls, and depending on the OS, 
  not all of the memory usage values are correctly filled.
  \code
  // How to check resource usage for a given part of the program
  ResourceUsage res;
  // --- Part of the program to be checked : Start
  // ...
  res.Update();
  cout << " Memory size increase (KB):" << res.getDeltaMemorySize() << endl;
  cout << " Resource usage info : \n" << res << endl;
  \endcode
*/
/*!
  Constructor. the \b Update() method is called.
  \param pg: Process group RU_Self or RU_Children or RU_All
  - pg = RU_Self : Resource usage for the process itself
  - pg = RU_Children : Resource usage for the child processes
  - pg = RU_All : resource usage for the process itself and its child processes
*/
ResourceUsage::ResourceUsage(RU_ProcGrp pg)
{
  procgrp = pg;
  struct rlimit rl;
  getrlimit(RLIMIT_DATA, &rl);
  max_datasz = rl.rlim_cur/1024;
#if defined(SunOS)
  // Max resident size ne semble pas etre defini sur SunOS
  max_rss = max_datasz; 
#else
  getrlimit(RLIMIT_RSS, &rl);
  max_rss = rl.rlim_cur/1024;
#endif
  getrlimit(RLIMIT_STACK, &rl);
  max_stack = rl.rlim_cur/1024;
#if defined(Linux)
  // recuperation des limites sous Linux 
  // car je ne comprends pas ce que renc=voie getrlimit
  // OPerdereau LAL Orsay  11/2002
  ReadLinuxTotMem();
#endif

  cur_datasz = 0;
  cur_stack = 0; 
  cur_rss = 0; 
  delta_rss = 0; 
  cur_tottm = 0;
  cur_usrtm = 0;
  cur_systm = 0;
  elapsed_time = 0;
  delta_rss = 0;
  delta_tottm = 0;
  delta_elapsed_time = 0;
  cur_pid = getpid();
  Update();
  
}
/*!
   Destructor.
*/
ResourceUsage::~ResourceUsage()
{
}

inline uint_8 s_timeval2msec(struct timeval & tv)
{ 
  uint_8 ret = tv.tv_sec; ret *= 1000; 
  ret += tv.tv_usec/1000; return(ret); 
}

/*!
  Update the CPU and memory usage information.
*/
int ResourceUsage::Update()
{
  struct rusage rsu;
  int rc;
  if ((procgrp == RU_Self) || (procgrp == RU_All)) 
    rc = getrusage(RUSAGE_SELF, &rsu);
  else 
    rc = getrusage(RUSAGE_CHILDREN, &rsu);

  delta_tottm = cur_tottm;
  delta_rss = cur_rss;
  cur_usrtm = s_timeval2msec(rsu.ru_utime);
  cur_systm = s_timeval2msec(rsu.ru_stime);
  if (procgrp == RU_All) {
    struct rusage rsuch;
    rc = getrusage(RUSAGE_CHILDREN, &rsuch);
    cur_usrtm += s_timeval2msec(rsuch.ru_utime);
    cur_systm += s_timeval2msec(rsuch.ru_stime);    
  }
  cur_tottm = cur_usrtm+cur_systm;
  delta_tottm = cur_tottm-delta_tottm;

#if defined(Linux)
  // Recuperation de la place en memoire ss Linux 
  ReadLinuxMem();
#else
  cur_rss = rsu.ru_maxrss;
  cur_datasz = rsu.ru_idrss;
  cur_stack = rsu.ru_isrss;
#endif

  delta_rss = cur_rss-delta_rss;
  delta_elapsed_time = elapsed_time;
  time_t tm = time(NULL);
  if (elapsed_time == 0) t0_time = tm;
  elapsed_time = (tm - t0_time)*1000;
  if (elapsed_time < 1) elapsed_time = 1;
  delta_elapsed_time = elapsed_time-delta_elapsed_time;
  return(rc);
}

/*!
   Prints the CPU and memory usage information.
   \param os : The output stream
   \param lp : The print level (0 .. 2)
   \param upd : if \c true , the Update method is called.
*/
void ResourceUsage::Print(ostream& os, int lp, bool upd) 
{
  if (upd) Update();
  os << " --------------- ResourceUsage::Print(lp=" << lp 
     <<" ) --------------- " << endl;
  int load = (int)(getAverageCPULoad()*100.);
  os << " CPU-Usage= " << getCPUTime() << " Elapsed= " << getElapsedTime()
     << " msec - Load= " << load << " %" << endl;
  long fracmem = getMemorySize()*100/getMaxMemorySize();
  os << " MemoryUsage= " << getMemorySize() << " /Max= " << getMaxMemorySize() 
     << " kbytes (" << fracmem << " %)" << endl;
  if (lp < 1) return;
  os << " CPU-Usage= " << cur_tottm << "ms (usr,sys)=(" 
     << cur_usrtm << "," << cur_systm << ")" << endl;
  os << "  DataSize=" << cur_datasz << " /Max " << max_datasz 
     << " kbytes" << endl;
  os << " StackSize=" << cur_stack  << " /Max " << max_stack 
     << " kbytes" << endl;
  os << "   ResSize=" << cur_rss  << " /Max " << max_rss 
     << " kbytes" << endl;
}
/*!
     Recuperation de la place en memoire ss Linux 
     ecrit d'apres les sources du code de top 
     O. Perdereau LAL Orsay 11/2002

*/  
void ResourceUsage::ReadLinuxMem(){ 

#if defined(Linux)

  char flnm[120];
  
  sprintf(flnm,"/proc/%d/statm",(int)cur_pid);
  ifstream fich(flnm) ;

 
  long lia,lib,lic,lid,lie,lif,lig;
  
  fich >> lia >> lib >> lic >> lid >> lie >> lif >> lig ;

  cur_rss = lia*4; // les valeurs sont en pages de 4 k 
  cur_datasz = lib*4;
  cur_stack = lic*4;

#endif

}


/*! 
   Recuperation de la memoire dispo ss Linux 
   ecrit d'apres les sources du code de top 
   O. Perdereau LAL Orsay 11/2002
*/
void ResourceUsage::ReadLinuxTotMem(){
#if defined(Linux)

  char flnm[]="/proc/meminfo";
  char buff[512];
  ifstream fich(flnm) ;

  fich.getline(buff,500); // on saute une ligne 

  string tst;
  long lia,lib,lic,lid,lie,lif;
  fich >> tst >> lia >> lib >> lic >> lid >> lie >> lif;

  max_rss = lia/1024; // conversion  en kbytes  

#endif

}
