/*  -*- 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <net/route.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifdef __alpha__
#include <netinet/ip_var.h>
#endif
#include <netinet/tcp.h>

#include <unistd.h>
#include <fcntl.h>

#include "cprintf.h"
#include "list.h"
#include "pkt.h"
#include "seq.h"
#include "flows.h"
#include "service.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "timeouts.h"
#include "print_util.h"
#include "report.h"
#include "output.h"
#include "tcpdump_patch.h"
#include "counters.h"
#include "writer.h"
#include "np_file.h"

#include "basic_defs.h"

#include "sysinfo.h"
#include "procstat.h"

extern void error(char *msg, char *pmsg);

/* probe_main.c */
long long utvsub(struct timeval *t1, struct timeval *t0);


/* This macro opens filename only if necessary and seeks to 0 so
 * that successive calls to the functions are more efficient.
 * It also reads the current contents of the file into the global buf.
 */
#define FILE_TO_BUF(filename, fd, buf, buflen, nread, err1) \
  MACRO_BEGIN \
    char errbuf[64];						\
    if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) \
      { \
        sprintf(errbuf, "open() %s", (filename)); \
        error((err1), errbuf); \
      } \
    lseek(fd, 0L, SEEK_SET);					\
    if (((nread) = read((fd), (buf), (buflen) -1)) <= 0) \
      { \
        sprintf(errbuf, "read() %s", (filename)); \
        error((err1), errbuf); \
      } \
    (buf)[(nread)] = '\0';						\
  MACRO_END

/************************************************************************
 * The following /proc/meminfo parsing routine assumes the following format:
 * [ <label> ... ]				# header lines
 * [ <label> ] <num> [ <num> ... ]		# table rows
 * [ repeats of above line ]
 * 
 * Any lines with fewer <num>s than <label>s get trailing <num>s set to zero.
 * The return value is a NULL terminated unsigned** which is the table of
 * numbers without labels.  Convenient enumeration constants for the major and
 * minor dimensions are available in the header file.  Note that this version
 * requires that labels do not contain digits.  It is readily extensible to
 * labels which do not *begin* with digits, though.
 */
#define MAX_ROW 3	/* these are a little liberal for flexibility */
#define MAX_COL 7
#define MEMINFOBUF_SZ 1024

unsigned long long 
**meminfo(void)
{
  static unsigned long long *row[MAX_ROW + 1];		/* row pointers */
  static unsigned long long num[MAX_ROW * MAX_COL];	/* number storage */
  char *p;
  char fieldbuf[12];		/* bigger than any field name or size in kb */
  int i, j, k, nread;
  static int meminfo_fd = -1;
  static char buf[MEMINFOBUF_SZ];
  
  FILE_TO_BUF("/proc/meminfo", meminfo_fd, buf, MEMINFOBUF_SZ, nread, 
	      "meminfo()");

  
  if (!row[0])				/* init ptrs 1st time through */
    for (i=0; i < MAX_ROW; i++)		/* std column major order: */
      row[i] = num + MAX_COL*i;		/* A[i][j] = A + COLS*i + j */
  p = buf;
  for (i=0; i < MAX_ROW; i++)			/* zero unassigned fields */
    for (j=0; j < MAX_COL; j++)
      row[i][j] = 0;

  while(*p) 
    {
      sscanf(p,"%11s%n",fieldbuf,&k);
      if(!strcmp(fieldbuf,"MemTotal:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_main][meminfo_total]));
	  row[meminfo_main][meminfo_total]<<=10;
	  while(*p++ != '\n');
	}
      else if(!strcmp(fieldbuf,"MemFree:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_main][meminfo_free]));
	  row[meminfo_main][meminfo_free]<<=10;
	  while(*p++ != '\n');
	}
      else if(!strcmp(fieldbuf,"MemShared:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_main][meminfo_shared]));
	  row[meminfo_main][meminfo_shared]<<=10;
	  while(*p++ != '\n');
	}
      else if(!strcmp(fieldbuf,"Buffers:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_main][meminfo_buffers]));
	  row[meminfo_main][meminfo_buffers]<<=10;
	  while(*p++ != '\n');
	}
      else if(!strcmp(fieldbuf,"Cached:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_main][meminfo_cached]));
	  row[meminfo_main][meminfo_cached]<<=10;
	  while(*p++ != '\n');
	}
      else if(!strcmp(fieldbuf,"SwapTotal:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_swap][meminfo_total]));
	  row[meminfo_swap][meminfo_total]<<=10;
	  while(*p++ != '\n');
	}
      else if(!strcmp(fieldbuf,"SwapFree:")) 
	{
	  p+=k;
	  sscanf(p," %Ld",&(row[meminfo_swap][meminfo_free]));
	  row[meminfo_swap][meminfo_free]<<=10;
	  while(*p++ != '\n');
	}
      else
	while(*p++ != '\n'); /* ignore lines we don't understand */
    		
      row[meminfo_swap][meminfo_used] = 
	row[meminfo_swap][meminfo_total]-row[meminfo_swap][meminfo_free];
      row[meminfo_main][meminfo_used] = 
	row[meminfo_main][meminfo_total]-row[meminfo_main][meminfo_free];
    }
  return row;					/* NULL return ==> error */
}

void
get_meminfo(procstat_memrec_t *mr) 
{
  unsigned long long** mem;
  
  if (!(mem = meminfo())) 
    error("get_meminfo:", "meminfo" );
  
  mr->mfree  = mem[meminfo_main][meminfo_free];
  mr->mbuff  = mem[meminfo_main][meminfo_buffers];
  mr->memc = mem[meminfo_main][meminfo_cached];
  mr->memswp = mem[meminfo_swap][meminfo_used];

  return;
}

#define BUFFSIZE 4096

void 
get_stat(procstat_statrec_t *sr) 
{
  static int stat;
  static char buff[BUFFSIZE]; /* used in the procedures */
  
  if ((stat=open("/proc/stat", O_RDONLY, 0)) != -1) 
    {
      char* b;
      buff[BUFFSIZE-1] = 0;  /* ensure null termination in buffer */
      read(stat,buff,BUFFSIZE-1);
      close(stat);
      b = strstr(buff, "cpu ");
      sscanf(b, "cpu  %u %u %u %u", &sr->utm, &sr->ntm, &sr->stm, &sr->itm);
      b = strstr(buff, "page ");
      sscanf(b, "page %u %u", &sr->bin, &sr->bout);
      b = strstr(buff, "swap ");
      sscanf(b, "swap %u %u", &sr->swin, &sr->swout);
      b = strstr(buff, "intr ");
      sscanf(b, "intr %u %*u", &sr->itot);
      b = strstr(buff, "ctxt ");
      sscanf(b, "ctxt %u", &sr->csw);
    }
  else 
    {
      error("get_stat():", "open /proc/stat");
    }
}

void 
fill_stat(char *buf, procstat_procrec_t *s)
{
  
  int num;
  char* cp = strrchr(buf, ')') + 2;	/* move to status field */

  num = sscanf(cp,		
	       "%*c "
	       "%*d %*d %*d %*d %*d "
	       "%*u %u %*u %u %*u %u %u "
	       "%*d %*d %*d %*d %*d %*d "
	       "%*u %u "
	       "%d "
	       "%*u %*u %*u %*u %*u %*u "
	       "%*s %*s %*s %*s " 
	       "%*u %u %*u %*u %*u ",
	       &s->minflt, &s->majflt, &s->utm, &s->stm, 
	       &s->vsz,
	       &s->rss,
	       &s->nswap);
  
  return;
}

void 
fill_statm(char *buf, procstat_procrec_t *s)
{
  
  int num;

  num = sscanf(buf, "%d %d %d %d %d %d %d",
	   &s->msz, &s->mrss, &s->mshare,
	   &s->mtrs, &s->mlrs, &s->mdrs, &s->mdt);

  return;
}

  

#define STATBUF_SZ 512

void get_my_stat(procstat_procrec_t *acc, procstat_procrec_t *abs) 
{
  static int statfd = -1, statmfd = -1;
  int nread;

  char statbuf[STATBUF_SZ], statmbuf[STATBUF_SZ];

  FILE_TO_BUF("/proc/self/stat", statfd, statbuf, STATBUF_SZ, nread, 
	      "get_mystat()");
  fill_stat(statbuf, acc);

  FILE_TO_BUF("/proc/self/statm", statmfd, statmbuf, STATBUF_SZ, nread, 
	      "get_mystat()");
  fill_statm(statmbuf, abs);

  return;
}

void
report_procstats(procstat_rec_t *prp)
{
  procstat_procrec_t *proc = &prp->proc;
  procstat_statrec_t *stat = &prp->stat;
  procstat_memrec_t *mem = &prp->mem;

  float per_sec, per_jiffies, per_time;
  float tot_cpu = (stat->utm + stat->stm + stat->itm);

  int hz =  counters.fh.hz;
  int ncpus = counters.fh.ncpus;

  static int started = 0;
  static struct timeval last;
  

  if (!started)
    {
      started = 1;
      per_time = 0.0;
      per_jiffies = 1000000.0;
      
    }
  else 
    {
      per_time = utvsub(&(prp->ts), &last)/1000000.0;
      per_jiffies = tot_cpu/ncpus;
    }

  per_sec = per_jiffies/hz;
      
  
  last = *(&prp->ts);			/* struct */

  REP(stderr, " %s per %.1fs (%.1fs)\n", time_string(&prp->ts), 
      per_time, per_sec);

  REP(stderr, " SYSTEM\n");
  REP(stderr, " Mem: Free %u Buffer %u Cache %u Swapped %u kB\n", 
      mem->mfree>>10, mem->mbuff>>10, mem->memc>>10, mem->memswp>>10);
  REP(stderr, " CPU: usr %.0f%% sys %.0f%% idle %.0f%%\n", 
      stat->utm*100/tot_cpu, 
      stat->stm*100/tot_cpu, stat->itm*100/tot_cpu);
  REP(stderr, 
      " Swap pages: in %.0f/s out %.0f/s, IO blocks: in %.0f/s out %.0f/s\n", 
      stat->swin/per_sec, stat->swout/per_sec, 
      stat->bin/per_sec, stat->bout/per_sec);
  REP(stderr, " Interrupts: %.0f/s, Cswitches: %.0f/s\n", 
      stat->itot/per_sec, stat->csw/per_sec);

  REP(stderr, " NPROBE\n");
  REP(stderr, " CPU: usr %.0f (%.0f)%% sys %.0f (%.0f)%% tot %.0f (%.0f)%%\n",
      proc->utm*100/tot_cpu, proc->utm*100/per_jiffies, 
      proc->stm*100/tot_cpu, proc->stm*100/per_jiffies, 
      (proc->utm+proc->stm)*100/tot_cpu, 
      (proc->utm+proc->stm)*100/per_jiffies);
  REP(stderr, " Page faults: min %.0f/s maj %.0f/s , Swaps: %.0f/s\n",
      proc->minflt/per_sec, proc->majflt/per_sec, proc->nswap/per_sec);
  REP(stderr, " VM %ukB,  RSS %d pages\n", proc->vsz>>10, proc->rss);
  REP(stderr, " Mem pages: total %d rss %d\n mmapped %d text %d lib %d data %d dirty %d\n", 
	  proc->msz, proc->mrss, proc->mshare, 
	  proc->mtrs, proc->mlrs, proc->mdrs, proc->mdt);

  REP(stderr, "\n");    
  
  return;
}


void 
record_procstats(struct timeval *ts)
{
  procstat_rec_t r, *tmp;
  static procstat_rec_t pr[2];
  static procstat_rec_t *curr = pr, *last = pr+1;

  tmp = curr;
  curr = last;
  last = tmp;

  r.ts = *ts;		/* struct */

  get_meminfo(&r.mem);
  get_stat(&curr->stat);
  get_my_stat(&curr->proc, &r.proc);

  /*fprintf(stderr, "CURR %u %u %u %u\n%u %u %u %u\n%u %u\n", 
	  curr->stat.utm, curr->stat.ntm, curr->stat.stm, curr->stat.itm, 
	  curr->stat.swin, curr->stat.swout, curr->stat.bin, curr->stat.bout, 
	  curr->stat.itot, curr->stat.csw);*/

  /*fprintf(stderr, "CURR %u %u %u %u\n%u %u %u\n%u %u %u\n%u %u %u %u\n",
	  curr->proc.minflt, curr->proc.majflt, curr->proc.utm, curr->proc.stm, 
	  curr->proc.vsz, curr->proc.rss, curr->proc.nswap, 
	  curr->proc.mvsz, curr->proc.mrss, curr->proc.mshare, 
	  curr->proc.mtrs, curr->proc.mlrs, curr->proc.mdrs, curr->proc.mdt);*/
  
  

  r.stat.utm = curr->stat.utm - last->stat.utm;
  r.stat.ntm = curr->stat.ntm - last->stat.ntm;
  r.stat.stm = curr->stat.stm - last->stat.stm;
  r.stat.itm = curr->stat.itm - last->stat.itm;
  r.stat.swin = curr->stat.swin - last->stat.swin;
  r.stat.swout = curr->stat.swout - last->stat.swout;
  r.stat.bin = curr->stat.bin  - last->stat.bin; 
  r.stat.bout = curr->stat.bout - last->stat.bout;
  r.stat.itot = curr->stat.itot - last->stat.itot;
  r.stat.csw = curr->stat.csw  - last->stat.csw;

  r.proc.minflt = curr->proc.minflt - last->proc.minflt;
  r.proc.majflt = curr->proc.majflt - last->proc.majflt;
  r.proc.utm = curr->proc.utm - last->proc.utm;
  r.proc.stm = curr->proc.stm - last->proc.stm;
  r.proc.nswap = curr->proc.nswap - last->proc.nswap;
  r.proc.vsz = curr->proc.vsz;
  r.proc.rss = curr->proc.rss;


  /*fprintf(stderr, "PER %u %u %u %u\n%u %u %u %u\n%u %u\n", 
	  r.stat.utm, r.stat.ntm, r.stat.stm, r.stat.itm, 
	  r.stat.swin, r.stat.swout, r.stat.bin, r.stat.bout, 
	  r.stat.itot, r.stat.csw);*/

  /*fprintf(stderr, "CURR %u %u %u %u\n%u %d %u\n%d %d %d\n%d %d %d %d\n",
	  r.proc.minflt, r.proc.majflt, r.proc.utm, r.proc.stm, 
	  r.proc.vsz, r.proc.rss, r.proc.nswap,
 
	  r.proc.msz, r.proc.mrss, r.proc.mshare, 
	  r.proc.mtrs, r.proc.mlrs, r.proc.mdrs, r.proc.mdt);*/

  rec_dump_start();
  DUMP_STRUCT(outp, &r, procstat_rec_t);
  rec_dump_end(REC_OTHER_PROCSTAT);
  
  report_procstats(&r);
  
}




/* End procstat.c */
  
 
