char netcpu_procstat_id[]="\ @(#)netcpu_procstat.c (c) Copyright 2005-2007 Version 2.4.3"; /* netcpu_procstat.c Implement the /proc/stat specific portions of netperf CPU utilization measurements. These are broken-out into a separate file to make life much nicer over in netlib.c which had become a maze of twisty, CPU-util-related, #ifdefs, all different. raj 2005-01-26 */ #ifdef HAVE_CONFIG_H #include #endif #include #ifdef HAVE_FCNTL_H # include #endif #if HAVE_UNISTD_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #include #include "netsh.h" #include "netlib.h" /* the lib_start_count and lib_end_count arrays hold the starting and ending values of whatever is counting when the system is idle. The rate at which this increments during a test is compared with a previous calibrarion to arrive at a CPU utilization percentage. raj 2005-01-26 */ static uint64_t lib_start_count[MAXCPUS]; static uint64_t lib_end_count[MAXCPUS]; /* The max. length of one line of /proc/stat cpu output */ #define CPU_LINE_LENGTH ((8 * sizeof (long) / 3 + 1) * 4 + 8) #define PROC_STAT_FILE_NAME "/proc/stat" #define N_CPU_LINES(nr) (nr == 1 ? 1 : 1 + nr) static int proc_stat_fd = -1; static char *proc_stat_buf = NULL; static int proc_stat_buflen = 0; void cpu_util_init(void) { if (debug) { fprintf(where, "cpu_util_init enter, proc_stat_fd %d proc_stat_buf %p\n", proc_stat_fd, proc_stat_buf); fflush(where); } if (proc_stat_fd < 0) { proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL); if (proc_stat_fd < 0) { fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME); exit (1); }; }; if (!proc_stat_buf) { proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH; if (debug) { fprintf(where, "lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n", lib_num_loc_cpus, N_CPU_LINES(lib_num_loc_cpus), CPU_LINE_LENGTH, proc_stat_buflen); fflush(where); } proc_stat_buf = (char *)malloc (proc_stat_buflen); if (!proc_stat_buf) { fprintf (stderr, "Cannot allocate buffer memory!\n"); exit (1); } } return; } void cpu_util_terminate(void) { close(proc_stat_fd); proc_stat_fd = -1; free(proc_stat_buf); proc_stat_buf = NULL; return; } int get_cpu_method() { return PROC_STAT; } float calibrate_idle_rate (int iterations, int interval) { if (proc_stat_fd < 0) { proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL); if (proc_stat_fd < 0) { fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME); exit (1); }; }; if (!proc_stat_buf) { proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH; if (debug) { fprintf(where, "calibrate: lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n", lib_num_loc_cpus, N_CPU_LINES(lib_num_loc_cpus), CPU_LINE_LENGTH, proc_stat_buflen); fflush(where); } proc_stat_buf = (char *)malloc (proc_stat_buflen); if (!proc_stat_buf) { fprintf (stderr, "Cannot allocate buffer memory!\n"); exit (1); }; }; return sysconf (_SC_CLK_TCK); } void get_cpu_idle (uint64_t *res) { int space; int i; int n = lib_num_loc_cpus; char *p = proc_stat_buf; lseek (proc_stat_fd, 0, SEEK_SET); read (proc_stat_fd, p, proc_stat_buflen); if (debug) { fprintf(where,"proc_stat_buf '%.*s'\n",proc_stat_buflen,p); fflush(where); } /* Skip first line (total) on SMP */ if (n > 1) p = strchr (p, '\n'); /* Idle time is the 4th space-separated token */ for (i = 0; i < n; i++) { for (space = 0; space < 4; space ++) { p = strchr (p, ' '); while (*++p == ' '); }; res[i] = strtoul (p, &p, 10); if (debug) { fprintf(where,"res[%d] is %llu\n",i,res[i]); fflush(where); } p = strchr (p, '\n'); }; } /* take the initial timestamp and start collecting CPU utilization if requested */ void measure_cpu_start() { cpu_method = PROC_STAT; get_cpu_idle(lib_start_count); } /* collect final CPU utilization raw data */ void measure_cpu_stop() { get_cpu_idle(lib_end_count); } float calc_cpu_util_internal(float elapsed_time) { int i; float actual_rate; float correction_factor; lib_local_cpu_util = (float)0.0; /* It is possible that the library measured a time other than */ /* the one that the user want for the cpu utilization */ /* calculations - for example, tests that were ended by */ /* watchdog timers such as the udp stream test. We let these */ /* tests tell up what the elapsed time should be. */ if (elapsed_time != 0.0) { correction_factor = (float) 1.0 + ((lib_elapsed - elapsed_time) / elapsed_time); } else { correction_factor = (float) 1.0; } for (i = 0; i < lib_num_loc_cpus; i++) { /* it would appear that on some systems, in loopback, nice is *very* effective, causing the looper process to stop dead in its tracks. if this happens, we need to ensure that the calculation does not go south. raj 6/95 and if we run completely out of idle, the same thing could in theory happen to the USE_KSTAT path. raj 8/2000 */ if (lib_end_count[i] == lib_start_count[i]) { lib_end_count[i]++; } actual_rate = (lib_end_count[i] > lib_start_count[i]) ? (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed : (float)(lib_end_count[i] - lib_start_count[i] + MAXLONG)/ lib_elapsed; lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / lib_local_maxrate * 100; if (debug) { fprintf(where, "calc_cpu_util: actual_rate on processor %d is %f start %llx end %llx util %f\n", i, actual_rate, lib_start_count[i], lib_end_count[i], lib_local_per_cpu_util[i]); } lib_local_cpu_util += lib_local_per_cpu_util[i]; } /* we want the average across all n processors */ lib_local_cpu_util /= (float)lib_num_loc_cpus; lib_local_cpu_util *= correction_factor; return lib_local_cpu_util; } void cpu_start_internal(void) { get_cpu_idle(lib_start_count); return; } void cpu_stop_internal(void) { get_cpu_idle(lib_end_count); }