Home | History | Annotate | Download | only in netperf
      1 char   netcpu_procstat_id[]="\
      2 @(#)netcpu_procstat.c (c) Copyright 2005-2007 Version 2.4.3";
      3 
      4 /* netcpu_procstat.c
      5 
      6    Implement the /proc/stat specific portions of netperf CPU
      7    utilization measurements. These are broken-out into a separate file
      8    to make life much nicer over in netlib.c which had become a maze of
      9    twisty, CPU-util-related, #ifdefs, all different.  raj 2005-01-26
     10    */
     11 
     12 #ifdef HAVE_CONFIG_H
     13 #include <config.h>
     14 #endif
     15 
     16 #include <stdio.h>
     17 
     18 #ifdef HAVE_FCNTL_H
     19 # include <fcntl.h>
     20 #endif
     21 #if HAVE_UNISTD_H
     22 # include <unistd.h>
     23 #endif
     24 #if STDC_HEADERS
     25 # include <stdlib.h>
     26 # include <stddef.h>
     27 #else
     28 # if HAVE_STDLIB_H
     29 #  include <stdlib.h>
     30 # endif
     31 #endif
     32 
     33 #include <string.h>
     34 
     35 #include "netsh.h"
     36 #include "netlib.h"
     37 
     38 /* the lib_start_count and lib_end_count arrays hold the starting
     39    and ending values of whatever is counting when the system is
     40    idle. The rate at which this increments during a test is compared
     41    with a previous calibrarion to arrive at a CPU utilization
     42    percentage. raj 2005-01-26 */
     43 static uint64_t  lib_start_count[MAXCPUS];
     44 static uint64_t  lib_end_count[MAXCPUS];
     45 
     46 
     47 /* The max. length of one line of /proc/stat cpu output */
     48 #define CPU_LINE_LENGTH ((8 * sizeof (long) / 3 + 1) * 4 + 8)
     49 #define PROC_STAT_FILE_NAME "/proc/stat"
     50 #define N_CPU_LINES(nr) (nr == 1 ? 1 : 1 + nr)
     51 
     52 static int proc_stat_fd = -1;
     53 static char *proc_stat_buf = NULL;
     54 static int proc_stat_buflen = 0;
     55 
     56 void
     57 cpu_util_init(void)
     58 {
     59 
     60   if (debug) {
     61     fprintf(where,
     62 	    "cpu_util_init enter, proc_stat_fd %d proc_stat_buf %p\n",
     63 	    proc_stat_fd,
     64 	    proc_stat_buf);
     65     fflush(where);
     66   }
     67   if (proc_stat_fd < 0) {
     68     proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL);
     69     if (proc_stat_fd < 0) {
     70       fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME);
     71       exit (1);
     72     };
     73   };
     74 
     75   if (!proc_stat_buf) {
     76     proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH;
     77     if (debug) {
     78       fprintf(where,
     79 	      "lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n",
     80 	      lib_num_loc_cpus,
     81 	      N_CPU_LINES(lib_num_loc_cpus),
     82 	      CPU_LINE_LENGTH,
     83 	      proc_stat_buflen);
     84       fflush(where);
     85     }
     86     proc_stat_buf = (char *)malloc (proc_stat_buflen);
     87     if (!proc_stat_buf) {
     88       fprintf (stderr, "Cannot allocate buffer memory!\n");
     89       exit (1);
     90     }
     91   }
     92   return;
     93 }
     94 
     95 void
     96 cpu_util_terminate(void)
     97 {
     98   close(proc_stat_fd);
     99   proc_stat_fd = -1;
    100   free(proc_stat_buf);
    101   proc_stat_buf = NULL;
    102   return;
    103 }
    104 
    105 int
    106 get_cpu_method()
    107 {
    108   return PROC_STAT;
    109 }
    110 
    111 float
    112 calibrate_idle_rate (int iterations, int interval)
    113 {
    114   if (proc_stat_fd < 0) {
    115     proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL);
    116     if (proc_stat_fd < 0) {
    117       fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME);
    118       exit (1);
    119     };
    120   };
    121 
    122   if (!proc_stat_buf) {
    123     proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH;
    124     if (debug) {
    125       fprintf(where,
    126 	      "calibrate: lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n",
    127 	      lib_num_loc_cpus,
    128 	      N_CPU_LINES(lib_num_loc_cpus),
    129 	      CPU_LINE_LENGTH,
    130 	      proc_stat_buflen);
    131       fflush(where);
    132     }
    133     proc_stat_buf = (char *)malloc (proc_stat_buflen);
    134     if (!proc_stat_buf) {
    135       fprintf (stderr, "Cannot allocate buffer memory!\n");
    136       exit (1);
    137     };
    138   };
    139 
    140   return sysconf (_SC_CLK_TCK);
    141 }
    142 
    143 void
    144 get_cpu_idle (uint64_t *res)
    145 {
    146   int space;
    147   int i;
    148   int n = lib_num_loc_cpus;
    149   char *p = proc_stat_buf;
    150 
    151   lseek (proc_stat_fd, 0, SEEK_SET);
    152   read (proc_stat_fd, p, proc_stat_buflen);
    153 
    154   if (debug) {
    155     fprintf(where,"proc_stat_buf '%.*s'\n",proc_stat_buflen,p);
    156     fflush(where);
    157   }
    158   /* Skip first line (total) on SMP */
    159   if (n > 1) p = strchr (p, '\n');
    160 
    161   /* Idle time is the 4th space-separated token */
    162   for (i = 0; i < n; i++) {
    163     for (space = 0; space < 4; space ++) {
    164       p = strchr (p, ' ');
    165       while (*++p == ' ');
    166     };
    167     res[i] = strtoul (p, &p, 10);
    168     if (debug) {
    169       fprintf(where,"res[%d] is %llu\n",i,res[i]);
    170       fflush(where);
    171     }
    172     p = strchr (p, '\n');
    173   };
    174 
    175 }
    176 
    177 /* take the initial timestamp and start collecting CPU utilization if
    178    requested */
    179 
    180 void
    181 measure_cpu_start()
    182 {
    183   cpu_method = PROC_STAT;
    184   get_cpu_idle(lib_start_count);
    185 }
    186 
    187 /* collect final CPU utilization raw data */
    188 void
    189 measure_cpu_stop()
    190 {
    191   get_cpu_idle(lib_end_count);
    192 }
    193 
    194 float
    195 calc_cpu_util_internal(float elapsed_time)
    196 {
    197   int i;
    198 
    199   float actual_rate;
    200   float correction_factor;
    201 
    202   lib_local_cpu_util = (float)0.0;
    203   /* It is possible that the library measured a time other than */
    204   /* the one that the user want for the cpu utilization */
    205   /* calculations - for example, tests that were ended by */
    206   /* watchdog timers such as the udp stream test. We let these */
    207   /* tests tell up what the elapsed time should be. */
    208 
    209   if (elapsed_time != 0.0) {
    210     correction_factor = (float) 1.0 +
    211       ((lib_elapsed - elapsed_time) / elapsed_time);
    212   }
    213   else {
    214     correction_factor = (float) 1.0;
    215   }
    216 
    217   for (i = 0; i < lib_num_loc_cpus; i++) {
    218 
    219     /* it would appear that on some systems, in loopback, nice is
    220      *very* effective, causing the looper process to stop dead in its
    221      tracks. if this happens, we need to ensure that the calculation
    222      does not go south. raj 6/95 and if we run completely out of idle,
    223      the same thing could in theory happen to the USE_KSTAT path. raj
    224      8/2000 */
    225 
    226     if (lib_end_count[i] == lib_start_count[i]) {
    227       lib_end_count[i]++;
    228     }
    229 
    230     actual_rate = (lib_end_count[i] > lib_start_count[i]) ?
    231       (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed :
    232       (float)(lib_end_count[i] - lib_start_count[i] +
    233 	      MAXLONG)/ lib_elapsed;
    234     lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) /
    235       lib_local_maxrate * 100;
    236     if (debug) {
    237       fprintf(where,
    238               "calc_cpu_util: actual_rate on processor %d is %f start %llx end %llx util %f\n",
    239               i,
    240               actual_rate,
    241               lib_start_count[i],
    242               lib_end_count[i],
    243 	      lib_local_per_cpu_util[i]);
    244     }
    245     lib_local_cpu_util += lib_local_per_cpu_util[i];
    246   }
    247   /* we want the average across all n processors */
    248   lib_local_cpu_util /= (float)lib_num_loc_cpus;
    249 
    250   lib_local_cpu_util *= correction_factor;
    251   return lib_local_cpu_util;
    252 }
    253 
    254 void
    255 cpu_start_internal(void)
    256 {
    257   get_cpu_idle(lib_start_count);
    258   return;
    259 }
    260 
    261 void
    262 cpu_stop_internal(void)
    263 {
    264   get_cpu_idle(lib_end_count);
    265 }
    266