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