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 <config.h>
#endif
#include <stdio.h>
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# if HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#include <string.h>
#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);
}