/*
* a timing utilities library
*
* Requires 64bit integers to work.
*
* %W% %@%
*
* Copyright (c) 2000 Carl Staelin.
* Copyright (c) 1994-1998 Larry McVoy.
* Distributed under the FSF GPL with
* additional restriction that results may published only if
* (1) the benchmark is unmodified, and
* (2) the version in the sccsid below is included in the report.
* Support for this development by Sun Microsystems is gratefully acknowledged.
*/
#define _LIB /* bench.h needs this */
#include "bench.h"
/* #define _DEBUG */
#define nz(x) ((x) == 0 ? 1 : (x))
/*
* I know you think these should be 2^10 and 2^20, but people are quoting
* disk sizes in powers of 10, and bandwidths are all power of ten.
* Deal with it.
*/
#define MB (1000*1000.0)
#define KB (1000.0)
static struct timeval start_tv, stop_tv;
FILE *ftiming;
static volatile uint64 use_result_dummy;
static uint64 iterations;
static void init_timing(void);
#if defined(hpux) || defined(__hpux)
#include <sys/mman.h>
#endif
#ifdef RUSAGE
#include <sys/resource.h>
#define SECS(tv) (tv.tv_sec + tv.tv_usec / 1000000.0)
#define mine(f) (int)(ru_stop.f - ru_start.f)
static struct rusage ru_start, ru_stop;
void
rusage(void)
{
double sys, user, idle;
double per;
sys = SECS(ru_stop.ru_stime) - SECS(ru_start.ru_stime);
user = SECS(ru_stop.ru_utime) - SECS(ru_start.ru_utime);
idle = timespent() - (sys + user);
per = idle / timespent() * 100;
if (!ftiming) ftiming = stderr;
fprintf(ftiming, "real=%.2f sys=%.2f user=%.2f idle=%.2f stall=%.0f%% ",
timespent(), sys, user, idle, per);
fprintf(ftiming, "rd=%d wr=%d min=%d maj=%d ctx=%d\n",
mine(ru_inblock), mine(ru_oublock),
mine(ru_minflt), mine(ru_majflt),
mine(ru_nvcsw) + mine(ru_nivcsw));
}
#endif /* RUSAGE */
void
lmbench_usage(int argc, char *argv[], char* usage)
{
fprintf(stderr,"Usage: %s %s", argv[0], usage);
exit(-1);
}
void
sigchld_wait_handler(int signo)
{
wait(0);
signal(SIGCHLD, sigchld_wait_handler);
}
static int benchmp_sigterm_received;
static int benchmp_sigchld_received;
static pid_t benchmp_sigalrm_pid;
static int benchmp_sigalrm_timeout;
void (*benchmp_sigterm_handler)(int);
void (*benchmp_sigchld_handler)(int);
void (*benchmp_sigalrm_handler)(int);
void
benchmp_sigterm(int signo)
{
benchmp_sigterm_received = 1;
}
void
benchmp_sigchld(int signo)
{
signal(SIGCHLD, SIG_DFL);
benchmp_sigchld_received = 1;
#ifdef _DEBUG
fprintf(stderr, "benchmp_sigchld handler\n");
#endif
}
void
benchmp_sigalrm(int signo)
{
signal(SIGALRM, SIG_IGN);
kill(benchmp_sigalrm_pid, SIGTERM);
/*
* Since we already waited a full timeout period for the child
* to die, we only need to wait a little longer for subsequent
* children to die.
*/
benchmp_sigalrm_timeout = 1;
}
void
benchmp_child(benchmp_f initialize,
benchmp_f benchmark,
benchmp_f cleanup,
int childid,
int response,
int start_signal,
int result_signal,
int exit_signal,
int parallel,
iter_t iterations,
int repetitions,
int enough,
void* cookie
);
void
benchmp_parent(int response,
int start_signal,
int result_signal,
int exit_signal,
pid_t* pids,
int parallel,
iter_t iterations,
int warmup,
int repetitions,
int enough
);
int
sizeof_result(int repetitions);
void
benchmp(benchmp_f initialize,
benchmp_f benchmark,
benchmp_f cleanup,
int enough,
int parallel,
int warmup,
int repetitions,
void* cookie)
{
iter_t iterations = 1;
double result = 0.;
double usecs;
long i, j;
pid_t pid;
pid_t *pids = NULL;
int response[2];
int start_signal[2];
int result_signal[2];
int exit_signal[2];
int need_warmup;
fd_set fds;
struct timeval timeout;
#ifdef _DEBUG
fprintf(stderr, "benchmp(%p, %p, %p, %d, %d, %d, %d, %p): entering\n", initialize, benchmark, cleanup, enough, parallel, warmup, repetitions, cookie);
#endif
enough = get_enough(enough);
#ifdef _DEBUG
fprintf(stderr, "\tenough=%d\n", enough);
#endif
/* initialize results */
settime(0);
save_n(1);
if (parallel > 1) {
/* Compute the baseline performance */
benchmp(initialize, benchmark, cleanup,
enough, 1, warmup, repetitions, cookie);
/* if we can't even do a single job, then give up */
if (gettime() == 0)
return;
/* calculate iterations for 1sec runtime */
iterations = get_n();
if (enough < SHORT) {
double tmp = (double)SHORT * (double)get_n();
tmp /= (double)gettime();
iterations = (iter_t)tmp + 1;
}
settime(0);
save_n(1);
}
/* Create the necessary pipes for control */
if (pipe(response) < 0
|| pipe(start_signal) < 0
|| pipe(result_signal) < 0
|| pipe(exit_signal) < 0) {
#ifdef _DEBUG
fprintf(stderr, "BENCHMP: Could not create control pipes\n");
#endif /* _DEBUG */
return;
}
/* fork the necessary children */
benchmp_sigchld_received = 0;
benchmp_sigterm_received = 0;
benchmp_sigterm_handler = signal(SIGTERM, benchmp_sigterm);
benchmp_sigchld_handler = signal(SIGCHLD, benchmp_sigchld);
pids = (pid_t*)malloc(parallel * sizeof(pid_t));
if (!pids) return;
bzero((void*)pids, parallel * sizeof(pid_t));
for (i = 0; i < parallel; ++i) {
if (benchmp_sigterm_received)
goto error_exit;
#ifdef _DEBUG
fprintf(stderr, "benchmp(%p, %p, %p, %d, %d, %d, %d, %p): creating child %d\n", initialize, benchmark, cleanup, enough, parallel, warmup, repetitions, cookie, i);
#endif
switch(pids[i] = fork()) {
case -1:
/* could not open enough children! */
#ifdef _DEBUG
fprintf(stderr, "BENCHMP: fork() failed!\n");
#endif /* _DEBUG */
goto error_exit;
case 0:
/* If child */
close(response[0]);
close(start_signal[1]);
close(result_signal[1]);
close(exit_signal[1]);
handle_scheduler(i, 0, 0);
benchmp_child(initialize,
benchmark,
cleanup,
i,
response[1],
start_signal[0],
result_signal[0],
exit_signal[0],
enough,
iterations,
parallel,
repetitions,
cookie
);
exit(0);
default:
break;
}
}
close(response[1]);
close(start_signal[0]);
close(result_signal[0]);
close(exit_signal[0]);
benchmp_parent(response[0],
start_signal[1],
result_signal[1],
exit_signal[1],
pids,
parallel,
iterations,
warmup,
repetitions,
enough
);
goto cleanup_exit;
error_exit:
/* give the children a chance to clean up gracefully */
signal(SIGCHLD, SIG_DFL);
while (--i >= 0) {
kill(pids[i], SIGTERM);
waitpid(pids[i], NULL, 0);
}
cleanup_exit:
/*
* Clean up and kill all children
*
* NOTE: the children themselves SHOULD exit, and
* Killing them could prevent them from
* cleanup up subprocesses, etc... So, we only
* want to kill child processes when it appears
* that they will not die of their own accord.
* We wait twice the timing interval plus two seconds
* for children to die. If they haven't died by
* that time, then we start killing them.
*/
benchmp_sigalrm_timeout = (int)((2 * enough)/1000000) + 2;
if (benchmp_sigalrm_timeout < 5)
benchmp_sigalrm_timeout = 5;
signal(SIGCHLD, SIG_DFL);
while (i-- > 0) {
/* wait timeout seconds for child to die, then kill it */
benchmp_sigalrm_pid = pids[i];
benchmp_sigalrm_handler = signal(SIGALRM, benchmp_sigalrm);
alarm(benchmp_sigalrm_timeout);
waitpid(pids[i], NULL, 0);
alarm(0);
signal(SIGALRM, benchmp_sigalrm_handler);
}
if (pids) free(pids);
#ifdef _DEBUG
fprintf(stderr, "benchmp(0x%x, 0x%x, 0x%x, %d, %d, 0x%x): exiting\n", (unsigned int)initialize, (unsigned int)benchmark, (unsigned int)cleanup, enough, parallel, (unsigned int)cookie);
#endif
}
void
benchmp_parent( int response,
int start_signal,
int result_signal,
int exit_signal,
pid_t* pids,
int parallel,