/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* bos720 src/bos/usr/ccs/lib/libperfstat/simplevmstat.c 1.2.1.2          */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 2008,2010              */
/* 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[] = "@(#)53        1.2.1.2  src/bos/usr/ccs/lib/libperfstat/simplevmstat.c, libperfstat, bos720 6/30/10 06:04:16";

/* 
 * This sample program simulates vmstat command with the option "-IP ALL".
 */
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <libperfstat.h>

#define INTERVAL_DEFAULT  	2
#define COUNT_DEFAULT  		10

static perfstat_memory_page_t *psize_mem_values, *last_psize_mem_values;
static perfstat_memory_total_t *vmtotal_mem_values, *last_vmtotal_mem_values;
static int total_psizes,avail_psizes;
static perfstat_cpu_total_t lcpu_values;
static u_longlong_t last_lcpu_user, last_lcpu_sys, last_lcpu_idle, last_lcpu_wait;

/* support for remote node statistics collection in a cluster environment */
static perfstat_id_node_t nodeid;
static char nodename[MAXHOSTNAMELEN] = "";
static int collect_remote_node_stats = 0;

static int do_initialization(void);
static int collect_metrics_pgsz(void);
static int collect_metrics_total(void);
static void do_cleanup(void);
static int display_psize_memory_stats(void);
static int display_total_memory_stats(void);
static void save_last_values_pgsz(void);
static void save_last_values_total(void);

/*
 * NAME: main
 *       This function determines the interval, iteration count.
 *       Then it calls functions to do initialization, displays statistics
 *       and finally does cleanup befor exiting.
 *
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - returns -1.
 */
int main(int argc, char* argv[])
{
   int interval = INTERVAL_DEFAULT;
   int count = COUNT_DEFAULT;
   int i, rc;
   char *optlist = "i:c:n:";

   /* Process the arguments */
   while ((i = getopt(argc, argv, optlist)) != EOF)
   {
       switch(i)
       {
           case 'i':               /* Interval */
                    interval = atoi(optarg);
                    if( interval <= 0 )
                        interval = INTERVAL_DEFAULT;
                    break;
           case 'c':               /* Number of interations */
                    count = atoi(optarg);
                    if( count <= 0 )
                        count = COUNT_DEFAULT;
                    break;
           case 'n':               /* Node name in a cluster environment */
                    strncpy(nodename, optarg, MAXHOSTNAMELEN);
                    nodename[MAXHOSTNAMELEN-1] = '\0';
                    collect_remote_node_stats = 1;
                    break;
           default:
                   /* Invalid arguments. Print the usage and terminate */
                   fprintf (stderr, "usage: %s [-i <interval in seconds> ] [-c <number of iterations> ] [-n <node name in the cluster> ]\n", argv[0]);
                   return(-1);
       }
   }
   if(collect_remote_node_stats)
   {   /* perfstat_config needs to be called to enable cluster statistics collection */
       rc = perfstat_config(PERFSTAT_ENABLE|PERFSTAT_CLUSTER_STATS, NULL);
       if (rc == -1)
       {
           perror("cluster statistics collection is not available");
           exit(-1);
       }
   }

    /* Initialize the data structures */
    rc = do_initialization();
    if(rc == 0)
    {
        if(collect_remote_node_stats)
        {
            fprintf (stdout,"Node name = %s\n", nodename);
        }
        /* Iterate "count" times */
        while (count > 0) {
            sleep(interval);
            /* print the header  */
            fprintf (stdout,"                          Multiple page size statistics\n");
            fprintf (stdout, 
                     "pgsz              memory                             page\n");
            fprintf(stdout,
                     "------- ------------------------------- ------------------------------------------\n");
            fprintf(stdout, "system\tsiz\tavm\tfre\tavail\tfi\tfo\tpi\tpo\tfr\tsr\n");
        
            /* Collect the statistics and print them */
            rc = display_psize_memory_stats();
            fprintf(stdout,"\n");
            
	    if (rc == -1) {
                /* Failed to display statistics properly. Exit safely */
                break;
            }
            fprintf (stdout,"            Total memory and paging activity\n");
            fprintf(stdout,"        memory                         page\n");
            fprintf(stdout,"----------------------- ----------------------------------\n");
            fprintf(stdout,"avm\tfre\tavail\tpi\tpo\tfr\tsr\tcy\n");

            rc = display_total_memory_stats();
            fprintf(stdout,"\n------------------------------------------------------------------------------------\n\n");
            if (rc == -1) {
               /* Failed to display statistics properly. Exit safely */
               break;
            }
            count--;
        }
    }
    if(collect_remote_node_stats)
    {   /* Now disable cluster statistics by calling perfstat_config */
        perfstat_config(PERFSTAT_DISABLE|PERFSTAT_CLUSTER_STATS, NULL);
    }
    /* Perform cleanup before exiting */
    do_cleanup();
    return(0);
}

/*
 * NAME: do_initialization
 *       This function initializes the data structues.
 *       It also collects initial set of values.
 * 
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - retruns -1.
 */
static int do_initialization(void)
{
    int rc;

    /* Request the total number of available pagesizes in the current system */
    if (collect_remote_node_stats)
    {
        strncpy(nodeid.u.nodename, nodename, MAXHOSTNAMELEN);
        nodeid.spec = NODENAME;
        total_psizes = perfstat_memory_page_node(&nodeid, NULL, NULL, 
                                                 sizeof(perfstat_memory_page_t), 0);
    }
    else
    {
        total_psizes = perfstat_memory_page(NULL, NULL, 
                                            sizeof(perfstat_memory_page_t), 0);
    }
    if(total_psizes < 1)
    {
        perror("do_initialization:"
               " Unable to retrieve the number of available pagesizes.");
        return(-1);
    }
    /* Allocate sufficient memory */
    psize_mem_values = (perfstat_memory_page_t *) 
                    malloc(sizeof(perfstat_memory_page_t) * total_psizes);
    if(psize_mem_values == NULL)
    { 
        perror("do_initialization: Unable to allocate sufficient"
               " memory for psize_mem_values buffer.");
        return(-1);
    }

    last_psize_mem_values = (perfstat_memory_page_t *) 
                    malloc(sizeof(perfstat_memory_page_t) * total_psizes);
    if(last_psize_mem_values == NULL)
    { 
        perror("do_initialization: Unable to allocate sufficient"
               " memory for last_psize_mem_values buffer.");
        return(-1);
    }

    vmtotal_mem_values = (perfstat_memory_total_t *) malloc(sizeof(perfstat_memory_total_t));
    
    if (vmtotal_mem_values == NULL) { 
        perror("do_initialization: Unable to allocate sufficient"
               " memory for vmtotal_mem_values buffer.");
        return(-1);
    }

    last_vmtotal_mem_values = (perfstat_memory_total_t *) malloc(sizeof(perfstat_memory_total_t));
    
    if (last_vmtotal_mem_values == NULL) { 
        perror("do_initialization: Unable to allocate sufficient"
               " memory for last_vmtotal_mem_values buffer.");
        return(-1);
    }

    rc = collect_metrics_pgsz();
    
    if (rc == -1) { 
        perror("do_initialization: Unable to collect data"
               " for variable page size stats.");
        return(-1);
    }
    save_last_values_pgsz();
    rc = collect_metrics_total();
    
    if (rc == -1) { 
        perror("do_initialization: Unable to collect data"
               " for total vmstat.");
        return(-1);
    }
    save_last_values_total();
    return(0) ;
}
/*
 * NAME: do_cleanup
 *       This function cleans up the data structues.
 * 
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - retruns -1.
 */
static void do_cleanup(void)
{
   if(psize_mem_values)
   {
       free(psize_mem_values);
   }
   if(last_psize_mem_values)
   {
       free(last_psize_mem_values);
   }
   if (vmtotal_mem_values) {
       free(vmtotal_mem_values);
   }

   if (last_vmtotal_mem_values) {
       free(last_vmtotal_mem_values);
   }

   return;
}

/*
 * NAME: collect_metrics_pgsz
 *       This function retrieves the total number of different pagsizes 
 * currently available and displays memory statistics related to each pagesize.
 * 
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - retruns -1.
 */
static int collect_metrics_pgsz(void)
{
    perfstat_psize_t psize;

    psize.psize = FIRST_PSIZE;
    if (collect_remote_node_stats)
    {
        strncpy(nodeid.u.nodename, nodename, MAXHOSTNAMELEN);
        nodeid.spec = NODENAME;
        avail_psizes = perfstat_memory_page_node(&nodeid, &psize, psize_mem_values, 
                                                 sizeof(perfstat_memory_page_t),
                                                 total_psizes); 
    }
    else
    {
        avail_psizes = perfstat_memory_page(&psize, psize_mem_values, 
                                            sizeof(perfstat_memory_page_t),
                                            total_psizes); 
    }

    if(avail_psizes < 1)
    {
        perror("display_psize_memory_stats: Unable to retrieve memory "
               "statistics for the available page sizes.");
        return(-1);
    }

    if (!perfstat_cpu_total(NULL, &lcpu_values, sizeof(perfstat_cpu_total_t), 1))
    {
        perror("do_initialization:"
               " Unable to retrieve the total cpu time.");
        return(-1);
    }
}
/*
 * NAME: collect_metrics_total
 *       This function retrieves the toatl memory and paging activity information 
 * and displays statistics related same.
 *
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - retruns -1.
 */
static int collect_metrics_total(void)
{
    int rc;

    if (collect_remote_node_stats)
    {
        strncpy(nodeid.u.nodename, nodename, MAXHOSTNAMELEN);
        nodeid.spec = NODENAME;
        rc = perfstat_memory_total_node(&nodeid, vmtotal_mem_values, sizeof(perfstat_memory_total_t),1);
    }
    else
    {
        rc = perfstat_memory_total(NULL,vmtotal_mem_values,sizeof(perfstat_memory_total_t),1);
    }

    if (rc != 1) {
        perror("collect_metrics_total:"
               " Unable to retrieve paging activity information.");
        return(-1); 
    }
}
static void save_last_values_pgsz(void)
{
    perfstat_memory_page_t *tmp_psize_mem_values;

    /* Save the current memory values as the last ones */
    tmp_psize_mem_values = last_psize_mem_values;
    last_psize_mem_values = psize_mem_values;
    psize_mem_values = tmp_psize_mem_values;

    last_lcpu_user = lcpu_values.user;
    last_lcpu_sys  = lcpu_values.sys;
    last_lcpu_idle = lcpu_values.idle;
    last_lcpu_wait = lcpu_values.wait;
}
static void save_last_values_total(void)
{
    last_vmtotal_mem_values = vmtotal_mem_values;
}
/*
 * NAME: display_psize_memory_stats
 *       This function retrieves the total number of different pagsizes 
 * currently available and displays memory statistics related to each pagesize.
 * 
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - retruns -1.
 */
static int display_psize_memory_stats(void)
{
    perfstat_memory_page_t *itr_psize_mem_values, *itr_last_psize_mem_values;
    int psize_index;
    int rc;
    u_longlong_t delta_pgins, delta_pgouts;
    u_longlong_t delta_pgspins, delta_pgspouts;
    u_longlong_t delta_pgsteals, delta_scans;
    u_longlong_t delta_time;


    rc = collect_metrics_pgsz();
    if(rc == -1)
    { 
        perror("do_initialization: Unable to allocate sufficient"
               " memory for last_psize_mem_values buffer.");
        return(-1);
    }

    /*fprintf (stdout, "System\n");*/
    itr_psize_mem_values = psize_mem_values;
    itr_last_psize_mem_values = last_psize_mem_values;
    for(psize_index = 0; psize_index < avail_psizes; psize_index++)
    {
        if(itr_psize_mem_values->psize != itr_last_psize_mem_values->psize)
        {
            /* Oops, Unknown error, return with error */
            perror("display_psize_memory_stats: curent and last saved values did not match.");
            return(-1);
        }
        /* Print the pagesize as a string */
        switch(itr_psize_mem_values->psize)
        {
             case PAGE_4K:
                 fprintf(stdout, "4K\t");
                 break;
             case PAGE_64K:
                 fprintf(stdout, "64K\t");
                 break;
             case PAGE_16M:
                 fprintf(stdout, "16M\t");
                 break;
             case PAGE_16G:
                 fprintf(stdout, "16G\t");
                 break;
             deafault:
                 /* Oops, Unknown page-size. return error */
                 perror("display_psize_memory_stats: unknown page-size");
                 return(-1);
         } 
         delta_pgins = (itr_psize_mem_values->pgins - itr_last_psize_mem_values->pgins);
         delta_pgouts = (itr_psize_mem_values->pgouts - itr_last_psize_mem_values->pgouts); 
         delta_pgspins = (itr_psize_mem_values->pgspins - itr_last_psize_mem_values->pgspins);
         delta_pgspouts = (itr_psize_mem_values->pgspouts - itr_last_psize_mem_values->pgspouts);
         delta_pgsteals = (itr_psize_mem_values->pgsteals - itr_last_psize_mem_values->pgsteals);
         delta_scans = (itr_psize_mem_values->scans - itr_last_psize_mem_values->scans);
         /* calculate total clock tics during the last interval */ 
         delta_time = (lcpu_values.user - last_lcpu_user)
                    + (lcpu_values.sys - last_lcpu_wait)
                    + (lcpu_values.idle - last_lcpu_idle)
                    + (lcpu_values.wait - last_lcpu_wait);

         /*              print the following statistics
          * --------------------------------------------------------------------------
          * siz     avm     fre     avail   fi      fo      pi      po      fr      sr
          * --------------------------------------------------------------------------
          * siz: Number of frames of the specified page size that exist on the
          *      system.
          * avm: Active virtual pages.
          * fre: Size of the free list.
          * avail: Number of pages of the specified page size that are
          *        available without paging out working segments.
          *  fi: File page-ins per second.
          *  fo: File page-outs per second.
          *  pi: Pages paged in from paging space.
          *  po: Pages paged out to paging space.
          *  fr: Pages freed (page replacement).
          *  sr: Pages scanned by page-replacement algorithm.
          */
         fprintf(stdout, "%llu\t", itr_psize_mem_values->real_total);
         fprintf(stdout, "%llu\t", itr_psize_mem_values->virt_active);
         fprintf(stdout, "%llu\t", itr_psize_mem_values->real_free);
         fprintf(stdout, "%llu\t", itr_psize_mem_values->real_avail);
         fprintf(stdout, "%llu\t", (delta_pgins - delta_pgspins)/delta_time);
         fprintf(stdout, "%llu\t", (delta_pgouts - delta_pgspouts)/delta_time);
         fprintf(stdout, "%llu\t", delta_pgspins/delta_time);
         fprintf(stdout, "%llu\t", delta_pgspouts/delta_time);
         fprintf(stdout, "%llu\t", delta_pgsteals/delta_time);
         fprintf(stdout, "%llu\n", delta_scans/delta_time);

         itr_psize_mem_values++;
         itr_last_psize_mem_values++;
    }
    fprintf(stdout, "\n");
    save_last_values_pgsz();

    return(0);
}
/*
 * NAME: display_total_memory_stats
 *       This function retrieves the total memory and paging activity information
 * and displays statistics for the same.
 *
 * RETURNS:
 * On successful completion:
 *   - returns 0.
 * In case of error
 *    - retruns -1.
 */
static int display_total_memory_stats(void)
{
    int rc;
    u_longlong_t delta_pgspins, delta_pgspouts;
    u_longlong_t delta_pgsteals, delta_scans;
    u_longlong_t delta_time;
    u_longlong_t delta_cycle;


    rc = collect_metrics_total();
    if(rc == -1) { 
        perror("do_initialization: Unable to allocate sufficient"
               " memory for last_psize_mem_values buffer.");
        return(-1);
    }

    delta_pgspins = (vmtotal_mem_values->pgspins - last_vmtotal_mem_values->pgspins);
    delta_pgspouts = (vmtotal_mem_values->pgspouts - last_vmtotal_mem_values->pgspouts);
    delta_pgsteals = (vmtotal_mem_values->pgsteals - last_vmtotal_mem_values->pgsteals);
    delta_scans = (vmtotal_mem_values->scans - last_vmtotal_mem_values->scans);
    delta_cycle = (vmtotal_mem_values->cycles - last_vmtotal_mem_values->cycles);
    /* calculate total clock tics during the last interval */ 
    delta_time = (lcpu_values.user - last_lcpu_user)
                 + (lcpu_values.sys - last_lcpu_wait)
                 + (lcpu_values.idle - last_lcpu_idle)
                 + (lcpu_values.wait - last_lcpu_wait);
    /*              print the following statistics
    * ------------------------------------------------------------------
    *   avm     fre     avail   pi      po      fr      sr      cy
    * ------------------------------------------------------------------
    * avm: Active virtual pages.
    * fre: Size of the free list.
    * avail: Number of pages (in 4K pagesize) that are
    *        available without paging out working segments.
    *  pi: Pages paged in from paging space.
    *  po: Pages paged out to paging space.
    *  fr: Pages freed (page replacement).
    *  sr: Pages scanned by page-replacement algorithm.
    *  cy: Clock cycles by page-replacement algorithm.
    */
    fprintf(stdout, "%llu\t", vmtotal_mem_values->virt_active);
    fprintf(stdout, "%llu\t", vmtotal_mem_values->real_free);
    fprintf(stdout, "%llu\t", vmtotal_mem_values->real_avail);
    fprintf(stdout, "%llu\t", delta_pgspins/delta_time);
    fprintf(stdout, "%llu\t", delta_pgspouts/delta_time);
    fprintf(stdout, "%llu\t", delta_pgsteals/delta_time);
    fprintf(stdout, "%llu\t", delta_scans/delta_time);
    fprintf(stdout, "%llu\n", delta_cycle/delta_time);
}