/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* bos720 src/bos/usr/ccs/lib/libperfstat/simplewparstat.c 1.6            */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 2007,2009              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char sccsid[] = "@(#)29        1.6  src/bos/usr/ccs/lib/libperfstat/simplewparstat.c, libperfstat, bos720 4/17/09 01:56:10";

/* NOTE : This example assumes that the sample program will be run in DLPAR without 
 * donation capability. For DLPAR donation capability and SPLPAR CPU usage 
 * calculation, please refer /usr/samples/libperfstat/simplelparstat.c and make
 * changes in the display_lpar_wpar_stats() function for calculating
 * delta wait and idle physical processor tics 
 */ 

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <wpars/wparcfg.h>
#include <libperfstat.h>

/* state of wpar */
#define ACTIVE 1 
#define INACTIVE 0

/* Check value returned by malloc for NULL */
#define CHECK_FOR_MALLOC_NULL(X) {  if ((X) == NULL) {\
                                       perror ("malloc");\
                                       exit(2);\
                                     } }

/* WPAR ID for global will always be zero */
#define IS_GLOBAL(X) (!(X)) 

/* Non zero WPAR ID indicates WPAR */
#define IS_WPAR(X) ((X))

/* For WPAR, use NULL else use the actual WPAR ID (for global) */
#define WPAR_ID ((cid)?NULL:&wparid)

/* Convert 4K pages to MB */
#define AS_MB(X) ((X) * 4096/1024/1024)

#define COUNT_DEFAULT 1
#define INTERVAL_DEFAULT 1


/* store LPAR level stats */
perfstat_cpu_total_t          cinfo;
perfstat_memory_total_t       minfo;
perfstat_partition_total_t    pinfo;

perfstat_id_wpar_t         wparid; /* stores wpar id for perfstat library */

/* store per WPAR stats */
perfstat_wpar_total_t        *winfo; 
perfstat_cpu_total_wpar_t    *cinfo_wpar; 
perfstat_memory_total_wpar_t *minfo_wpar;

corrallist_t *wpar_list; /* store wpar information for each WPAR */
cid_t         cid; /* store current WPAR ID */

unsigned long long  last_user, last_sys, last_idle, last_wait, last_timebase;
unsigned long long *last_wpar_user, *last_wpar_sys;
int *status;
int interval = INTERVAL_DEFAULT, count = COUNT_DEFAULT;
char wpar[MAXCORRALNAMELEN+1];


/* Store total number of WPARs in the LPAR.If
 * the program is executed from an LPAR it
 * holds 1 (when started from a WPAR)     */

int total_wpars = 0;

/*
 *Name: showusage
 *      displays the usage
 *
 */

void showusage()
{
  if(!cid)
      printf("Usage:simplewparstat [-@ { ALL | WPARNAME }] [interval] [count]\n ");
  else
      printf("Usage:simplewparstat [interval] [count]\n");

  exit(1);
}


/*
 *Name: get_WPAR_list
 *      get all the wpars and allocate memory for them
 *
 */

void get_WPAR_list(void)
{
   int i, rc;

   /* Check whether we are running inside WPAR */
   cid = corral_getcid();      /* Get the WPAR id */
   if (IS_WPAR(cid)) {         /* Check if the process is running inside WPAR */

      /* While running inside the WPAR, set number of WPARs as 1*/
      total_wpars = 1;
   }
   else {                      /* Running in LPAR .i.e. global system */
      
      /* Get the number of WPARs in the System */
      total_wpars = perfstat_wpar_total( NULL, NULL, sizeof(perfstat_wpar_total_t), 0);
      if (total_wpars < 0) {
          perror("perfstat_wpar_total:");
          exit(1);
      }
   }

   /* If WPARs exists in the system, get WPAR details */
   if (total_wpars > 0) {
       wpar_list = (corrallist_t*) malloc(total_wpars * sizeof(corrallist_t)); 
       CHECK_FOR_MALLOC_NULL(wpar_list);
   
       if (IS_GLOBAL(cid)) {     /* For global system, get list of WPARs */
           rc = getcorrallist(wpar_list, &total_wpars);
           if (rc != 0) {
               perror ("getcorrallist():");
               exit (1);
           }
       }
       else {                   /* sample is running inside a WPAR */
 
           /* set cid to the current id and print name as local */
           wpar_list[0].cid = cid;
           strcpy(wpar_list[0].cname, "Local\0");
       }
   }
}


/*
 *Name: allocate
 *      allocates memory for all data structures
 *
 */

void allocate(void){

   status = (int *) malloc (total_wpars * sizeof (int));
   CHECK_FOR_MALLOC_NULL(status);

   last_wpar_user = (unsigned long long *) malloc(total_wpars * sizeof (unsigned long long));
   CHECK_FOR_MALLOC_NULL(last_wpar_user);

   last_wpar_sys = (unsigned long long *) malloc(total_wpars * sizeof (unsigned long long));
   CHECK_FOR_MALLOC_NULL(last_wpar_sys);

   winfo = (perfstat_wpar_total_t *) malloc(total_wpars * sizeof(perfstat_wpar_total_t));
   CHECK_FOR_MALLOC_NULL(winfo);

   cinfo_wpar = (perfstat_cpu_total_wpar_t *) malloc(total_wpars * sizeof(perfstat_cpu_total_wpar_t));
   CHECK_FOR_MALLOC_NULL(cinfo_wpar);

   minfo_wpar = (perfstat_memory_total_wpar_t *) malloc(total_wpars * sizeof(perfstat_memory_total_wpar_t));
   CHECK_FOR_MALLOC_NULL(minfo_wpar);
}

/*
 *Name: getwpardata
 *      get all wpar data and find if they are active
 *
 */

void getwpardata(void){
   int i;
      
   /* Obtain WPAR Data */
   wparid.spec = WPARID;
   for (i=0; i < total_wpars; i++) {
        status[i]=ACTIVE;
        wparid.u.wpar_id = wpar_list[i].cid;
        if (perfstat_wpar_total (WPAR_ID, &winfo[i], sizeof(perfstat_wpar_total_t), 1) <= 0){
       	    status[i]= INACTIVE;
       	    continue;
        }
        wparid.u.wpar_id = wpar_list[i].cid;
        if (perfstat_cpu_total_wpar (WPAR_ID, &cinfo_wpar[i], sizeof(perfstat_cpu_total_wpar_t), 1) <= 0){
   	    status[i]= INACTIVE;
 	    continue;
	}
        if (perfstat_memory_total_wpar (WPAR_ID, &minfo_wpar[i], sizeof(perfstat_memory_total_wpar_t), 1) <= 0)
	    status[i] = INACTIVE;
	 
   }
}

/*
 *Name: save_last_values
 *      this function is used to store the values 
 *
 */ 

void save_last_values (void)
{
   int i;

   last_user  = cinfo.puser;
   last_sys   = cinfo.psys;
   last_idle  = cinfo.pidle;
   last_wait  = cinfo.pwait;
   last_timebase = pinfo.timebase_last;

   for (i=0; i < total_wpars; i++) {
        if (status[i] == INACTIVE){
	    last_wpar_user[i] = 0;
	    last_wpar_sys[i] = 0;
	    continue;
	}
        last_wpar_user[i]  = cinfo_wpar[i].puser;
        last_wpar_sys[i]   = cinfo_wpar[i].psys;
   }
}

/*
 *Name: display_configuration
 *      display /par/wpar configuration
 *
 */

void display_configuration (void)
{
   unsigned long long memlimit;
   double cpulimit;

   int i;

   /* gather LPAR level data */
   if (perfstat_partition_total(NULL, &pinfo, sizeof(perfstat_partition_total_t), 1) <= 0) {
       perror("perfstat_partition_total\n");
       exit(1);
   }
   if (perfstat_cpu_total(NULL, &cinfo, sizeof(perfstat_cpu_total_t), 1) <= 0) {
       perror("perfstat_cpu_total\n");
       exit(1);
   }
 
   if (perfstat_memory_total(NULL, &minfo, sizeof(perfstat_memory_total_t), 1) <= 0) {
       perror("perfstat_memory_total\n");
       exit(-1);
   }

   /* print WPAR general processor information */
   printf("\nWPARs Configuration:\n");
   for (i=0; i < total_wpars; i++) {
        if (status[i] == INACTIVE)
	    continue;
        /* perfstat_wpar_total_t.mem_limit and perfstat_wpar_total_t.cpu_limit are stored as 100s of 
         * a percentage (ex: 60% will stored as 60 * 100. So dividing them by 100 */

        /* memory limit of a WPAR as 4k pages */ 
        memlimit = (minfo_wpar[i].real_total * (winfo[i].mem_limit/10000.0));
      
        /* CPU limit of a WPAR as entitled capacity */
        cpulimit = ((double)pinfo.entitled_proc_capacity/100.0) * ((double)winfo[i].cpu_limit/10000.0); 

        /* print WPAR configuration */
        printf("%s Configuration: ",winfo[i].name); /* WPAR Name */
        printf("memlimit = %lluMB, ", AS_MB(memlimit)); /* WPAR Mem limit as MB */
        printf("cpulimit = %#5.2f\n",cpulimit); /* WPAR CPU limit */
   }
}

/*
 *Name: display_lpar_wpar_stats
 *      collect the data and display them
 *
 */

void display_lpar_wpar_stats (void)
{
   unsigned long long  delta_total, delta_user, delta_sys, delta_idle, delta_wait, delta_timebase;

   unsigned long long memlimit;
   double cpulimit;

   int i;

   /* retrive partition level statistics */
   if (perfstat_partition_total(NULL, &pinfo, sizeof(perfstat_partition_total_t), 1) <= 0) {
       perror("perfstat_partition_total\n");
       exit(-1);
   }
  
   if (perfstat_cpu_total(NULL, &cinfo, sizeof(perfstat_cpu_total_t), 1) <= 0) {
       perror("perfstat_cpu_total\n");
       exit(-1);
   }

   if (perfstat_memory_total(NULL, &minfo, sizeof(perfstat_memory_total_t), 1) <= 0) {
       perror("perfstat_memory_total\n");
       exit(-1);
   }

   /* retrieve WPAR level statistics */
   for (i=0; i < total_wpars; i++) {
        if (wparid.spec != WPARNAME)
            wparid.u.wpar_id = wpar_list[i].cid;
        if (perfstat_memory_total_wpar (WPAR_ID, &minfo_wpar[i], sizeof(perfstat_memory_total_wpar_t), 1) <= 0){
	    status[i] = INACTIVE;
	    continue;
        }
        else
	    status[i] = ACTIVE;
        if (wparid.spec == WPARNAME)
            strcpy(wparid.u.wparname,wpar);	
    
        if (perfstat_cpu_total_wpar (WPAR_ID, &cinfo_wpar[i], sizeof(perfstat_cpu_total_wpar_t), 1) <= 0)
            status[i] = INACTIVE;
        else
	    status[i] = ACTIVE;
   
        if (wparid.spec == WPARNAME)
            strcpy(wparid.u.wparname,wpar);	
   }

   /* calculate physical processor tics during the last interval in user, system, idle and wait mode  */
   delta_user = cinfo.puser - last_user;
   delta_sys  = cinfo.psys  - last_sys;
   delta_idle = cinfo.pidle - last_idle;
   delta_wait = cinfo.pwait - last_wait;

   /* calculate delta time in terms of physical processor tics */
   delta_timebase = pinfo.timebase_last - last_timebase;

   /* calculate total physcial processor tics during the last interval */
   delta_total  = delta_user + delta_sys + delta_idle + delta_wait;

   printf ("%-18s ",      "System\0"); /* WPAR name as System for global */
   printf ("%9llu ",      minfo.real_inuse); /* Memory in use as 4K pages */
   printf ("%9llu ",      minfo.real_free); /* Memory free as 4k Pages */
   printf ("%#5.1f     ", (((double)minfo.real_inuse/(double)minfo.real_total)*100.0));  /* mem consumed */
   printf ("%#4.1f ",     ((double)(delta_user)/(double)delta_total)*100.0); /* percentage of time spent in user mode */
   printf ("%#4.1f ",     ((double)(delta_sys)/(double)delta_total)*100.0); /* percentage of time spent in system mode */
   printf ("%#5.1f  ",    ((double)(delta_user + delta_sys)/(double)delta_timebase)*100.0); /* physical busy */
   printf ("%#5.1f\n",    ((double)(delta_total)/(double)(delta_timebase * ((double)pinfo.entitled_proc_capacity/100.0)))*100.0); /* physical consumed */

   for (i=0; i < total_wpars; i++) {
        if (status[i] == INACTIVE)
	    continue;

        /* memory limit of a WPAR as MB */ 
        memlimit = (minfo_wpar[i].real_total * (winfo[i].mem_limit/10000.0));

        /* CPU limit of a WPAR as entitled capacity */
        cpulimit = ((double)pinfo.entitled_proc_capacity/100.0) * ((double)winfo[i].cpu_limit/10000.0); 

        /* calculate physcial processor tics during the last interval in user and system  */
        delta_user = cinfo_wpar[i].puser - last_wpar_user[i];
        delta_sys  = cinfo_wpar[i].psys  - last_wpar_sys[i];
        delta_total= delta_user + delta_sys;
        printf ("%-18s ",      winfo[i].name); /* WPAR name */
        printf ("%9llu ",      minfo_wpar[i].real_inuse); /* Memory in use as 4K pages */
        printf ("%9llu ",      memlimit - minfo_wpar[i].real_inuse); /* Memory free as 4k Pages */
        printf ("%#5.1f     ", (((double)minfo_wpar[i].real_inuse/(double)memlimit)*100.0));  /* mem consumed */

        /* if delta_total is 0, it means the wpar is idle, so make both usr and sys as 0 */
        if (delta_total == 0){
            printf ("%#4.1f ", 0.0 ); /* percentage of time spent in user mode */
            printf ("%#4.1f ", 0.0 ); /* percentage of time spent in system mode */
        }
        else{
            printf ("%#4.1f ",     ((double)(delta_user)/(double)delta_total)*100.0); /* percentage of time spent in user mode */
            printf ("%#4.1f ",     ((double)(delta_sys)/(double)delta_total)*100.0);  /* percentage of time spent in system mode */
        }
        printf ("%#5.1f  ",    ((double)(delta_user + delta_sys)/(double)delta_timebase)*100.0); /* physical busy */
        printf ("%#5.1f\n",    ((double)(delta_user + delta_sys)/(double)(delta_timebase * cpulimit))*100.0); /* physical consumed */
   }        
}

/*
 *Name: main
 *
 */

int main(int argc, char *argv[]) 
{
   int c, atflag = 0;
   strcpy(wpar, NULL);

   /* Get the cid of the process */
   cid = corral_getcid();
   
   /* process all the options and get the interval and count */
   while((c = getopt(argc, argv, "@:"))!= EOF){
         if (c == '@'){
             if (IS_WPAR(cid))
                 showusage();
             atflag = 1;
             strcpy(wpar, optarg);
         }
   }
   argc -= optind;
   argv += optind;

   if (argc > 2)
       showusage();

   /* if interval or count is less than or equal to zero show the usage and exit */
   if(argc){
      if ((interval = atoi(argv[0])) <= 0)
          showusage();
      argc--;
   }

   if (argc){
       if ((count = atoi(argv[1])) <= 0)
           showusage();
   }

   /* if the argument passed with -@ is not ALL set the totalwpar to 1 */
   if (atflag && strcmp(wpar,"ALL")){
       total_wpars = 1;
       allocate();
       wparid.spec = WPARNAME;
       strcpy(wparid.u.wparname,wpar);
       if (perfstat_wpar_total (WPAR_ID, winfo, sizeof(perfstat_wpar_total_t), 1) <= 0){
           perror("perfstat_wpar_total :");
           exit(1);
       }
       strcpy(wparid.u.wparname,wpar);
       if (perfstat_cpu_total_wpar (WPAR_ID, cinfo_wpar, sizeof(perfstat_cpu_total_wpar_t), 1) <= 0){
	   perror("perfstat_cpu_total_wpar :");
	   exit(1);
       }
       if (perfstat_memory_total_wpar (WPAR_ID, minfo_wpar, sizeof(perfstat_memory_total_wpar_t), 1) <= 0){
	   perror("perfstat_memory_total_wpar :");
	   exit(1);
       }
       status[0] = ACTIVE;
       strcpy(wparid.u.wparname,wpar);
       display_configuration();
   }
   else{
       get_WPAR_list();
       allocate();
       getwpardata();
       display_configuration ();
   }
   /* Print headers */
   printf("\n%-18s %-30s %-30s\n", "WPAR\0", "Memory Utilization\0", "CPU Utilization\0");
   printf("                   InUse     Free      %%Cons     Usr  Sys  %%Phyb  %%Cons");
   printf("\n");

   save_last_values();
   while (count) {
        sleep (interval);
        display_lpar_wpar_stats();

        printf ("\n");
        count--;
        save_last_values();
   }
   
}