266 lines
6.3 KiB
C
266 lines
6.3 KiB
C
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);
|
|
}
|