1char 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 */ 43static uint64_t lib_start_count[MAXCPUS]; 44static 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 52static int proc_stat_fd = -1; 53static char *proc_stat_buf = NULL; 54static int proc_stat_buflen = 0; 55 56void 57cpu_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 95void 96cpu_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 105int 106get_cpu_method() 107{ 108 return PROC_STAT; 109} 110 111float 112calibrate_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 143void 144get_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 180void 181measure_cpu_start() 182{ 183 cpu_method = PROC_STAT; 184 get_cpu_idle(lib_start_count); 185} 186 187/* collect final CPU utilization raw data */ 188void 189measure_cpu_stop() 190{ 191 get_cpu_idle(lib_end_count); 192} 193 194float 195calc_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 254void 255cpu_start_internal(void) 256{ 257 get_cpu_idle(lib_start_count); 258 return; 259} 260 261void 262cpu_stop_internal(void) 263{ 264 get_cpu_idle(lib_end_count); 265} 266