1char netcpu_procstat_id[]="\ 2@(#)netcpu_procstat.c (c) Copyright 2005-2012 Version 2.6.0"; 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 44#define IDLE_IDX 4 45#define CPU_STATES 10 46 47typedef struct cpu_states 48{ 49 uint64_t user; 50 uint64_t nice; 51 uint64_t sys; 52 uint64_t idle; 53 uint64_t iowait; 54 uint64_t hard_irq; 55 uint64_t soft_irq; 56 uint64_t steal; 57 uint64_t guest; 58 uint64_t guest_nice; 59} cpu_states_t; 60 61static cpu_states_t lib_start_count[MAXCPUS]; 62static cpu_states_t lib_end_count[MAXCPUS]; 63 64 65/* The max. length of one line of /proc/stat cpu output */ 66#define CPU_LINE_LENGTH (int)((CPU_STATES * sizeof (long) / 3 + 1) * 4 + 8) 67#define PROC_STAT_FILE_NAME "/proc/stat" 68#define N_CPU_LINES(nr) (nr == 1 ? 1 : 1 + nr) 69 70static int proc_stat_fd = -1; 71static char *proc_stat_buf = NULL; 72static int proc_stat_buflen = 0; 73 74void 75cpu_util_init(void) 76{ 77 78 if (debug) { 79 fprintf(where, 80 "cpu_util_init enter, proc_stat_fd %d proc_stat_buf %p\n", 81 proc_stat_fd, 82 proc_stat_buf); 83 fflush(where); 84 } 85 if (proc_stat_fd < 0) { 86 proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL); 87 if (proc_stat_fd < 0) { 88 fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME); 89 exit (1); 90 }; 91 }; 92 93 if (!proc_stat_buf) { 94 proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH; 95 if (debug) { 96 fprintf(where, 97 "lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n", 98 lib_num_loc_cpus, 99 N_CPU_LINES(lib_num_loc_cpus), 100 CPU_LINE_LENGTH, 101 proc_stat_buflen); 102 fflush(where); 103 } 104 proc_stat_buf = (char *)malloc (proc_stat_buflen); 105 if (!proc_stat_buf) { 106 fprintf (stderr, "Cannot allocate buffer memory!\n"); 107 exit (1); 108 } 109 } 110 return; 111} 112 113void 114cpu_util_terminate(void) 115{ 116 close(proc_stat_fd); 117 proc_stat_fd = -1; 118 free(proc_stat_buf); 119 proc_stat_buf = NULL; 120 return; 121} 122 123int 124get_cpu_method() 125{ 126 return PROC_STAT; 127} 128 129float 130calibrate_idle_rate (int iterations, int interval) 131{ 132 if (proc_stat_fd < 0) { 133 proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL); 134 if (proc_stat_fd < 0) { 135 fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME); 136 exit (1); 137 }; 138 }; 139 140 if (!proc_stat_buf) { 141 proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH; 142 if (debug) { 143 fprintf(where, 144 "calibrate: lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n", 145 lib_num_loc_cpus, 146 N_CPU_LINES(lib_num_loc_cpus), 147 CPU_LINE_LENGTH, 148 proc_stat_buflen); 149 fflush(where); 150 } 151 proc_stat_buf = (char *)malloc (proc_stat_buflen); 152 if (!proc_stat_buf) { 153 fprintf (stderr, "Cannot allocate buffer memory!\n"); 154 exit (1); 155 }; 156 }; 157 158 return sysconf (_SC_CLK_TCK); 159} 160 161static void 162get_cpu (cpu_states_t *res) 163{ 164 int i; 165 int n = lib_num_loc_cpus; 166 char *p = proc_stat_buf; 167 168 lseek (proc_stat_fd, 0, SEEK_SET); 169 read (proc_stat_fd, p, proc_stat_buflen); 170 171 if (debug) { 172 fprintf(where,"proc_stat_buf '%.*s'\n",proc_stat_buflen,p); 173 fflush(where); 174 } 175 /* Skip first line (total) on SMP */ 176 if (n > 1) p = strchr (p, '\n'); 177 178 for (i = 0; i < n; i++) { 179 memset(&res[i], 0, sizeof (res[i])); 180 p = strchr (p, ' '); 181 sscanf(p, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", 182 (unsigned long long *)&res[i].user, 183 (unsigned long long *)&res[i].nice, 184 (unsigned long long *)&res[i].sys, 185 (unsigned long long *)&res[i].idle, 186 (unsigned long long *)&res[i].iowait, 187 (unsigned long long *)&res[i].hard_irq, 188 (unsigned long long *)&res[i].soft_irq, 189 (unsigned long long *)&res[i].steal, 190 (unsigned long long *)&res[i].guest, 191 (unsigned long long *)&res[i].guest_nice); 192 if (debug) { 193 fprintf(where, 194 "res[%d] is %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", 195 i, 196 (unsigned long long)res[i].user, 197 (unsigned long long)res[i].nice, 198 (unsigned long long)res[i].sys, 199 (unsigned long long)res[i].idle, 200 (unsigned long long)res[i].iowait, 201 (unsigned long long)res[i].hard_irq, 202 (unsigned long long)res[i].soft_irq, 203 (unsigned long long)res[i].steal, 204 (unsigned long long)res[i].guest, 205 (unsigned long long)res[i].guest_nice); 206 fflush(where); 207 } 208 p = strchr (p, '\n'); 209 }; 210 211} 212 213/* take the initial timestamp and start collecting CPU utilization if 214 requested */ 215 216void 217measure_cpu_start() 218{ 219 cpu_method = PROC_STAT; 220 get_cpu(lib_start_count); 221} 222 223/* collect final CPU utilization raw data */ 224void 225measure_cpu_stop() 226{ 227 get_cpu(lib_end_count); 228} 229 230static uint64_t 231tick_subtract(uint64_t start, uint64_t end) 232{ 233 if (end >= start || (start & 0xffffffff00000000ULL)) 234 return (end - start); 235 236 /* 237 * We wrapped, and it is likely that the kernel is suppling 32-bit 238 * counters, because "start" is less than 32-bits wide. If that's 239 * the case, then handle the wrap by subtracting off everything but 240 * the lower 32-bits so as to get back to unsigned 32-bit 241 * arithmetic. 242 */ 243 return (end - start + 0xffffffff00000000ULL); 244} 245 246float 247calc_cpu_util_internal(float elapsed_time) 248{ 249 int i; 250 251 float correction_factor; 252 cpu_states_t diff; 253 uint64_t total_ticks; 254 255 memset(&lib_local_cpu_stats, 0, sizeof(lib_local_cpu_stats)); 256 257 /* It is possible that the library measured a time other than the 258 one that the user want for the cpu utilization calculations - for 259 example, tests that were ended by watchdog timers such as the udp 260 stream test. We let these tests tell up what the elapsed time 261 should be. */ 262 263 if (elapsed_time != 0.0) { 264 correction_factor = (float) 1.0 + 265 ((lib_elapsed - elapsed_time) / elapsed_time); 266 } 267 else { 268 correction_factor = (float) 1.0; 269 } 270 271 if (debug) { 272 fprintf(where, 273 "lib_local_maxrate = %f\n", lib_local_maxrate); 274 } 275 for (i = 0; i < lib_num_loc_cpus; i++) { 276 277 /* Find the difference in all CPU stat fields */ 278 diff.user = 279 tick_subtract(lib_start_count[i].user, lib_end_count[i].user); 280 diff.nice = 281 tick_subtract(lib_start_count[i].nice, lib_end_count[i].nice); 282 diff.sys = 283 tick_subtract(lib_start_count[i].sys, lib_end_count[i].sys); 284 diff.idle = 285 tick_subtract(lib_start_count[i].idle, lib_end_count[i].idle); 286 diff.iowait = 287 tick_subtract(lib_start_count[i].iowait, lib_end_count[i].iowait); 288 diff.hard_irq = 289 tick_subtract(lib_start_count[i].hard_irq, lib_end_count[i].hard_irq); 290 diff.soft_irq = 291 tick_subtract(lib_start_count[i].soft_irq, lib_end_count[i].soft_irq); 292 diff.steal = 293 tick_subtract(lib_start_count[i].steal, lib_end_count[i].steal); 294 diff.guest = 295 tick_subtract(lib_start_count[i].guest, lib_end_count[i].guest); 296 diff.guest_nice = 297 tick_subtract(lib_start_count[i].guest_nice, lib_end_count[i].guest_nice); 298 total_ticks = diff.user + diff.nice + diff.sys + diff.idle + diff.iowait 299 + diff.hard_irq + diff.soft_irq + diff.steal 300 + diff.guest + diff.guest_nice; 301 302 /* calculate idle time as a percentage of all CPU states */ 303 if (total_ticks == 0) { 304 if (debug) { 305 fprintf(where, "Total ticks 0 on CPU %d, charging nothing!\n", i); 306 } 307 lib_local_per_cpu_util[i] = 0.0; 308 } else { 309#define CPU_STAT_PERCENTIZE(x) (100. * (((float)(x)) / ((float)(total_ticks)))) 310 /* utilization = 100% - %idle */ 311 lib_local_per_cpu_util[i] = 100. - CPU_STAT_PERCENTIZE(diff.idle); 312 lib_local_cpu_stats.cpu_util += lib_local_per_cpu_util[i]; 313 lib_local_cpu_stats.cpu_user += CPU_STAT_PERCENTIZE(diff.user); 314 lib_local_cpu_stats.cpu_system += CPU_STAT_PERCENTIZE(diff.sys); 315 lib_local_cpu_stats.cpu_iowait += CPU_STAT_PERCENTIZE(diff.iowait); 316 lib_local_cpu_stats.cpu_irq += CPU_STAT_PERCENTIZE(diff.hard_irq); 317 lib_local_cpu_stats.cpu_swintr += CPU_STAT_PERCENTIZE(diff.soft_irq); 318 } 319 /* apply correction factor */ 320 lib_local_per_cpu_util[i] *= correction_factor; 321 if (debug) { 322 fprintf(where, 323 "calc_cpu_util: util on processor %d, diff = %llu %llu %llu " 324 "%llu %llu %llu %llu %llu %llu util %f cf %f\n", 325 i, 326 (unsigned long long)diff.user, 327 (unsigned long long)diff.nice, 328 (unsigned long long)diff.sys, 329 (unsigned long long)diff.idle, 330 (unsigned long long)diff.iowait, 331 (unsigned long long)diff.hard_irq, 332 (unsigned long long)diff.soft_irq, 333 (unsigned long long)diff.steal, 334 (unsigned long long)diff.guest, 335 lib_local_per_cpu_util[i], 336 correction_factor); 337 } 338 } 339 340 /* we want to apply correction factor and average across all n processors */ 341#define CPU_STAT_FIXUP(fldname) \ 342 lib_local_cpu_stats.fldname = ((correction_factor \ 343 * lib_local_cpu_stats.fldname) \ 344 / ((float)lib_num_loc_cpus)) 345 346 CPU_STAT_FIXUP(cpu_util); 347 CPU_STAT_FIXUP(cpu_user); 348 CPU_STAT_FIXUP(cpu_system); 349 CPU_STAT_FIXUP(cpu_iowait); 350 CPU_STAT_FIXUP(cpu_irq); 351 CPU_STAT_FIXUP(cpu_swintr); 352 353 return lib_local_cpu_stats.cpu_util; 354} 355 356void 357cpu_start_internal(void) 358{ 359 get_cpu(lib_start_count); 360 return; 361} 362 363void 364cpu_stop_internal(void) 365{ 366 get_cpu(lib_end_count); 367} 368