4162 lines
114 KiB
C
4162 lines
114 KiB
C
char netlib_id[]="\
|
||
@(#)netlib.c (c) Copyright 1993-2007 Hewlett-Packard Company. Version 2.4.3";
|
||
|
||
|
||
/****************************************************************/
|
||
/* */
|
||
/* netlib.c */
|
||
/* */
|
||
/* the common utility routines available to all... */
|
||
/* */
|
||
/* establish_control() establish the control socket */
|
||
/* calibrate_local_cpu() do local cpu calibration */
|
||
/* calibrate_remote_cpu() do remote cpu calibration */
|
||
/* send_request() send a request to the remote */
|
||
/* recv_response() receive a response from remote */
|
||
/* send_response() send a response to the remote */
|
||
/* recv_request() recv a request from the remote */
|
||
/* dump_request() dump request contents */
|
||
/* dump_response() dump response contents */
|
||
/* cpu_start() start measuring cpu */
|
||
/* cpu_stop() stop measuring cpu */
|
||
/* calc_cpu_util() calculate the cpu utilization */
|
||
/* calc_service_demand() calculate the service demand */
|
||
/* calc_thruput() calulate the tput in units */
|
||
/* calibrate() really calibrate local cpu */
|
||
/* identify_local() print local host information */
|
||
/* identify_remote() print remote host information */
|
||
/* format_number() format the number (KB, MB,etc) */
|
||
/* format_units() return the format in english */
|
||
/* msec_sleep() sleep for some msecs */
|
||
/* start_timer() start a timer */
|
||
/* */
|
||
/* the routines you get when WANT_DLPI is defined... */
|
||
/* */
|
||
/* dl_open() open a file descriptor and */
|
||
/* attach to the card */
|
||
/* dl_mtu() find the MTU of the card */
|
||
/* dl_bind() bind the sap do the card */
|
||
/* dl_connect() sender's have of connect */
|
||
/* dl_accpet() receiver's half of connect */
|
||
/* dl_set_window() set the window size */
|
||
/* dl_stats() retrieve statistics */
|
||
/* dl_send_disc() initiate disconnect (sender) */
|
||
/* dl_recv_disc() accept disconnect (receiver) */
|
||
/****************************************************************/
|
||
|
||
/****************************************************************/
|
||
/* */
|
||
/* Global include files */
|
||
/* */
|
||
/****************************************************************/
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
#include <config.h>
|
||
#endif
|
||
|
||
/* It would seem that most of the includes being done here from */
|
||
/* "sys/" actually have higher-level wrappers at just /usr/include. */
|
||
/* This is based on a spot-check of a couple systems at my disposal. */
|
||
/* If you have trouble compiling you may want to add "sys/" raj 10/95 */
|
||
#include <limits.h>
|
||
#include <signal.h>
|
||
#ifdef MPE
|
||
# define NSIG _NSIG
|
||
#endif /* MPE */
|
||
#include <sys/types.h>
|
||
#include <fcntl.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <math.h>
|
||
#include <string.h>
|
||
#include <assert.h>
|
||
#ifdef HAVE_ENDIAN_H
|
||
#include <endian.h>
|
||
#endif
|
||
|
||
|
||
#ifndef WIN32
|
||
/* at some point, I would like to get rid of all these "sys/" */
|
||
/* includes where appropriate. if you have a system that requires */
|
||
/* them, speak now, or your system may not comile later revisions of */
|
||
/* netperf. raj 1/96 */
|
||
#include <unistd.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/times.h>
|
||
#ifndef MPE
|
||
#include <time.h>
|
||
#include <sys/time.h>
|
||
#endif /* MPE */
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <netdb.h>
|
||
#include <errno.h>
|
||
#include <sys/utsname.h>
|
||
#if !defined(MPE) && !defined(__VMS)
|
||
#include <sys/param.h>
|
||
#endif /* MPE */
|
||
|
||
#else /* WIN32 */
|
||
|
||
#include <process.h>
|
||
#include <time.h>
|
||
#include <winsock2.h>
|
||
#define netperf_socklen_t socklen_t
|
||
#include <windows.h>
|
||
|
||
/* the only time someone should need to define DONT_IPV6 in the
|
||
"sources" file is if they are trying to compile on Windows 2000 or
|
||
NT4 and I suspect this may not be their only problem :) */
|
||
#ifndef DONT_IPV6
|
||
#include <ws2tcpip.h>
|
||
#endif
|
||
|
||
#include <windows.h>
|
||
|
||
#define SIGALRM (14)
|
||
#define sleep(x) Sleep((x)*1000)
|
||
|
||
#endif /* WIN32 */
|
||
|
||
#ifdef _AIX
|
||
#include <sys/select.h>
|
||
#include <sys/sched.h>
|
||
#include <sys/pri.h>
|
||
#define PRIORITY PRI_LOW
|
||
#else/* _AIX */
|
||
#ifdef __sgi
|
||
#include <sys/prctl.h>
|
||
#include <sys/schedctl.h>
|
||
#define PRIORITY NDPLOMIN
|
||
#endif /* __sgi */
|
||
#endif /* _AIX */
|
||
|
||
#ifdef WANT_DLPI
|
||
#include <sys/stream.h>
|
||
#include <sys/stropts.h>
|
||
#include <sys/poll.h>
|
||
#ifdef __osf__
|
||
#include <sys/dlpihdr.h>
|
||
#else /* __osf__ */
|
||
#include <sys/dlpi.h>
|
||
#ifdef __hpux
|
||
#include <sys/dlpi_ext.h>
|
||
#endif /* __hpux */
|
||
#endif /* __osf__ */
|
||
#endif /* WANT_DLPI */
|
||
|
||
#ifdef HAVE_MPCTL
|
||
#include <sys/mpctl.h>
|
||
#endif
|
||
|
||
#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO)
|
||
# include "missing/getaddrinfo.h"
|
||
#endif
|
||
|
||
|
||
#ifdef WANT_HISTOGRAM
|
||
#include "hist.h"
|
||
#endif /* WANT_HISTOGRAM */
|
||
/****************************************************************/
|
||
/* */
|
||
/* Local Include Files */
|
||
/* */
|
||
/****************************************************************/
|
||
#define NETLIB
|
||
#include "netlib.h"
|
||
#include "netsh.h"
|
||
#include "netcpu.h"
|
||
|
||
/****************************************************************/
|
||
/* */
|
||
/* Global constants, macros and variables */
|
||
/* */
|
||
/****************************************************************/
|
||
|
||
#if defined(WIN32) || defined(__VMS)
|
||
struct timezone {
|
||
int dummy ;
|
||
} ;
|
||
#ifndef __VMS
|
||
SOCKET win_kludge_socket = INVALID_SOCKET;
|
||
SOCKET win_kludge_socket2 = INVALID_SOCKET;
|
||
#endif /* __VMS */
|
||
#endif /* WIN32 || __VMS */
|
||
|
||
#ifndef LONG_LONG_MAX
|
||
#define LONG_LONG_MAX 9223372036854775807LL
|
||
#endif /* LONG_LONG_MAX */
|
||
|
||
/* older versions of netperf knew about the HP kernel IDLE counter. */
|
||
/* this is now obsolete - in favor of either pstat(), times, or a */
|
||
/* process-level looper process. we also now require support for the */
|
||
/* "long" integer type. raj 4/95. */
|
||
|
||
int
|
||
lib_num_loc_cpus, /* the number of cpus in the system */
|
||
lib_num_rem_cpus; /* how many we think are in the remote */
|
||
|
||
#define PAGES_PER_CHILD 2
|
||
|
||
int lib_use_idle;
|
||
int cpu_method;
|
||
|
||
struct timeval time1, time2;
|
||
struct timezone tz;
|
||
float lib_elapsed,
|
||
lib_local_maxrate,
|
||
lib_remote_maxrate,
|
||
lib_local_cpu_util,
|
||
lib_remote_cpu_util;
|
||
|
||
float lib_local_per_cpu_util[MAXCPUS];
|
||
int lib_cpu_map[MAXCPUS];
|
||
|
||
int *request_array;
|
||
int *response_array;
|
||
|
||
/* INVALID_SOCKET == INVALID_HANDLE_VALUE == (unsigned int)(~0) == -1 */
|
||
SOCKET netlib_control = INVALID_SOCKET;
|
||
SOCKET server_sock = INVALID_SOCKET;
|
||
|
||
/* global variables to hold the value for processor affinity */
|
||
int local_proc_affinity,remote_proc_affinity = -1;
|
||
|
||
/* these are to allow netperf to be run easily through those evil,
|
||
end-to-end breaking things known as firewalls */
|
||
char local_data_port[10];
|
||
char remote_data_port[10];
|
||
|
||
char *local_data_address=NULL;
|
||
char *remote_data_address=NULL;
|
||
|
||
int local_data_family=AF_UNSPEC;
|
||
int remote_data_family=AF_UNSPEC;
|
||
|
||
/* in the past, I was overlaying a structure on an array of ints. now */
|
||
/* I am going to have a "real" structure, and point an array of ints */
|
||
/* at it. the real structure will be forced to the same alignment as */
|
||
/* the type "double." this change will mean that pre-2.1 netperfs */
|
||
/* cannot be mixed with 2.1 and later. raj 11/95 */
|
||
|
||
union netperf_request_struct netperf_request;
|
||
union netperf_response_struct netperf_response;
|
||
|
||
FILE *where;
|
||
|
||
char libfmt = '?';
|
||
|
||
#ifdef WANT_DLPI
|
||
/* some stuff for DLPI control messages */
|
||
#define DLPI_DATA_SIZE 2048
|
||
|
||
unsigned long control_data[DLPI_DATA_SIZE];
|
||
struct strbuf control_message = {DLPI_DATA_SIZE, 0, (char *)control_data};
|
||
|
||
#endif /* WANT_DLPI */
|
||
|
||
#ifdef WIN32
|
||
HANDLE hAlarm = INVALID_HANDLE_VALUE;
|
||
#endif
|
||
|
||
int times_up;
|
||
|
||
#ifdef WIN32
|
||
/* we use a getopt implementation from net.sources */
|
||
/*
|
||
* get option letter from argument vector
|
||
*/
|
||
int
|
||
opterr = 1, /* should error messages be printed? */
|
||
optind = 1, /* index into parent argv vector */
|
||
optopt; /* character checked for validity */
|
||
char
|
||
*optarg; /* argument associated with option */
|
||
|
||
#define EMSG ""
|
||
|
||
#endif /* WIN32 */
|
||
|
||
static int measuring_cpu;
|
||
int
|
||
netlib_get_page_size(void) {
|
||
|
||
/* not all systems seem to have the sysconf for page size. for
|
||
those which do not, we will assume that the page size is 8192
|
||
bytes. this should be more than enough to be sure that there is
|
||
no page or cache thrashing by looper processes on MP
|
||
systems. otherwise that's really just too bad - such systems
|
||
should define _SC_PAGE_SIZE - raj 4/95 */
|
||
|
||
#ifndef _SC_PAGE_SIZE
|
||
#ifdef WIN32
|
||
|
||
SYSTEM_INFO SystemInfo;
|
||
|
||
GetSystemInfo(&SystemInfo);
|
||
|
||
return SystemInfo.dwPageSize;
|
||
#else
|
||
return(8192L);
|
||
#endif /* WIN32 */
|
||
#else
|
||
return(sysconf(_SC_PAGE_SIZE));
|
||
#endif /* _SC_PAGE_SIZE */
|
||
|
||
}
|
||
|
||
|
||
#ifdef WANT_INTERVALS
|
||
static unsigned int usec_per_itvl;
|
||
|
||
|
||
void
|
||
stop_itimer()
|
||
|
||
{
|
||
|
||
struct itimerval new_interval;
|
||
struct itimerval old_interval;
|
||
|
||
new_interval.it_interval.tv_sec = 0;
|
||
new_interval.it_interval.tv_usec = 0;
|
||
new_interval.it_value.tv_sec = 0;
|
||
new_interval.it_value.tv_usec = 0;
|
||
if (setitimer(ITIMER_REAL,&new_interval,&old_interval) != 0) {
|
||
/* there was a problem arming the interval timer */
|
||
perror("netperf: setitimer");
|
||
exit(1);
|
||
}
|
||
return;
|
||
}
|
||
#endif /* WANT_INTERVALS */
|
||
|
||
|
||
|
||
#ifdef WIN32
|
||
static void
|
||
error(char *pch)
|
||
{
|
||
if (!opterr) {
|
||
return; /* without printing */
|
||
}
|
||
fprintf(stderr, "%s: %s: %c\n",
|
||
(NULL != program) ? program : "getopt", pch, optopt);
|
||
}
|
||
|
||
int
|
||
getopt(int argc, char **argv, char *ostr)
|
||
{
|
||
static char *place = EMSG; /* option letter processing */
|
||
register char *oli; /* option letter list index */
|
||
|
||
if (!*place) {
|
||
/* update scanning pointer */
|
||
if (optind >= argc || *(place = argv[optind]) != '-' || !*++place) {
|
||
return EOF;
|
||
}
|
||
if (*place == '-') {
|
||
/* found "--" */
|
||
++optind;
|
||
place = EMSG ; /* Added by shiva for Netperf */
|
||
return EOF;
|
||
}
|
||
}
|
||
|
||
/* option letter okay? */
|
||
if ((optopt = (int)*place++) == (int)':'
|
||
|| !(oli = strchr(ostr, optopt))) {
|
||
if (!*place) {
|
||
++optind;
|
||
}
|
||
error("illegal option");
|
||
return BADCH;
|
||
}
|
||
if (*++oli != ':') {
|
||
/* don't need argument */
|
||
optarg = NULL;
|
||
if (!*place)
|
||
++optind;
|
||
} else {
|
||
/* need an argument */
|
||
if (*place) {
|
||
optarg = place; /* no white space */
|
||
} else if (argc <= ++optind) {
|
||
/* no arg */
|
||
place = EMSG;
|
||
error("option requires an argument");
|
||
return BADCH;
|
||
} else {
|
||
optarg = argv[optind]; /* white space */
|
||
}
|
||
place = EMSG;
|
||
++optind;
|
||
}
|
||
return optopt; /* return option letter */
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
/*----------------------------------------------------------------------------
|
||
* WIN32 implementation of perror, does not deal very well with WSA errors
|
||
* The stdlib.h version of perror only deals with the ancient XENIX error codes.
|
||
*
|
||
* +*+SAF Why can't all WSA errors go through GetLastError? Most seem to...
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
#ifdef WIN32
|
||
void PrintWin32Error(FILE *stream, LPSTR text)
|
||
{
|
||
LPSTR szTemp;
|
||
DWORD dwResult;
|
||
DWORD dwError;
|
||
|
||
dwError = GetLastError();
|
||
dwResult = FormatMessage(
|
||
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||
NULL,
|
||
dwError,
|
||
LANG_NEUTRAL,
|
||
(LPTSTR)&szTemp,
|
||
0,
|
||
NULL );
|
||
|
||
if (dwResult)
|
||
fprintf(stream, "%s: %s\n", text, szTemp);
|
||
else
|
||
fprintf(stream, "%s: error 0x%x\n", text, dwError);
|
||
fflush(stream);
|
||
|
||
if (szTemp)
|
||
LocalFree((HLOCAL)szTemp);
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
|
||
char *
|
||
inet_ttos(int type)
|
||
{
|
||
switch (type) {
|
||
case SOCK_DGRAM:
|
||
return("SOCK_DGRAM");
|
||
break;
|
||
case SOCK_STREAM:
|
||
return("SOCK_STREAM");
|
||
break;
|
||
default:
|
||
return("SOCK_UNKNOWN");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
char unknown[32];
|
||
|
||
char *
|
||
inet_ptos(int protocol) {
|
||
switch (protocol) {
|
||
case IPPROTO_TCP:
|
||
return("IPPROTO_TCP");
|
||
break;
|
||
case IPPROTO_UDP:
|
||
return("IPPROTO_UDP");
|
||
break;
|
||
#if defined(IPPROTO_SCTP)
|
||
case IPPROTO_SCTP:
|
||
return("IPPROTO_SCTP");
|
||
break;
|
||
#endif
|
||
default:
|
||
snprintf(unknown,sizeof(unknown),"IPPROTO_UNKNOWN(%d)",protocol);
|
||
return(unknown);
|
||
}
|
||
}
|
||
|
||
/* one of these days, this should not be required */
|
||
#ifndef AF_INET_SDP
|
||
#define AF_INET_SDP 27
|
||
#define PF_INET_SDP AF_INET_SDP
|
||
#endif
|
||
|
||
char *
|
||
inet_ftos(int family)
|
||
{
|
||
switch(family) {
|
||
case AF_INET:
|
||
return("AF_INET");
|
||
break;
|
||
#if defined(AF_INET6)
|
||
case AF_INET6:
|
||
return("AF_INET6");
|
||
break;
|
||
#endif
|
||
#if defined(AF_INET_SDP)
|
||
case AF_INET_SDP:
|
||
return("AF_INET_SDP");
|
||
break;
|
||
#endif
|
||
default:
|
||
return("AF_UNSPEC");
|
||
}
|
||
}
|
||
|
||
int
|
||
inet_nton(int af, const void *src, char *dst, int cnt)
|
||
|
||
{
|
||
|
||
switch (af) {
|
||
case AF_INET:
|
||
/* magic constants again... :) */
|
||
if (cnt >= 4) {
|
||
memcpy(dst,src,4);
|
||
return 4;
|
||
}
|
||
else {
|
||
Set_errno(ENOSPC);
|
||
return(-1);
|
||
}
|
||
break;
|
||
#if defined(AF_INET6)
|
||
case AF_INET6:
|
||
if (cnt >= 16) {
|
||
memcpy(dst,src,16);
|
||
return(16);
|
||
}
|
||
else {
|
||
Set_errno(ENOSPC);
|
||
return(-1);
|
||
}
|
||
break;
|
||
#endif
|
||
default:
|
||
Set_errno(EAFNOSUPPORT);
|
||
return(-1);
|
||
}
|
||
}
|
||
|
||
double
|
||
ntohd(double net_double)
|
||
|
||
{
|
||
/* we rely on things being nicely packed */
|
||
union {
|
||
double whole_thing;
|
||
unsigned int words[2];
|
||
unsigned char bytes[8];
|
||
} conv_rec;
|
||
|
||
unsigned char scratch;
|
||
int i;
|
||
|
||
/* on those systems where ntohl is a no-op, we want to return the */
|
||
/* original value, unchanged */
|
||
|
||
if (ntohl(1L) == 1L) {
|
||
return(net_double);
|
||
}
|
||
|
||
conv_rec.whole_thing = net_double;
|
||
|
||
/* we know that in the message passing routines that ntohl will have */
|
||
/* been called on the 32 bit quantities. we need to put those back */
|
||
/* the way they belong before we swap */
|
||
conv_rec.words[0] = htonl(conv_rec.words[0]);
|
||
conv_rec.words[1] = htonl(conv_rec.words[1]);
|
||
|
||
/* now swap */
|
||
for (i=0; i<= 3; i++) {
|
||
scratch = conv_rec.bytes[i];
|
||
conv_rec.bytes[i] = conv_rec.bytes[7-i];
|
||
conv_rec.bytes[7-i] = scratch;
|
||
}
|
||
|
||
#if defined(__FLOAT_WORD_ORDER) && defined(__BYTE_ORDER)
|
||
if (__FLOAT_WORD_ORDER != __BYTE_ORDER) {
|
||
/* Fixup mixed endian floating point machines */
|
||
unsigned int scratch = conv_rec.words[0];
|
||
conv_rec.words[0] = conv_rec.words[1];
|
||
conv_rec.words[1] = scratch;
|
||
}
|
||
#endif
|
||
|
||
return(conv_rec.whole_thing);
|
||
|
||
}
|
||
|
||
double
|
||
htond(double host_double)
|
||
|
||
{
|
||
/* we rely on things being nicely packed */
|
||
union {
|
||
double whole_thing;
|
||
unsigned int words[2];
|
||
unsigned char bytes[8];
|
||
} conv_rec;
|
||
|
||
unsigned char scratch;
|
||
int i;
|
||
|
||
/* on those systems where ntohl is a no-op, we want to return the */
|
||
/* original value, unchanged */
|
||
|
||
if (ntohl(1L) == 1L) {
|
||
return(host_double);
|
||
}
|
||
|
||
conv_rec.whole_thing = host_double;
|
||
|
||
/* now swap */
|
||
for (i=0; i<= 3; i++) {
|
||
scratch = conv_rec.bytes[i];
|
||
conv_rec.bytes[i] = conv_rec.bytes[7-i];
|
||
conv_rec.bytes[7-i] = scratch;
|
||
}
|
||
|
||
#if defined(__FLOAT_WORD_ORDER) && defined(__BYTE_ORDER)
|
||
if (__FLOAT_WORD_ORDER != __BYTE_ORDER) {
|
||
/* Fixup mixed endian floating point machines */
|
||
unsigned int scratch = conv_rec.words[0];
|
||
conv_rec.words[0] = conv_rec.words[1];
|
||
conv_rec.words[1] = scratch;
|
||
}
|
||
#endif
|
||
|
||
/* we know that in the message passing routines htonl will */
|
||
/* be called on the 32 bit quantities. we need to set things up so */
|
||
/* that when this happens, the proper order will go out on the */
|
||
/* network */
|
||
conv_rec.words[0] = htonl(conv_rec.words[0]);
|
||
conv_rec.words[1] = htonl(conv_rec.words[1]);
|
||
|
||
return(conv_rec.whole_thing);
|
||
|
||
}
|
||
|
||
|
||
/* one of these days, this should be abstracted-out just like the CPU
|
||
util stuff. raj 2005-01-27 */
|
||
int
|
||
get_num_cpus()
|
||
|
||
{
|
||
|
||
/* on HP-UX, even when we use the looper procs we need the pstat */
|
||
/* call */
|
||
|
||
int temp_cpus;
|
||
|
||
#ifdef __hpux
|
||
#include <sys/pstat.h>
|
||
|
||
struct pst_dynamic psd;
|
||
|
||
if (pstat_getdynamic((struct pst_dynamic *)&psd,
|
||
(size_t)sizeof(psd), (size_t)1, 0) != -1) {
|
||
temp_cpus = psd.psd_proc_cnt;
|
||
}
|
||
else {
|
||
temp_cpus = 1;
|
||
}
|
||
|
||
#else
|
||
/* MW: <unistd.h> was included for non-Windows systems above. */
|
||
/* Thus if _SC_NPROC_ONLN is defined, we should be able to use sysconf. */
|
||
#ifdef _SC_NPROCESSORS_ONLN
|
||
temp_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||
|
||
#ifdef USE_PERFSTAT
|
||
temp_cpus = perfstat_cpu(NULL,NULL, sizeof(perfstat_cpu_t), 0);
|
||
#endif /* USE_PERFSTAT */
|
||
|
||
#else /* no _SC_NPROCESSORS_ONLN */
|
||
|
||
#ifdef WIN32
|
||
SYSTEM_INFO SystemInfo;
|
||
GetSystemInfo(&SystemInfo);
|
||
|
||
temp_cpus = SystemInfo.dwNumberOfProcessors;
|
||
#else
|
||
/* we need to know some other ways to do this, or just fall-back on */
|
||
/* a global command line option - raj 4/95 */
|
||
temp_cpus = shell_num_cpus;
|
||
#endif /* WIN32 */
|
||
#endif /* _SC_NPROCESSORS_ONLN */
|
||
#endif /* __hpux */
|
||
|
||
if (temp_cpus > MAXCPUS) {
|
||
fprintf(where,
|
||
"Sorry, this system has more CPUs (%d) than I can handle (%d).\n",
|
||
temp_cpus,
|
||
MAXCPUS);
|
||
fprintf(where,
|
||
"Please alter MAXCPUS in netlib.h and recompile.\n");
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
|
||
return(temp_cpus);
|
||
|
||
}
|
||
|
||
#ifdef WIN32
|
||
#ifdef __GNUC__
|
||
#define S64_SUFFIX(x) x##LL
|
||
#else
|
||
#define S64_SUFFIX(x) x##i64
|
||
#endif
|
||
|
||
/*
|
||
* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970
|
||
*/
|
||
#define EPOCH_BIAS S64_SUFFIX(116444736000000000)
|
||
|
||
/*
|
||
* Union to facilitate converting from FILETIME to unsigned __int64
|
||
*/
|
||
typedef union {
|
||
unsigned __int64 ft_scalar;
|
||
FILETIME ft_struct;
|
||
} FT;
|
||
|
||
void
|
||
gettimeofday( struct timeval *tv , struct timezone *not_used )
|
||
{
|
||
FT nt_time;
|
||
__int64 UnixTime; /* microseconds since 1/1/1970 */
|
||
|
||
GetSystemTimeAsFileTime( &(nt_time.ft_struct) );
|
||
|
||
UnixTime = ((nt_time.ft_scalar - EPOCH_BIAS) / S64_SUFFIX(10));
|
||
tv->tv_sec = (long)(time_t)(UnixTime / S64_SUFFIX(1000000));
|
||
tv->tv_usec = (unsigned long)(UnixTime % S64_SUFFIX(1000000));
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
|
||
|
||
/************************************************************************/
|
||
/* */
|
||
/* signal catcher */
|
||
/* */
|
||
/************************************************************************/
|
||
|
||
void
|
||
#if defined(__hpux)
|
||
catcher(sig, code, scp)
|
||
int sig;
|
||
int code;
|
||
struct sigcontext *scp;
|
||
#else
|
||
catcher(int sig)
|
||
#endif /* __hpux || __VMS */
|
||
{
|
||
|
||
#ifdef __hpux
|
||
if (debug > 2) {
|
||
fprintf(where,"caught signal %d ",sig);
|
||
if (scp) {
|
||
fprintf(where,"while in syscall %d\n",
|
||
scp->sc_syscall);
|
||
}
|
||
else {
|
||
fprintf(where,"null scp\n");
|
||
}
|
||
fflush(where);
|
||
}
|
||
#endif /* RAJ_DEBUG */
|
||
|
||
switch(sig) {
|
||
|
||
case SIGINT:
|
||
fprintf(where,"netperf: caught SIGINT\n");
|
||
fflush(where);
|
||
exit(1);
|
||
break;
|
||
case SIGALRM:
|
||
if (--test_len_ticks == 0) {
|
||
/* the test is over */
|
||
if (times_up != 0) {
|
||
fprintf(where,"catcher: timer popped with times_up != 0\n");
|
||
fflush(where);
|
||
}
|
||
times_up = 1;
|
||
#if defined(WANT_INTERVALS) && !defined(WANT_SPIN)
|
||
stop_itimer();
|
||
#endif /* WANT_INTERVALS */
|
||
break;
|
||
}
|
||
else {
|
||
#ifdef WANT_INTERVALS
|
||
#ifdef __hpux
|
||
/* the test is not over yet and we must have been using the */
|
||
/* interval timer. if we were in SYS_SIGSUSPEND we want to */
|
||
/* re-start the system call. Otherwise, we want to get out of */
|
||
/* the sigsuspend call. I NEED TO KNOW HOW TO DO THIS FOR OTHER */
|
||
/* OPERATING SYSTEMS. If you know how, please let me know. rick */
|
||
/* jones <raj@cup.hp.com> */
|
||
if (scp->sc_syscall != SYS_SIGSUSPEND) {
|
||
if (debug > 2) {
|
||
fprintf(where,
|
||
"catcher: Time to send burst > interval!\n");
|
||
fflush(where);
|
||
}
|
||
scp->sc_syscall_action = SIG_RESTART;
|
||
}
|
||
#endif /* __hpux */
|
||
#else /* WANT_INTERVALS */
|
||
fprintf(where,
|
||
"catcher: interval timer running unexpectedly!\n");
|
||
fflush(where);
|
||
times_up = 1;
|
||
#endif /* WANT_INTERVALS */
|
||
break;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
void
|
||
install_signal_catchers()
|
||
|
||
{
|
||
/* just a simple little routine to catch a bunch of signals */
|
||
|
||
#ifndef WIN32
|
||
struct sigaction action;
|
||
int i;
|
||
|
||
fprintf(where,"installing catcher for all signals\n");
|
||
fflush(where);
|
||
|
||
sigemptyset(&(action.sa_mask));
|
||
action.sa_handler = catcher;
|
||
|
||
#ifdef SA_INTERRUPT
|
||
action.sa_flags = SA_INTERRUPT;
|
||
#else /* SA_INTERRUPT */
|
||
action.sa_flags = 0;
|
||
#endif /* SA_INTERRUPT */
|
||
|
||
|
||
for (i = 1; i <= NSIG; i++) {
|
||
if (i != SIGALRM) {
|
||
if (sigaction(i,&action,NULL) != 0) {
|
||
fprintf(where,
|
||
"Could not install signal catcher for sig %d, errno %d\n",
|
||
i,
|
||
errno);
|
||
fflush(where);
|
||
|
||
}
|
||
}
|
||
}
|
||
#else
|
||
return;
|
||
#endif /* WIN32 */
|
||
}
|
||
|
||
|
||
#ifdef WIN32
|
||
#define SIGALRM (14)
|
||
void
|
||
emulate_alarm( int seconds )
|
||
{
|
||
DWORD ErrorCode;
|
||
|
||
/* Wait on this event for parm seconds. */
|
||
|
||
ErrorCode = WaitForSingleObject(hAlarm, seconds*1000);
|
||
if (ErrorCode == WAIT_FAILED)
|
||
{
|
||
perror("WaitForSingleObject failed");
|
||
exit(1);
|
||
}
|
||
|
||
if (ErrorCode == WAIT_TIMEOUT)
|
||
{
|
||
/* WaitForSingleObject timed out; this means the timer
|
||
wasn't canceled. */
|
||
|
||
times_up = 1;
|
||
|
||
/* We have yet to find a good way to fully emulate the effects */
|
||
/* of signals and getting EINTR from system calls under */
|
||
/* winsock, so what we do here is close the socket out from */
|
||
/* under the other thread. It is rather kludgy, but should be */
|
||
/* sufficient to get this puppy shipped. The concept can be */
|
||
/* attributed/blamed :) on Robin raj 1/96 */
|
||
|
||
if (win_kludge_socket != INVALID_SOCKET) {
|
||
closesocket(win_kludge_socket);
|
||
}
|
||
if (win_kludge_socket2 != INVALID_SOCKET) {
|
||
closesocket(win_kludge_socket2);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif /* WIN32 */
|
||
|
||
void
|
||
start_timer(int time)
|
||
{
|
||
|
||
#ifdef WIN32
|
||
/*+*+SAF What if StartTimer is called twice without the first timer */
|
||
/*+*+SAF expiring? */
|
||
|
||
DWORD thread_id ;
|
||
HANDLE tHandle;
|
||
|
||
if (hAlarm == (HANDLE) INVALID_HANDLE_VALUE)
|
||
{
|
||
/* Create the Alarm event object */
|
||
hAlarm = CreateEvent(
|
||
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
|
||
FALSE, /* auto reset event */
|
||
FALSE, /* init. state = reset */
|
||
(void *)NULL); /* unnamed event object */
|
||
if (hAlarm == (HANDLE) INVALID_HANDLE_VALUE)
|
||
{
|
||
perror("CreateEvent failure");
|
||
exit(1);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ResetEvent(hAlarm);
|
||
}
|
||
|
||
|
||
tHandle = CreateThread(0,
|
||
0,
|
||
(LPTHREAD_START_ROUTINE)emulate_alarm,
|
||
(LPVOID)(ULONG_PTR)time,
|
||
0,
|
||
&thread_id ) ;
|
||
CloseHandle(tHandle);
|
||
|
||
#else /* not WIN32 */
|
||
|
||
struct sigaction action;
|
||
|
||
if (debug) {
|
||
fprintf(where,"About to start a timer for %d seconds.\n",time);
|
||
fflush(where);
|
||
}
|
||
|
||
action.sa_handler = catcher;
|
||
sigemptyset(&(action.sa_mask));
|
||
sigaddset(&(action.sa_mask),SIGALRM);
|
||
|
||
#ifdef SA_INTERRUPT
|
||
/* on some systems (SunOS 4.blah), system calls are restarted. we do */
|
||
/* not want that */
|
||
action.sa_flags = SA_INTERRUPT;
|
||
#else /* SA_INTERRUPT */
|
||
action.sa_flags = 0;
|
||
#endif /* SA_INTERRUPT */
|
||
|
||
if (sigaction(SIGALRM, &action, NULL) < 0) {
|
||
fprintf(where,"start_timer: error installing alarm handler ");
|
||
fprintf(where,"errno %d\n",errno);
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
|
||
/* this is the easy case - just set the timer for so many seconds */
|
||
if (alarm(time) != 0) {
|
||
fprintf(where,
|
||
"error starting alarm timer, errno %d\n",
|
||
errno);
|
||
fflush(where);
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
test_len_ticks = 1;
|
||
|
||
}
|
||
|
||
|
||
/* this routine will disable any running timer */
|
||
void
|
||
stop_timer()
|
||
{
|
||
#ifndef WIN32
|
||
alarm(0);
|
||
#else
|
||
/* at some point we may need some win32 equivalent */
|
||
if (hAlarm != (HANDLE) INVALID_HANDLE_VALUE)
|
||
{
|
||
SetEvent(hAlarm);
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
}
|
||
|
||
|
||
#ifdef WANT_INTERVALS
|
||
/* this routine will enable the interval timer and set things up so */
|
||
/* that for a timed test the test will end at the proper time. it */
|
||
/* should detect the presence of POSIX.4 timer_* routines one of */
|
||
/* these days */
|
||
void
|
||
start_itimer(unsigned int interval_len_msec )
|
||
{
|
||
|
||
unsigned int ticks_per_itvl;
|
||
|
||
struct itimerval new_interval;
|
||
struct itimerval old_interval;
|
||
|
||
/* if -DWANT_INTERVALS was used, we will use the ticking of the itimer to */
|
||
/* tell us when the test is over. while the user will be specifying */
|
||
/* some number of milliseconds, we know that the interval timer is */
|
||
/* really in units of 1/HZ. so, to prevent the test from running */
|
||
/* "long" it would be necessary to keep this in mind when calculating */
|
||
/* the number of itimer events */
|
||
|
||
ticks_per_itvl = ((interval_wate * sysconf(_SC_CLK_TCK) * 1000) /
|
||
1000000);
|
||
|
||
if (ticks_per_itvl == 0) ticks_per_itvl = 1;
|
||
|
||
/* how many usecs in each interval? */
|
||
usec_per_itvl = ticks_per_itvl * (1000000 / sysconf(_SC_CLK_TCK));
|
||
|
||
/* how many times will the timer pop before the test is over? */
|
||
if (test_time > 0) {
|
||
/* this was a timed test */
|
||
test_len_ticks = (test_time * 1000000) / usec_per_itvl;
|
||
}
|
||
else {
|
||
/* this was not a timed test, use MAXINT */
|
||
test_len_ticks = INT_MAX;
|
||
}
|
||
|
||
if (debug) {
|
||
fprintf(where,"setting the interval timer to %d sec %d usec ",
|
||
usec_per_itvl / 1000000,
|
||
usec_per_itvl % 1000000);
|
||
fprintf(where,"test len %d ticks\n",
|
||
test_len_ticks);
|
||
fflush(where);
|
||
}
|
||
|
||
/* if this was not a timed test, then we really aught to enable the */
|
||
/* signal catcher raj 2/95 */
|
||
|
||
new_interval.it_interval.tv_sec = usec_per_itvl / 1000000;
|
||
new_interval.it_interval.tv_usec = usec_per_itvl % 1000000;
|
||
new_interval.it_value.tv_sec = usec_per_itvl / 1000000;
|
||
new_interval.it_value.tv_usec = usec_per_itvl % 1000000;
|
||
if (setitimer(ITIMER_REAL,&new_interval,&old_interval) != 0) {
|
||
/* there was a problem arming the interval timer */
|
||
perror("netperf: setitimer");
|
||
exit(1);
|
||
}
|
||
}
|
||
#endif /* WANT_INTERVALS */
|
||
|
||
void
|
||
netlib_init_cpu_map() {
|
||
|
||
int i;
|
||
#ifdef HAVE_MPCTL
|
||
int num;
|
||
i = 0;
|
||
/* I go back and forth on whether this should be the system-wide set
|
||
of calls, or if the processor set versions (sans the _SYS) should
|
||
be used. at the moment I believe that the system-wide version
|
||
should be used. raj 2006-04-03 */
|
||
num = mpctl(MPC_GETNUMSPUS_SYS,0,0);
|
||
lib_cpu_map[i] = mpctl(MPC_GETFIRSTSPU_SYS,0,0);
|
||
for (i = 1;((i < num) && (i < MAXCPUS)); i++) {
|
||
lib_cpu_map[i] = mpctl(MPC_GETNEXTSPU_SYS,lib_cpu_map[i-1],0);
|
||
}
|
||
/* from here, we set them all to -1 because if we launch more
|
||
loopers than actual CPUs, well, I'm not sure why :) */
|
||
for (; i < MAXCPUS; i++) {
|
||
lib_cpu_map[i] = -1;
|
||
}
|
||
|
||
#else
|
||
/* we assume that there is indeed a contiguous mapping */
|
||
for (i = 0; i < MAXCPUS; i++) {
|
||
lib_cpu_map[i] = i;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
/****************************************************************/
|
||
/* */
|
||
/* netlib_init() */
|
||
/* */
|
||
/* initialize the performance library... */
|
||
/* */
|
||
/****************************************************************/
|
||
|
||
void
|
||
netlib_init()
|
||
{
|
||
int i;
|
||
|
||
where = stdout;
|
||
|
||
request_array = (int *)(&netperf_request);
|
||
response_array = (int *)(&netperf_response);
|
||
|
||
for (i = 0; i < MAXCPUS; i++) {
|
||
lib_local_per_cpu_util[i] = 0.0;
|
||
}
|
||
|
||
/* on those systems where we know that CPU numbers may not start at
|
||
zero and be contiguous, we provide a way to map from a
|
||
contiguous, starting from 0 CPU id space to the actual CPU ids.
|
||
at present this is only used for the netcpu_looper stuff because
|
||
we ass-u-me that someone setting processor affinity from the
|
||
netperf commandline will provide a "proper" CPU identifier. raj
|
||
2006-04-03 */
|
||
|
||
netlib_init_cpu_map();
|
||
|
||
if (debug) {
|
||
fprintf(where,
|
||
"netlib_init: request_array at %p\n",
|
||
request_array);
|
||
fprintf(where,
|
||
"netlib_init: response_array at %p\n",
|
||
response_array);
|
||
|
||
fflush(where);
|
||
}
|
||
|
||
}
|
||
|
||
/* this routine will conver the string into an unsigned integer. it */
|
||
/* is used primarily for the command-line options taking a number */
|
||
/* (such as the socket size) which could be rather large. If someone */
|
||
/* enters 32M, then the number will be converted to 32 * 1024 * 1024. */
|
||
/* If they inter 32m, the number will be converted to 32 * 1000 * */
|
||
/* 1000 */
|
||
unsigned int
|
||
convert(char *string)
|
||
|
||
{
|
||
unsigned int base;
|
||
base = atoi(string);
|
||
if (strstr(string,"K")) {
|
||
base *= 1024;
|
||
}
|
||
if (strstr(string,"M")) {
|
||
base *= (1024 * 1024);
|
||
}
|
||
if (strstr(string,"G")) {
|
||
base *= (1024 * 1024 * 1024);
|
||
}
|
||
if (strstr(string,"k")) {
|
||
base *= (1000);
|
||
}
|
||
if (strstr(string,"m")) {
|
||
base *= (1000 * 1000);
|
||
}
|
||
if (strstr(string,"g")) {
|
||
base *= (1000 * 1000 * 1000);
|
||
}
|
||
return(base);
|
||
}
|
||
|
||
/* this routine is like convert, but it is used for an interval time
|
||
specification instead of stuff like socket buffer or send sizes.
|
||
it converts everything to microseconds for internal use. if there
|
||
is an 'm' at the end it assumes the user provided milliseconds, s
|
||
will imply seconds, u will imply microseconds. in the future n
|
||
will imply nanoseconds but for now it will be ignored. if there is
|
||
no suffix or an unrecognized suffix, it will be assumed the user
|
||
provided milliseconds, which was the long-time netperf default. one
|
||
of these days, we should probably revisit that nanosecond business
|
||
wrt the return value being just an int rather than a uint64_t or
|
||
something. raj 2006-02-06 */
|
||
|
||
unsigned int
|
||
convert_timespec(char *string) {
|
||
|
||
unsigned int base;
|
||
base = atoi(string);
|
||
if (strstr(string,"m")) {
|
||
base *= 1000;
|
||
}
|
||
else if (strstr(string,"u")) {
|
||
base *= (1);
|
||
}
|
||
else if (strstr(string,"s")) {
|
||
base *= (1000 * 1000);
|
||
}
|
||
else {
|
||
base *= (1000);
|
||
}
|
||
return(base);
|
||
}
|
||
|
||
|
||
/* this routine will allocate a circular list of buffers for either */
|
||
/* send or receive operations. each of these buffers will be aligned */
|
||
/* and offset as per the users request. the circumference of this */
|
||
/* ring will be controlled by the setting of send_width. the buffers */
|
||
/* will be filled with data from the file specified in fill_file. if */
|
||
/* fill_file is an empty string, the buffers will not be filled with */
|
||
/* any particular data */
|
||
|
||
struct ring_elt *
|
||
allocate_buffer_ring(int width, int buffer_size, int alignment, int offset)
|
||
{
|
||
|
||
struct ring_elt *first_link = NULL;
|
||
struct ring_elt *temp_link = NULL;
|
||
struct ring_elt *prev_link;
|
||
|
||
int i;
|
||
int malloc_size;
|
||
int bytes_left;
|
||
int bytes_read;
|
||
int do_fill;
|
||
|
||
FILE *fill_source;
|
||
char default_fill[] = "netperf";
|
||
int fill_cursor = 0;
|
||
|
||
malloc_size = buffer_size + alignment + offset;
|
||
|
||
/* did the user wish to have the buffers pre-filled with data from a */
|
||
/* particular source? */
|
||
if (strcmp(fill_file,"") == 0) {
|
||
do_fill = 0;
|
||
fill_source = NULL;
|
||
}
|
||
else {
|
||
do_fill = 1;
|
||
fill_source = (FILE *)fopen(fill_file,"r");
|
||
if (fill_source == (FILE *)NULL) {
|
||
perror("Could not open requested fill file");
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
assert(width >= 1);
|
||
|
||
prev_link = NULL;
|
||
for (i = 1; i <= width; i++) {
|
||
/* get the ring element */
|
||
temp_link = (struct ring_elt *)malloc(sizeof(struct ring_elt));
|
||
if (temp_link == NULL) {
|
||
printf("malloc(%zu) failed!\n", sizeof(struct ring_elt));
|
||
exit(1);
|
||
}
|
||
/* remember the first one so we can close the ring at the end */
|
||
if (i == 1) {
|
||
first_link = temp_link;
|
||
}
|
||
temp_link->buffer_base = (char *)malloc(malloc_size);
|
||
if (temp_link == NULL) {
|
||
printf("malloc(%d) failed!\n", malloc_size);
|
||
exit(1);
|
||
}
|
||
|
||
#ifndef WIN32
|
||
temp_link->buffer_ptr = (char *)(( (long)(temp_link->buffer_base) +
|
||
(long)alignment - 1) &
|
||
~((long)alignment - 1));
|
||
#else
|
||
temp_link->buffer_ptr = (char *)(( (ULONG_PTR)(temp_link->buffer_base) +
|
||
(ULONG_PTR)alignment - 1) &
|
||
~((ULONG_PTR)alignment - 1));
|
||
#endif
|
||
temp_link->buffer_ptr += offset;
|
||
/* is where the buffer fill code goes. */
|
||
if (do_fill) {
|
||
char *bufptr = temp_link->buffer_ptr;
|
||
bytes_left = buffer_size;
|
||
while (bytes_left) {
|
||
if (((bytes_read = (int)fread(bufptr,
|
||
1,
|
||
bytes_left,
|
||
fill_source)) == 0) &&
|
||
(feof(fill_source))){
|
||
rewind(fill_source);
|
||
}
|
||
bufptr += bytes_read;
|
||
bytes_left -= bytes_read;
|
||
}
|
||
}
|
||
else {
|
||
/* use the default fill to ID our data traffic on the
|
||
network. it ain't exactly pretty, but it should work */
|
||
int j;
|
||
char *bufptr = temp_link->buffer_ptr;
|
||
for (j = 0; j < buffer_size; j++) {
|
||
bufptr[j] = default_fill[fill_cursor];
|
||
fill_cursor += 1;
|
||
/* the Windows DDK compiler with an x86_64 target wants a cast
|
||
here */
|
||
if (fill_cursor > (int)strlen(default_fill)) {
|
||
fill_cursor = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
temp_link->next = prev_link;
|
||
prev_link = temp_link;
|
||
}
|
||
if (first_link) { /* SAF Prefast made me do it... */
|
||
first_link->next = temp_link;
|
||
}
|
||
|
||
return(first_link); /* it's a circle, doesn't matter which we return */
|
||
}
|
||
|
||
/* this routine will dirty the first dirty_count bytes of the
|
||
specified buffer and/or read clean_count bytes from the buffer. it
|
||
will go N bytes at a time, the only question is how large should N
|
||
be and if we should be going continguously, or based on some
|
||
assumption of cache line size */
|
||
|
||
void
|
||
access_buffer(char *buffer_ptr,int length, int dirty_count, int clean_count) {
|
||
|
||
char *temp_buffer;
|
||
char *limit;
|
||
int i, dirty_totals;
|
||
|
||
temp_buffer = buffer_ptr;
|
||
limit = temp_buffer + length;
|
||
dirty_totals = 0;
|
||
|
||
for (i = 0;
|
||
((i < dirty_count) && (temp_buffer < limit));
|
||
i++) {
|
||
*temp_buffer += (char)i;
|
||
dirty_totals += *temp_buffer;
|
||
temp_buffer++;
|
||
}
|
||
|
||
for (i = 0;
|
||
((i < clean_count) && (temp_buffer < limit));
|
||
i++) {
|
||
dirty_totals += *temp_buffer;
|
||
temp_buffer++;
|
||
}
|
||
|
||
if (debug > 100) {
|
||
fprintf(where,
|
||
"This was here to try to avoid dead-code elimination %d\n",
|
||
dirty_totals);
|
||
fflush(where);
|
||
}
|
||
}
|
||
|
||
|
||
#ifdef HAVE_ICSC_EXS
|
||
|
||
#include <sys/mman.h>
|
||
#include <sys/exs.h>
|
||
|
||
/* this routine will allocate a circular list of buffers for either */
|
||
/* send or receive operations. each of these buffers will be aligned */
|
||
/* and offset as per the users request. the circumference of this */
|
||
/* ring will be controlled by the setting of send_width. the buffers */
|
||
/* will be filled with data from the file specified in fill_file. if */
|
||
/* fill_file is an empty string, the buffers will not be filled with */
|
||
/* any particular data */
|
||
|
||
struct ring_elt *
|
||
allocate_exs_buffer_ring (int width, int buffer_size, int alignment, int offset, exs_mhandle_t *mhandlep)
|
||
{
|
||
|
||
struct ring_elt *first_link;
|
||
struct ring_elt *temp_link;
|
||
struct ring_elt *prev_link;
|
||
|
||
int i;
|
||
int malloc_size;
|
||
int bytes_left;
|
||
int bytes_read;
|
||
int do_fill;
|
||
|
||
FILE *fill_source;
|
||
|
||
int mmap_size;
|
||
char *mmap_buffer, *mmap_buffer_aligned;
|
||
|
||
malloc_size = buffer_size + alignment + offset;
|
||
|
||
/* did the user wish to have the buffers pre-filled with data from a */
|
||
/* particular source? */
|
||
if (strcmp (fill_file, "") == 0) {
|
||
do_fill = 0;
|
||
fill_source = NULL;
|
||
} else {
|
||
do_fill = 1;
|
||
fill_source = (FILE *) fopen (fill_file, "r");
|
||
if (fill_source == (FILE *) NULL) {
|
||
perror ("Could not open requested fill file");
|
||
exit (1);
|
||
}
|
||
}
|
||
|
||
assert (width >= 1);
|
||
|
||
if (debug) {
|
||
fprintf (where, "allocate_exs_buffer_ring: "
|
||
"width=%d buffer_size=%d alignment=%d offset=%d\n",
|
||
width, buffer_size, alignment, offset);
|
||
}
|
||
|
||
/* allocate shared memory */
|
||
mmap_size = width * malloc_size;
|
||
mmap_buffer = (char *) mmap ((caddr_t)NULL, mmap_size+NBPG-1,
|
||
PROT_READ|PROT_WRITE,
|
||
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
||
if (mmap_buffer == NULL) {
|
||
perror ("allocate_exs_buffer_ring: mmap failed");
|
||
exit (1);
|
||
}
|
||
mmap_buffer_aligned = (char *) ((uintptr_t)mmap_buffer & ~(NBPG-1));
|
||
if (debug) {
|
||
fprintf (where, "allocate_exs_buffer_ring: "
|
||
"mmap buffer size=%d address=0x%p aligned=0x%p\n",
|
||
mmap_size, mmap_buffer, mmap_buffer_aligned);
|
||
}
|
||
|
||
/* register shared memory */
|
||
*mhandlep = exs_mregister ((void *)mmap_buffer_aligned, (size_t)mmap_size, 0);
|
||
if (*mhandlep == EXS_MHANDLE_INVALID) {
|
||
perror ("allocate_exs_buffer_ring: exs_mregister failed");
|
||
exit (1);
|
||
}
|
||
if (debug) {
|
||
fprintf (where, "allocate_exs_buffer_ring: mhandle=%d\n",
|
||
*mhandlep);
|
||
}
|
||
|
||
/* allocate ring elements */
|
||
first_link = (struct ring_elt *) malloc (width * sizeof (struct ring_elt));
|
||
if (first_link == NULL) {
|
||
printf ("malloc(%d) failed!\n", width * sizeof (struct ring_elt));
|
||
exit (1);
|
||
}
|
||
|
||
/* initialize buffer ring */
|
||
prev_link = first_link + width - 1;
|
||
|
||
for (i = 0, temp_link = first_link; i < width; i++, temp_link++) {
|
||
|
||
temp_link->buffer_base = (char *) mmap_buffer_aligned + (i*malloc_size);
|
||
#ifndef WIN32
|
||
temp_link->buffer_ptr = (char *)
|
||
(((long)temp_link->buffer_base + (long)alignment - 1) &
|
||
~((long)alignment - 1));
|
||
#else
|
||
temp_link->buffer_ptr = (char *)
|
||
(((ULONG_PTR)temp_link->buffer_base + (ULONG_PTR)alignment - 1) &
|
||
~((ULONG_PTR)alignment - 1));
|
||
#endif
|
||
temp_link->buffer_ptr += offset;
|
||
|
||
if (debug) {
|
||
fprintf (where, "allocate_exs_buffer_ring: "
|
||
"buffer: index=%d base=0x%p ptr=0x%p\n",
|
||
i, temp_link->buffer_base, temp_link->buffer_ptr);
|
||
}
|
||
|
||
/* is where the buffer fill code goes. */
|
||
if (do_fill) {
|
||
bytes_left = buffer_size;
|
||
while (bytes_left) {
|
||
if (((bytes_read = (int) fread (temp_link->buffer_ptr,
|
||
1,
|
||
bytes_left,
|
||
fill_source)) == 0) &&
|
||
(feof (fill_source))) {
|
||
rewind (fill_source);
|
||
}
|
||
bytes_left -= bytes_read;
|
||
}
|
||
}
|
||
|
||
/* do linking */
|
||
prev_link->next = temp_link;
|
||
prev_link = temp_link;
|
||
}
|
||
|
||
return (first_link); /* it's a circle, doesn't matter which we return */
|
||
}
|
||
|
||
#endif /* HAVE_ICSC_EXS */
|
||
|
||
|
||
|
||
#ifdef HAVE_SENDFILE
|
||
/* this routine will construct a ring of sendfile_ring_elt structs
|
||
that the routine sendfile_tcp_stream() will use to get parameters
|
||
to its calls to sendfile(). It will setup the ring to point at the
|
||
file specified in the global -F option that is already used to
|
||
pre-fill buffers in the send() case. 08/2000
|
||
|
||
if there is no file specified in a global -F option, we will create
|
||
a tempoarary file and fill it with random data and use that
|
||
instead. raj 2007-08-09 */
|
||
|
||
struct sendfile_ring_elt *
|
||
alloc_sendfile_buf_ring(int width,
|
||
int buffer_size,
|
||
int alignment,
|
||
int offset)
|
||
|
||
{
|
||
|
||
struct sendfile_ring_elt *first_link = NULL;
|
||
struct sendfile_ring_elt *temp_link = NULL;
|
||
struct sendfile_ring_elt *prev_link;
|
||
|
||
int i;
|
||
int fildes;
|
||
struct stat statbuf;
|
||
|
||
/* if the user has not specified a file with the -F option, we will
|
||
fail the test. otherwise, go ahead and try to open the
|
||
file. 08/2000 */
|
||
if (strcmp(fill_file,"") == 0) {
|
||
/* use an temp file for the fill file */
|
||
char *temp_file;
|
||
int *temp_buffer;
|
||
|
||
/* make sure we have at least an ints worth, even if the user is
|
||
using an insane buffer size for a sendfile test. we are
|
||
ass-u-me-ing that malloc will return something at least aligned
|
||
on an int boundary... */
|
||
temp_buffer = (int *) malloc(buffer_size + sizeof(int));
|
||
if (temp_buffer) {
|
||
/* ok, we have the buffer we are going to write, lets get a
|
||
temporary filename */
|
||
temp_file = tmpnam(NULL);
|
||
if (NULL != temp_file) {
|
||
fildes = open(temp_file,O_RDWR | O_EXCL | O_CREAT,0600);
|
||
if (-1 != fildes) {
|
||
int count;
|
||
int *int_ptr;
|
||
|
||
/* initialize the random number generator */
|
||
srand(getpid());
|
||
|
||
/* unlink the file so it goes poof when we
|
||
exit. unless/until shown to be a problem we will
|
||
blissfully ignore the return value. raj 2007-08-09 */
|
||
unlink(temp_file);
|
||
|
||
/* now fill-out the file with at least buffer_size * width bytes */
|
||
for (count = 0; count < width; count++) {
|
||
/* fill the buffer with random data. it doesn't have to be
|
||
really random, just "random enough" :) we do this here rather
|
||
than up above because we want each write to the file to be
|
||
different random data */
|
||
int_ptr = temp_buffer;
|
||
for (i = 0; i <= buffer_size/sizeof(int); i++) {
|
||
*int_ptr = rand();
|
||
int_ptr++;
|
||
}
|
||
if (write(fildes,temp_buffer,buffer_size+sizeof(int)) !=
|
||
buffer_size + sizeof(int)) {
|
||
perror("allocate_sendfile_buf_ring: incomplete write");
|
||
exit(-1);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
perror("allocate_sendfile_buf_ring: could not open tempfile");
|
||
exit(-1);
|
||
}
|
||
}
|
||
else {
|
||
perror("allocate_sendfile_buf_ring: could not allocate temp name");
|
||
exit(-1);
|
||
}
|
||
}
|
||
else {
|
||
perror("alloc_sendfile_buf_ring: could not allocate buffer for file");
|
||
exit(-1);
|
||
}
|
||
}
|
||
else {
|
||
/* the user pointed us at a file, so try it */
|
||
fildes = open(fill_file , O_RDONLY);
|
||
if (fildes == -1){
|
||
perror("alloc_sendfile_buf_ring: Could not open requested file");
|
||
exit(1);
|
||
}
|
||
/* make sure there is enough file there to allow us to make a
|
||
complete ring. that way we do not need additional logic in the
|
||
ring setup to deal with wrap-around issues. we might want that
|
||
someday, but not just now. 08/2000 */
|
||
if (stat(fill_file,&statbuf) != 0) {
|
||
perror("alloc_sendfile_buf_ring: could not stat file");
|
||
exit(1);
|
||
}
|
||
if (statbuf.st_size < (width * buffer_size)) {
|
||
/* the file is too short */
|
||
fprintf(stderr,"alloc_sendfile_buf_ring: specified file too small.\n");
|
||
fprintf(stderr,"file must be larger than send_width * send_size\n");
|
||
fflush(stderr);
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
/* so, at this point we know that fildes is a descriptor which
|
||
references a file of sufficient size for our nefarious
|
||
porpoises. raj 2007-08-09 */
|
||
|
||
prev_link = NULL;
|
||
for (i = 1; i <= width; i++) {
|
||
/* get the ring element. we should probably make sure the malloc()
|
||
was successful, but for now we'll just let the code bomb
|
||
mysteriously. 08/2000 */
|
||
|
||
temp_link = (struct sendfile_ring_elt *)
|
||
malloc(sizeof(struct sendfile_ring_elt));
|
||
if (temp_link == NULL) {
|
||
printf("malloc(%u) failed!\n", sizeof(struct sendfile_ring_elt));
|
||
exit(1);
|
||
}
|
||
|
||
/* remember the first one so we can close the ring at the end */
|
||
|
||
if (i == 1) {
|
||
first_link = temp_link;
|
||
}
|
||
|
||
/* now fill-in the fields of the structure with the apropriate
|
||
stuff. just how should we deal with alignment and offset I
|
||
wonder? until something better comes-up, I think we will just
|
||
ignore them. 08/2000 */
|
||
|
||
temp_link->fildes = fildes; /* from which file do we send? */
|
||
temp_link->offset = offset; /* starting at which offset? */
|
||
offset += buffer_size; /* get ready for the next elt */
|
||
temp_link->length = buffer_size; /* how many bytes to send */
|
||
temp_link->hdtrl = NULL; /* no header or trailer */
|
||
temp_link->flags = 0; /* no flags */
|
||
|
||
/* is where the buffer fill code went. */
|
||
|
||
temp_link->next = prev_link;
|
||
prev_link = temp_link;
|
||
}
|
||
/* close the ring */
|
||
first_link->next = temp_link;
|
||
|
||
return(first_link); /* it's a dummy ring */
|
||
}
|
||
|
||
#endif /* HAVE_SENDFILE */
|
||
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* dump_request() */
|
||
/* */
|
||
/* display the contents of the request array to the user. it will */
|
||
/* display the contents in decimal, hex, and ascii, with four bytes */
|
||
/* per line. */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
dump_request()
|
||
{
|
||
size_t counter = 0;
|
||
fprintf(where,"request contents:\n");
|
||
for (counter = 0; counter < ((sizeof(netperf_request)/4)-3); counter += 4) {
|
||
fprintf(where,"%zu:\t%8x %8x %8x %8x \t|%4.4s| |%4.4s| |%4.4s| |%4.4s|\n",
|
||
counter,
|
||
request_array[counter],
|
||
request_array[counter+1],
|
||
request_array[counter+2],
|
||
request_array[counter+3],
|
||
(char *)&request_array[counter],
|
||
(char *)&request_array[counter+1],
|
||
(char *)&request_array[counter+2],
|
||
(char *)&request_array[counter+3]);
|
||
}
|
||
fflush(where);
|
||
}
|
||
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* dump_response() */
|
||
/* */
|
||
/* display the content of the response array to the user. it will */
|
||
/* display the contents in decimal, hex, and ascii, with four bytes */
|
||
/* per line. */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
dump_response()
|
||
{
|
||
size_t counter = 0;
|
||
|
||
fprintf(where,"response contents\n");
|
||
for (counter = 0; counter < ((sizeof(netperf_response)/4)-3); counter += 4) {
|
||
fprintf(where,"%zu:\t%8x %8x %8x %8x \t>%4.4s< >%4.4s< >%4.4s< >%4.4s<\n",
|
||
counter,
|
||
response_array[counter],
|
||
response_array[counter+1],
|
||
response_array[counter+2],
|
||
response_array[counter+3],
|
||
(char *)&response_array[counter],
|
||
(char *)&response_array[counter+1],
|
||
(char *)&response_array[counter+2],
|
||
(char *)&response_array[counter+3]);
|
||
}
|
||
fflush(where);
|
||
}
|
||
|
||
/*
|
||
|
||
format_number()
|
||
|
||
return a pointer to a formatted string containing the value passed
|
||
translated into the units specified. It assumes that the base units
|
||
are bytes. If the format calls for bits, it will use SI units (10^)
|
||
if the format calls for bytes, it will use CS units (2^)... This
|
||
routine should look familiar to uses of the latest ttcp...
|
||
|
||
we would like to use "t" or "T" for transactions, but probably
|
||
should leave those for terabits and terabytes respectively, so for
|
||
transactions, we will use "x" which will, by default, do absolutely
|
||
nothing to the result. why? so we don't have to special case code
|
||
elsewhere such as in the TCP_RR-as-bidirectional test case.
|
||
|
||
*/
|
||
|
||
|
||
char *
|
||
format_number(double number)
|
||
{
|
||
static char fmtbuf[64];
|
||
|
||
switch (libfmt) {
|
||
case 'K':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f" , number / 1024.0);
|
||
break;
|
||
case 'M':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number / 1024.0 / 1024.0);
|
||
break;
|
||
case 'G':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number / 1024.0 / 1024.0 / 1024.0);
|
||
break;
|
||
case 'k':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number * 8 / 1000.0);
|
||
break;
|
||
case 'm':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number * 8 / 1000.0 / 1000.0);
|
||
break;
|
||
case 'g':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number * 8 / 1000.0 / 1000.0 / 1000.0);
|
||
break;
|
||
case 'x':
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number);
|
||
break;
|
||
default:
|
||
snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number / 1024.0);
|
||
}
|
||
|
||
return fmtbuf;
|
||
}
|
||
|
||
char
|
||
format_cpu_method(int method)
|
||
{
|
||
|
||
char method_char;
|
||
|
||
switch (method) {
|
||
case CPU_UNKNOWN:
|
||
method_char = 'U';
|
||
break;
|
||
case HP_IDLE_COUNTER:
|
||
method_char = 'I';
|
||
break;
|
||
case PSTAT:
|
||
method_char = 'P';
|
||
break;
|
||
case KSTAT:
|
||
method_char = 'K';
|
||
break;
|
||
case KSTAT_10:
|
||
method_char = 'M';
|
||
break;
|
||
case PERFSTAT:
|
||
method_char = 'E';
|
||
break;
|
||
case TIMES: /* historical only, completely unsuitable
|
||
for netperf's purposes */
|
||
method_char = 'T';
|
||
break;
|
||
case GETRUSAGE: /* historical only, completely unsuitable
|
||
for netperf;s purposes */
|
||
method_char = 'R';
|
||
break;
|
||
case LOOPER:
|
||
method_char = 'L';
|
||
break;
|
||
case NT_METHOD:
|
||
method_char = 'N';
|
||
break;
|
||
case PROC_STAT:
|
||
method_char = 'S';
|
||
break;
|
||
case SYSCTL:
|
||
method_char = 'C';
|
||
break;
|
||
case OSX:
|
||
method_char = 'O';
|
||
break;
|
||
default:
|
||
method_char = '?';
|
||
}
|
||
|
||
return method_char;
|
||
|
||
}
|
||
|
||
char *
|
||
format_units()
|
||
{
|
||
static char unitbuf[64];
|
||
|
||
switch (libfmt) {
|
||
case 'K':
|
||
strcpy(unitbuf, "KBytes");
|
||
break;
|
||
case 'M':
|
||
strcpy(unitbuf, "MBytes");
|
||
break;
|
||
case 'G':
|
||
strcpy(unitbuf, "GBytes");
|
||
break;
|
||
case 'k':
|
||
strcpy(unitbuf, "10^3bits");
|
||
break;
|
||
case 'm':
|
||
strcpy(unitbuf, "10^6bits");
|
||
break;
|
||
case 'g':
|
||
strcpy(unitbuf, "10^9bits");
|
||
break;
|
||
case 'x':
|
||
strcpy(unitbuf, "Trans");
|
||
break;
|
||
|
||
default:
|
||
strcpy(unitbuf, "KBytes");
|
||
}
|
||
|
||
return unitbuf;
|
||
}
|
||
|
||
|
||
/****************************************************************/
|
||
/* */
|
||
/* shutdown_control() */
|
||
/* */
|
||
/* tear-down the control connection between me and the server. */
|
||
/****************************************************************/
|
||
|
||
void
|
||
shutdown_control()
|
||
{
|
||
|
||
char *buf = (char *)&netperf_response;
|
||
int buflen = sizeof(netperf_response);
|
||
|
||
/* stuff for select, use fd_set for better compliance */
|
||
fd_set readfds;
|
||
struct timeval timeout;
|
||
|
||
if (debug) {
|
||
fprintf(where,
|
||
"shutdown_control: shutdown of control connection requested.\n");
|
||
fflush(where);
|
||
}
|
||
|
||
/* first, we say that we will be sending no more data on the */
|
||
/* connection */
|
||
if (shutdown(netlib_control,1) == SOCKET_ERROR) {
|
||
Print_errno(where,
|
||
"shutdown_control: error in shutdown");
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
|
||
/* Now, we hang on a select waiting for the socket to become */
|
||
/* readable to receive the shutdown indication from the remote. this */
|
||
/* will be "just" like the recv_response() code */
|
||
|
||
/* we only select once. it is assumed that if the response is split */
|
||
/* (which should not be happening, that we will receive the whole */
|
||
/* thing and not have a problem ;-) */
|
||
|
||
FD_ZERO(&readfds);
|
||
FD_SET(netlib_control,&readfds);
|
||
timeout.tv_sec = 60; /* wait one minute then punt */
|
||
timeout.tv_usec = 0;
|
||
|
||
/* select had better return one, or there was either a problem or a */
|
||
/* timeout... */
|
||
if (select(FD_SETSIZE,
|
||
&readfds,
|
||
0,
|
||
0,
|
||
&timeout) != 1) {
|
||
Print_errno(where,
|
||
"shutdown_control: no response received");
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
|
||
/* we now assume that the socket has come ready for reading */
|
||
recv(netlib_control, buf, buflen,0);
|
||
|
||
}
|
||
|
||
/*
|
||
bind_to_specific_processor will bind the calling process to the
|
||
processor in "processor" It has lots of ugly ifdefs to deal with
|
||
all the different ways systems do processor affinity. this is a
|
||
generalization of work initially done by stephen burger. raj
|
||
2004/12/13 */
|
||
|
||
void
|
||
bind_to_specific_processor(int processor_affinity, int use_cpu_map)
|
||
{
|
||
|
||
int mapped_affinity;
|
||
|
||
/* this is in place because the netcpu_looper processor affinity
|
||
ass-u-me-s a contiguous CPU id space starting with 0. for the
|
||
regular netperf/netserver affinity, we ass-u-me the user has used
|
||
a suitable CPU id even when the space is not contiguous and
|
||
starting from zero */
|
||
if (use_cpu_map) {
|
||
mapped_affinity = lib_cpu_map[processor_affinity];
|
||
}
|
||
else {
|
||
mapped_affinity = processor_affinity;
|
||
}
|
||
|
||
#ifdef HAVE_MPCTL
|
||
/* indeed, at some point it would be a good idea to check the return
|
||
status and pass-along notification of error... raj 2004/12/13 */
|
||
mpctl(MPC_SETPROCESS_FORCE, mapped_affinity, getpid());
|
||
#elif HAVE_PROCESSOR_BIND
|
||
#include <sys/types.h>
|
||
#include <sys/processor.h>
|
||
#include <sys/procset.h>
|
||
processor_bind(P_PID,P_MYID,mapped_affinity,NULL);
|
||
#elif HAVE_BINDPROCESSOR
|
||
#include <sys/processor.h>
|
||
/* this is the call on AIX. It takes a "what" of BINDPROCESS or
|
||
BINDTHRAD, then "who" and finally "where" which is a CPU number
|
||
or it seems PROCESSOR_CLASS_ANY there also seems to be a mycpu()
|
||
call to return the current CPU assignment. this is all based on
|
||
the sys/processor.h include file. from empirical testing, it
|
||
would seem that the my_cpu() call returns the current CPU on
|
||
which we are running rather than the CPU binding, so it's return
|
||
value will not tell you if you are bound vs unbound. */
|
||
bindprocessor(BINDPROCESS,getpid(),(cpu_t)mapped_affinity);
|
||
#elif HAVE_SCHED_SETAFFINITY
|
||
#include <sched.h>
|
||
/* in theory this should cover systems with more CPUs than bits in a
|
||
long, without having to specify __USE_GNU. we "cheat" by taking
|
||
defines from /usr/include/bits/sched.h, which we ass-u-me is
|
||
included by <sched.h>. If they are not there we will just
|
||
fall-back on what we had before, which is to use just the size of
|
||
an unsigned long. raj 2006-09-14 */
|
||
|
||
#if defined(__CPU_SETSIZE)
|
||
#define NETPERF_CPU_SETSIZE __CPU_SETSIZE
|
||
#define NETPERF_CPU_SET(cpu, cpusetp) __CPU_SET(cpu, cpusetp)
|
||
#define NETPERF_CPU_ZERO(cpusetp) __CPU_ZERO (cpusetp)
|
||
typedef cpu_set_t netperf_cpu_set_t;
|
||
#else
|
||
#define NETPERF_CPU_SETSIZE sizeof(unsigned long)
|
||
#define NETPERF_CPU_SET(cpu, cpusetp) *cpusetp = 1 << cpu
|
||
#define NETPERF_CPU_ZERO(cpusetp) *cpusetp = (unsigned long)0
|
||
typedef unsigned long netperf_cpu_set_t;
|
||
#endif
|
||
|
||
netperf_cpu_set_t netperf_cpu_set;
|
||
unsigned int len = sizeof(netperf_cpu_set);
|
||
|
||
if (mapped_affinity < 8*sizeof(netperf_cpu_set)) {
|
||
NETPERF_CPU_ZERO(&netperf_cpu_set);
|
||
NETPERF_CPU_SET(mapped_affinity,&netperf_cpu_set);
|
||
|
||
if (sched_setaffinity(getpid(), len, &netperf_cpu_set)) {
|
||
if (debug) {
|
||
fprintf(stderr, "failed to set PID %d's CPU affinity errno %d\n",
|
||
getpid(),errno);
|
||
fflush(stderr);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
if (debug) {
|
||
fprintf(stderr,
|
||
"CPU number larger than pre-compiled limits. Consider a recompile.\n");
|
||
fflush(stderr);
|
||
}
|
||
}
|
||
|
||
#elif HAVE_BIND_TO_CPU_ID
|
||
/* this is the one for Tru64 */
|
||
#include <sys/types.h>
|
||
#include <sys/resource.h>
|
||
#include <sys/processor.h>
|
||
|
||
/* really should be checking a return code one of these days. raj
|
||
2005/08/31 */
|
||
|
||
bind_to_cpu_id(getpid(), mapped_affinity,0);
|
||
|
||
#elif WIN32
|
||
|
||
{
|
||
ULONG_PTR AffinityMask;
|
||
ULONG_PTR ProcessAffinityMask;
|
||
ULONG_PTR SystemAffinityMask;
|
||
|
||
if ((mapped_affinity < 0) ||
|
||
(mapped_affinity > MAXIMUM_PROCESSORS)) {
|
||
fprintf(where,
|
||
"Invalid processor_affinity specified: %d\n", mapped_affinity); fflush(where);
|
||
return;
|
||
}
|
||
|
||
if (!GetProcessAffinityMask(
|
||
GetCurrentProcess(),
|
||
&ProcessAffinityMask,
|
||
&SystemAffinityMask))
|
||
{
|
||
perror("GetProcessAffinityMask failed");
|
||
fflush(stderr);
|
||
exit(1);
|
||
}
|
||
|
||
AffinityMask = (ULONG_PTR)1 << mapped_affinity;
|
||
|
||
if (AffinityMask & ProcessAffinityMask) {
|
||
if (!SetThreadAffinityMask( GetCurrentThread(), AffinityMask)) {
|
||
perror("SetThreadAffinityMask failed");
|
||
fflush(stderr);
|
||
}
|
||
} else if (debug) {
|
||
fprintf(where,
|
||
"Processor affinity set to CPU# %d\n", mapped_affinity);
|
||
fflush(where);
|
||
}
|
||
}
|
||
|
||
#else
|
||
if (debug) {
|
||
fprintf(where,
|
||
"Processor affinity not available for this platform!\n");
|
||
fflush(where);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
/*
|
||
* Sets a socket to non-blocking operation.
|
||
*/
|
||
int
|
||
set_nonblock (SOCKET sock)
|
||
{
|
||
#ifdef WIN32
|
||
unsigned long flags = 1;
|
||
return (ioctlsocket(sock, FIONBIO, &flags) != SOCKET_ERROR);
|
||
#else
|
||
return (fcntl(sock, F_SETFL, O_NONBLOCK) != -1);
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* send_request() */
|
||
/* */
|
||
/* send a netperf request on the control socket to the remote half of */
|
||
/* the connection. to get us closer to intervendor interoperability, */
|
||
/* we will call htonl on each of the int that compose the message to */
|
||
/* be sent. the server-half of the connection will call the ntohl */
|
||
/* routine to undo any changes that may have been made... */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
send_request()
|
||
{
|
||
size_t counter=0;
|
||
|
||
/* display the contents of the request if the debug level is high */
|
||
/* enough. otherwise, just send the darned thing ;-) */
|
||
|
||
if (debug > 1) {
|
||
fprintf(where,"entered send_request...contents before htonl:\n");
|
||
dump_request();
|
||
}
|
||
|
||
/* pass the processor affinity request value to netserver */
|
||
/* this is a kludge and I know it. sgb 8/11/04 */
|
||
|
||
netperf_request.content.dummy = remote_proc_affinity;
|
||
|
||
/* put the entire request array into network order. We do this */
|
||
/* arbitrarily rather than trying to figure-out just how much */
|
||
/* of the request array contains real information. this should */
|
||
/* be simpler, and at any rate, the performance of sending */
|
||
/* control messages for this benchmark is not of any real */
|
||
/* concern. */
|
||
|
||
for (counter=0;counter < sizeof(netperf_request)/4; counter++) {
|
||
request_array[counter] = htonl(request_array[counter]);
|
||
}
|
||
|
||
if (debug > 1) {
|
||
fprintf(where,"send_request...contents after htonl:\n");
|
||
dump_request();
|
||
|
||
fprintf(where,
|
||
"\nsend_request: about to send %zu bytes from %p\n",
|
||
sizeof(netperf_request),
|
||
&netperf_request);
|
||
fflush(where);
|
||
}
|
||
|
||
if (send(netlib_control,
|
||
(char *)&netperf_request,
|
||
sizeof(netperf_request),
|
||
0) != sizeof(netperf_request)) {
|
||
perror("send_request: send call failure");
|
||
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* send_response() */
|
||
/* */
|
||
/* send a netperf response on the control socket to the remote half of */
|
||
/* the connection. to get us closer to intervendor interoperability, */
|
||
/* we will call htonl on each of the int that compose the message to */
|
||
/* be sent. the other half of the connection will call the ntohl */
|
||
/* routine to undo any changes that may have been made... */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
send_response()
|
||
{
|
||
size_t counter=0;
|
||
int bytes_sent;
|
||
|
||
/* display the contents of the request if the debug level is high */
|
||
/* enough. otherwise, just send the darned thing ;-) */
|
||
|
||
if (debug > 1) {
|
||
fprintf(where,
|
||
"send_response: contents of %zu ints before htonl\n",
|
||
sizeof(netperf_response)/4);
|
||
dump_response();
|
||
}
|
||
|
||
/* put the entire response_array into network order. We do this */
|
||
/* arbitrarily rather than trying to figure-out just how much of the */
|
||
/* request array contains real information. this should be simpler, */
|
||
/* and at any rate, the performance of sending control messages for */
|
||
/* this benchmark is not of any real concern. */
|
||
|
||
for (counter=0;counter < sizeof(netperf_response)/4; counter++) {
|
||
response_array[counter] = htonl(response_array[counter]);
|
||
}
|
||
|
||
if (debug > 1) {
|
||
fprintf(where,
|
||
"send_response: contents after htonl\n");
|
||
dump_response();
|
||
fprintf(where,
|
||
"about to send %zu bytes from %p\n",
|
||
sizeof(netperf_response),
|
||
&netperf_response);
|
||
fflush(where);
|
||
}
|
||
|
||
/*KC*/
|
||
if ((bytes_sent = send(server_sock,
|
||
(char *)&netperf_response,
|
||
sizeof(netperf_response),
|
||
0)) != sizeof(netperf_response)) {
|
||
perror("send_response: send call failure");
|
||
fprintf(where, "BytesSent: %d\n", bytes_sent);
|
||
exit(1);
|
||
}
|
||
|
||
}
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* recv_request() */
|
||
/* */
|
||
/* receive the remote's request on the control socket. we will put */
|
||
/* the entire response into host order before giving it to the */
|
||
/* calling routine. hopefully, this will go most of the way to */
|
||
/* insuring intervendor interoperability. if there are any problems, */
|
||
/* we will just punt the entire situation. */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
recv_request()
|
||
{
|
||
int tot_bytes_recvd,
|
||
bytes_recvd,
|
||
bytes_left;
|
||
char *buf = (char *)&netperf_request;
|
||
int buflen = sizeof(netperf_request);
|
||
size_t counter;
|
||
|
||
tot_bytes_recvd = 0;
|
||
bytes_recvd = 0; /* nt_lint; bytes_recvd uninitialized if buflen == 0 */
|
||
bytes_left = buflen;
|
||
while ((tot_bytes_recvd != buflen) &&
|
||
((bytes_recvd = recv(server_sock, buf, bytes_left,0)) > 0 )) {
|
||
tot_bytes_recvd += bytes_recvd;
|
||
buf += bytes_recvd;
|
||
bytes_left -= bytes_recvd;
|
||
}
|
||
|
||
/* put the request into host order */
|
||
|
||
for (counter = 0; counter < sizeof(netperf_request)/sizeof(int); counter++) {
|
||
request_array[counter] = ntohl(request_array[counter]);
|
||
}
|
||
|
||
if (debug) {
|
||
fprintf(where,
|
||
"recv_request: received %d bytes of request.\n",
|
||
tot_bytes_recvd);
|
||
fflush(where);
|
||
}
|
||
|
||
if (bytes_recvd == SOCKET_ERROR) {
|
||
Print_errno(where,
|
||
"recv_request: error on recv");
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
|
||
if (bytes_recvd == 0) {
|
||
/* the remote has shutdown the control connection, we should shut it */
|
||
/* down as well and exit */
|
||
|
||
if (debug) {
|
||
fprintf(where,
|
||
"recv_request: remote requested shutdown of control\n");
|
||
fflush(where);
|
||
}
|
||
|
||
if (netlib_control != INVALID_SOCKET) {
|
||
shutdown_control();
|
||
}
|
||
exit(0);
|
||
}
|
||
|
||
if (tot_bytes_recvd < buflen) {
|
||
if (debug > 1)
|
||
dump_request();
|
||
|
||
fprintf(where,
|
||
"recv_request: partial request received of %d bytes\n",
|
||
tot_bytes_recvd);
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
|
||
if (debug > 1) {
|
||
dump_request();
|
||
}
|
||
|
||
/* get the processor affinity request value from netperf */
|
||
/* this is a kludge and I know it. sgb 8/11/04 */
|
||
|
||
local_proc_affinity = netperf_request.content.dummy;
|
||
|
||
if (local_proc_affinity != -1) {
|
||
bind_to_specific_processor(local_proc_affinity,0);
|
||
}
|
||
|
||
}
|
||
|
||
/*
|
||
|
||
recv_response_timed()
|
||
|
||
receive the remote's response on the control socket. we will put the
|
||
entire response into host order before giving it to the calling
|
||
routine. hopefully, this will go most of the way to insuring
|
||
intervendor interoperability. if there are any problems, we will just
|
||
punt the entire situation.
|
||
|
||
The call to select at the beginning is to get us out of hang
|
||
situations where the remote gives-up but we don't find-out about
|
||
it. This seems to happen only rarely, but it would be nice to be
|
||
somewhat robust ;-)
|
||
|
||
The "_timed" part is to allow the caller to add (or I suppose
|
||
subtract) from the length of timeout on the select call. this was
|
||
added since not all the CPU utilization mechanisms require a 40
|
||
second calibration, and we used to have an aribtrary 40 second sleep
|
||
in "calibrate_remote_cpu" - since we don't _always_ need that, we
|
||
want to simply add 40 seconds to the select() timeout from that call,
|
||
but don't want to change all the "recv_response" calls in the code
|
||
right away. sooo, we push the functionality of the old
|
||
recv_response() into a new recv_response_timed(addl_timout) call, and
|
||
have recv_response() call recv_response_timed(0). raj 2005-05-16
|
||
|
||
*/
|
||
|
||
|
||
void
|
||
recv_response_timed(int addl_time)
|
||
{
|
||
int tot_bytes_recvd,
|
||
bytes_recvd = 0,
|
||
bytes_left;
|
||
char *buf = (char *)&netperf_response;
|
||
int buflen = sizeof(netperf_response);
|
||
size_t counter;
|
||
|
||
/* stuff for select, use fd_set for better compliance */
|
||
fd_set readfds;
|
||
struct timeval timeout;
|
||
|
||
tot_bytes_recvd = 0;
|
||
bytes_left = buflen;
|
||
|
||
/* zero out the response structure */
|
||
|
||
/* BUG FIX SJB 2/4/93 - should be < not <= */
|
||
for (counter = 0; counter < sizeof(netperf_response)/sizeof(int); counter++) {
|
||
response_array[counter] = 0;
|
||
}
|
||
|
||
/* we only select once. it is assumed that if the response is split */
|
||
/* (which should not be happening, that we will receive the whole */
|
||
/* thing and not have a problem ;-) */
|
||
|
||
FD_ZERO(&readfds);
|
||
FD_SET(netlib_control,&readfds);
|
||
timeout.tv_sec = 120 + addl_time; /* wait at least two minutes
|
||
before punting - the USE_LOOPER
|
||
CPU stuff may cause remote's to
|
||
have a bit longer time of it
|
||
than 60 seconds would allow.
|
||
triggered by fix from Jeff
|
||
Dwork. */
|
||
timeout.tv_usec = 0;
|
||
|
||
/* select had better return one, or there was either a problem or a */
|
||
/* timeout... */
|
||
|
||
if ((counter = select(FD_SETSIZE,
|
||
&readfds,
|
||
0,
|
||
0,
|
||
&timeout)) != 1) {
|
||
fprintf(where,
|
||
"netperf: receive_response: no response received. errno %d counter %zu\n",
|
||
errno,
|
||
counter);
|
||
exit(1);
|
||
}
|
||
|
||
while ((tot_bytes_recvd != buflen) &&
|
||
((bytes_recvd = recv(netlib_control, buf, bytes_left,0)) > 0 )) {
|
||
tot_bytes_recvd += bytes_recvd;
|
||
buf += bytes_recvd;
|
||
bytes_left -= bytes_recvd;
|
||
}
|
||
|
||
if (debug) {
|
||
fprintf(where,"recv_response: received a %d byte response\n",
|
||
tot_bytes_recvd);
|
||
fflush(where);
|
||
}
|
||
|
||
/* put the response into host order */
|
||
|
||
for (counter = 0; counter < sizeof(netperf_response)/sizeof(int); counter++) {
|
||
response_array[counter] = ntohl(response_array[counter]);
|
||
}
|
||
|
||
if (bytes_recvd == SOCKET_ERROR) {
|
||
perror("recv_response");
|
||
exit(1);
|
||
}
|
||
if (tot_bytes_recvd < buflen) {
|
||
fprintf(stderr,
|
||
"recv_response: partial response received: %d bytes\n",
|
||
tot_bytes_recvd);
|
||
fflush(stderr);
|
||
if (debug > 1)
|
||
dump_response();
|
||
exit(1);
|
||
}
|
||
if (debug > 1) {
|
||
dump_response();
|
||
}
|
||
}
|
||
|
||
void
|
||
recv_response()
|
||
{
|
||
recv_response_timed(0);
|
||
}
|
||
|
||
|
||
|
||
#if defined(USE_PSTAT) || defined (USE_SYSCTL)
|
||
int
|
||
hi_32(big_int)
|
||
long long *big_int;
|
||
{
|
||
union overlay_u {
|
||
long long dword;
|
||
long words[2];
|
||
} *overlay;
|
||
|
||
overlay = (union overlay_u *)big_int;
|
||
/* on those systems which are byte swapped, we really wish to */
|
||
/* return words[1] - at least I think so - raj 4/95 */
|
||
if (htonl(1L) == 1L) {
|
||
/* we are a "normal" :) machine */
|
||
return(overlay->words[0]);
|
||
}
|
||
else {
|
||
return(overlay->words[1]);
|
||
}
|
||
}
|
||
|
||
int
|
||
lo_32(big_int)
|
||
long long *big_int;
|
||
{
|
||
union overlay_u {
|
||
long long dword;
|
||
long words[2];
|
||
} *overlay;
|
||
|
||
overlay = (union overlay_u *)big_int;
|
||
/* on those systems which are byte swapped, we really wish to */
|
||
/* return words[0] - at least I think so - raj 4/95 */
|
||
if (htonl(1L) == 1L) {
|
||
/* we are a "normal" :) machine */
|
||
return(overlay->words[1]);
|
||
}
|
||
else {
|
||
return(overlay->words[0]);
|
||
}
|
||
}
|
||
|
||
#endif /* USE_PSTAT || USE_SYSCTL */
|
||
|
||
|
||
void libmain()
|
||
{
|
||
fprintf(where,"hello world\n");
|
||
fprintf(where,"debug: %d\n",debug);
|
||
}
|
||
|
||
|
||
void
|
||
set_sock_buffer (SOCKET sd, enum sock_buffer which, int requested_size, int *effective_sizep)
|
||
{
|
||
#ifdef SO_SNDBUF
|
||
int optname = (which == SEND_BUFFER) ? SO_SNDBUF : SO_RCVBUF;
|
||
netperf_socklen_t sock_opt_len;
|
||
|
||
/* seems that under Windows, setting a value of zero is how one
|
||
tells the stack you wish to enable copy-avoidance. Knuth only
|
||
knows what it will do on other stacks, but it might be
|
||
interesting to find-out, so we won't bother #ifdef'ing the change
|
||
to allow asking for 0 bytes. Courtesy of SAF, 2007-05 raj
|
||
2007-05-31 */
|
||
if (requested_size >= 0) {
|
||
if (setsockopt(sd, SOL_SOCKET, optname,
|
||
(char *)&requested_size, sizeof(int)) < 0) {
|
||
fprintf(where, "netperf: set_sock_buffer: %s option: errno %d\n",
|
||
(which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF",
|
||
errno);
|
||
fflush(where);
|
||
exit(1);
|
||
}
|
||
if (debug > 1) {
|
||
fprintf(where, "netperf: set_sock_buffer: %s of %d requested.\n",
|
||
(which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF",
|
||
requested_size);
|
||
fflush(where);
|
||
}
|
||
}
|
||
|
||
/* Now, we will find-out what the size actually became, and report */
|
||
/* that back to the user. If the call fails, we will just report a -1 */
|
||
/* back to the initiator for the recv buffer size. */
|
||
|
||
sock_opt_len = sizeof(netperf_socklen_t);
|
||
if (getsockopt(sd, SOL_SOCKET, optname, (char *)effective_sizep,
|
||
&sock_opt_len) < 0) {
|
||
fprintf(where, "netperf: set_sock_buffer: getsockopt %s: errno %d\n",
|
||
(which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF", errno);
|
||
fflush(where);
|
||
*effective_sizep = -1;
|
||
}
|
||
|
||
if (debug) {
|
||
fprintf(where, "netperf: set_sock_buffer: "
|
||
"%s socket size determined to be %d\n",
|
||
(which == SEND_BUFFER) ? "send" : "receive", *effective_sizep);
|
||
fflush(where);
|
||
}
|
||
#else /* SO_SNDBUF */
|
||
*effective_size = -1;
|
||
#endif /* SO_SNDBUF */
|
||
}
|
||
|
||
void
|
||
dump_addrinfo(FILE *dumploc, struct addrinfo *info,
|
||
char *host, char *port, int family)
|
||
{
|
||
struct sockaddr *ai_addr;
|
||
struct addrinfo *temp;
|
||
temp=info;
|
||
|
||
fprintf(dumploc, "getaddrinfo returned the following for host '%s' ", host);
|
||
fprintf(dumploc, "port '%s' ", port);
|
||
fprintf(dumploc, "family %s\n", inet_ftos(family));
|
||
while (temp) {
|
||
/* seems that Solaris 10 GA bits will not give a canonical name
|
||
for ::0 or 0.0.0.0, and their fprintf() cannot deal with a null
|
||
pointer, so we have to check for a null pointer. probably a
|
||
safe thing to do anyway, eventhough it was not necessary on
|
||
linux or hp-ux. raj 2005-02-09 */
|
||
if (temp->ai_canonname) {
|
||
fprintf(dumploc,
|
||
"\tcannonical name: '%s'\n",temp->ai_canonname);
|
||
}
|
||
else {
|
||
fprintf(dumploc,
|
||
"\tcannonical name: '%s'\n","(nil)");
|
||
}
|
||
fprintf(dumploc,
|
||
"\tflags: %x family: %s: socktype: %s protocol %s addrlen %d\n",
|
||
temp->ai_flags,
|
||
inet_ftos(temp->ai_family),
|
||
inet_ttos(temp->ai_socktype),
|
||
inet_ptos(temp->ai_protocol),
|
||
temp->ai_addrlen);
|
||
ai_addr = temp->ai_addr;
|
||
if (ai_addr != NULL) {
|
||
fprintf(dumploc,
|
||
"\tsa_family: %s sadata: %d %d %d %d %d %d\n",
|
||
inet_ftos(ai_addr->sa_family),
|
||
(u_char)ai_addr->sa_data[0],
|
||
(u_char)ai_addr->sa_data[1],
|
||
(u_char)ai_addr->sa_data[2],
|
||
(u_char)ai_addr->sa_data[3],
|
||
(u_char)ai_addr->sa_data[4],
|
||
(u_char)ai_addr->sa_data[5]);
|
||
}
|
||
temp = temp->ai_next;
|
||
}
|
||
fflush(dumploc);
|
||
}
|
||
|
||
/*
|
||
establish_control()
|
||
|
||
set-up the control connection between netperf and the netserver so we
|
||
can actually run some tests. if we cannot establish the control
|
||
connection, that may or may not be a good thing, so we will let the
|
||
caller decide what to do.
|
||
|
||
to assist with pesky end-to-end-unfriendly things like firewalls, we
|
||
allow the caller to specify both the remote hostname and port, and the
|
||
local addressing info. i believe that in theory it is possible to
|
||
have an IPv4 endpoint and an IPv6 endpoint communicate with one
|
||
another, but for the time being, we are only going to take-in one
|
||
requested address family parameter. this means that the only way
|
||
(iirc) that we might get a mixed-mode connection would be if the
|
||
address family is specified as AF_UNSPEC, and getaddrinfo() returns
|
||
different families for the local and server names.
|
||
|
||
the "names" can also be IP addresses in ASCII string form.
|
||
|
||
raj 2003-02-27 */
|
||
|
||
SOCKET
|
||
establish_control_internal(char *hostname,
|
||
char *port,
|
||
int remfam,
|
||
char *localhost,
|
||
char *localport,
|
||
int locfam)
|
||
{
|
||
int not_connected;
|
||
SOCKET control_sock;
|
||
int count;
|
||
int error;
|
||
|
||
struct addrinfo hints;
|
||
struct addrinfo *local_res;
|
||
struct addrinfo *remote_res;
|
||
struct addrinfo *local_res_temp;
|
||
struct addrinfo *remote_res_temp;
|
||
|
||
if (debug) {
|
||
fprintf(where,
|
||
"establish_control called with host '%s' port '%s' remfam %s\n",
|
||
hostname,
|
||
port,
|
||
inet_ftos(remfam));
|
||
fprintf(where,
|
||
"\t\tlocal '%s' port '%s' locfam %s\n",
|
||
localhost,
|
||
localport,
|
||
inet_ftos(locfam));
|
||
fflush(where);
|
||
}
|
||
|
||
/* first, we do the remote */
|
||
memset(&hints, 0, sizeof(hints));
|
||
hints.ai_family = remfam;
|
||
hints.ai_socktype = SOCK_STREAM;
|
||
hints.ai_protocol = IPPROTO_TCP;
|
||
hints.ai_flags = 0|AI_CANONNAME;
|
||
count = 0;
|
||
do {
|
||
error = getaddrinfo((char *)hostname,
|
||
(char *)port,
|
||
&hints,
|
||
&remote_res);
|
||
count += 1;
|
||
if (error == EAI_AGAIN) {
|
||
if (debug) {
|
||
fprintf(where,"Sleeping on getaddrinfo EAI_AGAIN\n");
|
||
fflush(where);
|
||
}
|
||
sleep(1);
|
||
}
|
||
} while ((error == EAI_AGAIN) && (count <= 5));
|
||
|
||
if (error) {
|
||
printf("establish control: could not resolve remote '%s' port '%s' af %s",
|
||
hostname,
|
||
port,
|
||
inet_ftos(remfam));
|
||
printf("\n\tgetaddrinfo returned %d %s\n",
|
||
error,
|
||
gai_strerror(error));
|
||
return(INVALID_SOCKET);
|
||
}
|
||
|
||
if (debug) {
|
||
dump_addrinfo(where, remote_res, hostname, port, remfam);
|
||
}
|
||
|
||
/* now we do the local */
|
||
memset(&hints, 0, sizeof(hints));
|
||
hints.ai_family = locfam;
|
||
hints.ai_socktype = SOCK_STREAM;
|
||
hints.ai_protocol = IPPROTO_TCP;
|
||
hints.ai_flags = AI_PASSIVE|AI_CANONNAME;
|
||
count = 0;
|
||
do {
|
||
count += 1;
|
||
error = getaddrinfo((char *)localhost,
|
||
(char *)localport,
|
||
&hints,
|
||
&local_res);
|
||
if (error == EAI_AGAIN) {
|
||
if (debug) {
|
||
fprintf(where,
|
||
"Sleeping on getaddrinfo(%s,%s) EAI_AGAIN count %d \n",
|
||
localhost,
|
||
localport,
|
||
count);
|
||
fflush(where);
|
||
}
|
||
sleep(1);
|
||
}
|
||
} while ((error == EAI_AGAIN) && (count <= 5));
|
||
|
||
if (error) {
|
||
printf("establish control: could not resolve local '%s' port '%s' af %s",
|
||
localhost,
|
||
localport,
|
||
inet_ftos(locfam));
|
||
printf("\n\tgetaddrinfo returned %d %s\n",
|
||
error,
|
||
gai_strerror(error));
|
||
return(INVALID_SOCKET);
|
||
}
|
||
|
||
if (debug) {
|
||
dump_addrinfo(where, local_res, localhost, localport, locfam);
|
||
}
|
||
|
||
not_connected = 1;
|
||
local_res_temp = local_res;
|
||
remote_res_temp = remote_res;
|
||
/* we want to loop through all the possibilities. looping on the
|
||
local addresses will be handled within the while loop. I suppose
|
||
these is some more "C-expert" way to code this, but it has not
|
||
lept to mind just yet :) raj 2003-02024 */
|
||
|
||
while (remote_res_temp != NULL) {
|
||
|
||
/* I am guessing that we should use the address family of the
|
||
local endpoint, and we will not worry about mixed family types
|
||
- presumeably the stack or other transition mechanisms will be
|
||
able to deal with that for us. famous last words :) raj 2003-02-26 */
|
||
control_sock = socket(local_res_temp->ai_family,
|
||
SOCK_STREAM,
|
||
0);
|
||
if (control_sock == INVALID_SOCKET) {
|
||
/* at some point we'll need a more generic "display error"
|
||
message for when/if we use GUIs and the like. unlike a bind
|
||
or connect failure, failure to allocate a socket is
|
||
"immediately fatal" and so we return to the caller. raj 2003-02-24 */
|
||
if (debug) {
|
||
perror("establish_control: unable to allocate control socket");
|
||
}
|
||
return(INVALID_SOCKET);
|
||
}
|
||
|
||
/* if we are going to control the local enpoint addressing, we
|
||
need to call bind. of course, we should probably be setting one
|
||
of the SO_REUSEmumble socket options? raj 2005-02-04 */
|
||
if (bind(control_sock,
|
||
local_res_temp->ai_addr,
|
||
local_res_temp->ai_addrlen) == 0) {
|
||
if (debug) {
|
||
fprintf(where,
|
||
"bound control socket to %s and %s\n",
|
||
localhost,
|
||
localport);
|
||
}
|
||
|
||
if (connect(control_sock,
|
||
remote_res_temp->ai_addr,
|
||
remote_res_temp->ai_addrlen) == 0) {
|
||
/* we have successfully connected to the remote netserver */
|
||
if (debug) {
|
||
fprintf(where,
|
||
"successful connection to remote netserver at %s and %s\n",
|
||
hostname,
|
||
port);
|
||
}
|
||
not_connected = 0;
|
||
/* this should get us out of the while loop */
|
||
break;
|
||
} else {
|
||
/* the connect call failed */
|
||
if (debug) {
|
||
fprintf(where,
|
||
"establish_control: connect failed, errno %d %s\n",
|
||
errno,
|
||
strerror(errno));
|
||
fprintf(where, " trying next address combination\n");
|
||
fflush(where);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
/* the bind failed */
|
||
if (debug) {
|
||
fprintf(where,
|
||
"establish_control: bind failed, errno %d %s\n",
|
||
errno,
|
||
strerror(errno));
|
||
fprintf(where, " trying next address combination\n");
|
||
fflush(where);
|
||
}
|
||
}
|
||
|
||
if ((local_res_temp = local_res_temp->ai_next) == NULL) {
|
||
/* wrap the local and move to the next server, don't forget to
|
||
close the current control socket. raj 2003-02-24 */
|
||
local_res_temp = local_res;
|
||
/* the outer while conditions will deal with the case when we
|
||
get to the end of all the possible remote addresses. */
|
||
remote_res_temp = remote_res_temp->ai_next;
|
||
/* it is simplest here to just close the control sock. since
|
||
this is not a performance critical section of code, we
|
||
don't worry about overheads for socket allocation or
|
||
close. raj 2003-02-24 */
|
||
}
|
||
close(control_sock);
|
||
}
|
||
|
||
/* we no longer need the addrinfo stuff */
|
||
freeaddrinfo(local_res);
|
||
freeaddrinfo(remote_res);
|
||
|
||
/* so, we are either connected or not */
|
||
if (not_connected) {
|
||
fprintf(where, "establish control: are you sure there is a netserver listening on %s at port %s?\n",hostname,port);
|
||
fflush(where);
|
||
return(INVALID_SOCKET);
|
||
}
|
||
/* at this point, we are connected. we probably want some sort of
|
||
version check with the remote at some point. raj 2003-02-24 */
|
||
return(control_sock);
|
||
}
|
||
|
||
void
|
||
establish_control(char *hostname,
|
||
char *port,
|
||
int remfam,
|
||
char *localhost,
|
||
char *localport,
|
||
int locfam)
|
||
|
||
{
|
||
|
||
netlib_control = establish_control_internal(hostname,
|
||
port,
|
||
remfam,
|
||
localhost,
|
||
localport,
|
||
locfam);
|
||
if (netlib_control == INVALID_SOCKET) {
|
||
fprintf(where,
|
||
"establish_control could not establish the control connection from %s port %s address family %s to %s port %s address family %s\n",
|
||
localhost,localport,inet_ftos(locfam),
|
||
hostname,port,inet_ftos(remfam));
|
||
fflush(where);
|
||
exit(INVALID_SOCKET);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* get_id() */
|
||
/* */
|
||
/* Return a string to the calling routine that contains the */
|
||
/* identifying information for the host we are running on. This */
|
||
/* information will then either be displayed locally, or returned to */
|
||
/* a remote caller for display there. */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
char *
|
||
get_id()
|
||
{
|
||
static char id_string[80];
|
||
#ifdef WIN32
|
||
char system_name[MAX_COMPUTERNAME_LENGTH+1] ;
|
||
DWORD name_len = MAX_COMPUTERNAME_LENGTH + 1 ;
|
||
#else
|
||
struct utsname system_name;
|
||
#endif /* WIN32 */
|
||
|
||
#ifdef WIN32
|
||
SYSTEM_INFO SystemInfo;
|
||
GetSystemInfo( &SystemInfo ) ;
|
||
if ( !GetComputerName(system_name , &name_len) )
|
||
strcpy(system_name , "no_name") ;
|
||
#else
|
||
if (uname(&system_name) <0) {
|
||
perror("identify_local: uname");
|
||
exit(1);
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
snprintf(id_string, sizeof(id_string),
|
||
#ifdef WIN32
|
||
"%-15s%-15s%d.%d%d",
|
||
"Windows NT",
|
||
system_name ,
|
||
GetVersion() & 0xFF ,
|
||
GetVersion() & 0xFF00 ,
|
||
SystemInfo.dwProcessorType
|
||
|
||
#else
|
||
"%-15s%-15s%-15s%-15s%-15s",
|
||
system_name.sysname,
|
||
system_name.nodename,
|
||
system_name.release,
|
||
system_name.version,
|
||
system_name.machine
|
||
#endif /* WIN32 */
|
||
);
|
||
return (id_string);
|
||
}
|
||
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* identify_local() */
|
||
/* */
|
||
/* Display identifying information about the local host to the user. */
|
||
/* At first release, this information will be the same as that which */
|
||
/* is returned by the uname -a command, with the exception of the */
|
||
/* idnumber field, which seems to be a non-POSIX item, and hence */
|
||
/* non-portable. */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
identify_local()
|
||
{
|
||
|
||
char *local_id;
|
||
|
||
local_id = get_id();
|
||
|
||
fprintf(where,"Local Information \n\
|
||
Sysname Nodename Release Version Machine\n");
|
||
|
||
fprintf(where,"%s\n",
|
||
local_id);
|
||
|
||
}
|
||
|
||
|
||
/***********************************************************************/
|
||
/* */
|
||
/* identify_remote() */
|
||
/* */
|
||
/* Display identifying information about the remote host to the user. */
|
||
/* At first release, this information will be the same as that which */
|
||
/* is returned by the uname -a command, with the exception of the */
|
||
/* idnumber field, which seems to be a non-POSIX item, and hence */
|
||
/* non-portable. A request is sent to the remote side, which will */
|
||
/* return a string containing the utsname information in a */
|
||
/* pre-formatted form, which is then displayed after the header. */
|
||
/* */
|
||
/***********************************************************************/
|
||
|
||
void
|
||
identify_remote()
|
||
{
|
||
|
||
char *remote_id="";
|
||
|
||
/* send a request for node info to the remote */
|
||
netperf_request.content.request_type = NODE_IDENTIFY;
|
||
|
||
send_request();
|
||
|
||
/* and now wait for the reply to come back */
|
||
|
||
recv_response();
|
||
|
||
if (netperf_response.content.serv_errno) {
|
||
Set_errno(netperf_response.content.serv_errno);
|
||
perror("identify_remote: on remote");
|
||
exit(1);
|
||
}
|
||
|
||
fprintf(where,"Remote Information \n\
|
||
Sysname Nodename Release Version Machine\n");
|
||
|
||
fprintf(where,"%s",
|
||
remote_id);
|
||
}
|
||
|
||
void
|
||
cpu_start(int measure_cpu)
|
||
{
|
||
|
||
gettimeofday(&time1,
|
||
&tz);
|
||
|
||
if (measure_cpu) {
|
||
cpu_util_init();
|
||
measuring_cpu = 1;
|
||
cpu_method = get_cpu_method();
|
||
cpu_start_internal();
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
cpu_stop(int measure_cpu, float *elapsed)
|
||
|
||
{
|
||
|
||
int sec,
|
||
usec;
|
||
|
||
if (measure_cpu) {
|
||
cpu_stop_internal();
|
||
cpu_util_terminate();
|
||
}
|
||
|
||
gettimeofday(&time2,
|
||
&tz);
|
||
|
||
if (time2.tv_usec < time1.tv_usec) {
|
||
time2.tv_usec += 1000000;
|
||
time2.tv_sec -= 1;
|
||
}
|
||
|
||
sec = time2.tv_sec - time1.tv_sec;
|
||
usec = time2.tv_usec - time1.tv_usec;
|
||
lib_elapsed = (float)sec + ((float)usec/(float)1000000.0);
|
||
|
||
*elapsed = lib_elapsed;
|
||
|
||
}
|
||
|
||
|
||
double
|
||
calc_thruput_interval(double units_received,double elapsed)
|
||
|
||
{
|
||
double divisor;
|
||
|
||
/* We will calculate the thruput in libfmt units/second */
|
||
switch (libfmt) {
|
||
case 'K':
|
||
divisor = 1024.0;
|
||
break;
|
||
case 'M':
|
||
divisor = 1024.0 * 1024.0;
|
||
break;
|
||
case 'G':
|
||
divisor = 1024.0 * 1024.0 * 1024.0;
|
||
break;
|
||
case 'k':
|
||
divisor = 1000.0 / 8.0;
|
||
break;
|
||
case 'm':
|
||
divisor = 1000.0 * 1000.0 / 8.0;
|
||
break;
|
||
case 'g':
|
||
divisor = 1000.0 * 1000.0 * 1000.0 / 8.0;
|
||
break;
|
||
|
||
default:
|
||
divisor = 1024.0;
|
||
}
|
||
|
||
return (units_received / divisor / elapsed);
|
||
|
||
}
|
||
|
||
double
|
||
calc_thruput(double units_received)
|
||
|
||
{
|
||
return(calc_thruput_interval(units_received,lib_elapsed));
|
||
}
|
||
|
||
/* these "_omni" versions are ones which understand 'x' as a unit,
|
||
meaning transactions/s. we have a separate routine rather than
|
||
convert the existing routine so we don't have to go and change
|
||
_all_ the nettest_foo.c files at one time. raj 2007-06-08 */
|
||
|
||
double
|
||
calc_thruput_interval_omni(double units_received,double elapsed)
|
||
|
||
{
|
||
double divisor;
|
||
|
||
/* We will calculate the thruput in libfmt units/second */
|
||
switch (libfmt) {
|
||
case 'K':
|
||
divisor = 1024.0;
|
||
break;
|
||
case 'M':
|
||
divisor = 1024.0 * 1024.0;
|
||
break;
|
||
case 'G':
|
||
divisor = 1024.0 * 1024.0 * 1024.0;
|
||
break;
|
||
case 'k':
|
||
divisor = 1000.0 / 8.0;
|
||
break;
|
||
case 'm':
|
||
divisor = 1000.0 * 1000.0 / 8.0;
|
||
break;
|
||
case 'g':
|
||
divisor = 1000.0 * 1000.0 * 1000.0 / 8.0;
|
||
break;
|
||
case 'x':
|
||
divisor = 1.0;
|
||
break;
|
||
|
||
default:
|
||
fprintf(where,
|
||
"WARNING calc_throughput_internal_omni: unknown units %c\n",
|
||
libfmt);
|
||
fflush(where);
|
||
divisor = 1024.0;
|
||
}
|
||
|
||
return (units_received / divisor / elapsed);
|
||
|
||
}
|
||
|
||
double
|
||
calc_thruput_omni(double units_received)
|
||
|
||
{
|
||
return(calc_thruput_interval_omni(units_received,lib_elapsed));
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
float
|
||
calc_cpu_util(float elapsed_time)
|
||
{
|
||
return(calc_cpu_util_internal(elapsed_time));
|
||
}
|
||
|
||
float
|
||
calc_service_demand_internal(double unit_divisor,
|
||
double units_sent,
|
||
float elapsed_time,
|
||
float cpu_utilization,
|
||
int num_cpus)
|
||
|
||
{
|
||
|
||
double service_demand;
|
||
double thruput;
|
||
|
||
if (debug) {
|
||
fprintf(where,"calc_service_demand called: units_sent = %f\n",
|
||
units_sent);
|
||
fprintf(where," elapsed_time = %f\n",
|
||
elapsed_time);
|
||
fprintf(where," cpu_util = %f\n",
|
||
cpu_utilization);
|
||
fprintf(where," num cpu = %d\n",
|
||
num_cpus);
|
||
fflush(where);
|
||
}
|
||
|
||
if (num_cpus == 0) num_cpus = lib_num_loc_cpus;
|
||
|
||
if (elapsed_time == 0.0) {
|
||
elapsed_time = lib_elapsed;
|
||
}
|
||
if (cpu_utilization == 0.0) {
|
||
cpu_utilization = lib_local_cpu_util;
|
||
}
|
||
|
||
thruput = (units_sent /
|
||
(double) unit_divisor /
|
||
(double) elapsed_time);
|
||
|
||
/* on MP systems, it is necessary to multiply the service demand by */
|
||
/* the number of CPU's. at least, I believe that to be the case:) */
|
||
/* raj 10/95 */
|
||
|
||
/* thruput has a "per second" component. if we were using 100% ( */
|
||
/* 100.0) of the CPU in a second, that would be 1 second, or 1 */
|
||
/* millisecond, so we multiply cpu_utilization by 10 to go to */
|
||
/* milliseconds, or 10,000 to go to micro seconds. With revision */
|
||
/* 2.1, the service demand measure goes to microseconds per unit. */
|
||
/* raj 12/95 */
|
||
service_demand = (cpu_utilization*10000.0/thruput) *
|
||
(float) num_cpus;
|
||
|
||
if (debug) {
|
||
fprintf(where,"calc_service_demand using: units_sent = %f\n",
|
||
units_sent);
|
||
fprintf(where," elapsed_time = %f\n",
|
||
elapsed_time);
|
||
fprintf(where," cpu_util = %f\n",
|
||
cpu_utilization);
|
||
fprintf(where," num cpu = %d\n",
|
||
num_cpus);
|
||
fprintf(where,"calc_service_demand got: thruput = %f\n",
|
||
thruput);
|
||
fprintf(where," servdem = %f\n",
|
||
service_demand);
|
||
fflush(where);
|
||
}
|
||
return (float)service_demand;
|
||
}
|
||
|
||
float calc_service_demand(double units_sent,
|
||
float elapsed_time,
|
||
float cpu_utilization,
|
||
int num_cpus)
|
||
|
||
{
|
||
|
||
double unit_divisor = (double)1024.0;
|
||
|
||
return(calc_service_demand_internal(unit_divisor,
|
||
units_sent,
|
||
elapsed_time,
|
||
cpu_utilization,
|
||
num_cpus));
|
||
}
|
||
|
||
float calc_service_demand_trans(double units_sent,
|
||
float elapsed_time,
|
||
float cpu_utilization,
|
||
int num_cpus)
|
||
|
||
{
|
||
|
||
double unit_divisor = (double)1.0;
|
||
|
||
return(calc_service_demand_internal(unit_divisor,
|
||
units_sent,
|
||
elapsed_time,
|
||
cpu_utilization,
|
||
num_cpus));
|
||
}
|
||
|
||
|
||
|
||
float
|
||
calibrate_local_cpu(float local_cpu_rate)
|
||
{
|
||
|
||
lib_num_loc_cpus = get_num_cpus();
|
||
|
||
lib_use_idle = 0;
|
||
#ifdef USE_LOOPER
|
||
cpu_util_init();
|
||
lib_use_idle = 1;
|
||
#endif /* USE_LOOPER */
|
||
|
||
if (local_cpu_rate > 0) {
|
||
/* The user think that he knows what the cpu rate is. We assume */
|
||
/* that all the processors of an MP system are essentially the */
|
||
/* same - for this reason we do not have a per processor maxrate. */
|
||
/* if the machine has processors which are different in */
|
||
/* performance, the CPU utilization will be skewed. raj 4/95 */
|
||
lib_local_maxrate = local_cpu_rate;
|
||
}
|
||
else {
|
||
/* if neither USE_LOOPER nor USE_PSTAT are defined, we return a */
|
||
/* 0.0 to indicate that times or getrusage should be used. raj */
|
||
/* 4/95 */
|
||
lib_local_maxrate = (float)0.0;
|
||
#if defined(USE_PROC_STAT) || defined(USE_LOOPER) || defined(USE_PSTAT) || defined(USE_KSTAT) || defined(USE_PERFSTAT) || defined(USE_SYSCTL)
|
||
lib_local_maxrate = calibrate_idle_rate(4,10);
|
||
#endif
|
||
}
|
||
return lib_local_maxrate;
|
||
}
|
||
|
||
|
||
float
|
||
calibrate_remote_cpu()
|
||
{
|
||
float remrate;
|
||
|
||
netperf_request.content.request_type = CPU_CALIBRATE;
|
||
send_request();
|
||
/* we know that calibration will last at least 40 seconds, so go to */
|
||
/* sleep for that long so the 60 second select in recv_response will */
|
||
/* not pop. raj 7/95 */
|
||
|
||
/* we know that CPU calibration may last as long as 40 seconds, so
|
||
make sure we "select" for at least that long while looking for
|
||
the response. raj 2005-05-16 */
|
||
recv_response_timed(40);
|
||
|
||
if (netperf_response.content.serv_errno) {
|
||
/* initially, silently ignore remote errors and pass */
|
||
/* back a zero to the caller this should allow us to */
|
||
/* mix rev 1.0 and rev 1.1 netperfs... */
|
||
return((float)0.0);
|
||
}
|
||
else {
|
||
/* the rate is the first word of the test_specific data */
|
||
bcopy((char *)netperf_response.content.test_specific_data,
|
||
(char *)&remrate,
|
||
sizeof(remrate));
|
||
bcopy((char *)netperf_response.content.test_specific_data + sizeof(remrate),
|
||
(char *)&lib_num_rem_cpus,
|
||
sizeof(lib_num_rem_cpus));
|
||
/* remrate = (float) netperf_response.content.test_specific_data[0]; */
|
||
return(remrate);
|
||
}
|
||
}
|
||
|
||
#ifndef WIN32
|
||
/* WIN32 requires that at least one of the file sets to select be non-null. */
|
||
/* Since msec_sleep routine is only called by nettest_dlpi & nettest_unix, */
|
||
/* let's duck this issue. */
|
||
|
||
int
|
||
msec_sleep( int msecs )
|
||
{
|
||
int rval ;
|
||
|
||
struct timeval timeout;
|
||
|
||
timeout.tv_sec = msecs / 1000;
|
||
timeout.tv_usec = (msecs - (msecs/1000) *1000) * 1000;
|
||
if ((rval = select(0,
|
||
0,
|
||
0,
|
||
0,
|
||
&timeout))) {
|
||
if ( SOCKET_EINTR(rval) ) {
|
||
return(1);
|
||
}
|
||
perror("msec_sleep: select");
|
||
exit(1);
|
||
}
|
||
return(0);
|
||
}
|
||
#endif /* WIN32 */
|
||
|
||
#ifdef WANT_HISTOGRAM
|
||
/* hist.c
|
||
|
||
Given a time difference in microseconds, increment one of 61
|
||
different buckets:
|
||
|
||
0 - 9 in increments of 1 usec
|
||
0 - 9 in increments of 10 usecs
|
||
0 - 9 in increments of 100 usecs
|
||
1 - 9 in increments of 1 msec
|
||
1 - 9 in increments of 10 msecs
|
||
1 - 9 in increments of 100 msecs
|
||
1 - 9 in increments of 1 sec
|
||
1 - 9 in increments of 10 sec
|
||
> 100 secs
|
||
|
||
This will allow any time to be recorded to within an accuracy of
|
||
10%, and provides a compact representation for capturing the
|
||
distribution of a large number of time differences (e.g.
|
||
request-response latencies).
|
||
|
||
Colin Low 10/6/93
|
||
Rick Jones 2004-06-15 extend to unit and ten usecs
|
||
*/
|
||
|
||
/* #include "sys.h" */
|
||
|
||
/*#define HIST_TEST*/
|
||
|
||
HIST
|
||
HIST_new(void){
|
||
HIST h;
|
||
if((h = (HIST) malloc(sizeof(struct histogram_struct))) == NULL) {
|
||
perror("HIST_new - malloc failed");
|
||
exit(1);
|
||
}
|
||
HIST_clear(h);
|
||
return h;
|
||
}
|
||
|
||
void
|
||
HIST_clear(HIST h){
|
||
int i;
|
||
for(i = 0; i < 10; i++){
|
||
h->unit_usec[i] = 0;
|
||
h->ten_usec[i] = 0;
|
||
h->hundred_usec[i] = 0;
|
||
h->unit_msec[i] = 0;
|
||
h->ten_msec[i] = 0;
|
||
h->hundred_msec[i] = 0;
|
||
h->unit_sec[i] = 0;
|
||
h->ten_sec[i] = 0;
|
||
}
|
||
h->ridiculous = 0;
|
||
h->total = 0;
|
||
}
|
||
|
||
void
|
||
HIST_add(register HIST h, int time_delta){
|
||
register int val;
|
||
h->total++;
|
||
val = time_delta;
|
||
if(val <= 9) h->unit_usec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->ten_usec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->hundred_usec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->unit_msec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->ten_msec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->hundred_msec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->unit_sec[val]++;
|
||
else {
|
||
val = val/10;
|
||
if(val <= 9) h->ten_sec[val]++;
|
||
else h->ridiculous++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#define RB_printf printf
|
||
|
||
void
|
||
output_row(FILE *fd, char *title, int *row){
|
||
register int i;
|
||
RB_printf("%s", title);
|
||
for(i = 0; i < 10; i++) RB_printf(": %4d", row[i]);
|
||
RB_printf("\n");
|
||
}
|
||
|
||
int
|
||
sum_row(int *row) {
|
||
int sum = 0;
|
||
int i;
|
||
for (i = 0; i < 10; i++) sum += row[i];
|
||
return(sum);
|
||
}
|
||
|
||
void
|
||
HIST_report(HIST h){
|
||
#ifndef OLD_HISTOGRAM
|
||
output_row(stdout, "UNIT_USEC ", h->unit_usec);
|
||
output_row(stdout, "TEN_USEC ", h->ten_usec);
|
||
output_row(stdout, "HUNDRED_USEC ", h->hundred_usec);
|
||
#else
|
||
h->hundred_usec[0] += sum_row(h->unit_usec);
|
||
h->hundred_usec[0] += sum_row(h->ten_usec);
|
||
output_row(stdout, "TENTH_MSEC ", h->hundred_usec);
|
||
#endif
|
||
output_row(stdout, "UNIT_MSEC ", h->unit_msec);
|
||
output_row(stdout, "TEN_MSEC ", h->ten_msec);
|
||
output_row(stdout, "HUNDRED_MSEC ", h->hundred_msec);
|
||
output_row(stdout, "UNIT_SEC ", h->unit_sec);
|
||
output_row(stdout, "TEN_SEC ", h->ten_sec);
|
||
RB_printf(">100_SECS: %d\n", h->ridiculous);
|
||
RB_printf("HIST_TOTAL: %d\n", h->total);
|
||
}
|
||
|
||
#endif
|
||
|
||
/* with the advent of sit-and-spin intervals support, we might as well
|
||
make these things available all the time, not just for demo or
|
||
histogram modes. raj 2006-02-06 */
|
||
#ifdef HAVE_GETHRTIME
|
||
|
||
void
|
||
HIST_timestamp(hrtime_t *timestamp)
|
||
{
|
||
*timestamp = gethrtime();
|
||
}
|
||
|
||
int
|
||
delta_micro(hrtime_t *begin, hrtime_t *end)
|
||
{
|
||
long nsecs;
|
||
nsecs = (*end) - (*begin);
|
||
return(nsecs/1000);
|
||
}
|
||
|
||
#elif defined(HAVE_GET_HRT)
|
||
#include "hrt.h"
|
||
|
||
void
|
||
HIST_timestamp(hrt_t *timestamp)
|
||
{
|
||
*timestamp = get_hrt();
|
||
}
|
||
|
||
int
|
||
delta_micro(hrt_t *begin, hrt_t *end)
|
||
{
|
||
|
||
return((int)get_hrt_delta(*end,*begin));
|
||
|
||
}
|
||
#elif defined(WIN32)
|
||
void HIST_timestamp(LARGE_INTEGER *timestamp)
|
||
{
|
||
QueryPerformanceCounter(timestamp);
|
||
}
|
||
|
||
int delta_micro(LARGE_INTEGER *begin, LARGE_INTEGER *end)
|
||
{
|
||
LARGE_INTEGER DeltaTimestamp;
|
||
static LARGE_INTEGER TickHz = {0,0};
|
||
|
||
if (TickHz.QuadPart == 0)
|
||
{
|
||
QueryPerformanceFrequency(&TickHz);
|
||
}
|
||
|
||
/*+*+ Rick; this will overflow after ~2000 seconds, is that
|
||
good enough? Spencer: Yes, that should be more than good
|
||
enough for histogram support */
|
||
|
||
DeltaTimestamp.QuadPart = (end->QuadPart - begin->QuadPart) *
|
||
1000000/TickHz.QuadPart;
|
||
assert((DeltaTimestamp.HighPart == 0) &&
|
||
((int)DeltaTimestamp.LowPart >= 0));
|
||
|
||
return (int)DeltaTimestamp.LowPart;
|
||
}
|
||
|
||
#else
|
||
|
||
void
|
||
HIST_timestamp(struct timeval *timestamp)
|
||
{
|
||
gettimeofday(timestamp,NULL);
|
||
}
|
||
|
||
/* return the difference (in micro seconds) between two timeval */
|
||
/* timestamps */
|
||
int
|
||
delta_micro(struct timeval *begin,struct timeval *end)
|
||
|
||
{
|
||
|
||
int usecs, secs;
|
||
|
||
if (end->tv_usec < begin->tv_usec) {
|
||
/* borrow a second from the tv_sec */
|
||
end->tv_usec += 1000000;
|
||
end->tv_sec--;
|
||
}
|
||
usecs = end->tv_usec - begin->tv_usec;
|
||
secs = end->tv_sec - begin->tv_sec;
|
||
|
||
usecs += (secs * 1000000);
|
||
|
||
return(usecs);
|
||
|
||
}
|
||
#endif /* HAVE_GETHRTIME */
|
||
|
||
|
||
#ifdef WANT_DLPI
|
||
|
||
int
|
||
put_control(fd, len, pri, ack)
|
||
int fd, len, pri, ack;
|
||
{
|
||
int error;
|
||
int flags = 0;
|
||
dl_error_ack_t *err_ack = (dl_error_ack_t *)control_data;
|
||
|
||
control_message.len = len;
|
||
|
||
if ((error = putmsg(fd, &control_message, 0, pri)) < 0 ) {
|
||
fprintf(where,"put_control: putmsg error %d\n",error);
|
||
fflush(where);
|
||
return(-1);
|
||
}
|
||
if ((error = getmsg(fd, &control_message, 0, &flags)) < 0) {
|
||
fprintf(where,"put_control: getsmg error %d\n",error);
|
||
fflush(where);
|
||
return(-1);
|
||
}
|
||
if (err_ack->dl_primitive != ack) {
|
||
fprintf(where,"put_control: acknowledgement error wanted %u got %u \n",
|
||
ack,err_ack->dl_primitive);
|
||
if (err_ack->dl_primitive == DL_ERROR_ACK) {
|
||
fprintf(where," dl_error_primitive: %u\n",
|
||
err_ack->dl_error_primitive);
|
||
fprintf(where," dl_errno: %u\n",
|
||
err_ack->dl_errno);
|
||
fprintf(where," dl_unix_errno %u\n",
|
||
err_ack->dl_unix_errno);
|
||
}
|
||
fflush(where);
|
||
return(-1);
|
||
}
|
||
|
||
return(0);
|
||
}
|
||
|
||
int
|
||
dl_open(char devfile[], int ppa)
|
||
{
|
||
int fd;
|
||
dl_attach_req_t *attach_req = (dl_attach_req_t *)control_data;
|
||
|
||
if ((fd = open(devfile, O_RDWR)) == -1) {
|
||
fprintf(where,"netperf: dl_open: open of %s failed, errno = %d\n",
|
||
devfile,
|
||
errno);
|
||
return(-1);
|
||
}
|
||
|
||
attach_req->dl_primitive = DL_ATTACH_REQ;
|
||
attach_req->dl_ppa = ppa;
|
||
|
||
if (put_control(fd, sizeof(dl_attach_req_t), 0, DL_OK_ACK) < 0) {
|
||
fprintf(where,
|
||
"netperf: dl_open: could not send control message, errno = %d\n",
|
||
errno);
|
||
return(-1);
|
||
}
|
||
return(fd);
|
||
}
|
||
|
||
int
|
||
dl_bind(int fd, int sap, int mode, char *dlsap_ptr, int *dlsap_len)
|
||
{
|
||
dl_bind_req_t *bind_req = (dl_bind_req_t *)control_data;
|
||
dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)control_data;
|
||
|
||
bind_req->dl_primitive = DL_BIND_REQ;
|
||
bind_req->dl_sap = sap;
|
||
bind_req->dl_max_conind = 1;
|
||
bind_req->dl_service_mode = mode;
|
||
bind_req->dl_conn_mgmt = 0;
|
||
bind_req->dl_xidtest_flg = 0;
|
||
|
||
if (put_control(fd, sizeof(dl_bind_req_t), 0, DL_BIND_ACK) < 0) {
|
||
fprintf(where,
|
||
"netperf: dl_bind: could not send control message, errno = %d\n",
|
||
errno);
|
||
return(-1);
|
||
}
|
||
|
||
/* at this point, the control_data portion of the control message */
|
||
/* structure should contain a DL_BIND_ACK, which will have a full */
|
||
/* DLSAP in it. we want to extract this and pass it up so that */
|
||
/* it can be passed around. */
|
||
if (*dlsap_len >= bind_ack->dl_addr_length) {
|
||
bcopy((char *)bind_ack+bind_ack->dl_addr_offset,
|
||
dlsap_ptr,
|
||
bind_ack->dl_addr_length);
|
||
*dlsap_len = bind_ack->dl_addr_length;
|
||
return(0);
|
||
}
|
||
else {
|
||
return (-1);
|
||
}
|
||
}
|
||
|
||
int
|
||
dl_connect(int fd, unsigned char *remote_addr, int remote_addr_len)
|
||
{
|
||
dl_connect_req_t *connection_req = (dl_connect_req_t *)control_data;
|
||
dl_connect_con_t *connection_con = (dl_connect_con_t *)control_data;
|
||
struct pollfd pinfo;
|
||
|
||
int flags = 0;
|
||
|
||
/* this is here on the off chance that we really want some data */
|
||
u_long data_area[512];
|
||
struct strbuf data_message;
|
||
|
||
int error;
|
||
|
||
data_message.maxlen = 2048;
|
||
data_message.len = 0;
|
||
data_message.buf = (char *)data_area;
|
||
|
||
connection_req->dl_primitive = DL_CONNECT_REQ;
|
||
connection_req->dl_dest_addr_length = remote_addr_len;
|
||
connection_req->dl_dest_addr_offset = sizeof(dl_connect_req_t);
|
||
connection_req->dl_qos_length = 0;
|
||
connection_req->dl_qos_offset = 0;
|
||
bcopy (remote_addr,
|
||
(unsigned char *)control_data + sizeof(dl_connect_req_t),
|
||
remote_addr_len);
|
||
|
||
/* well, I would call the put_control routine here, but the sequence */
|
||
/* of connection stuff with DLPI is a bit screwey with all this */
|
||
/* message passing - Toto, I don't think were in Berkeley anymore. */
|
||
|
||
control_message.len = sizeof(dl_connect_req_t) + remote_addr_len;
|
||
if ((error = putmsg(fd,&control_message,0,0)) !=0) {
|
||
fprintf(where,"dl_connect: putmsg failure, errno = %d, error 0x%x \n",
|
||
errno,error);
|
||
fflush(where);
|
||
return(-1);
|
||
};
|
||
|
||
pinfo.fd = fd;
|
||
pinfo.events = POLLIN | POLLPRI;
|
||
pinfo.revents = 0;
|
||
|
||
if ((error = getmsg(fd,&control_message,&data_message,&flags)) != 0) {
|
||
fprintf(where,"dl_connect: getmsg failure, errno = %d, error 0x%x \n",
|
||
errno,error);
|
||
fflush(where);
|
||
return(-1);
|
||
}
|
||
while (control_data[0] == DL_TEST_CON) {
|
||
/* i suppose we spin until we get an error, or a connection */
|
||
/* indication */
|
||
if((error = getmsg(fd,&control_message,&data_message,&flags)) !=0) {
|
||
fprintf(where,"dl_connect: getmsg failure, errno = %d, error = 0x%x\n",
|
||
errno,error);
|
||
fflush(where);
|
||
return(-1);
|
||
}
|
||
}
|
||
|
||
/* we are out - it either worked or it didn't - which was it? */
|
||
if (control_data[0] == DL_CONNECT_CON) {
|
||
return(0);
|
||
}
|
||
else {
|
||
return(-1);
|
||
}
|
||
}
|
||
|
||
int
|
||
dl_accept(fd, remote_addr, remote_addr_len)
|
||
int fd;
|
||
unsigned char *remote_addr;
|
||
int remote_addr_len;
|
||
{
|
||
dl_connect_ind_t *connect_ind = (dl_connect_ind_t *)control_data;
|
||
dl_connect_res_t *connect_res = (dl_connect_res_t *)control_data;
|
||
int tmp_cor;
|
||
int flags = 0;
|
||
|
||
/* hang around and wait for a connection request */
|
||
getmsg(fd,&control_message,0,&flags);
|
||
while (control_data[0] != DL_CONNECT_IND) {
|
||
getmsg(fd,&control_message,0,&flags);
|
||
}
|
||
|
||
/* now respond to the request. at some point, we may want to be sure */
|
||
/* that the connection came from the correct station address, but */
|
||
/* will assume that we do not have to worry about it just now. */
|
||
|
||
tmp_cor = connect_ind->dl_correlation;
|
||
|
||
connect_res->dl_primitive = DL_CONNECT_RES;
|
||
connect_res->dl_correlation = tmp_cor;
|
||
connect_res->dl_resp_token = 0;
|
||
connect_res->dl_qos_length = 0;
|
||
connect_res->dl_qos_offset = 0;
|
||
connect_res->dl_growth = 0;
|
||
|
||
return(put_control(fd, sizeof(dl_connect_res_t), 0, DL_OK_ACK));
|
||
|
||
}
|
||
|
||
int
|
||
dl_set_window(fd, window)
|
||
int fd, window;
|
||
{
|
||
return(0);
|
||
}
|
||
|
||
void
|
||
dl_stats(fd)
|
||
int fd;
|
||
{
|
||
}
|
||
|
||
int
|
||
dl_send_disc(fd)
|
||
int fd;
|
||
{
|
||
}
|
||
|
||
int
|
||
dl_recv_disc(fd)
|
||
int fd;
|
||
{
|
||
}
|
||
#endif /* WANT_DLPI*/
|
||
|
||
/* these routines for confidence intervals are courtesy of IBM. They */
|
||
/* have been modified slightly for more general usage beyond TCP/UDP */
|
||
/* tests. raj 11/94 I would suspect that this code carries an IBM */
|
||
/* copyright that is much the same as that for the original HP */
|
||
/* netperf code */
|
||
int confidence_iterations; /* for iterations */
|
||
|
||
double
|
||
result_confid=-10.0,
|
||
loc_cpu_confid=-10.0,
|
||
rem_cpu_confid=-10.0,
|
||
|
||
measured_sum_result=0.0,
|
||
measured_square_sum_result=0.0,
|
||
measured_mean_result=0.0,
|
||
measured_var_result=0.0,
|
||
|
||
measured_sum_local_cpu=0.0,
|
||
measured_square_sum_local_cpu=0.0,
|
||
measured_mean_local_cpu=0.0,
|
||
measured_var_local_cpu=0.0,
|
||
|
||
measured_sum_remote_cpu=0.0,
|
||
measured_square_sum_remote_cpu=0.0,
|
||
measured_mean_remote_cpu=0.0,
|
||
measured_var_remote_cpu=0.0,
|
||
|
||
measured_sum_local_service_demand=0.0,
|
||
measured_square_sum_local_service_demand=0.0,
|
||
measured_mean_local_service_demand=0.0,
|
||
measured_var_local_service_demand=0.0,
|
||
|
||
measured_sum_remote_service_demand=0.0,
|
||
measured_square_sum_remote_service_demand=0.0,
|
||
measured_mean_remote_service_demand=0.0,
|
||
measured_var_remote_service_demand=0.0,
|
||
|
||
measured_sum_local_time=0.0,
|
||
measured_square_sum_local_time=0.0,
|
||
measured_mean_local_time=0.0,
|
||
measured_var_local_time=0.0,
|
||
|
||
measured_mean_remote_time=0.0,
|
||
|
||
measured_fails,
|
||
measured_local_results,
|
||
confidence=-10.0;
|
||
/* interval=0.1; */
|
||
|
||
/************************************************************************/
|
||
/* */
|
||
/* Constants for Confidence Intervals */
|
||
/* */
|
||
/************************************************************************/
|
||
void
|
||
init_stat()
|
||
{
|
||
measured_sum_result=0.0;
|
||
measured_square_sum_result=0.0;
|
||
measured_mean_result=0.0;
|
||
measured_var_result=0.0;
|
||
|
||
measured_sum_local_cpu=0.0;
|
||
measured_square_sum_local_cpu=0.0;
|
||
measured_mean_local_cpu=0.0;
|
||
measured_var_local_cpu=0.0;
|
||
|
||
measured_sum_remote_cpu=0.0;
|
||
measured_square_sum_remote_cpu=0.0;
|
||
measured_mean_remote_cpu=0.0;
|
||
measured_var_remote_cpu=0.0;
|
||
|
||
measured_sum_local_service_demand=0.0;
|
||
measured_square_sum_local_service_demand=0.0;
|
||
measured_mean_local_service_demand=0.0;
|
||
measured_var_local_service_demand=0.0;
|
||
|
||
measured_sum_remote_service_demand=0.0;
|
||
measured_square_sum_remote_service_demand=0.0;
|
||
measured_mean_remote_service_demand=0.0;
|
||
measured_var_remote_service_demand=0.0;
|
||
|
||
measured_sum_local_time=0.0;
|
||
measured_square_sum_local_time=0.0;
|
||
measured_mean_local_time=0.0;
|
||
measured_var_local_time=0.0;
|
||
|
||
measured_mean_remote_time=0.0;
|
||
|
||
measured_fails = 0.0;
|
||
measured_local_results=0.0,
|
||
confidence=-10.0;
|
||
}
|
||
|
||
/* this routine does a simple table lookup for some statistical */
|
||
/* function that I would remember if I stayed awake in my probstats */
|
||
/* class... raj 11/94 */
|
||
double
|
||
confid(int level, int freedom)
|
||
{
|
||
double t99[35],t95[35];
|
||
|
||
t95[1]=12.706;
|
||
t95[2]= 4.303;
|
||
t95[3]= 3.182;
|
||
t95[4]= 2.776;
|
||
t95[5]= 2.571;
|
||
t95[6]= 2.447;
|
||
t95[7]= 2.365;
|
||
t95[8]= 2.306;
|
||
t95[9]= 2.262;
|
||
t95[10]= 2.228;
|
||
t95[11]= 2.201;
|
||
t95[12]= 2.179;
|
||
t95[13]= 2.160;
|
||
t95[14]= 2.145;
|
||
t95[15]= 2.131;
|
||
t95[16]= 2.120;
|
||
t95[17]= 2.110;
|
||
t95[18]= 2.101;
|
||
t95[19]= 2.093;
|
||
t95[20]= 2.086;
|
||
t95[21]= 2.080;
|
||
t95[22]= 2.074;
|
||
t95[23]= 2.069;
|
||
t95[24]= 2.064;
|
||
t95[25]= 2.060;
|
||
t95[26]= 2.056;
|
||
t95[27]= 2.052;
|
||
t95[28]= 2.048;
|
||
t95[29]= 2.045;
|
||
t95[30]= 2.042;
|
||
|
||
t99[1]=63.657;
|
||
t99[2]= 9.925;
|
||
t99[3]= 5.841;
|
||
t99[4]= 4.604;
|
||
t99[5]= 4.032;
|
||
t99[6]= 3.707;
|
||
t99[7]= 3.499;
|
||
t99[8]= 3.355;
|
||
t99[9]= 3.250;
|
||
t99[10]= 3.169;
|
||
t99[11]= 3.106;
|
||
t99[12]= 3.055;
|
||
t99[13]= 3.012;
|
||
t99[14]= 2.977;
|
||
t99[15]= 2.947;
|
||
t99[16]= 2.921;
|
||
t99[17]= 2.898;
|
||
t99[18]= 2.878;
|
||
t99[19]= 2.861;
|
||
t99[20]= 2.845;
|
||
t99[21]= 2.831;
|
||
t99[22]= 2.819;
|
||
t99[23]= 2.807;
|
||
t99[24]= 2.797;
|
||
t99[25]= 2.787;
|
||
t99[26]= 2.779;
|
||
t99[27]= 2.771;
|
||
t99[28]= 2.763;
|
||
t99[29]= 2.756;
|
||
t99[30]= 2.750;
|
||
|
||
if(level==95){
|
||
return(t95[freedom]);
|
||
} else if(level==99){
|
||
return(t99[freedom]);
|
||
} else{
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
void
|
||
calculate_confidence(int confidence_iterations,
|
||
float time,
|
||
double result,
|
||
float loc_cpu,
|
||
float rem_cpu,
|
||
float loc_sd,
|
||
float rem_sd)
|
||
{
|
||
|
||
if (debug) {
|
||
fprintf(where,
|
||
"calculate_confidence: itr %d; time %f; res %f\n",
|
||
confidence_iterations,
|
||
time,
|
||
result);
|
||
fprintf(where,
|
||
" lcpu %f; rcpu %f\n",
|
||
loc_cpu,
|
||
rem_cpu);
|
||
fprintf(where,
|
||
" lsdm %f; rsdm %f\n",
|
||
loc_sd,
|
||
rem_sd);
|
||
fflush(where);
|
||
}
|
||
|
||
/* the test time */
|
||
measured_sum_local_time +=
|
||
(double) time;
|
||
measured_square_sum_local_time +=
|
||
(double) time*time;
|
||
measured_mean_local_time =
|
||
(double) measured_sum_local_time/confidence_iterations;
|
||
measured_var_local_time =
|
||
(double) measured_square_sum_local_time/confidence_iterations
|
||
-measured_mean_local_time*measured_mean_local_time;
|
||
|
||
/* the test result */
|
||
measured_sum_result +=
|
||
(double) result;
|
||
measured_square_sum_result +=
|
||
(double) result*result;
|
||
measured_mean_result =
|
||
(double) measured_sum_result/confidence_iterations;
|
||
measured_var_result =
|
||
(double) measured_square_sum_result/confidence_iterations
|
||
-measured_mean_result*measured_mean_result;
|
||
|
||
/* local cpu utilization */
|
||
measured_sum_local_cpu +=
|
||
(double) loc_cpu;
|
||
measured_square_sum_local_cpu +=
|
||
(double) loc_cpu*loc_cpu;
|
||
measured_mean_local_cpu =
|
||
(double) measured_sum_local_cpu/confidence_iterations;
|
||
measured_var_local_cpu =
|
||
(double) measured_square_sum_local_cpu/confidence_iterations
|
||
-measured_mean_local_cpu*measured_mean_local_cpu;
|
||
|
||
/* remote cpu util */
|
||
measured_sum_remote_cpu +=
|
||
(double) rem_cpu;
|
||
measured_square_sum_remote_cpu+=
|
||
(double) rem_cpu*rem_cpu;
|
||
measured_mean_remote_cpu =
|
||
(double) measured_sum_remote_cpu/confidence_iterations;
|
||
measured_var_remote_cpu =
|
||
(double) measured_square_sum_remote_cpu/confidence_iterations
|
||
-measured_mean_remote_cpu*measured_mean_remote_cpu;
|
||
|
||
/* local service demand */
|
||
measured_sum_local_service_demand +=
|
||
(double) loc_sd;
|
||
measured_square_sum_local_service_demand+=
|
||
(double) loc_sd*loc_sd;
|
||
measured_mean_local_service_demand =
|
||
(double) measured_sum_local_service_demand/confidence_iterations;
|
||
measured_var_local_service_demand =
|
||
(double) measured_square_sum_local_service_demand/confidence_iterations
|
||
-measured_mean_local_service_demand*measured_mean_local_service_demand;
|
||
|
||
/* remote service demand */
|
||
measured_sum_remote_service_demand +=
|
||
(double) rem_sd;
|
||
measured_square_sum_remote_service_demand+=
|
||
(double) rem_sd*rem_sd;
|
||
measured_mean_remote_service_demand =
|
||
(double) measured_sum_remote_service_demand/confidence_iterations;
|
||
measured_var_remote_service_demand =
|
||
(double) measured_square_sum_remote_service_demand/confidence_iterations
|
||
-measured_mean_remote_service_demand*measured_mean_remote_service_demand;
|
||
|
||
if(confidence_iterations>1){
|
||
result_confid= (double) interval -
|
||
2.0 * confid(confidence_level,confidence_iterations-1)*
|
||
sqrt(measured_var_result/(confidence_iterations-1.0)) /
|
||
measured_mean_result;
|
||
|
||
loc_cpu_confid= (double) interval -
|
||
2.0 * confid(confidence_level,confidence_iterations-1)*
|
||
sqrt(measured_var_local_cpu/(confidence_iterations-1.0)) /
|
||
measured_mean_local_cpu;
|
||
|
||
rem_cpu_confid= (double) interval -
|
||
2.0 * confid(confidence_level,confidence_iterations-1)*
|
||
sqrt(measured_var_remote_cpu/(confidence_iterations-1.0)) /
|
||
measured_mean_remote_cpu;
|
||
|
||
if(debug){
|
||
printf("Conf_itvl %2d: results:%4.1f%% loc_cpu:%4.1f%% rem_cpu:%4.1f%%\n",
|
||
confidence_iterations,
|
||
(interval-result_confid)*100.0,
|
||
(interval-loc_cpu_confid)*100.0,
|
||
(interval-rem_cpu_confid)*100.0);
|
||
}
|
||
|
||
/* if the user has requested that we only wait for the result to
|
||
be confident rather than the result and CPU util(s) then do
|
||
so. raj 2007-08-08 */
|
||
if (!result_confidence_only) {
|
||
confidence = min(min(result_confid,loc_cpu_confid),rem_cpu_confid);
|
||
}
|
||
else {
|
||
confidence = result_confid;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* here ends the IBM code */
|
||
|
||
void
|
||
retrieve_confident_values(float *elapsed_time,
|
||
double *thruput,
|
||
float *local_cpu_utilization,
|
||
float *remote_cpu_utilization,
|
||
float *local_service_demand,
|
||
float *remote_service_demand)
|
||
|
||
{
|
||
*elapsed_time = (float)measured_mean_local_time;
|
||
*thruput = measured_mean_result;
|
||
*local_cpu_utilization = (float)measured_mean_local_cpu;
|
||
*remote_cpu_utilization = (float)measured_mean_remote_cpu;
|
||
*local_service_demand = (float)measured_mean_local_service_demand;
|
||
*remote_service_demand = (float)measured_mean_remote_service_demand;
|
||
}
|
||
|
||
/* display_confidence() is called when we could not achieve the */
|
||
/* desirec confidence in the results. it will print the achieved */
|
||
/* confidence to "where" raj 11/94 */
|
||
void
|
||
display_confidence()
|
||
|
||
{
|
||
fprintf(where,
|
||
"!!! WARNING\n");
|
||
fprintf(where,
|
||
"!!! Desired confidence was not achieved within ");
|
||
fprintf(where,
|
||
"the specified iterations.\n");
|
||
fprintf(where,
|
||
"!!! This implies that there was variability in ");
|
||
fprintf(where,
|
||
"the test environment that\n");
|
||
fprintf(where,
|
||
"!!! must be investigated before going further.\n");
|
||
fprintf(where,
|
||
"!!! Confidence intervals: Throughput : %4.1f%%\n",
|
||
100.0 * (interval - result_confid));
|
||
fprintf(where,
|
||
"!!! Local CPU util : %4.1f%%\n",
|
||
100.0 * (interval - loc_cpu_confid));
|
||
fprintf(where,
|
||
"!!! Remote CPU util : %4.1f%%\n\n",
|
||
100.0 * (interval - rem_cpu_confid));
|
||
}
|
||
|