diff --git a/netlib.c b/netlib.c new file mode 100644 index 0000000..884319a --- /dev/null +++ b/netlib.c @@ -0,0 +1,4968 @@ +char netlib_id[]="\ +@(#)netlib.c (c) Copyright 1993-2012 Hewlett-Packard Company. Version 2.6.0"; + + +/****************************************************************/ +/* */ +/* 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 */ +/* random_ip_address() select a random IP address from */ +/* specified range */ +/* */ +/* the routines you get when WANT_DLPI is defined... */ +/* ...all moved to src/nettest_dlpi.c */ +/* */ +/* 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 +#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 +#include +#ifdef HAVE_SYSCALL_H +#include +#endif +#ifdef MPE +# define NSIG _NSIG +#endif /* MPE */ +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ENDIAN_H +#include +#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 compile later revisions of + netperf. raj 1/96 */ +#include +#include +#include +#ifndef MPE +#include +#include +#endif /* MPE */ +#include +#include +#include +#include +#include +#include +#if !defined(MPE) && !defined(__VMS) +#include +#endif /* MPE */ + +#else /* WIN32 */ + +#include +#include +#include +#define netperf_socklen_t socklen_t +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +/* 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 +#endif + +#define SIGALRM (14) +#define sleep(x) Sleep((x)*1000) + +#endif /* WIN32 */ + +#ifdef HAVE_UNAME +#include +#endif + +#ifdef _AIX +#include +#include +#include +#define PRIORITY PRI_LOW +#else/* _AIX */ +#ifdef __sgi +#include +#include +#define PRIORITY NDPLOMIN +#endif /* __sgi */ +#endif /* _AIX */ + + +#ifdef HAVE_MPCTL +#include +#endif + +#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) +# include "missing/getaddrinfo.h" +#endif + + +#include "hist.h" + +/****************************************************************/ +/* */ +/* Local Include Files */ +/* */ +/****************************************************************/ +#define NETLIB +#include "netlib.h" +#include "netsh.h" +#include "netcpu.h" +#include "netperf_version.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 */ + +struct cpu_stats_struct + lib_local_cpu_stats, + lib_remote_cpu_stats; + +#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; + +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; +int control_family = AF_UNSPEC; + +/* global variables to hold the value for processor affinity */ +int local_proc_affinity = -1,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; + +char *local_sysname, *remote_sysname; +char *local_release, *remote_release; +char *local_version, *remote_version; +char *local_machine, *remote_machine; + +int local_data_family=AF_UNSPEC; +int remote_data_family=AF_UNSPEC; + +char *netperf_version; + +enum netperf_output_modes netperf_output_mode = HUMAN; + +/* 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 WIN32 +HANDLE hAlarm = INVALID_HANDLE_VALUE; +int timed_out=0; +#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 +#ifdef WIN32 +HANDLE WinTimer; +UINT timerRes; +void stop_itimer() +{ + CancelWaitableTimer(WinTimer); + CloseHandle(WinTimer); + timeEndPeriod(timerRes); +} +#else +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 /* WIN32 */ +#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 * +nsec_enabled_to_str(int enabled) { + switch (enabled) { + case NSEC_UNKNOWN: + return("Unknown"); + case NSEC_DISABLED: + return("Disabled"); + case NSEC_PERMISSIVE: + return("Permissive"); + case NSEC_ENFORCING: + return("Enforcing"); + default: + return("UNKNOWN MODE"); + } +} + +char * nsec_type_to_str(int type) { + switch (type) { + case NSEC_TYPE_UNKNOWN: + return("Unknown"); + case NSEC_TYPE_SELINUX: + return("SELinux"); + default: + return("UNKNOWN TYPE"); + } +} + + +char * +inet_ttos(int type) +{ + switch (type) { + case SOCK_DGRAM: + return("SOCK_DGRAM"); + break; + case SOCK_STREAM: + return("SOCK_STREAM"); + break; +#ifdef SOCK_DCCP + case SOCK_DCCP: + return("SOCK_DCCP"); +#endif +#ifdef SOCK_SEQPACKET + case SOCK_SEQPACKET: + return("SOCK_SEQPACKET"); +#endif + 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 +#if defined(IPPROTO_DCCP) + case IPPROTO_DCCP: + return "IPPROTO_DCCP"; + break; +#endif +#if defined(IPPROTO_UDPLITE) + case IPPROTO_UDPLITE: + return "IPPROTO_UDPLITE"; + 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"); +#if defined(AF_INET6) + case AF_INET6: + return("AF_INET6"); +#endif +#if defined(AF_INET_SDP) + case AF_INET_SDP: + return("AF_INET_SDP"); +#endif +#if defined(AF_RDS) + case AF_RDS: + return("AF_RDS"); +#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 +#if defined(AF_RDS) + case AF_RDS: + if (cnt >= 4) { + memcpy(dst,src,4); + return 4; + } +#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); + +} + + + +/* The original patch from Google used lrand48, but I have been + informed that is not easily available under Windows. So, rather + than have some #ifdefs here I'll just simplistically replace + lrand48 with rand(), which should be "good enough" at some point it + may be sufficient to just call rand() directly rather than call + this raj 20101130 */ + +unsigned int +rand32(){ + return (unsigned int)rand() * 2 + rand() % 2; +} + +/* this routine will set the ip address of the sockaddr in the + addrinfo to a random number in range, based on the address + family. for grins, we will sanity check the value of mask_len + against the address family. initial version from google, + enhancements by raj 20101129 */ +void +random_ip_address(struct addrinfo *res, int mask_len) +{ + switch(res->ai_family) { + case AF_INET: { + struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; + unsigned int addr = ntohl(foo->sin_addr.s_addr); + unsigned int mask = ((unsigned int)1 << (32 - mask_len)) - 1; + + if ((mask_len < 0) || (mask_len > 32)) { + fprintf(where, + "Mask length must be between 0 and 32 inclusive for AF_INET\n"); + fflush(where); + exit(-1); + } + + addr = ntohl(foo->sin_addr.s_addr); + do { + addr = (addr & ~mask) | (rand32() & mask); + } while ((addr & 0xff) == 0xff); + foo->sin_addr.s_addr = htonl(addr); + break; + } +#if defined(AF_INET6) + case AF_INET6: { + struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; + + unsigned int i, len; + unsigned int *addr = (unsigned int *)&(foo->sin6_addr.s6_addr); + unsigned int mask; + + if ((mask_len < 0) || (mask_len > 128)) { + fprintf(where, + "Mask length must be between 0 and 128 inclusive for AF_INET\n"); + fflush(where); + exit(-1); + } + + for (i = 0; i < 4; i ++){ + addr[i] = ntohl(addr[i]); + len = mask_len - i * 32; + len = ((len < 32) ? len : 32); + len = ((len > 0) ? len : 0); + mask = ((unsigned int)1 << (32 - len)) - 1; + addr[i] = (addr[i] & ~mask) | (rand32() & mask); + addr[i] = htonl(addr[i]); + } + break; + } +#endif + default: + fprintf(where, + "Unexpected Address Family of %u\n",res->ai_family); + fflush(where); + exit(-1); + } +} + +#if defined(HAVE_SENDFILE) +int netperf_sendfile(SOCKET send_socket, struct ring_elt *send_ring) { + + int len; + int ret = 0; + +#if defined(__linux) || defined(__sun) + off_t scratch_offset; /* the linux sendfile() call will update + the offset variable, which is + something we do _not_ want to happen + to the value in the send_ring! so, we + have to use a scratch variable. */ +#endif /* __linux || defined(__sun) */ + +#if defined (__sun) + size_t scratch_len; /* the sun sendfilev() needs a place to + tell us how many bytes were written, + even though it also returns the value */ + sendfilevec_t sv; +#endif /* __sun */ + + /* you can look at netlib.h for a description of the fields we + are passing to sendfile(). 08/2000 */ +#if defined(__linux) + scratch_offset = send_ring->offset; + len=sendfile(send_socket, + send_ring->fildes, + &scratch_offset, /* modified after the call! */ + send_ring->length); +#elif defined (__sun) + /* We must call with SFV_NOWAIT and a large file size (>= 16MB) + to get zero-copy, as well as compiling with + -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 */ + sv.sfv_fd = send_ring->fildes; + sv.sfv_flag = SFV_NOWAIT; + sv.sfv_off = send_ring->offset; + sv.sfv_len = send_ring->length; + len = sendfilev(send_socket, &sv, 1, &scratch_len); +#elif defined(__FreeBSD__) + /* so close to HP-UX and yet so far away... :) */ + ret = sendfile(send_ring->fildes, + send_socket, + send_ring->offset, + send_ring->length, + NULL, + (off_t *)&len, + send_ring->flags); +#elif defined(USE_OSX) + len = send_ring->length; + ret = sendfile(send_ring->fildes, + send_socket, + send_ring->offset, + (off_t *)&len, + NULL, + send_ring->flags); +#else /* original sendile HP-UX */ + len=sendfile(send_socket, + send_ring->fildes, + send_ring->offset, + send_ring->length, + send_ring->hdtrl, + send_ring->flags); +#endif + + /* for OSX and FreeBSD, a non-zero ret means something failed. + I would hope that the length fields are set to -1 or the + like, but at the moment I do not know I can count on + that. for other platforms, ret will be set to zero and we can + rely directly on len. raj 2013-05-01 */ + if (ret != 0) + return -1; + else + return len; + +} +#endif + + +/* 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 + + 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: 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" + "Please alter MAXCPUS in netlib.h and recompile.\n", + temp_cpus, + MAXCPUS); + 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 */ + + + /* 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 */ + +} + + + +/************************************************************************/ +/* */ +/* signal catcher */ +/* */ +/************************************************************************/ +#ifndef WIN32 +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: + times_up = 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(); + /* we should also stop the normal test timer lest it fire at an + inopportune moment - we do not know if we got here off the + interval timer or the test timer... */ + stop_timer(); +#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 */ + 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; +} +#endif /* WIN32 */ + +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++) { + switch (i) { + case SIGALRM: + case SIGPROF: + case SIGSTOP: + case SIGKILL: + break; + default: + 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; + DWORD HandlesClosedFlags = 0; + + /* 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; + + /* Give the other threads time to notice that times_up has + changed state before taking the harsh step of closing the + sockets. */ + timed_out=0; + if (WaitForSingleObject(hAlarm, PAD_TIME/2*1000) == + WAIT_TIMEOUT) { + timed_out=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) { + HandlesClosedFlags |= 1; + closesocket(win_kludge_socket); + } + if (win_kludge_socket2 != INVALID_SOCKET) { + HandlesClosedFlags |= 2; + closesocket(win_kludge_socket2); + } + } + if(debug) { + fprintf(where, + "emulate_alarm - HandlesClosedFlags: %x\n", + HandlesClosedFlags); + fflush(where); + } + } +} + + +#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; +int ret; + +if (debug) { + fprintf(where,"About to start a timer for %d seconds.\n",time); + fflush(where); +} + + action.sa_handler = catcher; + +#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 */ + + sigemptyset(&(action.sa_mask)); + sigaddset(&(action.sa_mask),SIGALRM); + if (sigaction(SIGALRM, &action, NULL) < 0) { + fprintf(where, + "start_timer: error installing alarm handler errno %d\n", + errno); + fflush(where); + exit(-1); + } + + sigemptyset(&(action.sa_mask)); + sigaddset(&(action.sa_mask),SIGINT); + if (sigaction(SIGINT, &action, NULL) < 0) { + fprintf(where, + "start_timer: error installing SIGINT handler errno %d\n", + errno); + fflush(where); + exit(-1); + } + + /* this is the easy case - just set the timer for so many seconds */ + ret = alarm(time); + if (ret != 0) { + fprintf(where, + "error starting alarm timer, ret %d errno %d\n", + ret, + errno); + fflush(where); + exit(-1); + } +#endif /* WIN32 */ + + test_len_ticks = 1; + +} + + + +#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 ) +{ +#ifdef WIN32 + LARGE_INTEGER liDueTime; + TIMECAPS ptc; + MMRESULT mmr; + + /* make sure timer resolution is at least as small as interval length */ + timerRes=interval_len_msec; + mmr=timeGetDevCaps(&ptc, sizeof (ptc)); + if (mmr==TIMERR_NOERROR){ + if (interval_len_msec 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 test len %d ticks\n", + usec_per_itvl / 1000000, + usec_per_itvl % 1000000, + 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 /* WIN32*/ +} +#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] = -1.0; + } + + lib_local_cpu_stats.peak_cpu_id = -1; + lib_local_cpu_stats.peak_cpu_util = -1.0; + lib_remote_cpu_stats.peak_cpu_id = -1; + lib_remote_cpu_stats.peak_cpu_util = -1.0; + + netperf_version = strdup(NETPERF_VERSION); + + /* 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" + "netlib_init: response_array at %p\n", + request_array, + response_array); + fflush(where); + } + + /* some functionality might want to use random numbers, so we should + initialize the random number generator */ + srand(getpid()); + +} + +/* 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 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 be filled from "default_fill" + which will be "netperf" so anyone sniffing the traffic will have a + better idea what this traffic happens to be. */ + +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(local_fill_file,"") == 0) { + do_fill = 0; + fill_source = NULL; + } + else { + do_fill = 1; + fill_source = (FILE *)fopen(local_fill_file,"r"); + if (fill_source == (FILE *)NULL) { + fprintf(where,"Could not open requested fill file: %s\n", + strerror(errno)); + fflush(where); + } + } + + 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) { + fprintf(where, + "malloc(%u) failed!\n", + (unsigned int)sizeof(struct ring_elt)); + exit(-1); + } + temp_link->completion_ptr = NULL; + /* 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->buffer_base == NULL) { + fprintf(where, + "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 +#include + +/* 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 local_fill_file. if + local_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 (local_fill_file, "") == 0) { + do_fill = 0; + fill_source = NULL; + } else { + do_fill = 1; + fill_source = (FILE *) fopen (local_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 is 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 ring_elt * +alloc_sendfile_buf_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 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(local_fill_file,"") == 0) { + /* use an temp file for the fill file */ + char temp_file[] = {"netperfXXXXXX\0"}; + 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 */ + fildes = mkstemp(temp_file); + /* no need to call open because mkstemp did it */ + if (-1 != fildes) { + int count; + int *int_ptr; + + /* we initialize the random number generator in + netlib_init() now. raj 20110111 */ + + /* 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("alloc_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(local_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(local_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" + "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 ring_elt *) + malloc(sizeof(struct ring_elt)); + if (temp_link == NULL) { + fprintf(where, + "malloc(%u) failed!\n", + (unsigned int) 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; + } + + /* 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() +{ +int counter = 0; +fprintf(where,"request contents:\n"); +for (counter = 0; counter < ((sizeof(netperf_request)/4)-3); counter += 4) { + fprintf(where,"%d:\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() +{ +int counter = 0; + +fprintf(where,"response contents\n"); +for (counter = 0; counter < ((sizeof(netperf_response)/4)-3); counter += 4) { + fprintf(where,"%d:\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 'B': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f" , number); + break; + 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 'b': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f" , number * 8); + 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 'B': + strcpy(unitbuf, "Bytes"); + break; + case 'K': + strcpy(unitbuf, "KBytes"); + break; + case 'M': + strcpy(unitbuf, "MBytes"); + break; + case 'G': + strcpy(unitbuf, "GBytes"); + break; + case 'b': + strcpy(unitbuf, "10^0bits"); + 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; + case 'u': + strcpy(unitbuf,"Usec"); + 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 use_cpu_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[use_cpu_affinity]; + } + else { + mapped_affinity = use_cpu_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 +#include +#include + processor_bind(P_PID,P_MYID,mapped_affinity,NULL); +#elif HAVE_BINDPROCESSOR +#include + /* 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 + /* 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 . 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 +#if defined(__CPU_SET_S) +#define NETPERF_CPU_SET(cpu, cpusetp) __CPU_SET_S(cpu, sizeof (cpu_set_t), cpusetp) +#define NETPERF_CPU_ZERO(cpusetp) __CPU_ZERO_S (sizeof (cpu_set_t), cpusetp) +#else +#define NETPERF_CPU_SET(cpu, cpusetp) __CPU_SET(cpu, cpusetp) +#define NETPERF_CPU_ZERO(cpusetp) __CPU_ZERO (cpusetp) +#endif + 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 +#include +#include + + /* 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 use_cpu_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); + } + } + +#elif defined(__FreeBSD__) +#include + /* FreeBSD introduced cpuset_setaffinity() in version 7.1 */ +#if (__FreeBSD_version > 701000) +#include + + cpuset_t mask; + + CPU_ZERO(&mask); + CPU_SET(mapped_affinity, &mask); + if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, + sizeof(mask), &mask)) { + perror("cpuset_setaffinity failed"); + fflush(stderr); + } +#endif /* __FreeBSD_version */ +#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 a request, only converting the first n ints-worth of the + test-specific data via htonl() before sending on the + connection. the first two ints, which are before the test-specific + portion are always converted. raj 2008-02-05 */ + +void +send_request_n(int n) +{ + + int counter,count; + + if (n < 0) count = sizeof(netperf_request)/4; + else count = 2 + n; + + /* silently truncate if the caller called for more than we have */ + if (count > sizeof(netperf_request)/4) { + if (debug > 1) { + fprintf(where, + "WARNING, htonl conversion count of %d was larger than netperf_request\n", + count - 2); + fflush(where); + } + count = sizeof(netperf_request)/4; + } + + /* 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_n...contents before %d htonls:\n", + count); + dump_request(); + } + + /* pass the processor affinity request value to netserver this is a + kludge and I know it. sgb 8/11/04. we keep this here to deal + with there being two paths to this place - direct and via + send_request() */ + + 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 < count; counter++) { + request_array[counter] = htonl(request_array[counter]); + } + + if (debug > 1) { + fprintf(where,"send_request_n...contents after %d htonls:\n", + count); + dump_request(); + + fprintf(where, + "\nsend_request: about to send %u bytes from %p\n", + (unsigned int) 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_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() +{ + + /* 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; + + /* call send_request_n telling it to convert everything */ + + send_request_n(-1); + +} + +/* send a response, only converting the first n ints-worth of the + test-specific data via htonl() before sending on the + connection. the first two ints, which are before the test-specific + portion are always converted. raj 2008-02-05 */ + +void +send_response_n(int n) +{ + int counter, count; + int bytes_sent; + + if (n < 0) count = sizeof(netperf_request)/4; + else count = 2 + n; + + /* silently truncate if the caller called for more than we have */ + if (count > sizeof(netperf_request)/4) { + if (debug > 1) { + fprintf(where, + "WARNING, htonl conversion count of %d was larger than netperf_request\n", + count - 2); + fflush(where); + } + count = sizeof(netperf_request)/4; + } + + /* 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_n: contents of %u ints before %d htonl,\n", + (unsigned int) sizeof(netperf_response)/4, + count); + 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 < count; counter++) { + response_array[counter] = htonl(response_array[counter]); + } + + if (debug > 1) { + fprintf(where, + "send_response_n: contents after htonl\n"); + dump_response(); + fprintf(where, + "about to send %u bytes from %p\n", + (unsigned int) 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_n: send call failure"); + fprintf(where, "BytesSent: %d\n", bytes_sent); + 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() +{ + + send_response_n(-1); + +} + +/* go back and "undo" the ntohl that recv_request() did, starting with + the specified point and going to the end of the request array */ +void +fixup_request_n(int n) +{ + int i; + int limit; + + limit = sizeof(netperf_request) / 4; + /* we must remember that the request_array also contains two ints of + "other" stuff, so we start the fixup two in - at least I think we + should. raj 2012-04-02 */ + for (i = n + 2; i < limit; i++) { + request_array[i] = htonl(request_array[i]); + } + if (debug > 1) { + fprintf(where, + "%s: request contents after fixup at the %d th int\n", + __FUNCTION__, + n); + dump_request(); + fflush(where); + } +} + +/* receive a request, only converting the first n ints-worth of the + test-specific data via htonl() before sending on the + connection. the first two ints, which are before the test-specific + portion are always converted. raj 2008-02-05 */ + +int +recv_request_timed_n(int n, int seconds) +{ + int tot_bytes_recvd, + bytes_recvd, + bytes_left; + char *buf = (char *)&netperf_request; + int buflen = sizeof(netperf_request); + int counter,count; + + fd_set readfds; + struct timeval timeout; + + if (n < 0) count = sizeof(netperf_request)/4; + else count = 2 + n; + + /* silently truncate if the caller called for more than we have */ + if (count > sizeof(netperf_request)/4) { + if (debug > 1) { + fprintf(where, + "WARNING, htonl conversion count of %d was larger than netperf_request\n", + count - 2); + fflush(where); + } + count = sizeof(netperf_request)/4; + } + + /* for the time being, we rather rely on select decrementing timeout + each time to preclude someone with nefarious intent from just + dribbling data to us piecemeal. of course, who knows what + someone with nefarious intent might come-up with. raj 2012-01-23 */ + tot_bytes_recvd = 0; + bytes_recvd = 0; /* nt_lint; bytes_recvd uninitialized if buflen == 0 */ + bytes_left = buflen; + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + do { + FD_ZERO(&readfds); + FD_SET(server_sock,&readfds); + if (select(FD_SETSIZE, + &readfds, + 0, + 0, + (seconds > 0) ? &timeout : NULL) != 1) { + fprintf(where, + "Issue receiving request on control connection. Errno %d (%s)\n", + errno, + strerror(errno)); + fflush(where); + close(server_sock); + return -1; + } + + if ((bytes_recvd = recv(server_sock, buf, bytes_left, 0)) > 0) { + tot_bytes_recvd += bytes_recvd; + buf += bytes_recvd; + bytes_left -= bytes_recvd; + } + } while ((tot_bytes_recvd != buflen) && + (bytes_recvd > 0 )); + + /* put the request into host order */ + + for (counter = 0; counter < count; 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); + close(server_sock); + return -1; + } + + if (bytes_recvd == 0) { + /* the remote has shutdown the control connection, we should shut + it down as well and return */ + if (debug) { + fprintf(where, + "recv_request: remote requested shutdown of control\n"); + fflush(where); + } + + close(server_sock); + return 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); + close(server_sock); + return -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); + } + + return buflen; +} + +/* receive a request, only converting the first n ints-worth of the + test-specific data via htonl() before sending on the + connection. the first two ints, which are before the test-specific + portion are always converted. raj 2008-02-05 */ + +int +recv_request_n(int n) +{ + + return recv_request_timed_n(n,0); + +} + + /***********************************************************************/ + /* */ + /* 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. */ + /* */ + /***********************************************************************/ + +int +recv_request() +{ + + return recv_request_n(-1); + +} + +void +recv_response_timed_n(int addl_time, int n) +{ + int tot_bytes_recvd, + bytes_recvd = 0, + bytes_left; + char *buf = (char *)&netperf_response; + int buflen = sizeof(netperf_response); + int counter,count; + + /* stuff for select, use fd_set for better compliance */ + fd_set readfds; + struct timeval timeout; + + tot_bytes_recvd = 0; + bytes_left = buflen; + + if (n < 0) count = sizeof(netperf_request)/4; + else count = 2 + n; + + /* silently truncate if the caller called for more than we have */ + if (count > sizeof(netperf_request)/4) { + if (debug > 1) { + fprintf(where, + "WARNING, htonl conversion count of %d was larger than netperf_response\n", + count - 2); + fflush(where); + } + count = sizeof(netperf_request)/4; + } + + /* 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, + "%s: no response received. errno %d counter %d\n", + __FUNCTION__, + 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 desired quantity of the response into host order */ + + for (counter = 0; counter < count; 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(); + } +} + +/* + + 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) +{ + + /* -1 => convert all the test-specific data via ntohl */ + recv_response_timed_n(addl_time,-1); + +} + +void +recv_response() +{ + /* 0 => no additional time, -1 => convert all test-specific data */ + recv_response_timed_n(0,-1); +} + +void +recv_response_n(int n) +{ + recv_response_timed_n(0,n); +} + + + + +#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 +get_sock_buffer (SOCKET sd, enum sock_buffer which, int *effective_sizep) +{ +#ifdef SO_SNDBUF + int optname = (which == SEND_BUFFER) ? SO_SNDBUF : SO_RCVBUF; + netperf_socklen_t sock_opt_len; + + sock_opt_len = sizeof(*effective_sizep); + if (getsockopt(sd, SOL_SOCKET, optname, (char *)effective_sizep, + &sock_opt_len) < 0) { + fprintf(where, "netperf: get_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: get_sock_buffer: " + "%s socket size determined to be %d\n", + (which == SEND_BUFFER) ? "send" : "receive", *effective_sizep); + fflush(where); + } + +#else + *effective_sizep = -1; +#endif +} + +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; + + /* 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 (%s)\n", + (which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF", + errno, + strerror(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); + } + } + + /* the getsockopt() call that used to be here has been hoisted into + its own routine to be used on those platforms where the socket + buffer sizes might change from the beginning to the end of the + run. raj 2008-01-15 */ + + get_sock_buffer(sd, which, effective_sizep); + +#else /* SO_SNDBUF */ + *effective_sizep = -1; +#endif /* SO_SNDBUF */ +} + +void +dump_addrinfo(FILE *dumploc, struct addrinfo *info, + const char *host, char *port, int family) +{ + struct sockaddr *ai_addr; + struct addrinfo *temp; + temp=info; + + fprintf(dumploc, + "getaddrinfo returned the following for host '%s' port '%s' " + " family %s\n", + host, + port, + 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 */ + fprintf(dumploc, + "\tcannonical name: '%s'\n" + "\tflags: %x family: %s: socktype: %s protocol %s addrlen %d\n", + (temp->ai_canonname) ? temp->ai_canonname : "(nil)", + 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) { + int i; + fprintf(dumploc, + "\tsa_family: %s sadata:", + inet_ftos(ai_addr->sa_family)); + for (i = 0; i < (int) temp->ai_addrlen; i++) { + fprintf(dumploc, + (temp->ai_family == AF_INET) ? " %d" : " %.2x", + (u_char)ai_addr->sa_data[i]); + } + fprintf(dumploc,"\n"); + } + temp = temp->ai_next; + } + fflush(dumploc); +} + +struct addrinfo * +resolve_host(char *hostname, + char *port, + int family) +{ + struct addrinfo hints; + struct addrinfo *ai; + int count; + int error; + + if (debug) { + fprintf(where, + "resolve_host called with host '%s' port '%s' family %s\n", + hostname, + port, + inet_ftos(family)); + fflush(where); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + count = 0; + do { + error = getaddrinfo((char *)hostname, + (char *)port, + &hints, + &ai); + 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("%s: could not resolve host '%s' port '%s' af %s" + "\n\tgetaddrinfo returned %d %s\n", + __FUNCTION__, + hostname, + port, + inet_ftos(family), + error, + gai_strerror(error)); + return(NULL); + } + + if (debug) { + dump_addrinfo(where, ai, hostname, port, family); + } + + return (ai); +} + +/* + 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 + + 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; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + struct addrinfo *local_res_temp; + struct addrinfo *remote_res_temp; + + remote_res = resolve_host(hostname, port, remfam); + if (!remote_res) + return(INVALID_SOCKET); + + local_res = resolve_host(localhost, localport, locfam); + if (!local_res) + return(INVALID_SOCKET); + + if (debug) { + fprintf(where, + "establish_control called with host '%s' port '%s' remfam %s\n" + "\t\tlocal '%s' port '%s' locfam %s\n", + hostname, + port, + inet_ftos(remfam), + localhost, + localport, + inet_ftos(locfam)); + fflush(where); + } + + 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" + " trying next address combination\n", + errno, + strerror(errno)); + fflush(where); + } + } + } + else { + /* the bind failed */ + if (debug) { + fprintf(where, + "establish_control: bind failed, errno %d %s\n" + " trying next address combination\n", + errno, + strerror(errno)); + 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); + } + + control_family = local_res_temp->ai_family; + + /* 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); + control_family = AF_UNSPEC; + 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); +#ifdef WIN32 + if (timed_out) lib_elapsed-=PAD_TIME/2; +#endif + *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; + case 'x': + case 'b': + case 'B': + divisor = 1.0; + break; + case 'u': + /* latency in microseconds a bit squirrely but we don't want to + really muck with things for the default return statement. + invert transactions per second and multiply to get microseconds + per transaction */ + return (1 / (units_received / elapsed)) * 1000000.0; + + 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': + case 'b': + case 'B': + divisor = 1.0; + break; + case 'u': + /* latency in microseconds a bit squirrely but we don't want to + really muck with things for the default return statement. + invert transactions per second and multiply to get microseconds + per transaction */ + return (1 / (units_received / elapsed)) * 1000000.0; + + 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) +{ + float temp_util; + int i; + temp_util = calc_cpu_util_internal(elapsed_time); + + /* now, what was the most utilized CPU and its util? */ + for (i = 0; i < MAXCPUS; i++) { + if (lib_local_per_cpu_util[i] > lib_local_cpu_stats.peak_cpu_util) { + lib_local_cpu_stats.peak_cpu_util = lib_local_per_cpu_util[i]; + lib_local_cpu_stats.peak_cpu_id = lib_cpu_map[i]; + } + } + + return temp_util; +} + +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" + " elapsed_time = %f\n" + " cpu_util = %f\n" + " num cpu = %d\n", + units_sent, + elapsed_time, + cpu_utilization, + 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_stats.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" + " elapsed_time = %f\n" + " cpu_util = %f\n" + " num cpu = %d\n" + "calc_service_demand got: thruput = %f\n" + " servdem = %f\n", + units_sent, + elapsed_time, + cpu_utilization, + num_cpus, + thruput, + 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)); +} + +/* use the value of libfmt to determine the unit_divisor */ +float calc_service_demand_fmt(double units_sent, + float elapsed_time, + float cpu_utilization, + int num_cpus) + +{ + double unit_divisor; + + if ('x' == libfmt) unit_divisor = 1.0; + else unit_divisor = 1024.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 */ + +#if defined(WANT_INTERVALS) || defined(WANT_DEMO) + +int demo_mode; /* are we actually in demo mode? = 0 + == not in demo mode; 1 == classic + unit based demo mode; 2 == always + timestamp demo mode */ +double demo_interval = 1000000.0; /* what is the desired interval to + display interval results. default + is one second in units of + microseconds */ +double demo_units = 0.0; /* what is our current best guess as + to how many work units must be + done to be near the desired + reporting interval? */ + +double units_this_tick; +#endif + +#ifdef WANT_DEMO +#ifdef HAVE_GETHRTIME +static hrtime_t demo_one; +static hrtime_t demo_two; +static hrtime_t *demo_one_ptr = &demo_one; +static hrtime_t *demo_two_ptr = &demo_two; +static hrtime_t *temp_demo_ptr = &demo_one; +#elif defined(WIN32) +static LARGE_INTEGER demo_one; +static LARGE_INTEGER demo_two; +static LARGE_INTEGER *demo_one_ptr = &demo_one; +static LARGE_INTEGER *demo_two_ptr = &demo_two; +static LARGE_INTEGER *temp_demo_ptr = &demo_one; +#else +static struct timeval demo_one; +static struct timeval demo_two; +static struct timeval *demo_one_ptr = &demo_one; +static struct timeval *demo_two_ptr = &demo_two; +static struct timeval *temp_demo_ptr = &demo_one; +#endif + +void demo_first_timestamp() { + HIST_timestamp(demo_one_ptr); +} + +void demo_reset() { + if (debug) { + fprintf(where, + "Resetting interim results\n"); + fflush(where); + } + units_this_tick = 0; + demo_first_timestamp(); +} + +/* for a _STREAM test, "a" should be lss_size and "b" should be + rsr_size. for a _MAERTS test, "a" should be lsr_size and "b" should + be rss_size. raj 2005-04-06 */ +void demo_stream_setup(uint32_t a, uint32_t b) { + if ((demo_mode) && (demo_units == 0)) { + /* take our default value of demo_units to be the larger of + twice the remote's SO_RCVBUF or twice our SO_SNDBUF */ + if (a > b) { + demo_units = 2*a; + } + else { + demo_units = 2*b; + } + } +} + +void demo_interval_display(double actual_interval) +{ + static int count = 0; + struct timeval now; + + gettimeofday(&now,NULL); + switch (netperf_output_mode) { + case HUMAN: + fprintf(where, + "Interim result: %7.2f %s/s over %.3f seconds ending at %ld.%.3ld\n", + calc_thruput_interval(units_this_tick, + actual_interval/1000000.0), + format_units(), + actual_interval/1000000.0, + now.tv_sec, + (long) now.tv_usec/1000); + break; + case CSV: + fprintf(where, + "%7.2f,%s/s,%.3f,%ld.%.3ld\n", + calc_thruput_interval(units_this_tick, + actual_interval/1000000.0), + format_units(), + actual_interval/1000000.0, + now.tv_sec, + (long) now.tv_usec/1000); + break; + case KEYVAL: + fprintf(where, + "NETPERF_INTERIM_RESULT[%d]=%.2f\n" + "NETPERF_UNITS[%d]=%s/s\n" + "NETPERF_INTERVAL[%d]=%.3f\n" + "NETPERF_ENDING[%d]=%ld.%.3ld\n", + count, + calc_thruput_interval(units_this_tick, + actual_interval/1000000.0), + count, + format_units(), + count, + actual_interval/1000000.0, + count, + now.tv_sec, + (long) now.tv_usec/1000); + count += 1; + break; + default: + fprintf(where, + "Hey Ricky you not fine, theres a bug at demo time. Hey Ricky!"); + fflush(where); + exit(-1); + } + fflush(where); +} + +/* this has gotten long enough to warrant being an inline function + rather than a macro, and it has been enough years since all the + important compilers have supported such a construct so it should + not be a big deal. raj 2012-01-23 */ + +void demo_interval_tick(uint32_t units) +{ + double actual_interval = 0.0; + + switch (demo_mode) { + case 0: + return; + case 1: /* use the unit accumulation first */ + units_this_tick += units; + if (units_this_tick >= demo_units) { + /* time to possibly update demo_units and maybe output an + interim result */ + HIST_timestamp(demo_two_ptr); + actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); + /* we always want to fine-tune demo_units here whether we emit + an interim result or not. if we are short, this will + lengthen demo_units. if we are long, this will shorten it */ + demo_units = demo_units * (demo_interval / actual_interval); + } + else + return; + break; + case 2: /* Always timestamp */ + units_this_tick += units; + HIST_timestamp(demo_two_ptr); + actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); + + break; + default: + fprintf(where, + "Unexpected value of demo_mode of %d. Please report this as a bug.\n", + demo_mode); + fflush(where); + exit(-1); + } + + + + /* units == 0 will be when we have completed a test. we want to + emit a final interim results if there is anything to report */ + if (actual_interval >= demo_interval) { + /* time to emit an interim result, giving the current time to the + millisecond for compatability with RRD */ + demo_interval_display(actual_interval); + units_this_tick = 0.0; + /* now get a new starting timestamp. we could be clever + and swap pointers - the math we do probably does not + take all that long, but for now this will suffice */ + temp_demo_ptr = demo_one_ptr; + demo_one_ptr = demo_two_ptr; + demo_two_ptr = temp_demo_ptr; + + } +} + +void demo_interval_final() { + double actual_interval; + + switch (demo_mode) { + case 0: + return; + case 1: + case 2: + if (units_this_tick > 0.0) { + HIST_timestamp(demo_two_ptr); + actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); + demo_interval_display(actual_interval); + units_this_tick = 0.0; + } + } +} + +void demo_stream_interval(uint32_t units) { + demo_interval_tick(units); +} + +void demo_rr_setup(uint32_t a) { + if ((demo_mode) && (demo_units == 0)) { + /* take whatever we are given */ + demo_units = a; + } +} + +void demo_rr_interval(uint32_t units) { + demo_interval_tick(units); +} + +#endif + +/* 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_n(int max_outstanding) { + HIST h; + + if((h = (HIST) malloc(sizeof(struct histogram_struct))) == NULL) { + perror("HIST_new_n - histogram_struct malloc failed"); + exit(1); + } + HIST_clear(h); + + /* we never want to have a full queue, so will trade a little space + for that. one day we may still have to check for a full queue */ + h->limit = max_outstanding + 1; + + /* now allocate the time_ones based on h->limit */ +#ifdef HAVE_GETHRTIME + h->time_ones = (hrtime_t *) malloc(h->limit * sizeof(hrtime_t)); +#elif HAVE_GET_HRT + h->time_ones = (hrt_t *) malloc(h->limit * sizeof(hrt_t)); +#elif defined(WIN32) + h->time_ones = (LARGE_INTEGER *) malloc(h->limit * + sizeof(LARGE_INTEGER)); +#else + h->time_ones = (struct timeval *) malloc(h->limit * + sizeof(struct timeval)); +#endif /* HAVE_GETHRTIME */ + if (h->time_ones == NULL) { + perror("HIST_new_n - time_ones malloc failed"); + exit(1); + } + + return h; +} + +HIST +HIST_new(void){ + return HIST_new_n(0); +} + + +void +HIST_clear(HIST h){ + int i; + for(i = 0; i < HIST_NUM_OF_BUCKET; 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; + h->sum = 0; + h->sumsquare = 0; + h->hmin = 0; + h->hmax = 0; + h->limit = 0; + h->count = 0; + h->producer = 0; + h->consumer = 0; + h->time_ones = NULL; +} + +void +HIST_purge(HIST h) { + h->count = 0; + h->producer = 0; + h->consumer = 0; +} + +void +HIST_add(register HIST h, int time_delta){ + register float val; + register int base = HIST_NUM_OF_BUCKET / 10; + + /* check for < 0 added via VMware ESX patches. */ + + /* hoisted up to the top because we do not want to count any + ridiculous values in the actual statistics. right? raj + 2011-07-28 */ + if (time_delta < 0) { + h->ridiculous++; + return; + } + + if (!h->total) + h->hmin = h->hmax = time_delta; + h->total++; + h->sum += time_delta; + /* am I just being paranoid about the overhead of pow() when we + aren't all that interested in the statistics derived from it? + raj 20100914 */ + if (keep_statistics) { + h->sumsquare += pow(time_delta, 2); + } + h->hmin = ((h->hmin < time_delta) ? h->hmin : time_delta); + h->hmax = ((h->hmax > time_delta) ? h->hmax : time_delta); + val = (float) time_delta; + if(val < 10) h->unit_usec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->ten_usec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->hundred_usec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->unit_msec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->ten_msec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->hundred_msec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->unit_sec[(int)(val * base)]++; + else { + val /= 10; + if(val < 10) h->ten_sec[(int)(val * base)]++; + else h->ridiculous++; + } + } + } + } + } + } + } +} + +void +output_row(FILE *fd, char *title, int *row){ + register int i; + register int j; + register int base = HIST_NUM_OF_BUCKET / 10; + register int sum; + fprintf(where,"%s", title); + for(i = 0; i < 10; i++){ + sum = 0; + for (j = i * base; j < (i + 1) * base; j++) { + sum += row[j]; + } + fprintf(where,": %4d", sum); + } + fprintf(where,"\n"); +} + +int +sum_row(int *row) { + int sum = 0; + int i; + for (i = 0; i < HIST_NUM_OF_BUCKET; 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); + fprintf(where,">100_SECS: %d\n", h->ridiculous); + fprintf(where,"HIST_TOTAL: %d\n", h->total); + if (debug) { + fprintf(where, + "sum %"PRIi64", sumsquare %f, limit %d count %d\n", + h->sum, + h->sumsquare, + h->limit, + h->count); + } +} + +/* search buckets for each unit */ +int +HIST_search_bucket(int *unit, int num, int *last, int *current, double scale){ + int base = HIST_NUM_OF_BUCKET / 10; + int i; + for (i = 0; i < HIST_NUM_OF_BUCKET; i++){ + *last = *current; + *current += unit[i]; + if (*current >= num) + return (int)((i + (double)(num - *last)/(*current - *last)) * scale/base); + } + return 0; +} + +/* get percentile from histogram */ +int +HIST_get_percentile(HIST h, const double percentile){ + double win_kludge = percentile * (double) h->total; + int num = (int) win_kludge; + int last = 0; + int current = 0; + int result; + + if (!num) + return 0; + + /* search in unit usec range */ + result = HIST_search_bucket(h->unit_usec, num, &last, ¤t, 1e0); + if (result) + return result; + + /* search in ten usec range */ + result = HIST_search_bucket(h->ten_usec, num, &last, ¤t, 1e1); + if (result) + return result; + + /* search in ten hundred usec range */ + result = HIST_search_bucket(h->hundred_usec, num, &last, ¤t, 1e2); + if (result) + return result; + + /* search in unic msec range */ + result = HIST_search_bucket(h->unit_msec, num, &last, ¤t, 1e3); + if (result) + return result; + + /* search in ten msec range */ + result = HIST_search_bucket(h->ten_msec, num, &last, ¤t, 1e4); + if (result) + return result; + + /* search in hundred msec range */ + result = HIST_search_bucket(h->hundred_msec, num, &last, ¤t, 1e5); + if (result) + return result; + + /* search in unit sec range */ + result = HIST_search_bucket(h->unit_sec, num, &last, ¤t, 1e6); + if (result) + return result; + + /* search in ten sec range */ + result = HIST_search_bucket(h->ten_sec, num, &last, ¤t, 1e7); + if (result) + return result; + + return (int)(1e8); +} + + +/* get basic stats */ +void +HIST_get_stats(HIST h, int *min, int *max, double *mean, double *stddev){ + *min = h->hmin; + *max = h->hmax; + if (h->total){ + *mean = (double)h->sum / (double)h->total; + *stddev = (h->sumsquare * h->total - pow((double)h->sum, 2)) / + pow(h->total, 2); + *stddev = sqrt(*stddev); + } + else{ + *mean = 0; + *stddev = 0; + } +} + + +/* 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 */ + +void +HIST_timestamp_start(HIST h) { + + if (NULL == h) { + fprintf(where,"HIST_timestamp_start called with NULL histogram\n"); + fflush(where); + exit(-1); + } + if (h->count == h->limit) { + fprintf(where,"HIST_timestamp_start called with full time_ones\n"); + } + + HIST_timestamp(&(h->time_ones[h->producer])); + h->producer += 1; + h->producer %= h->limit; + h->count += 1; + + +} + +/* snap an ending timestamp and add the delta to the histogram */ +void +HIST_timestamp_stop_add(HIST h) { + + if (NULL == h) { + fprintf(where,"HIST_timestamp_stop called with NULL histogram\n"); + fflush(where); + exit(-1); + } + + if (h->consumer == h->producer) { + fprintf(where, + "HIST_timestamp_stop called with empty time_ones consumer %d producer %d\n", + h->consumer, + h->producer); + fflush(where); + exit(-1); + } + /* take our stopping timestamp */ + HIST_timestamp(&(h->time_two)); + + /* now add it */ + HIST_add(h,delta_micro(&(h->time_ones[h->consumer]),&(h->time_two))); + h->consumer += 1; + h->consumer %= h->limit; + h->count -= 1; + +} + + + +/* 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" + " lcpu %f; rcpu %f\n" + " lsdm %f; rsdm %f\n", + confidence_iterations, + time, + result, + loc_cpu, + rem_cpu, + 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; +} + +double +get_result_confid() +{ + return (double) (100.0 * (interval - result_confid)); +} + +double +get_loc_cpu_confid() +{ + return (double) (100.0 * (interval - loc_cpu_confid)); +} + +double +get_rem_cpu_confid() +{ + return (double) (100.0 * (interval - rem_cpu_confid)); +} + +/* display_confidence() is called when we could not achieve the + desired confidence in the results. it will print the achieved + confidence to "where" raj 11/94 */ +void +display_confidence() + +{ + fprintf(where, + "!!! WARNING\n" + "!!! Desired confidence was not achieved within " + "the specified iterations.\n" + "!!! This implies that there was variability in " + "the test environment that\n" + "!!! must be investigated before going further.\n" + "!!! Confidence intervals: Throughput : %4.3f%%\n" + "!!! Local CPU util : %4.3f%%\n" + "!!! Remote CPU util : %4.3f%%\n\n", + 100.0 * (interval - result_confid), + 100.0 * (interval - loc_cpu_confid), + 100.0 * (interval - rem_cpu_confid)); +}