Nicstat.c

Page last edited hace 2208 días 8 horas
De Solaris-ES
Saltar a: navegación, buscar

/*

* Definición de variables
*
* exec bash
*
* export LD_LIBRARY_PATH_64=/opt/solarisstudio12.3/prod/lib/amd64:$LD_LIBRARY_PATH_64
* export LD_LIBRARY_PATH=/opt/solarisstudio12.3/lib:$LD_LIBRARY_PATH
* export PATH=/opt/solarisstudio12.3/bin:/usr/sfw/bin:$PATH
* export CC="/opt/solarisstudio12.3/bin/cc"
* export CFLAGS="-xtarget=generic -m64 -xO4" 
* export CXX="CC" 
* export CXXFLAGS="-xtarget=generic -m64 -xO4" 
* export LDFLAGS="-m64" 
*
* cc -lgen -lkstat -lrt -o nicstat nicstat.c
*
*
*
* nicstat - print network traffic, Kb/s read and written. Solaris 8+.
*      "netstat -i" only gives a packet count, this program gives Kbytes.
*
* 25-Jul-2006, ver 0.92  (check for new versions, http://www.brendangregg.com)
*
* COMPILE:
*      cc -lgen -lkstat -lrt -o nicstat nicstat.c
*
* USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]]
*
*          -h              # help
*          -i interface    # track interface only
*          -s              # summary output
*          -z              # skip zero value lines
*     eg,
*        nicstat           # print summary since boot only
*        nicstat 1         # print every 1 second
*        nicstat 1 5       # print 5 times only
*        nicstat -z 1      # print every 1 second, skip zero lines
*        nicstat -i hme0 1 # print hme0 only every 1 second
*
* This prints out the Kb/s transferred for all the network cards (NICs),
*  including packet counts and average sizes. The first line is the historic
*  data since boot.
*
* FIELDS:
*              Int     Interface
*              rKb/s   read Kbytes/s
*              wKb/s   write Kbytes/s
*              rPk/s   read Packets/s
*              wPk/s   write Packets/s
*              rAvs    read Average size, bytes
*              wAvs    write Average size, bytes
*              %Util   %Utilisation (r+w/ifspeed)
*              Sat     Saturation (defer, nocanput, norecvbuf, noxmtbuf)
*
* NOTE: Some unusual network cards may not provide all the details to KStat,
*  (or provide different symbols). Check for newer versions of this program,
*  and the g_network array in the code below.
*
* Utilisation is based on bytes transferred divided by speed of the interface.
*  It should be impossible to reach 100% as there are overheads due to bus
*  negotiation and timing.
*
* Saturation is determined by counting read and write errors caused by the
*  interface running at saturation. This approach is not ideal, and the value
*  reported is often lower than it should be (eg, 0.0). Reading the rKb/s and
*  wKb/s fields may be more useful.
*
*
* SEE ALSO:
*      nicstat                                 # the Perl version
*      kstat -n hme0 [interval [count]]        # or qfe0, ...
*      netstat -iI hme0 [interval [count]]
*      se netstat.se [interval]                # SE Toolkit
*      se nx.se [interval]                     # SE Toolkit
*
* Standard Disclaimer: This is freeware, use at your own risk.
*
* COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
*
* AUTHOR: Brendan Gregg  [Sydney, Australia].
*
* HISTORY:
*      07-Jan-2005     Brendan Gregg   Created this, based on perl version
*      07-Jan-2005        "      "     added summary style (Peter Tribble)
*      03-Jun-2005     Tim.Cook@sun.com   modified "nocanput" lookup for ce
*      25-Jul-2006     Tim.Cook@sun.com   use nanosleep(3),gethrtime(3) for
*                              accurate period; use fflush(stdout)
*/
  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <strings.h>
  4. include <unistd.h>
  5. include <kstat.h>
  6. include <time.h>
  7. include <sys/time.h>
  8. include <libgen.h>
  1. define PAGESIZE 20
  2. define INTERVAL 1
  3. define LOOP_MAX 1
  4. define NIC_NAME_MAX 64
  5. define NIC_COUNT_MAX 256

/* nicdata - useful kstat NIC data */ typedef char nicname[NIC_NAME_MAX]; typedef struct nicdata {

       nicname name;                   /* name of interface */
       uint64_t rbytes;                /* total read bytes */
       uint64_t wbytes;                /* total written bytes */
       uint64_t rpackets;              /* total read packets */
       uint64_t wpackets;              /* total written packets */
       uint64_t speed;                 /* speed of interface */
       uint64_t sat;                   /* saturation value */
       time_t time;                    /* time of sample */

} nicdata;


/*

* Global variables
*/

static char *g_network[] = { "be", "bge", "ce", "ci", "dmfe", "e1000g", "el",

       "eri", "elxl", "fa", "ge", "hme", "ipge", "ipdptp", "iprb", "lane",
       "le", "nf", "ppp", "qe", "qfe", "rtls", "sppp", "vge", "nxge", "nge", NULL };

static nicdata g_idnew[NIC_COUNT_MAX]; /* Interface Data, new */ static nicdata g_idold[NIC_COUNT_MAX]; /* Interface Data, old */ static int g_interfacemax; /* number of found NICs */ static int g_style; /* output style */ static int g_skipzero; /* skip zero value lines */ static int g_someints; /* trace some interfaces only */ static int g_forever; /* run forever */ static char *g_tracked[NIC_COUNT_MAX]; /* Tracked interfaces */


/*

* die - print stderr message and exit.
*
* This subroutine prints an error message and exits with a non-zero
* exit status.
*/

static void die(char *message, int status) {

       (void) fprintf(stderr, "%s\n", message);
       exit(status);

}

/*

* usage - print a usage message and exit.
*/

static void usage(void) {

       (void) fprintf(stderr,
           "USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]]\n"
           "\n"
           "         -h              # help\n"
           "         -i interface    # track interface only\n"
           "         -s              # summary output\n"
           "         -z              # skip zero value lines\n"
           "    eg,\n");
       (void) fprintf(stderr,
           "       nicstat           # print summary since boot only\n"
           "       nicstat 1         # print every 1 second\n"
           "       nicstat 1 5       # print 5 times only\n"
           "       nicstat -z 1      # print every 1 second, skip zero lines\n"
           "       nicstat -i hme0 1 # print hme0 only every 1 second\n");
       exit(1);

}

/*

* fetch64 - return a uint64_t value from kstat.
*
* The arguments are a kstat pointer, the value name,
* and a default value in case the lookup fails.
*/

static uint64_t fetch64(kstat_t *ksp, char *value64, uint64_t def) {

       kstat_named_t *knp;     /* Kstat named pointer */
       /* try a lookup and return */
       if ((knp = kstat_data_lookup(ksp, value64)) != NULL)
               return (knp->value.ui64);
       return (def);

}

/*

* fetch32 - return a uint32_t value from kstat.
*
* The arguments are a kstat pointer, the value name,
* and a default value in case the lookup fails.
*/

static uint32_t fetch32(kstat_t *ksp, char *value, uint32_t def) {

       kstat_named_t *knp;     /* Kstat named pointer */
       /* try a lookup and return */
       if ((knp = kstat_data_lookup(ksp, value)) != NULL)
               return (knp->value.ui32);
       return (def);

}

/*

* fetch6432 - return a uint64_t or a uint32_t value from kstat.
*
* The arguments are a kstat pointer, a potential ui64 value name,
* a potential ui32 value name, and a default value in case both
* lookup fails. The ui64 value is attempted first.
*/

static uint64_t fetch6432(kstat_t *ksp, char *value64, char *value, uint64_t def) {

       kstat_named_t *knp;     /* Kstat named pointer */
       /* try lookups and return */
       if ((knp = kstat_data_lookup(ksp, value64)) != NULL)
               return (knp->value.ui64);
       if ((knp = kstat_data_lookup(ksp, value)) != NULL)
               return (knp->value.ui32);
       return (def);

}

/*

* fetch_nocanput - return nocanput value, whose name(s) are driver-dependent.
*
* Most drivers have a kstat "nocanput", but the ce driver
* at least has "rx_nocanput" and "tx_nocanput"
*/

static uint32_t fetch_nocanput(kstat_t *ksp, uint32_t def) {

       kstat_named_t *knp;     /* Kstat named pointer */
       uint32_t sum;
       /* Check "nocanput" first */
       if ((knp = kstat_data_lookup(ksp, "nocanput")) != NULL) {
               return (knp->value.ui32);
       } else {
               if ((knp = kstat_data_lookup(ksp, "rx_nocanput")) != NULL) {
                       sum = knp->value.ui32;
                       if ((knp = kstat_data_lookup(ksp, "tx_nocanput"))
                           != NULL) {
                               sum += knp->value.ui32;
                               return (sum);
                       }
               }
       }
       return (def);

}

/*

* fetch_boot_time - return the boot time in secs.
*
* This takes a kstat control pointer and looks up the boot time
* from unix:0:system_misc:boot:time. If found, this is returned,
* else 0.
*/

static time_t fetch_boot_time(kstat_ctl_t *kc) {

       kstat_t *ksp;           /* Kstat struct pointer */
       kstat_named_t *knp;     /* Kstat named pointer */
       if ((ksp = kstat_lookup(kc, "unix", 0, "system_misc")) == NULL)
               die("ERROR2: Can't read boot_time.\n", 2);
       if ((kstat_read(kc, ksp, NULL) != -1) &&
           ((knp = kstat_data_lookup(ksp, "boot_time")) != NULL)) {
               /* summary since boot */
               return (knp->value.ui32);
       } else {
               /* summary since, erm, epoch */
               return (0);
       }

}

/*

* populate_g_idnew - the master kstat function.
*
* This fetches all the network data from kstat and populates the
* global variables g_idnew and g_interfacemax. It uses a kstat control
* pointer as an argument, and the global array g_network.
*
* This function works by climbing down the kstat chains looking
* for modules that look like network interfaces. The first step is
* to check the module name against the global array g_network (the code
* for this will need maintenance as new network cards are developed);
* then a kstat variable is checked "obytes" or "obytes64" to ensure
* that this really is a network module. This approach is not ideal,
* I'd rather base the test on the kstat class == "net", however this
* data does not yet appear reliable across all interfaces.
*/

static void populate_g_idnew(kstat_ctl_t *kc) {

       kstat_t *ksp;           /* Kstat struct pointer */
       int ok, i;
       int num = 0;
       for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
               /* Search all modules */
               for (ok = 0, i = 0; g_network[i] != NULL; i++) {
                       if (strcmp(ksp->ks_module, g_network[i]) == 0)
                               ok = 1;
               }
               /* Skip if this isn't a network module */
               if (ok == 0) continue;
               if (kstat_read(kc, ksp, NULL) == -1) continue;
               if ((kstat_data_lookup(ksp, "obytes") == NULL) &&
                   (kstat_data_lookup(ksp, "obytes64") == NULL)) continue;
               /* Check for tracked interfaces */
               if (g_someints) {
                       for (ok = 0, i = 0; *g_tracked[i] != NULL; i++) {
                               if (strcmp(ksp->ks_name, g_tracked[i]) == 0)
                                       ok = 1;
                       }
                       if (ok == 0) continue;
               }
               /* Save network values */
               g_idnew[num].rbytes = fetch6432(ksp, "rbytes64", "rbytes", 0);
               g_idnew[num].wbytes = fetch6432(ksp, "obytes64", "obytes", 0);
               g_idnew[num].rpackets =
                   fetch6432(ksp, "ipackets64", "ipackets", 0);
               g_idnew[num].wpackets =
                   fetch6432(ksp, "opackets64", "opackets", 0);
               g_idnew[num].sat = fetch32(ksp, "defer", 0);
               g_idnew[num].sat += fetch_nocanput(ksp, 0);
               g_idnew[num].sat += fetch32(ksp, "norcvbuf", 0);
               g_idnew[num].sat += fetch32(ksp, "noxmtbuf", 0);
               g_idnew[num].time = time(0);
               /* if the speed can't be fetched, this makes %util 0.0 */
               g_idnew[num].speed = fetch64(ksp, "ifspeed", 1LL << 48);
               (void) strcpy(g_idnew[num].name, ksp->ks_name);
               num++;
       }
       g_interfacemax = num - 1;

}

/*

* print_header - print the header line.
*/

static void print_header(void) {

       if (g_style)
               (void) printf("%8s %5s %14s %14s\n",
                   "Time", "Int", "rKb/s", "wKb/s");
       else
               (void) printf(
                   "%8s %5s %7s %7s %7s %7s %7s %7s %7s %7s\n",
                   "Time", "Int", "rKb/s", "wKb/s", "rPk/s",
                   "wPk/s", "rAvs", "wAvs", "%Util", "Sat");

}


/*

* Main Program
*/

int main(int argc, char *argv[]) {

       /*
        * Variable Declaration
        */
       kstat_ctl_t *kc;        /* Kstat controller */
       double rbps;            /* read bytes per sec */
       double wbps;            /* write bytes per sec */
       double rkps;            /* read Kb per sec */
       double wkps;            /* write Kb per sec */
       double rpps;            /* read packets per sec */
       double wpps;            /* write packets per sec */
       double ravs;            /* read average packet size */
       double wavs;            /* write average packet size */
       double sats;            /* saturation value per sec */
       double tdiff;           /* time difference between samples */
       double util;            /* utilisation */
       struct tm *times;       /* time struct */
       char timestr[16];       /* time string */
       time_t boot_time;       /* boot time */
       int num;                /* NIC counter */
       int interval;           /* interval, secs */
       int loop_max;           /* max output lines */
       int loop;               /* current loop number */
       int line;               /* output line counter */
       int option;             /* command line switch */
       hrtime_t period_n;      /* period of each iteration in nanoseconds */
       hrtime_t start_n;       /* start point of an iteration, nsec */
       hrtime_t end_n;         /* end time of work in an iteration, nsec */
       hrtime_t pause_n;       /* time until start of next iteration, nsec */
       struct timespec pause_tv;
                               /* time until start of next iteration */
       /* defaults */
       interval = INTERVAL;
       loop_max = LOOP_MAX;
       line = PAGESIZE;
       loop = 0;
       g_style = 0;
       g_skipzero = 0;
       g_someints = 0;
       g_forever = 0;
       for (num = 0; num <= g_interfacemax; num++) {
               g_idold[num].rbytes = 0;
               g_idold[num].wbytes = 0;
               g_idold[num].rpackets = 0;
               g_idold[num].wpackets = 0;
               g_idold[num].sat = 0;
       }
       /*
        * Process arguments
        */
       while ((option = getopt(argc, argv, "hi:sz")) != -1) {
               switch (option) {
                       case 'h':
                               usage();
                               break;
                       case 'i':
                               g_someints = 1;
                               (void) bufsplit(",", 0, (char **)0);
                               (void) bufsplit(optarg, NIC_COUNT_MAX,
                                   g_tracked);
                               break;
                       case 's':
                               g_style = 1;
                               break;
                       case 'z':
                               g_skipzero = 1;
                               break;
                       default:
                               usage();
               }
       }
       argv += optind;
       if ((argc - optind) >= 1) {
               interval = atoi(*argv);
               if (interval == 0)
                       usage();
               argv++;
               if ((argc - optind) >= 2)
                       loop_max = atoi(*argv);
               else
                       g_forever = 1;
       }
       /* Open Kstat */
       if ((kc = kstat_open()) == NULL)
               die("ERROR3: Can't open /dev/kstat.\n", 3);
       /* Fetch boot time */
       boot_time = fetch_boot_time(kc);
       for (num = 0; num <= g_interfacemax; num++)
               g_idold[num].time = boot_time;
       /* Calculate the period of each iteration */
       period_n = (hrtime_t) interval * NANOSEC;
       /* Get time when we started */
       start_n = gethrtime();
       /*
        * Main Loop
        */
       for (;;)
       {
               /* Print header line */
               if (line >= PAGESIZE) {
                       line = 0;
                       print_header();
               }
               /*
                * Fetch Data
                */
               populate_g_idnew(kc);
               /* Check we matched some NICs */
               if (g_interfacemax == -1)
                       die("ERROR4: No such interface found.", 4);
               /*
                * Calculate and Print Data
                */
               for (num = 0; num <= g_interfacemax; num++) {
                       /* Calculate time difference */
                       tdiff = g_idnew[num].time - g_idold[num].time;
                       if (tdiff == 0) tdiff = 1;
                       /* Calculate per second values */
                       rbps = (g_idnew[num].rbytes - g_idold[num].rbytes)
                           / tdiff;
                       wbps = (g_idnew[num].wbytes - g_idold[num].wbytes)
                           / tdiff;
                       rpps = (g_idnew[num].rpackets - g_idold[num].rpackets)
                           / tdiff;
                       wpps = (g_idnew[num].wpackets - g_idold[num].wpackets)
                           / tdiff;
                       sats = (g_idnew[num].sat - g_idold[num].sat) / tdiff;
                       rkps = rbps / 1024;
                       wkps = wbps / 1024;
                       if (rpps > 0) ravs = rbps / rpps; else ravs = 0;
                       if (wpps > 0) wavs = wbps / wpps; else wavs = 0;
                       /* Calculate utilisation */
                       if (g_idnew[num].speed > 0) {
                               /*
                                * the following has a mysterious "800", it is
                                * 100 for the % conversion, and 8 for
                                * bytes2bits.
                                */
                               util = (rbps + wbps) * 800 / g_idnew[num].speed;
                               if (util > 100) util = 100;
                       } else
                               util = 0;
                       /* always print header if there are multiple NICs */
                       if (g_interfacemax > 0)
                               line += PAGESIZE;
                       else
                               line++;
                       /* Skip zero lines */
                       if (g_skipzero && (util == 0)) continue;
                       /* Fetch time */
                       times = localtime(&g_idnew[num].time);
                       (void) strftime(timestr, sizeof (timestr),
                           "%H:%M:%S", times);
                       /* Print output line */
                       (void) printf("%s %5s ", timestr, g_idnew[num].name);
                       if (g_style)
                               (void) printf("%14.3f %14.3f\n", rkps, wkps);
                       else
                               (void) printf(
                                   "%7.2f %7.2f %7.2f %7.2f "
                                   "%7.2f %7.2f %7.2f %7.2f\n",
                                   rkps, wkps, rpps, wpps, ravs, wavs,
                                   util, sats);
                       g_idold[num] = g_idnew[num];
               }
               /* end point */
               if (!g_forever)
                       if (++loop == loop_max) break;
               /* flush output */
               if (fflush(stdout) != 0)
                       die("ERROR5: fflush(stdout) failed\n", 5);
               /* have a kip */
               end_n = gethrtime();
               pause_n = start_n + period_n - end_n;
               if (pause_n > 0) {
                       pause_tv.tv_sec = pause_n / NANOSEC;
                       pause_tv.tv_nsec = pause_n % NANOSEC;
                       (void) nanosleep(&pause_tv, (struct timespec *) NULL);
               }
               start_n += period_n;
       }
       /*
        * Close Kstat
        */
       (void) kstat_close(kc);
       return (0);

}