diff options
Diffstat (limited to 'src/nettest_omni.c')
-rw-r--r-- | src/nettest_omni.c | 7490 |
1 files changed, 7490 insertions, 0 deletions
diff --git a/src/nettest_omni.c b/src/nettest_omni.c new file mode 100644 index 0000000..d4221a8 --- /dev/null +++ b/src/nettest_omni.c @@ -0,0 +1,7490 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WANT_OMNI +char nettest_omni_id[]="\ +@(#)nettest_omni.c (c) Copyright 2008-2012 Hewlett-Packard Co. Version 2.6.0"; + +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#if HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif +#if HAVE_SCHED_H +# include <sched.h> +#endif + +#include <fcntl.h> +#ifndef WIN32 +#include <errno.h> +#include <signal.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#include <ctype.h> + +#ifdef NOSTDLIBH +#include <malloc.h> +#endif /* NOSTDLIBH */ + +#include <assert.h> + +#ifndef WIN32 +#include <sys/socket.h> +#include <netinet/in.h> + +/* it would seem that including both <netinet/tcp.h> and <linux/tcp.h> + is not a path to happiness and joy when one wishes to grab tcp_info + stats and not get something like the compiler complaining about + either redefinitions, or missing tcpi_total_retrans. */ +#ifdef HAVE_LINUX_TCP_H +#include <linux/tcp.h> +#else +#include <netinet/tcp.h> +#endif + +/* this is to get us the definition of MSG_FASTOPEN. we may need to + cheat just a litle at first */ +#ifdef HAVE_LINUX_SOCKET_H +# include <linux/socket.h> +# ifndef MSG_FASTOPEN +# warning Using our own value for MSG_FASTOPEN +# define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */ +# endif +# ifndef TCP_FASTOPEN +# warning Using our own value for TCP_FASTOPEN +# define TCP_FASTOPEN 23 +# endif +#endif + +#ifdef HAVE_NETINET_SCTP_H +#include <netinet/sctp.h> +#endif + +#include <arpa/inet.h> +#include <netdb.h> +#else /* WIN32 */ +#include <process.h> +#define netperf_socklen_t socklen_t +#include <winsock2.h> +/* while it is unlikely that anyone running Windows 2000 or NT 4 is + going to be trying to compile this, if they are they will want to + define DONT_IPV6 in the sources file */ +#ifndef DONT_IPV6 +#include <ws2tcpip.h> +#endif +#include <windows.h> + +#define sleep(x) Sleep((x)*1000) + +#include "missing\stdint.h" +#endif /* WIN32 */ + +/* We don't want to use bare constants in the shutdown() call. In the + extremely unlikely event that SHUT_WR isn't defined, we will define + it to the value we used to be passing to shutdown() anyway. raj + 2007-02-08 */ +#if !defined(SHUT_WR) +#define SHUT_WR 1 +#endif + +#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) +# include "missing/getaddrinfo.h" +#endif + +#include "netlib.h" +#include "netsh.h" +#include "nettest_bsd.h" + +/* we only really use this once, but the initial patch to + src/nettest_bsd.c used it in several places. keep it as a macro + just for kicks and just in case we do end-up needing to use it + multiple times. */ + +#define WAIT_BEFORE_DATA_TRAFFIC() \ +{ \ + if (wait_time_secs) \ + sleep(wait_time_secs); \ +} \ + + +/* since someone can ask for latency stats, we will always include + this and do the other other things */ +#include "hist.h" + +static HIST time_hist; + + + +#ifdef WANT_INTERVALS +int interval_count; +unsigned int interval_wait_microseconds; + +/* hoist the timestamps up here so we can use them to factor-out the + time spent "waiting" */ +/* first out timestamp */ +#ifdef HAVE_GETHRTIME +static hrtime_t intvl_one; +static hrtime_t intvl_two; +static hrtime_t intvl_wait_start; +static hrtime_t *intvl_one_ptr = &intvl_one; +static hrtime_t *intvl_two_ptr = &intvl_two; +static hrtime_t *temp_intvl_ptr = &intvl_one; +#elif defined(WIN32) +static LARGE_INTEGER intvl_one; +static LARGE_INTEGER intvl_two; +static LARGE_INTEGER intvl_wait_start; +static LARGE_INTEGER *intvl_one_ptr = &intvl_one; +static LARGE_INTEGER *intvl_two_ptr = &intvl_two; +static LARGE_INTEGER *temp_intvl_ptr = &intvl_one; +#else +static struct timeval intvl_one; +static struct timeval intvl_two; +static struct timeval intvl_wait_start; +static struct timeval *intvl_one_ptr = &intvl_one; +static struct timeval *intvl_two_ptr = &intvl_two; +static struct timeval *temp_intvl_ptr = &intvl_one; +#endif + +#ifndef WANT_SPIN +#ifdef WIN32 +#define INTERVALS_INIT() \ + if (interval_burst) { \ + /* zero means that we never pause, so we never should need the \ + interval timer. we used to use it for demo mode, but we deal \ + with that with a variant on watching the clock rather than \ + waiting for a timer. raj 2006-02-06 */ \ + start_itimer(interval_wate); \ + } \ + interval_count = interval_burst; \ + interval_wait_microseconds = 0; +#else +sigset_t signal_set; +#define INTERVALS_INIT() \ + if (interval_burst) { \ + /* zero means that we never pause, so we never should need the \ + interval timer. we used to use it for demo mode, but we deal \ + with that with a variant on watching the clock rather than \ + waiting for a timer. raj 2006-02-06 */ \ + start_itimer(interval_wate); \ + } \ + interval_count = interval_burst; \ + interval_wait_microseconds = 0; \ + /* get the signal set for the call to sigsuspend */ \ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { \ + fprintf(where, \ + "%s: unable to get sigmask errno %d\n", \ + __FUNCTION__, \ + errno); \ + fflush(where); \ + exit(1); \ + } +#endif /* WIN32 */ + +#ifdef WIN32 +#define INTERVALS_WAIT() \ + /* in this case, the interval count is the count-down counter \ + to decide to sleep for a little bit */ \ + if ((interval_burst) && (--interval_count == 0)) { \ + /* call WaitForSingleObject and wait for the interval timer to get us \ + out */ \ + if (debug > 1) { \ + fprintf(where,"about to suspend\n"); \ + fflush(where); \ + } \ + HIST_timestamp(&intvl_wait_start); \ + if (WaitForSingleObject(WinTimer, INFINITE) != WAIT_OBJECT_0) { \ + fprintf(where, "WaitForSingleObject failed (%d)\n", GetLastError()); \ + fflush(where); \ + exit(1); \ + } \ + HIST_timestamp(&intvl_two); \ + interval_wait_microseconds += \ + delta_micro(&intvl_wait_start,&intvl_two); \ + interval_count = interval_burst; \ + } +#else +#define INTERVALS_WAIT() \ + /* in this case, the interval count is the count-down couter \ + to decide to sleep for a little bit */ \ + if ((interval_burst) && (--interval_count == 0)) { \ + /* call sigsuspend and wait for the interval timer to get us \ + out */ \ + if (debug > 1) { \ + fprintf(where,"about to suspend\n"); \ + fflush(where); \ + } \ + HIST_timestamp(&intvl_wait_start); \ + if (sigsuspend(&signal_set) == EFAULT) { \ + fprintf(where, \ + "%s: fault with sigsuspend.\n", \ + __FUNCTION__); \ + fflush(where); \ + exit(1); \ + } \ + HIST_timestamp(&intvl_two); \ + interval_wait_microseconds += \ + delta_micro(&intvl_wait_start,&intvl_two); \ + interval_count = interval_burst; \ + } +#endif /* WIN32 */ +#else + +#define INTERVALS_INIT() \ + if (interval_burst) { \ + HIST_timestamp(intvl_one_ptr); \ + } \ + interval_wait_microseconds = 0; \ + interval_count = interval_burst; \ + +#define INTERVALS_WAIT() \ + /* in this case, the interval count is the count-down couter \ + to decide to sleep for a little bit */ \ + if ((interval_burst) && (--interval_count == 0)) { \ + /* spin and wait for the interval timer to get us \ + out */ \ + if (debug > 1) { \ + fprintf(where,"about to spin suspend\n"); \ + fflush(where); \ + } \ + \ + HIST_timestamp(&intvl_wait_start); \ + do { \ + HIST_timestamp(intvl_two_ptr); } \ + while(delta_micro(intvl_one_ptr,intvl_two_ptr) < interval_usecs); \ + interval_wait_microseconds += \ + delta_micro(&intvl_wait_start,&intvl_two); \ + temp_intvl_ptr = intvl_one_ptr; \ + intvl_one_ptr = intvl_two_ptr; \ + intvl_two_ptr = temp_intvl_ptr; \ + interval_count = interval_burst; \ + } +#endif +#endif + +#define NETPERF_WAITALL 0x1 + +extern void get_uuid_string(char *string, size_t size); + +/* a boatload of globals while I settle things out */ +char *output_selection_spec = NULL; + +char test_uuid[38]; + +double result_confid_pct = -1.0; +double loc_cpu_confid_pct = -1.0; +double rem_cpu_confid_pct = -1.0; +double interval_pct = -1.0; + +int protocol; +int direction; +int remote_send_size = -1; +int remote_recv_size = -1; +int remote_send_size_req = -1; +int remote_recv_size_req = -1; +int remote_use_sendfile; + +extern int loc_dirty_count; +extern int loc_clean_count; +extern int rem_dirty_count; +extern int rem_clean_count; +int remote_checksum_off; +int connection_test; +int dont_give_up = 0; +int use_fastopen = 0; +int use_write = 0; +int need_to_connect; +int need_connection; +int bytes_to_send; +double bytes_per_send; +int failed_sends; +int bytes_to_recv; +double bytes_per_recv; +int null_message_ok = 0; + +int was_legacy = 0; +int legacy = 0; +int implicit_direction = 0; +int explicit_data_address = 0; +int want_defer_accept = 0; + +uint64_t trans_completed = 0; +int64_t units_remaining; +uint64_t bytes_sent = 0; +uint64_t bytes_received = 0; +uint64_t local_send_calls = 0; +uint64_t local_receive_calls = 0; +uint64_t remote_bytes_sent; +uint64_t remote_bytes_received; +uint64_t remote_send_calls; +uint64_t remote_receive_calls; +double bytes_xferd; +double remote_bytes_xferd; +double remote_bytes_per_recv; +double remote_bytes_per_send; +float elapsed_time; +float local_cpu_utilization; +float local_service_demand; +float remote_cpu_utilization; +float remote_service_demand; +double thruput; +double local_send_thruput; +double local_recv_thruput; +double remote_send_thruput; +double remote_recv_thruput; + +/* kludges for omni output */ +double elapsed_time_double; +double local_service_demand_double; +double remote_service_demand_double; +double transaction_rate = 1.0; +double rtt_latency = -1.0; +int32_t transport_mss = -2; +int32_t local_transport_retrans = -2; +int32_t remote_transport_retrans = -2; +char *deprecated_str = "Deprecated"; +int remote_interface_vendor = -2; +int remote_interface_device = -2; +int remote_interface_subvendor = -2; +int remote_interface_subdevice = -2; +int local_interface_vendor = -2; +int local_interface_device = -2; +int local_interface_subvendor = -2; +int local_interface_subdevice = -2; +int local_cpu_frequency = 0; +int remote_cpu_frequency = 0; + +int local_security_type_id; +int local_security_enabled_num; +int remote_security_type_id; +int remote_security_enabled_num; + +char local_cong_control[16] = ""; +char remote_cong_control[16] = ""; +char local_cong_control_req[16] = ""; +char remote_cong_control_req[16] = ""; + +int receive_timeout = -1; + +/* new statistics based on code diffs from Google, with raj's own + personal twist added to make them compatible with the omni + tests... 20100913 */ + +/* min and max "latency" */ +int min_latency = -1, max_latency = -1; +/* the percentiles */ +int p50_latency = -1, p90_latency = -1, p99_latency = -1; +/* mean and stddev - while the mean is reduntant with the *_RR test we + keep it because it won't be for other tests */ +double mean_latency = -1.0, stddev_latency = -1.0; + +/* default to zero to avoid randomizing */ +int local_mask_len = 0; +int remote_mask_len = 0; + +int printing_initialized = 0; + +char *sd_str; +char *thruput_format_str; + +char *socket_type_str; +char *protocol_str; +char *direction_str; + +extern int first_burst_size; + +int parallel_connections = 1; + +static int socket_debug = 0; + +#if defined(HAVE_SENDFILE) && (defined(__linux) || defined(__sun)) +#include <sys/sendfile.h> +#endif /* HAVE_SENDFILE && (__linux || __sun) */ + +static int confidence_iteration; + +static int local_cpu_method; +static int remote_cpu_method; + +/* these will control the width of port numbers we try to use in the */ +/* TCP_CRR and/or TCP_TRR tests. raj 3/95 */ +static int client_port_min = 5000; +static int client_port_max = 65535; + + /* different options for the sockets */ + +int + loc_nodelay, /* don't/do use NODELAY locally */ + rem_nodelay, /* don't/do use NODELAY remotely */ + loc_sndavoid, /* avoid send copies locally */ + loc_rcvavoid, /* avoid recv copies locally */ + rem_sndavoid, /* avoid send copies remotely */ + rem_rcvavoid; /* avoid recv_copies remotely */ + +extern int + loc_tcpcork, + rem_tcpcork, + local_connected, + remote_connected; + +enum netperf_output_type { + NETPERF_TYPE_UNKNOWN, + NETPERF_TYPE_UINT32, + NETPERF_TYPE_INT32, + NETPERF_TYPE_UINT64, + NETPERF_TYPE_INT64, + NETPERF_TYPE_CHAR, + NETPERF_TYPE_FLOAT, + NETPERF_TYPE_DOUBLE, +}; + +/* you should add to this in the order in which they should appear in + the default csv (everything) output */ + +enum netperf_output_name { + NETPERF_OUTPUT_UNKNOWN, + OUTPUT_NONE, + SOCKET_TYPE, + PROTOCOL, + DIRECTION, + ELAPSED_TIME, + THROUGHPUT, + THROUGHPUT_UNITS, + LSS_SIZE_REQ, + LSS_SIZE, + LSS_SIZE_END, + LSR_SIZE_REQ, + LSR_SIZE, + LSR_SIZE_END, + RSS_SIZE_REQ, + RSS_SIZE, + RSS_SIZE_END, + RSR_SIZE_REQ, + RSR_SIZE, + RSR_SIZE_END, + LOCAL_SEND_SIZE, + LOCAL_RECV_SIZE, + REMOTE_SEND_SIZE, + REMOTE_RECV_SIZE, + REQUEST_SIZE, + RESPONSE_SIZE, + LOCAL_CPU_UTIL, + LOCAL_CPU_PERCENT_USER, + LOCAL_CPU_PERCENT_SYSTEM, + LOCAL_CPU_PERCENT_IOWAIT, + LOCAL_CPU_PERCENT_IRQ, + LOCAL_CPU_PERCENT_SWINTR, + LOCAL_CPU_METHOD, + LOCAL_SD, + REMOTE_CPU_UTIL, + REMOTE_CPU_PERCENT_USER, + REMOTE_CPU_PERCENT_SYSTEM, + REMOTE_CPU_PERCENT_IOWAIT, + REMOTE_CPU_PERCENT_IRQ, + REMOTE_CPU_PERCENT_SWINTR, + REMOTE_CPU_METHOD, + REMOTE_SD, + SD_UNITS, + CONFIDENCE_LEVEL, + CONFIDENCE_INTERVAL, + CONFIDENCE_ITERATION, + THROUGHPUT_CONFID, + LOCAL_CPU_CONFID, + REMOTE_CPU_CONFID, + TRANSACTION_RATE, + RT_LATENCY, + BURST_SIZE, + LOCAL_TRANSPORT_RETRANS, + REMOTE_TRANSPORT_RETRANS, + TRANSPORT_MSS, + LOCAL_SEND_THROUGHPUT, + LOCAL_RECV_THROUGHPUT, + REMOTE_SEND_THROUGHPUT, + REMOTE_RECV_THROUGHPUT, + LOCAL_CPU_BIND, + LOCAL_CPU_COUNT, + LOCAL_CPU_PEAK_UTIL, + LOCAL_CPU_PEAK_ID, + LOCAL_CPU_MODEL, + LOCAL_CPU_FREQUENCY, + REMOTE_CPU_BIND, + REMOTE_CPU_COUNT, + REMOTE_CPU_PEAK_UTIL, + REMOTE_CPU_PEAK_ID, + REMOTE_CPU_MODEL, + REMOTE_CPU_FREQUENCY, + SOURCE_PORT, + SOURCE_ADDR, + SOURCE_FAMILY, + DEST_PORT, + DEST_ADDR, + DEST_FAMILY, + LOCAL_SEND_CALLS, + LOCAL_RECV_CALLS, + LOCAL_BYTES_PER_RECV, + LOCAL_BYTES_PER_SEND, + LOCAL_BYTES_SENT, + LOCAL_BYTES_RECVD, + LOCAL_BYTES_XFERD, + LOCAL_SEND_OFFSET, + LOCAL_RECV_OFFSET, + LOCAL_SEND_ALIGN, + LOCAL_RECV_ALIGN, + LOCAL_SEND_WIDTH, + LOCAL_RECV_WIDTH, + LOCAL_SEND_DIRTY_COUNT, + LOCAL_RECV_DIRTY_COUNT, + LOCAL_RECV_CLEAN_COUNT, + LOCAL_NODELAY, + LOCAL_CORK, + REMOTE_SEND_CALLS, + REMOTE_RECV_CALLS, + REMOTE_BYTES_PER_RECV, + REMOTE_BYTES_PER_SEND, + REMOTE_BYTES_SENT, + REMOTE_BYTES_RECVD, + REMOTE_BYTES_XFERD, + REMOTE_SEND_OFFSET, + REMOTE_RECV_OFFSET, + REMOTE_SEND_ALIGN, + REMOTE_RECV_ALIGN, + REMOTE_SEND_WIDTH, + REMOTE_RECV_WIDTH, + REMOTE_SEND_DIRTY_COUNT, + REMOTE_RECV_DIRTY_COUNT, + REMOTE_RECV_CLEAN_COUNT, + REMOTE_NODELAY, + REMOTE_CORK, + LOCAL_SYSNAME, + LOCAL_SYSTEM_MODEL, + LOCAL_RELEASE, + LOCAL_VERSION, + LOCAL_MACHINE, + REMOTE_SYSNAME, + REMOTE_SYSTEM_MODEL, + REMOTE_RELEASE, + REMOTE_VERSION, + REMOTE_MACHINE, + LOCAL_INTERFACE_NAME, + LOCAL_INTERFACE_VENDOR, + LOCAL_INTERFACE_DEVICE, + LOCAL_INTERFACE_SUBVENDOR, + LOCAL_INTERFACE_SUBDEVICE, + LOCAL_DRIVER_NAME, + LOCAL_DRIVER_VERSION, + LOCAL_DRIVER_FIRMWARE, + LOCAL_DRIVER_BUS, + LOCAL_INTERFACE_SLOT, + REMOTE_INTERFACE_NAME, + REMOTE_INTERFACE_VENDOR, + REMOTE_INTERFACE_DEVICE, + REMOTE_INTERFACE_SUBVENDOR, + REMOTE_INTERFACE_SUBDEVICE, + REMOTE_DRIVER_NAME, + REMOTE_DRIVER_VERSION, + REMOTE_DRIVER_FIRMWARE, + REMOTE_DRIVER_BUS, + REMOTE_INTERFACE_SLOT, + LOCAL_INTERVAL_USECS, + LOCAL_INTERVAL_BURST, + REMOTE_INTERVAL_USECS, + REMOTE_INTERVAL_BURST, + LOCAL_SECURITY_TYPE_ID, + LOCAL_SECURITY_TYPE, + LOCAL_SECURITY_ENABLED_NUM, + LOCAL_SECURITY_ENABLED, + LOCAL_SECURITY_SPECIFIC, + REMOTE_SECURITY_TYPE_ID, + REMOTE_SECURITY_TYPE, + REMOTE_SECURITY_ENABLED_NUM, + REMOTE_SECURITY_ENABLED, + REMOTE_SECURITY_SPECIFIC, + RESULT_BRAND, + UUID, + MIN_LATENCY, + MAX_LATENCY, + P50_LATENCY, + P90_LATENCY, + P99_LATENCY, + MEAN_LATENCY, + STDDEV_LATENCY, + LOCAL_SOCKET_PRIO, + REMOTE_SOCKET_PRIO, + LOCAL_SOCKET_TOS, + REMOTE_SOCKET_TOS, + LOCAL_CONG_CONTROL, + REMOTE_CONG_CONTROL, + LOCAL_FILL_FILE, + REMOTE_FILL_FILE, + COMMAND_LINE, /* COMMAND_LINE should always be "last" */ + OUTPUT_END, + NETPERF_OUTPUT_MAX +}; + +/* flags for the output groups, lower 16 bits for remote, upper 16 + bits for local */ + +#define OMNI_WANT_REM_IFNAME 0X00000001 +#define OMNI_WANT_LOC_IFNAME 0X00010000 +#define OMNI_WANT_REM_IFSLOT 0X00000002 +#define OMNI_WANT_LOC_IFSLOT 0X00020000 +#define OMNI_WANT_REM_IFIDS 0X00000004 +#define OMNI_WANT_LOC_IFIDS 0X00040000 +#define OMNI_WANT_REM_DRVINFO 0X00000008 +#define OMNI_WANT_LOC_DRVINFO 0X00080000 +#define OMNI_WANT_STATS 0X00100010 +#define OMNI_WANT_REM_CONG 0X00000020 +#define OMNI_WANT_LOC_CONG 0X00200000 + +unsigned int desired_output_groups = 0; + +typedef struct netperf_output_elt { + enum netperf_output_name output_name; /* belt and suspenders */ + int max_line_len; /* length of the longest of the "lines" */ + int tot_line_len; /* total length of all lines, including spaces */ + char *line[4]; + char *format; /* format to apply to value */ + void *display_value; /* where to find the value */ + enum netperf_output_type output_type; /* what type is the value? */ + int output_default; /* is it included in the default output */ + unsigned int output_group; /* used to avoid some lookups */ +} netperf_output_elt_t; + +netperf_output_elt_t netperf_output_source[NETPERF_OUTPUT_MAX]; + +#define NETPERF_MAX_BLOCKS 4 + +/* let us simply use one, two-dimensional list, and either use or some + of the additional dimension depending on the type of output we are + doing. this should help simplify matters. raj 20110120 */ + +enum netperf_output_name output_list[NETPERF_MAX_BLOCKS][NETPERF_OUTPUT_MAX]; + +/* some things for setting the source IP address on outgoing UDP + sends. borrows liberally from + http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket */ + +int want_use_pktinfo = 0; +int use_pktinfo = 0; +int have_pktinfo = 0; +#ifdef IP_PKTINFO +struct in_pktinfo in_pktinfo; +#endif + +char *direction_to_str(int direction) { + if (NETPERF_RECV_ONLY(direction)) return "Receive"; + if (NETPERF_XMIT_ONLY(direction)) return "Send"; + if (NETPERF_CC(direction)) return "Connection"; + else if (connection_test) { + return "Connect|Send|Recv"; + } + else return "Send|Recv"; +} + +static unsigned short +get_port_number(struct addrinfo *res) +{ + switch(res->ai_family) { + case AF_INET: { + struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; + return(ntohs(foo->sin_port)); + break; + } +#if defined(AF_INET6) + case AF_INET6: { + struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; + return(ntohs(foo->sin6_port)); + break; + } +#endif + default: + fprintf(where, + "Unexpected Address Family %u\n",res->ai_family); + fflush(where); + exit(-1); + } +} + +/* does this need to become conditional on the presence of the macros + or might we ass-u-me that we will not be compiled on something so + old as to not have what we use? raj 20090803 */ +static int +is_multicast_addr(struct addrinfo *res) { + switch(res->ai_family) { + case AF_INET: { + /* IPv4 multicast runs from 224.0.0.0 to 239.255.255.255 or + 0xE0000000 to 0xEFFFFFFF. Thankfully though there are macros + available to make the checks for one */ + struct in_addr bar = ((struct sockaddr_in *)res->ai_addr)->sin_addr; + /* and here I thought IN_MULTICAST would operate on things in + network byte order??? raj 20100315 */ + return IN_MULTICAST(ntohl(bar.s_addr)); + } +#if defined(AF_INET6) + case AF_INET6: { + struct in6_addr *bar = &(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr); + return IN6_IS_ADDR_MULTICAST(bar); + } +#endif + default: + fprintf(where, + "Unexpected Address Family for Multicast Check %u\n", + res->ai_family); + fflush(where); + return 0; /* or should we exit? */ + } +} + +static void +set_multicast_ttl(SOCKET sock) { + int optlen = sizeof(int); + + /* now set/get the TTL */ + if (multicast_ttl >= 0) { + if (setsockopt(sock, + IPPROTO_IP, +#if defined(IP_MULTICAST_TTL) + IP_MULTICAST_TTL, +#else + IP_TTL, +#endif + (const char *)&multicast_ttl, + sizeof(multicast_ttl)) == SOCKET_ERROR) { + fprintf(where, + "setsockopt(IP_TTL) failed errno %d\n", + errno); + } + } + if (getsockopt(sock, + IPPROTO_IP, + IP_TTL, + (char *)&multicast_ttl, + (netperf_socklen_t *)&optlen) < 0) { + fprintf(where, + "getsockopt(IP_TTL) failed errno %d\n", + errno); + multicast_ttl = -2; + } +} + +/* we presume we are only called with something which is actually a + multicast address. raj 20100315 */ +static void +join_multicast_addr(SOCKET sock, struct addrinfo *res) { + switch(res->ai_family) { + case AF_INET: { + struct ip_mreq mreq; + struct in_addr bar = ((struct sockaddr_in *)res->ai_addr)->sin_addr; + int optlen = sizeof(int); + int one = 1; + + mreq.imr_multiaddr.s_addr=bar.s_addr; + mreq.imr_interface.s_addr=htonl(INADDR_ANY); + if (setsockopt(sock, + IPPROTO_IP, + IP_ADD_MEMBERSHIP, + (const char *)&mreq, + sizeof(mreq)) == 0) { + + /* let others do the same */ + if (setsockopt(sock, + SOL_SOCKET, + SO_REUSEADDR, + (const char *)&one, + sizeof(one)) == SOCKET_ERROR) { + if (debug) { + fprintf(where, + "join_multicast_addr SO_REUSADDR failed errno %d\n", + errno); + fflush(where); + } + } + + /* now set/get the TTL */ + if (multicast_ttl >= 0) { + if (setsockopt(sock, + IPPROTO_IP, + IP_TTL, + (const char *)&multicast_ttl, + sizeof(multicast_ttl)) == SOCKET_ERROR) { + fprintf(where, + "setsockopt(IP_TTL) failed errno %d\n", + errno); + } + } + if (getsockopt(sock, + IPPROTO_IP, + IP_TTL, + (char *)&multicast_ttl, + (netperf_socklen_t *)&optlen) == SOCKET_ERROR) { + fprintf(where, + "getsockopt(IP_TTL) failed errno %d\n", + errno); + multicast_ttl = -2; + } + } + else { + if (debug) { + fprintf(where, + "setsockopt(IP_ADD_MEMBERSHIP) failed errno %d\n", + errno); + fflush(where); + } + } + break; + } + case AF_INET6: { + fprintf(where,"I do not know how to join an IPv6 multicast group\n"); + break; + } + + } + return; +} + +static void +extract_inet_address_and_port(struct addrinfo *res, void *addr, int len, int *port) +{ + switch(res->ai_family) { + case AF_INET: { + struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; + *port = foo->sin_port; + memcpy(addr,&(foo->sin_addr),min(len,sizeof(foo->sin_addr))); + break; + } +#if defined(AF_INET6) + case AF_INET6: { + struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; + *port = foo->sin6_port; + memcpy(addr,&(foo->sin6_addr),min(len,sizeof(foo->sin6_addr))); + break; + } +#endif + default: + *port = 0xDEADBEEF; + strncpy(addr,"UNKN FAMILY",len); + } +} + +void +pick_next_port_number(struct addrinfo *local_res, struct addrinfo *remote_res) { + + static int myport_init = 0; + static unsigned short myport = 0; + + if (0 == myport_init) { + /* pick a nice random spot between client_port_min and + client_port_max for our initial port number, but only for a + connection oriented test. otherwise, we will want to set myport + to a specific port provided by the user if they have so provided + a specific port :) raj 2008-01-08 */ + srand(getpid()); + if (client_port_max - client_port_min) { + myport = client_port_min + + (rand() % (client_port_max - client_port_min)); + } + else { + myport = (unsigned short)client_port_min; + } + /* there will be a ++ before the first call to bind, so subtract one */ + myport--; + myport_init = 1; + } + + /* newport: */ + /* pick a new port number */ + myport++; + + /* check to see if we are using the port number on which the + server is sitting _before_ we check against the boundaries lest + the server sits at the upper boundary. if this happens to be a + loopback test, trying to use the same portnumber would lead to + unsatisfying results and should be avoided. if this isn't a + loopback test, avoiding using the same port number doesn't + seriously affect anything anyway */ + + if (myport == get_port_number(remote_res)) myport++; + + /* wrap the port number when we reach the upper bound. for + students of networking history, some ancient stacks (1980's and + early 1990's perhaps) mistakenly treated these port numbers as + signed 16 bit quantities. we make no effort here to support + such stacks. raj 2008-01-08 */ + if (myport >= client_port_max) { + myport = (unsigned short)client_port_min; + } + + /* set up the data socket */ + set_port_number(local_res, (unsigned short)myport); +} + +/* at some point this should become a table lookup... raj 20090813 */ +char * +netperf_output_enum_to_str(enum netperf_output_name output_name) +{ + switch (output_name) { + case OUTPUT_NONE: + return "OUTPUT_NONE"; + case COMMAND_LINE: + return "COMMAND_LINE"; + case UUID: + return "UUID"; + case RESULT_BRAND: + return "RESULT_BRAND"; + case SOCKET_TYPE: + return "SOCKET_TYPE"; + case DIRECTION: + return "DIRECTION"; + case PROTOCOL: + return "PROTOCOL"; + case ELAPSED_TIME: + return "ELAPSED_TIME"; + case SOURCE_PORT: + return "SOURCE_PORT"; + case SOURCE_ADDR: + return "SOURCE_ADDR"; + case SOURCE_FAMILY: + return "SOURCE_FAMILY"; + case DEST_PORT: + return "DEST_PORT"; + case DEST_ADDR: + return "DEST_ADDR"; + case DEST_FAMILY: + return "DEST_FAMILY"; + case THROUGHPUT: + return "THROUGHPUT"; + case LOCAL_SEND_THROUGHPUT: + return "LOCAL_SEND_THROUGHPUT"; + case LOCAL_RECV_THROUGHPUT: + return "LOCAL_RECV_THROUGHPUT"; + case REMOTE_SEND_THROUGHPUT: + return "REMOTE_SEND_THROUGHPUT"; + case REMOTE_RECV_THROUGHPUT: + return "REMOTE_RECV_THROUGHPUT"; + case THROUGHPUT_UNITS: + return "THROUGHPUT_UNITS"; + case CONFIDENCE_LEVEL: + return "CONFIDENCE_LEVEL"; + case CONFIDENCE_INTERVAL: + return "CONFIDENCE_INTERVAL"; + case CONFIDENCE_ITERATION: + return "CONFIDENCE_ITERATION"; + case THROUGHPUT_CONFID: + return "THROUGHPUT_CONFID"; + case LOCAL_CPU_CONFID: + return "LOCAL_CPU_CONFID"; + case REMOTE_CPU_CONFID: + return "REMOTE_CPU_CONFID"; + case RT_LATENCY: + return "RT_LATENCY"; + case TRANSACTION_RATE: + return "TRANSACTION_RATE"; + case BURST_SIZE: + return "BURST_SIZE"; + case LOCAL_TRANSPORT_RETRANS: + return "LOCAL_TRANSPORT_RETRANS"; + case REMOTE_TRANSPORT_RETRANS: + return "REMOTE_TRANSPORT_RETRANS"; + case TRANSPORT_MSS: + return "TRANSPORT_MSS"; + case REQUEST_SIZE: + return "REQUEST_SIZE"; + case RESPONSE_SIZE: + return "RESPONSE_SIZE"; + case LSS_SIZE_REQ: + return "LSS_SIZE_REQ"; + case LSS_SIZE: + return "LSS_SIZE"; + case LSS_SIZE_END: + return "LSS_SIZE_END"; + case LSR_SIZE_REQ: + return "LSR_SIZE_REQ"; + case LSR_SIZE: + return "LSR_SIZE"; + case LSR_SIZE_END: + return "LSR_SIZE_END"; + case LOCAL_SEND_SIZE: + return "LOCAL_SEND_SIZE"; + case LOCAL_RECV_SIZE: + return "LOCAL_RECV_SIZE"; + case LOCAL_SEND_CALLS: + return "LOCAL_SEND_CALLS"; + case LOCAL_RECV_CALLS: + return "LOCAL_RECV_CALLS"; + case LOCAL_BYTES_PER_RECV: + return "LOCAL_BYTES_PER_RECV"; + case LOCAL_BYTES_PER_SEND: + return "LOCAL_BYTES_PER_SEND"; + case LOCAL_BYTES_SENT: + return "LOCAL_BYTES_SENT"; + case LOCAL_BYTES_RECVD: + return "LOCAL_BYTES_RECVD"; + case LOCAL_BYTES_XFERD: + return "LOCAL_BYTES_XFERD"; + case LOCAL_SEND_OFFSET: + return "LOCAL_SEND_OFFSET"; + case LOCAL_RECV_OFFSET: + return "LOCAL_RECV_OFFSET"; + case LOCAL_RECV_ALIGN: + return "LOCAL_RECV_ALIGN"; + case LOCAL_SEND_ALIGN: + return "LOCAL_SEND_ALIGN"; + case LOCAL_SEND_WIDTH: + return "LOCAL_SEND_WIDTH"; + case LOCAL_RECV_WIDTH: + return "LOCAL_RECV_WIDTH"; + case LOCAL_SEND_DIRTY_COUNT: + return "LOCAL_SEND_DIRTY_COUNT"; + case LOCAL_RECV_DIRTY_COUNT: + return "LOCAL_RECV_DIRTY_COUNT"; + case LOCAL_RECV_CLEAN_COUNT: + return "LOCAL_RECV_CLEAN_COUNT"; + case LOCAL_CPU_UTIL: + return "LOCAL_CPU_UTIL"; + case LOCAL_CPU_PERCENT_USER: + return "LOCAL_CPU_PERCENT_USER"; + case LOCAL_CPU_PERCENT_SYSTEM: + return "LOCAL_CPU_PERCENT_SYSTEM"; + case LOCAL_CPU_PERCENT_IOWAIT: + return "LOCAL_CPU_PERCENT_IOWAIT"; + case LOCAL_CPU_PERCENT_IRQ: + return "LOCAL_CPU_PERCENT_IRQ"; + case LOCAL_CPU_PERCENT_SWINTR: + return "LOCAL_CPU_PERCENT_SWINTR"; + case LOCAL_CPU_BIND: + return "LOCAL_CPU_BIND"; + case LOCAL_SD: + return "LOCAL_SD"; + case SD_UNITS: + return "SD_UNITS"; + case LOCAL_CPU_METHOD: + return "LOCAL_CPU_METHOD"; + case LOCAL_CPU_COUNT: + return "LOCAL_CPU_COUNT"; + case LOCAL_CPU_PEAK_UTIL: + return "LOCAL_CPU_PEAK_UTIL"; + case LOCAL_CPU_PEAK_ID: + return "LOCAL_CPU_PEAK_ID"; + case LOCAL_NODELAY: + return "LOCAL_NODELAY"; + case LOCAL_CORK: + return "LOCAL_CORK"; + case RSS_SIZE_REQ: + return "RSS_SIZE_REQ"; + case RSS_SIZE: + return "RSS_SIZE"; + case RSS_SIZE_END: + return "RSS_SIZE_END"; + case RSR_SIZE_REQ: + return "RSR_SIZE_REQ"; + case RSR_SIZE: + return "RSR_SIZE"; + case RSR_SIZE_END: + return "RSR_SIZE_END"; + case REMOTE_SEND_SIZE: + return "REMOTE_SEND_SIZE"; + case REMOTE_RECV_SIZE: + return "REMOTE_RECV_SIZE"; + case REMOTE_SEND_CALLS: + return "REMOTE_SEND_CALLS"; + case REMOTE_RECV_CALLS: + return "REMOTE_RECV_CALLS"; + case REMOTE_BYTES_PER_RECV: + return "REMOTE_BYTES_PER_RECV"; + case REMOTE_BYTES_PER_SEND: + return "REMOTE_BYTES_PER_SEND"; + case REMOTE_BYTES_SENT: + return "REMOTE_BYTES_SENT"; + case REMOTE_BYTES_RECVD: + return "REMOTE_BYTES_RECVD"; + case REMOTE_BYTES_XFERD: + return "REMOTE_BYTES_XFERD"; + case REMOTE_SEND_OFFSET: + return "REMOTE_SEND_OFFSET"; + case REMOTE_RECV_OFFSET: + return "REMOTE_RECV_OFFSET"; + case REMOTE_RECV_ALIGN: + return "REMOTE_RECV_ALIGN"; + case REMOTE_SEND_ALIGN: + return "REMOTE_SEND_ALIGN"; + case REMOTE_SEND_WIDTH: + return "REMOTE_SEND_WIDTH"; + case REMOTE_RECV_WIDTH: + return "REMOTE_RECV_WIDTH"; + case REMOTE_SEND_DIRTY_COUNT: + return "REMOTE_SEND_DIRTY_COUNT"; + case REMOTE_RECV_DIRTY_COUNT: + return "REMOTE_RECV_DIRTY_COUNT"; + case REMOTE_RECV_CLEAN_COUNT: + return "REMOTE_RECV_CLEAN_COUNT"; + case REMOTE_CPU_UTIL: + return "REMOTE_CPU_UTIL"; + case REMOTE_CPU_PERCENT_USER: + return "REMOTE_CPU_PERCENT_USER"; + case REMOTE_CPU_PERCENT_SYSTEM: + return "REMOTE_CPU_PERCENT_SYSTEM"; + case REMOTE_CPU_PERCENT_IOWAIT: + return "REMOTE_CPU_PERCENT_IOWAIT"; + case REMOTE_CPU_PERCENT_IRQ: + return "REMOTE_CPU_PERCENT_IRQ"; + case REMOTE_CPU_PERCENT_SWINTR: + return "REMOTE_CPU_PERCENT_SWINTR"; + case REMOTE_CPU_BIND: + return "REMOTE_CPU_BIND"; + case REMOTE_SD: + return "REMOTE_SD"; + case REMOTE_CPU_METHOD: + return "REMOTE_CPU_METHOD"; + case REMOTE_CPU_COUNT: + return "REMOTE_CPU_COUNT"; + case REMOTE_CPU_PEAK_UTIL: + return "REMOTE_CPU_PEAK_UTIL"; + case REMOTE_CPU_PEAK_ID: + return "REMOTE_CPU_PEAK_ID"; + case REMOTE_NODELAY: + return "REMOTE_NODELAY"; + case REMOTE_CORK: + return "REMOTE_CORK"; + case LOCAL_INTERFACE_SLOT: + return "LOCAL_INTERFACE_SLOT"; + case REMOTE_INTERFACE_SLOT: + return "REMOTE_INTERFACE_SLOT"; + case REMOTE_INTERFACE_SUBDEVICE: + return "REMOTE_INTERFACE_SUBDEVICE"; + case REMOTE_INTERFACE_SUBVENDOR: + return "REMOTE_INTERFACE_SUBVENDOR"; + case REMOTE_INTERFACE_DEVICE: + return "REMOTE_INTERFACE_DEVICE"; + case REMOTE_INTERFACE_VENDOR: + return "REMOTE_INTERFACE_VENDOR"; + case LOCAL_INTERFACE_SUBDEVICE: + return "LOCAL_INTERFACE_SUBDEVICE"; + case LOCAL_INTERFACE_SUBVENDOR: + return "LOCAL_INTERFACE_SUBVENDOR"; + case LOCAL_INTERFACE_DEVICE: + return "LOCAL_INTERFACE_DEVICE"; + case LOCAL_INTERFACE_VENDOR: + return "LOCAL_INTERFACE_VENDOR"; + case LOCAL_INTERFACE_NAME: + return "LOCAL_INTERFACE_NAME"; + case REMOTE_INTERFACE_NAME: + return "REMOTE_INTERFACE_NAME"; + case REMOTE_DRIVER_NAME: + return "REMOTE_DRIVER_NAME"; + case REMOTE_DRIVER_VERSION: + return "REMOTE_DRIVER_VERSION"; + case REMOTE_DRIVER_FIRMWARE: + return "REMOTE_DRIVER_FIRMWARE"; + case REMOTE_DRIVER_BUS: + return "REMOTE_DRIVER_BUS"; + case LOCAL_DRIVER_NAME: + return "LOCAL_DRIVER_NAME"; + case LOCAL_DRIVER_VERSION: + return "LOCAL_DRIVER_VERSION"; + case LOCAL_DRIVER_FIRMWARE: + return "LOCAL_DRIVER_FIRMWARE"; + case LOCAL_INTERVAL_USECS: + return "LOCAL_INTERVAL_USECS"; + case LOCAL_INTERVAL_BURST: + return "LOCAL_INTERVAL_BURST"; + case REMOTE_INTERVAL_USECS: + return "REMOTE_INTERVAL_USECS"; + case REMOTE_INTERVAL_BURST: + return "REMOTE_INTERVAL_BURST"; + case LOCAL_SECURITY_TYPE_ID: + return "LOCAL_SECURITY_TYPE_ID"; + case LOCAL_SECURITY_ENABLED_NUM: + return "LOCAL_SECURITY_ENABLED_NUM"; + case LOCAL_SECURITY_TYPE: + return "LOCAL_SECURITY_TYPE"; + case LOCAL_SECURITY_ENABLED: + return "LOCAL_SECURITY_ENABLED"; + case LOCAL_SECURITY_SPECIFIC: + return "LOCAL_SECURITY_SPECIFIC"; + case REMOTE_SECURITY_TYPE_ID: + return "REMOTE_SECURITY_TYPE_ID"; + case REMOTE_SECURITY_ENABLED_NUM: + return "REMOTE_SECURITY_ENABLED_NUM"; + case REMOTE_SECURITY_TYPE: + return "REMOTE_SECURITY_TYPE"; + case REMOTE_SECURITY_ENABLED: + return "REMOTE_SECURITY_ENABLED"; + case REMOTE_SECURITY_SPECIFIC: + return "REMOTE_SECURITY_SPECIFIC"; + case LOCAL_DRIVER_BUS: + return "LOCAL_DRIVER_BUS"; + case REMOTE_SYSNAME: + return "REMOTE_SYSNAME"; + case REMOTE_MACHINE: + return "REMOTE_MACHINE"; + case REMOTE_VERSION: + return "REMOTE_VERSION"; + case REMOTE_RELEASE: + return "REMOTE_RELEASE"; + case LOCAL_SYSNAME: + return "LOCAL_SYSNAME"; + case LOCAL_MACHINE: + return "LOCAL_MACHINE"; + case LOCAL_VERSION: + return "LOCAL_VERSION"; + case LOCAL_RELEASE: + return "LOCAL_RELEASE"; + case REMOTE_CPU_MODEL: + return "REMOTE_CPU_MODEL"; + case REMOTE_CPU_FREQUENCY: + return "REMOTE_CPU_FREQUENCY"; + case REMOTE_SYSTEM_MODEL: + return "REMOTE_SYSTEM_MODEL"; + case LOCAL_CPU_MODEL: + return "LOCAL_CPU_MODEL"; + case LOCAL_CPU_FREQUENCY: + return "LOCAL_CPU_FREQUENCY"; + case LOCAL_SYSTEM_MODEL: + return "LOCAL_SYSTEM_MODEL"; + case MIN_LATENCY: + return "MIN_LATENCY"; + case MAX_LATENCY: + return "MAX_LATENCY"; + case P50_LATENCY: + return "P50_LATENCY"; + case P90_LATENCY: + return "P90_LATENCY"; + case P99_LATENCY: + return "P99_LATENCY"; + case MEAN_LATENCY: + return "MEAN_LATENCY"; + case STDDEV_LATENCY: + return "STDDEV_LATENCY"; + case LOCAL_SOCKET_PRIO: + return "LOCAL_SOCKET_PRIO"; + case REMOTE_SOCKET_PRIO: + return "REMOTE_SOCKET_PRIO"; + case LOCAL_SOCKET_TOS: + return "LOCAL_SOCKET_TOS"; + case REMOTE_SOCKET_TOS: + return "REMOTE_SOCKET_TOS"; + case LOCAL_CONG_CONTROL: + return "LOCAL_CONG_CONTROL"; + case REMOTE_CONG_CONTROL: + return "REMOTE_CONG_CONTROL"; + case LOCAL_FILL_FILE: + return "LOCAL_FILL_FILE"; + case REMOTE_FILL_FILE: + return "REMOTE_FILL_FILE"; + case OUTPUT_END: + return "OUTPUT_END"; + default: + return "!UNKNOWN OUTPUT SELECTOR!"; + } +} + +void +print_netperf_output_entry(FILE *where, enum netperf_output_name what) +{ +} + +void print_omni_init_list(); + +void +dump_netperf_output_list(FILE *where) { + + int i,j; + + for (i = 0; i < NETPERF_MAX_BLOCKS; i++) { + fprintf(where,"Output Block %d\n",i + 1); + for (j = 0; j < NETPERF_OUTPUT_MAX; j++) { + fprintf(where,"%s ",netperf_output_enum_to_str(output_list[i][j])); + } + fprintf(where,"\n"); + } + fflush(where); +} + +void +dump_netperf_output_choices(FILE *where, int csv) { + int i; + + print_omni_init_list(); + + for (i = OUTPUT_NONE; i < NETPERF_OUTPUT_MAX; i++){ + if (OUTPUT_NONE != i) { + fprintf(where,"%c",(csv) ? ',' : '\n'); + } + fprintf(where, + "%s", + netperf_output_enum_to_str(netperf_output_source[i].output_name)); + } + fprintf(where,"\n"); + fflush(where); +} + +void +dump_netperf_output_source(FILE *where) +{ + int i; + + /* belts and suspenders everyone... */ + for (i = OUTPUT_NONE; i < NETPERF_OUTPUT_MAX; i++) { + fprintf(where, + "Output Name: %s\n" + "\tmax_line_len %d tot_line_len %d display_value %p\n" + "\tline[0]: |%s|\n" + "\tline[1]: |%s|\n" + "\tline[2]: |%s|\n" + "\tline[3]: |%s|\n" + "\tformat: |%s|\n", + netperf_output_enum_to_str(netperf_output_source[i].output_name), + netperf_output_source[i].max_line_len, + netperf_output_source[i].tot_line_len, + netperf_output_source[i].display_value, + (netperf_output_source[i].line[0] == NULL) ? "" : + netperf_output_source[i].line[0], + (netperf_output_source[i].line[1] == NULL) ? "" : + netperf_output_source[i].line[1], + (netperf_output_source[i].line[2] == NULL) ? "" : + netperf_output_source[i].line[2], + (netperf_output_source[i].line[3] == NULL) ? "" : + netperf_output_source[i].line[3], + (netperf_output_source[i].format == NULL) ? "" : + netperf_output_source[i].format); + } + fflush(where); +} + +#define MY_MAX(a,b) ((a > b) ? a : b) + +#define NETPERF_LINE_MAX(x) \ + MY_MAX(MY_MAX(MY_MAX(strlen(netperf_output_source[x].line[0]),\ + strlen(netperf_output_source[x].line[1])),\ + strlen(netperf_output_source[x].line[2])),\ + strlen(netperf_output_source[x].line[3])) + +#define NETPERF_LINE_TOT(x) \ + strlen(netperf_output_source[x].line[0]) +\ + strlen(netperf_output_source[x].line[1]) +\ + strlen(netperf_output_source[x].line[2]) +\ + strlen(netperf_output_source[x].line[3]) + 4 + +enum netperf_output_name +match_string_to_output_mnenomic(char *candidate) { + + enum netperf_output_name name; + for (name = OUTPUT_NONE; name < NETPERF_OUTPUT_MAX; name++) { + if(!strcasecmp(candidate,netperf_output_enum_to_str(name))) + return name; + } + return NETPERF_OUTPUT_UNKNOWN; +} + +enum netperf_output_name +match_string_to_output(char *candidate) +{ + char *h1,*temp; + enum netperf_output_name name; + int k,len; + + /* at some point we may need/want to worry about leading and + trailing spaces, but for now we will leave that onus on the + user. */ + + for (name = OUTPUT_NONE; name < NETPERF_OUTPUT_MAX; name++) { + /* try for a match based on the nmemonic/enum */ + if (!strcasecmp(candidate,netperf_output_enum_to_str(name))) + return name; + + /* try for a match on the actual header text */ + temp = malloc(NETPERF_LINE_TOT(name)); + h1 = temp; + if (h1 != NULL) { + for (k = 0; ((k < 4) && + (NULL != netperf_output_source[name].line[k]) && + (strcmp("",netperf_output_source[name].line[k]))); k++) { + len = sprintf(h1, + "%s", + netperf_output_source[name].line[k]); + *(h1 + len) = ' '; + /* now move to the next starting column. for csv we aren't worried + about alignment between the header and the value lines */ + h1 += len + 1; + } + /* this time we want null termination please */ + *(h1 - 1) = 0; + if (!strcasecmp(candidate,temp)) { + free(temp); + return name; + } + else + free(temp); + } + } + /* if we get here it means there was no match */ + return OUTPUT_NONE; +} + + +void +set_output_list_all() { + + int i, j; /* line, column */ + enum netperf_output_name k; + + /* Line One SOCKET_TYPE to RESPONSE_SIZE */ + i = 0; + j = 0; + for (k = SOCKET_TYPE; k <= RESPONSE_SIZE; k++) { + output_list[i][j++] = k; + desired_output_groups |= netperf_output_source[k].output_group; + } + + /* Line Two LOCAL_CPU_UTIL to TRANSPORT_MSS */ + i = 1; + j = 0; + for (k = LOCAL_CPU_UTIL; k <= TRANSPORT_MSS; k++) { + output_list[i][j++] = k; + desired_output_groups |= netperf_output_source[k].output_group; + } + + /* Line Three LOCAL_SEND_THROUGHPUT throught REMOTE_CORK */ + i = 2; + j = 0; + for (k = LOCAL_SEND_THROUGHPUT; k <= REMOTE_CORK; k++) { + output_list[i][j++] = k; + desired_output_groups |= netperf_output_source[k].output_group; + } + + /* Line Four LOCAL_SYSNAME through COMMAND_LINE */ + i = 3; + j = 0; + for (k = LOCAL_SYSNAME; k <= COMMAND_LINE; k++) { + output_list[i][j++] = k; + desired_output_groups |= netperf_output_source[k].output_group; + } + +} + +void +parse_output_selection_file(char *selection_file) { + FILE *selections; + char name[81]; /* best be more than enough */ + int namepos; + int c; + int j; + int line,column; + + selections = fopen(selection_file,"r"); + if (!selections) { + fprintf(where, + "Could not open output selection file '%s' errno %d\n", + selection_file, + errno); + fflush(where); + exit(-1); + } + + line = 0; + column = 1; + namepos = 0; + name[0] = 0; + name[80] = 0; + j = 0; + while (((c = fgetc(selections)) != EOF) && (line < 4)) { + if (namepos == 80) { + /* too long */ + + fprintf(where, + "Output selection starting column %d on line %d is too long\n", + line + 1, + column); + fflush(where); + exit(-1); + } + if (c == ',') { + /* time to check for a match, but only if we won't overflow the + current row of the array */ + if (j == NETPERF_OUTPUT_MAX) { + fprintf(where,"Too many output selectors on line %d\n",line); + fflush(where); + exit(-1); + } + name[namepos] = 0; + output_list[line][j++] = match_string_to_output(name); + namepos = 0; + } + else if (c == '\n') { + /* move to the next line after checking for a match */ + name[namepos] = 0; + output_list[line++][j++] = match_string_to_output(name); + namepos = 0; + j = 0; + } + else if (isprint(c)) { + name[namepos++] = (char)c; + } + column++; + } + + /* ok, do we need/want to do anything here? at present we will + silently ignore the rest of the file if we exit the loop on line + count */ + if ((c == EOF) && (namepos > 0)) { + name[namepos] = 0; + output_list[line][j] = match_string_to_output(name); + } + +} + +void +parse_output_selection_line(int line, char *list) { + + char *token; + int j; + enum netperf_output_name name; + + /* belt and suspenders */ + if (line < 0) { + fprintf(where, + "parse_output_selection_line called with negative line number %d\n",line); + fflush(where); + exit(-1); + } + + /* silently ignore extra lines and only warn if debug is set */ + if (line >= NETPERF_MAX_BLOCKS) { + if (debug) { + fprintf(where, + "There can be no more than %d output selection lines." + " Ignoring output selection line %d |%s|\n", + NETPERF_MAX_BLOCKS, + line + 1, + list); + fflush(where); + } + return; + } + + + j=0; + token = strtok(list," ,"); + while ((token) && (j < NETPERF_OUTPUT_MAX)) { + + name = match_string_to_output_mnenomic(token); + + if ((name == NETPERF_OUTPUT_UNKNOWN) && (debug)) { + fprintf(where,"Ignoring unknown output selector %d |%s| on line %d\n", + j + 1, + token, + line +1); + fflush(where); + } + else { + output_list[line][j] = name; + desired_output_groups |= netperf_output_source[name].output_group; + j++; + } + + token = strtok(NULL," ,"); + } + if ((token) && (debug)) { + fprintf(where, + "There can be no more than %d output selectors per line. " + "Ignoring remaining selectors on line %d\n", + NETPERF_OUTPUT_MAX,line +1); + fflush(where); + } +} + +void +parse_output_selection_direct(char *output_selection) { + + char *source,*line,*remainder,*temp; + char *f1, *f2, *f3; + int i,len,done; + + len = strlen(output_selection); + + source = strdup(output_selection); + line = (char *) malloc(len+1); + remainder = (char *) malloc(len+1); + + if ((NULL == source) || + (NULL == line) || + (NULL == remainder)) { + fprintf(where,"Unable to malloc memory for output selection parsing\n"); + fflush(where); + exit(-1); + } + + f1 = source; + f2 = line; + f3 = remainder; + + i = 0; + done = 0; + do { + break_args_explicit_sep(source,';',line,remainder); + if (line[0]) { + parse_output_selection_line(i,line); + } + if (remainder[0]) { + temp = source; + source = remainder; + remainder = temp; + i++; + /* + if (i == NETPERF_MAX_BLOCKS) { + fprintf(where, + "Too many output blocks requested, maximum is %d\n", + NETPERF_MAX_BLOCKS); + fflush(where); + exit(-1); + } + */ + continue; + } + else { + done = 1; + } + } while (!done); + + free(f1); + free(f2); + free(f3); + +} + +/* building blocks for output selection */ +#define NETPERF_TPUT "ELAPSED_TIME,THROUGHPUT,THROUGHPUT_UNITS" +#define NETPERF_OUTPUT_STREAM "LSS_SIZE_END,RSR_SIZE_END,LOCAL_SEND_SIZE" +#define NETPERF_OUTPUT_MAERTS "RSS_SIZE_END,LSR_SIZE_END,REMOTE_SEND_SIZE" +#define NETPERF_CPU "LOCAL_CPU_UTIL,LOCAL_CPU_PERCENT_USER,LOCAL_CPU_PERCENT_SYSTEM,LOCAL_CPU_PERCENT_IOWAIT,LOCAL_CPU_PERCENT_IRQ,LOCAL_CPU_PERCENT_SWINTR,LOCAL_CPU_METHOD,REMOTE_CPU_UTIL,REMOTE_CPU_PERCENT_USER,REMOTE_CPU_PERCENT_SYSTEM,REMOTE_CPU_PERCENT_IOWAIT,REMOTE_CPU_PERCENT_IRQ,REMOTE_CPU_PERCENT_SWINTR,REMOTE_CPU_METHOD,LOCAL_SD,REMOTE_SD,SD_UNITS" +#define NETPERF_RR "LSS_SIZE_END,LSR_SIZE_END,RSR_SIZE_END,RSS_SIZE_END,REQUEST_SIZE,RESPONSE_SIZE" + +void +set_output_list_by_test() { + + char *stream_no_cpu = NETPERF_OUTPUT_STREAM "," NETPERF_TPUT; + char *stream_cpu = NETPERF_OUTPUT_STREAM "," NETPERF_TPUT "," NETPERF_CPU; + char *maerts_no_cpu = NETPERF_OUTPUT_MAERTS "," NETPERF_TPUT; + char *maerts_cpu = NETPERF_OUTPUT_MAERTS "," NETPERF_TPUT "," NETPERF_CPU; + char *rr_no_cpu = NETPERF_RR "," NETPERF_TPUT; + char *rr_cpu = NETPERF_RR "," NETPERF_TPUT "," NETPERF_CPU; + + if (debug) { + fprintf(where,"%s setting the output list by test\n", + __FUNCTION__); + fflush(where); + } + + if (NETPERF_XMIT_ONLY(direction)) { + if (!(local_cpu_usage || remote_cpu_usage)) + parse_output_selection_direct(stream_no_cpu); + else + parse_output_selection_direct(stream_cpu); + } + else if (NETPERF_RECV_ONLY(direction)) { + if (!(local_cpu_usage || remote_cpu_usage)) + parse_output_selection_direct(maerts_no_cpu); + else + parse_output_selection_direct(maerts_cpu); + } + else if (NETPERF_CC(direction)) { + if (!(local_cpu_usage || remote_cpu_usage)) + parse_output_selection_direct(rr_no_cpu); + else + parse_output_selection_direct(rr_cpu); + } + else if (NETPERF_IS_RR(direction)) { + if (!(local_cpu_usage || remote_cpu_usage)) + parse_output_selection_direct(rr_no_cpu); + else + parse_output_selection_direct(rr_cpu); + } + else { + /* no idea */ + if (debug) { + fprintf(where,"Cannot determine default test output, using mins\n"); + fflush(where); + } + parse_output_selection_direct(NETPERF_TPUT "," NETPERF_CPU); + } +} + +void +parse_output_selection(char *output_selection) { + + if (debug) { + fprintf(where,"%s is parsing the output selection '%s'\n", + __FUNCTION__, + output_selection); + fflush(where); + } + + /* is it the magic keyword? */ + if (strcasecmp(output_selection,"all") == 0) { + set_output_list_all(); + } + /* do not forget the case when the output_selection is a single + mnemonic without any separators... */ + else if (strchr(output_selection,',') || + strchr(output_selection,';') || + (match_string_to_output_mnenomic(output_selection) != + NETPERF_OUTPUT_UNKNOWN)) { + parse_output_selection_direct(output_selection); + } + else { + parse_output_selection_file(output_selection); + } + if (debug > 2) { + dump_netperf_output_list(stderr); + } + return; +} + +static void +set_output_elt(enum netperf_output_name name, + char *line0, char *line1, char *line2, char *line3, + char *format, + void *value, + unsigned int out_default, + unsigned int group, + enum netperf_output_type type) { + + netperf_output_source[name].output_name = name; + netperf_output_source[name].line[0] = line0; + netperf_output_source[name].line[1] = line1; + netperf_output_source[name].line[2] = line2; + netperf_output_source[name].line[3] = line3; + netperf_output_source[name].format = format; + netperf_output_source[name].display_value = value; + netperf_output_source[name].output_default = out_default; + netperf_output_source[name].output_group = group; + netperf_output_source[name].max_line_len = NETPERF_LINE_MAX(name); + netperf_output_source[name].tot_line_len = NETPERF_LINE_TOT(name); + netperf_output_source[name].output_type = type; +} + +void +print_omni_init_list() { + + int i; + + if (debug) { + fprintf(where,"%s called\n", + __FUNCTION__); + } + + /* belts and suspenders everyone... */ + for (i = NETPERF_OUTPUT_UNKNOWN; i < NETPERF_OUTPUT_MAX; i++) { + netperf_output_source[i].output_name = i; + netperf_output_source[i].max_line_len = 0; + netperf_output_source[i].tot_line_len = 0; + netperf_output_source[i].line[0] = ""; + netperf_output_source[i].line[1] = ""; + netperf_output_source[i].line[2] = ""; + netperf_output_source[i].line[3] = ""; + netperf_output_source[i].format = ""; + netperf_output_source[i].display_value = NULL; + netperf_output_source[i].output_default = 1; + netperf_output_source[i].output_group = 0; + netperf_output_source[i].output_type = NETPERF_TYPE_UNKNOWN; + } + + set_output_elt(OUTPUT_NONE, " ", "", "", "", "%s", &" ",1, 0, + NETPERF_TYPE_CHAR); + + set_output_elt(COMMAND_LINE, "Command","Line","","","\"%s\"", + command_line,1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(UUID, "Test", "UUID", "", "", "%s", test_uuid, 1, 0, + NETPERF_TYPE_CHAR); + + set_output_elt(RESULT_BRAND, "Result", "Tag", "", "", "\"%s\"", + result_brand, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(SOCKET_TYPE, "Socket", "Type", "", "", "%s", + socket_type_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(DIRECTION, "Direction", "", "", "", "%s", + direction_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(PROTOCOL, "Protocol", "", "", "", "%s", + protocol_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(ELAPSED_TIME, "Elapsed", "Time", "(sec)", "", "%.2f", + &elapsed_time_double, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(SOURCE_PORT, "Source", "Port", "", "", "%s", + local_data_port, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(SOURCE_ADDR, "Source", "Address", "", "", "%s", + local_data_address, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(SOURCE_FAMILY, "Source", "Family", "", "", "%d", + &local_data_family, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(DEST_PORT, "Destination", "Port", "", "", "%s", + remote_data_port, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(DEST_ADDR, "Destination", "Address", "", "", "%s", + remote_data_address, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(DEST_FAMILY, "Destination", "Family", "", "", "%d", + &remote_data_family, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(THROUGHPUT, "Throughput", "", "", "", "%.2f", + &thruput, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_SEND_THROUGHPUT, "Local", "Send", "Throughput", "", + "%.2f", &local_send_thruput, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_RECV_THROUGHPUT, "Local", "Recv", "Throughput", "", + "%.2f", &local_recv_thruput, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_SEND_THROUGHPUT, "Remote", "Send", "Throughput", "", + "%.2f", &remote_send_thruput, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_RECV_THROUGHPUT, "Remote", "Recv", "Throughput", "", + "%.2f", &remote_recv_thruput, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(THROUGHPUT_UNITS, "Throughput", "Units", "", "", "%s/s", + thruput_format_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(CONFIDENCE_LEVEL, "Confidence", "Level", "Percent", "", "%d", + &confidence_level, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(CONFIDENCE_INTERVAL, "Confidence", "Width", "Target", "", + "%f", &interval_pct, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(CONFIDENCE_ITERATION, "Confidence", "Iterations", "Run", "", + "%d", &confidence_iteration, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(THROUGHPUT_CONFID, "Throughput", "Confidence", "Width (%)", + "", "%.3f", &result_confid_pct, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_CPU_CONFID, "Local", "CPU", "Confidence", "Width (%)", + "%.3f", &loc_cpu_confid_pct, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_CPU_CONFID, "Remote", "CPU", "Confidence", "Width (%)", + "%.3f", &rem_cpu_confid_pct, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(RT_LATENCY, "Round", "Trip", "Latency", "usec/tran", "%.3f", + &rtt_latency, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(TRANSACTION_RATE, "Transaction", "Rate", "Tran/s", "", "%.3f", + &transaction_rate, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(TRANSPORT_MSS, "Transport", "MSS", "bytes", "", "%d", + &transport_mss, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_TRANSPORT_RETRANS, "Local", "Transport", + "Retransmissions", "", "%d", &local_transport_retrans, 1, 0, + NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_TRANSPORT_RETRANS, "Remote", "Transport", + "Retransmissions", "", "%d", &remote_transport_retrans, 1, 0, + NETPERF_TYPE_INT32); + + set_output_elt(REQUEST_SIZE, "Request", "Size", "Bytes", "", "%d", + &req_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RESPONSE_SIZE, "Response", "Size", "Bytes", "", "%d", + &rsp_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(BURST_SIZE, "Initial", "Burst", "Requests", "", "%d", + &first_burst_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LSS_SIZE_REQ, "Local", "Send Socket", "Size", "Requested", + "%d", &lss_size_req, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LSS_SIZE, "Local", "Send Socket", "Size", "Initial", "%d", + &lss_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LSS_SIZE_END, "Local", "Send Socket", "Size", "Final", "%d", + &lss_size_end, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LSR_SIZE_REQ, "Local", "Recv Socket", "Size", "Requested", + "%d", &lsr_size_req, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LSR_SIZE, "Local", "Recv Socket", "Size", "Initial", "%d", + &lsr_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LSR_SIZE_END, "Local", "Recv Socket", "Size", "Final", "%d", + &lsr_size_end, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SEND_SIZE, "Local", "Send", "Size", "", "%d", + &send_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_RECV_SIZE, "Local", "Recv", "Size", "", "%d", + &recv_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SEND_CALLS, "Local", "Send", "Calls", "", "%"PRIu64, + &local_send_calls, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(LOCAL_RECV_CALLS, "Local", "Recv", "Calls", "", "%"PRIu64, + &local_receive_calls, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(LOCAL_BYTES_PER_RECV, "Local", "Bytes", "Per", "Recv", "%.2f", + &bytes_per_recv, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_BYTES_PER_SEND, "Local", "Bytes", "Per", "Send", "%.2f", + &bytes_per_send, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_BYTES_RECVD, "Local", "Bytes", "Received", "", "%"PRIu64, + &bytes_received, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(LOCAL_BYTES_SENT, "Local", "Bytes", "Sent", "", "%"PRIu64, + &bytes_sent, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(LOCAL_BYTES_XFERD, "Local", "Bytes", "Xferred", "", "%.0f", + &bytes_xferd, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_SEND_WIDTH, "Local", "Send", "Width", "", "%d", + &send_width, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_RECV_WIDTH, "Local", "Recv", "Width", "", "%d", + &recv_width, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SEND_OFFSET, "Local", "Send", "Offset", "", "%d", + &local_send_offset, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_RECV_OFFSET, "Local", "Recv", "Offset", "", "%d", + &local_recv_offset, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_RECV_ALIGN, "Local", "Recv", "Alignment", "", "%d", + &local_recv_align, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SEND_ALIGN, "Local", "Send", "Alignment", "", "%d", + &local_send_align, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SEND_DIRTY_COUNT, "Local", "Send", "Dirty", "Count", + "%d", &loc_dirty_count, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_RECV_DIRTY_COUNT, "Local", "Recv", "Dirty", "Count", + "%d", &loc_dirty_count, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_RECV_CLEAN_COUNT, "Local", "Recv", "Clean", "Count", + "%d", &loc_clean_count, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_CPU_UTIL, "Local", "CPU", "Util", "%", "%.2f", + &local_cpu_utilization, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PERCENT_USER, "Local", "CPU", "User", "%", "%.2f", + &lib_local_cpu_stats.cpu_user, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PERCENT_SYSTEM, + "Local", "CPU", "System", "%", "%.2f", + &lib_local_cpu_stats.cpu_system, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PERCENT_IOWAIT, + "Local", "CPU", "I/O", "%", "%.2f", + &lib_local_cpu_stats.cpu_iowait, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PERCENT_IRQ, + "Local", "CPU", "IRQ", "%", "%.2f", + &lib_local_cpu_stats.cpu_irq, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PERCENT_SWINTR, + "Local", "CPU", "swintr", "%", "%.2f", + &lib_local_cpu_stats.cpu_swintr, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PEAK_UTIL, "Local", "Peak", "Per CPU", "Util %", + "%.2f", &lib_local_cpu_stats.peak_cpu_util, 1, 0, + NETPERF_TYPE_FLOAT); + + set_output_elt(LOCAL_CPU_PEAK_ID, "Local", "Peak", "Per CPU", "ID", "%d", + &lib_local_cpu_stats.peak_cpu_id, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_CPU_BIND, "Local", "CPU", "Bind", "", "%d", + &local_proc_affinity, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SD, "Local", "Service", "Demand", "", "%.3f", + &local_service_demand_double, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(SD_UNITS, "Service", "Demand", "Units", "", "%s", + sd_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_CPU_METHOD, "Local", "CPU", "Util", "Method", "%c", + &local_cpu_method, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_CPU_COUNT, "Local", "CPU", "Count", "", "%d", + &lib_num_loc_cpus, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_NODELAY, "Local", "NODELAY", "", "", "%d", + &loc_nodelay, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_CORK, "Local", "Cork", "", "", "%d", + &loc_tcpcork, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RSS_SIZE_REQ, "Remote", "Send Socket", "Size", "Requested", + "%d", &rss_size_req, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RSS_SIZE, "Remote", "Send Socket", "Size", "Initial", "%d", + &rss_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RSS_SIZE_END, "Remote", "Send Socket", "Size", "Final", "%d", + &rss_size_end, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RSR_SIZE_REQ, "Remote", "Recv Socket", "Size", "Requested", + "%d", &rsr_size_req, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RSR_SIZE, "Remote", "Recv Socket", "Size", "Initial", "%d", + &rsr_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(RSR_SIZE_END, "Remote", "Recv Socket", "Size", "Final", "%d", + &rsr_size_end, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SEND_SIZE, "Remote", "Send", "Size", "", "%d", + &remote_send_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_RECV_SIZE, "Remote", "Recv", "Size", "", "%d", + &remote_recv_size, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SEND_CALLS, "Remote", "Send", "Calls", "", "%"PRIu64, + &remote_send_calls, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(REMOTE_RECV_CALLS, "Remote", "Recv", "Calls", "", "%"PRIu64, + &remote_receive_calls, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(REMOTE_BYTES_PER_RECV, "Remote", "Bytes", "Per", "Recv", + "%.2f", &remote_bytes_per_recv, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_BYTES_PER_SEND, "Remote", "Bytes", "Per", "Send", + "%.2f", &remote_bytes_per_send, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_BYTES_RECVD, "Remote", "Bytes", "Received", "", + "%"PRIu64, &remote_bytes_received, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(REMOTE_BYTES_SENT, "Remote", "Bytes", "Sent", "", "%"PRIu64, + &remote_bytes_sent, 1, 0, NETPERF_TYPE_UINT64); + + set_output_elt(REMOTE_BYTES_XFERD, "Remote", "Bytes", "Xferred", "", "%.0f", + &remote_bytes_xferd, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_SEND_WIDTH, "Remote", "Send", "Width", "", "%d", + &remote_send_width, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_RECV_WIDTH, "Remote", "Recv", "Width", "", "%d", + &remote_recv_width, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SEND_OFFSET, "Remote", "Send", "Offset", "", "%d", + &remote_send_offset, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_RECV_OFFSET, "Remote", "Recv", "Offset", "", "%d", + &remote_recv_offset, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_RECV_ALIGN, "Remote", "Recv", "Alignment", "", "%d", + &remote_recv_align, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SEND_ALIGN, "Remote", "Send", "Alignment", "", "%d", + &remote_send_align, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SEND_DIRTY_COUNT, "Remote", "Send", "Dirty", "Count", + "%d", &rem_dirty_count, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_RECV_DIRTY_COUNT, "Remote", "Recv", "Dirty", "Count", + "%d", &rem_dirty_count, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_RECV_CLEAN_COUNT, "Remote", "Recv", "Clean", "Count", + "%d", &rem_clean_count, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_CPU_UTIL, "Remote", "CPU", "Util", "%", "%.2f", + &remote_cpu_utilization, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PERCENT_USER, "Remote", "CPU", "User", "%", "%.2f", + &lib_remote_cpu_stats.cpu_user, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PERCENT_SYSTEM, + "Remote", "CPU", "System", "%", "%.2f", + &lib_remote_cpu_stats.cpu_system, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PERCENT_IOWAIT, + "Remote", "CPU", "I/O", "%", "%.2f", + &lib_remote_cpu_stats.cpu_iowait, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PERCENT_IRQ, + "Remote", "CPU", "IRQ", "%", "%.2f", + &lib_remote_cpu_stats.cpu_irq, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PERCENT_SWINTR, + "Remote", "CPU", "swintr", "%", "%.2f", + &lib_remote_cpu_stats.cpu_swintr, 1, 0, NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PEAK_UTIL, "Remote", "Peak", "Per CPU", "Util %", + "%.2f", &lib_remote_cpu_stats.peak_cpu_util, 1, 0, + NETPERF_TYPE_FLOAT); + + set_output_elt(REMOTE_CPU_PEAK_ID, "Remote", "Peak", "Per CPU", "ID", "%d", + &lib_remote_cpu_stats.peak_cpu_id, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_CPU_BIND, "Remote", "CPU", "Bind", "", "%d", + &remote_proc_affinity, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SD, "Remote", "Service", "Demand", "", "%.3f", + &remote_service_demand_double, 1, 0, NETPERF_TYPE_DOUBLE); + + set_output_elt(REMOTE_CPU_METHOD, "Remote", "CPU", "Util", "Method", "%c", + &remote_cpu_method, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_CPU_COUNT, "Remote", "CPU", "Count", "", "%d", + &lib_num_rem_cpus, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_NODELAY, "Remote", "NODELAY", "", "", "%d", + &rem_nodelay, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_CORK, "Remote", "Cork", "", "", "%d", + &rem_tcpcork, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_DRIVER_NAME, "Local", "Driver", "Name", "", "%s", + deprecated_str, 1, OMNI_WANT_LOC_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_DRIVER_VERSION, "Local", "Driver", "Version", "", "%s", + deprecated_str, 1, OMNI_WANT_LOC_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_DRIVER_FIRMWARE, "Local", "Driver", "Firmware", "", + "%s", deprecated_str, 1, OMNI_WANT_LOC_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_DRIVER_BUS, "Local", "Driver", "Bus", "", "%s", + deprecated_str, 1, OMNI_WANT_LOC_DRVINFO, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_DRIVER_NAME, "Remote", "Driver", "Name", "", "%s", + deprecated_str, 1, OMNI_WANT_REM_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_DRIVER_VERSION, "Remote", "Driver", "Version", "", + "%s", deprecated_str, 1, OMNI_WANT_REM_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_DRIVER_FIRMWARE, "Remote", "Driver", "Firmware", "", + "%s", deprecated_str, 1, OMNI_WANT_REM_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_DRIVER_BUS, "Remote", "Driver", "Bus", "", "%s", + deprecated_str, 1, OMNI_WANT_REM_DRVINFO, + NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_INTERFACE_SUBDEVICE, "Local", "Interface", "Subdevice", + "", "0x%.4x", &local_interface_subdevice, 1, + OMNI_WANT_LOC_IFIDS, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_INTERFACE_DEVICE, "Local", "Interface", "Device", "", + "0x%.4x", &local_interface_device, 1, OMNI_WANT_LOC_IFIDS, + NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_INTERFACE_SUBVENDOR, "Local", "Interface", "Subvendor", + "", "0x%.4x", &local_interface_subvendor, 1, + OMNI_WANT_LOC_IFIDS, NETPERF_TYPE_UINT32); + + set_output_elt(LOCAL_INTERFACE_VENDOR, "Local", "Interface", "Vendor", "", + "0x%.4x", &local_interface_vendor, 1, OMNI_WANT_LOC_IFIDS, + NETPERF_TYPE_UINT32); + + set_output_elt(REMOTE_INTERFACE_SUBDEVICE, "Remote", "Interface", + "Subdevice", "", "0x%.4x", &remote_interface_subdevice, 1, + OMNI_WANT_REM_IFIDS, NETPERF_TYPE_UINT32); + + set_output_elt(REMOTE_INTERFACE_DEVICE, "Remote", "Interface", "Device", "", + "0x%.4x", &remote_interface_device, 1, OMNI_WANT_REM_IFIDS, + NETPERF_TYPE_UINT32); + + set_output_elt(REMOTE_INTERFACE_SUBVENDOR, "Remote", "Interface", + "Subvendor", "", "0x%.4x", &remote_interface_subvendor, 1, + OMNI_WANT_REM_IFIDS, NETPERF_TYPE_UINT32); + + set_output_elt(REMOTE_INTERFACE_VENDOR, "Remote", "Interface", "Vendor", "", + "0x%.4x", &remote_interface_vendor, 1, OMNI_WANT_REM_IFIDS, + NETPERF_TYPE_UINT32); + + set_output_elt(LOCAL_INTERFACE_NAME, "Local", "Interface", "Name", "", "%s", + deprecated_str, 1, OMNI_WANT_LOC_IFNAME, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_INTERFACE_NAME, "Remote", "Interface", "Name", "", + "%s", deprecated_str, 1, OMNI_WANT_REM_IFNAME, + NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_INTERFACE_SLOT, "Local", "Interface", "Slot", "", "%s", + deprecated_str, 1, OMNI_WANT_LOC_IFSLOT, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_INTERFACE_SLOT, "Remote", "Interface", "Slot", "", + "%s", deprecated_str, 1, OMNI_WANT_REM_IFSLOT, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_MACHINE, "Remote", "Machine", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_VERSION, "Remote", "Version", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_RELEASE, "Remote", "Release", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_SYSNAME, "Remote", "Sysname", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_MACHINE, "Local", "Machine", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_VERSION, "Local", "Version", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_RELEASE, "Local", "Release", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_SYSNAME, "Local", "Sysname", "", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_INTERVAL_USECS, "Remote", "Interval", "Usecs", "", + "%d", &remote_interval_usecs, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_INTERVAL_BURST, "Remote", "Interval", "Burst", "", + "%d", &remote_interval_burst, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SECURITY_ENABLED, "Local", "OS", "Security", "Enabled", + "%s", deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_SECURITY_TYPE, "Local", "OS", "Security", "Type", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_SECURITY_SPECIFIC, "Local", "OS", "Security", + "Specific", "%s", deprecated_str, 1, 0, + NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_SECURITY_ENABLED_NUM, "Local", "OS", "Security", + "Enabled Num", "%d", &local_security_enabled_num, 1, 0, + NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SECURITY_TYPE_ID, "Local", "OS", "Security", "Type ID", + "%d", &local_security_type_id, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SECURITY_ENABLED, "Remote", "OS", "Security", + "Enabled", "%s", deprecated_str, 1, 0, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_SECURITY_TYPE, "Remote", "OS", "Security", "Type", + "%s", deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_SECURITY_SPECIFIC, "Remote", "OS", "Security", + "Specific", "%s", deprecated_str, 1, 0, + NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_SECURITY_ENABLED_NUM, "Remote", "OS", "Security", + "Enabled", "%d", &remote_security_enabled_num, 1, 0, + NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SECURITY_TYPE_ID, "Remote", "OS", "Security", "Type", + "%d", &remote_security_type_id, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_INTERVAL_USECS, "Local", "Interval", "Usecs", "", "%d", + &interval_usecs, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_INTERVAL_BURST, "Local", "Interval", "Burst", "", "%d", + &interval_burst, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SYSTEM_MODEL, "Remote", "System", "Model", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_CPU_MODEL, "Remote", "CPU", "Model", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_CPU_FREQUENCY, "Remote", "CPU", "Frequency", "MHz", + "%d", &remote_cpu_frequency, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SYSTEM_MODEL, "Local", "System", "Model", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_CPU_MODEL, "Local", "CPU", "Model", "", "%s", + deprecated_str, 1, 0, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_CPU_FREQUENCY, "Local", "CPU", "Frequency", "MHz", "%d", + &local_cpu_frequency, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(MIN_LATENCY, "Minimum", "Latency", "Microseconds", "", "%d", + &min_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_INT32); + + set_output_elt(MAX_LATENCY, "Maximum", "Latency", "Microseconds", "", "%d", + &max_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_INT32); + + set_output_elt(P50_LATENCY, "50th", "Percentile", "Latency", "Microseconds", + "%d", &p50_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_INT32); + + set_output_elt(P90_LATENCY, "90th", "Percentile", "Latency", "Microseconds", + "%d", &p90_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_INT32); + + set_output_elt(P99_LATENCY, "99th", "Percentile", "Latency", "Microseconds", + "%d", &p99_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_INT32); + + set_output_elt(MEAN_LATENCY, "Mean", "Latency", "Microseconds", "", "%.2f", + &mean_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_DOUBLE); + + set_output_elt(STDDEV_LATENCY, "Stddev", "Latency", "Microseconds", "", + "%.2f", &stddev_latency, 0, OMNI_WANT_STATS, NETPERF_TYPE_DOUBLE); + + set_output_elt(LOCAL_SOCKET_PRIO, "Local", "Socket", "Priority", "", "%d", + &local_socket_prio, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SOCKET_PRIO, "Remote", "Socket", "Priority", "", "%d" + , &remote_socket_prio, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_SOCKET_TOS, "Local", "Socket", "TOS", "", "0x%.2x", + &local_socket_tos, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(REMOTE_SOCKET_TOS, "Remote", "Socket", "TOS", "", "0x%.2x", + &remote_socket_tos, 1, 0, NETPERF_TYPE_INT32); + + set_output_elt(LOCAL_CONG_CONTROL, "Local", "Congestion", "Control", + "Algorithm", "%s", local_cong_control, 0, + OMNI_WANT_LOC_CONG, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_CONG_CONTROL, "Remote", "Congestion", "Control", + "Algorithm", "%s", remote_cong_control, 0, + OMNI_WANT_REM_CONG, NETPERF_TYPE_CHAR); + + set_output_elt(LOCAL_FILL_FILE, "Local", "Fill", "File", "", "%s", + local_fill_file, 0, 0, NETPERF_TYPE_CHAR); + + set_output_elt(REMOTE_FILL_FILE, "Remote", "Fill", "File", "", "%s", + remote_fill_file, 0, 0, NETPERF_TYPE_CHAR); + + set_output_elt(OUTPUT_END, "This", "Is", "The", "End", "%s", + NULL, 0, 0, NETPERF_TYPE_CHAR); + +} + +void +print_omni_init() { + + int i,j; + + if (debug) { + fprintf(where,"%s entered\n", + __FUNCTION__); + fflush(where); + } + + /* why is this before the if you ask? because some of the output + specifiers are char * rather than char[] and when I wanted to + start setting output_group flags I was needing to call + print_omni_init() before the char * 's were malloced, which meant + the netperf_output_source got NULL pointers. there is + undoubtedly a cleaner way to do all this. raj 20110629 */ + + print_omni_init_list(); + + if (printing_initialized) return; + + printing_initialized = 1; + + + /* belts and suspenders */ + for (j = 0; j < NETPERF_MAX_BLOCKS; j++) + for (i = 0; i < NETPERF_OUTPUT_MAX; i++) + output_list[j][i] = OUTPUT_END; + + + if (output_selection_spec) { + parse_output_selection(output_selection_spec); + } + else { + set_output_list_by_test(); + } + +} + +/* why? because one cannot simply pass a pointer to snprintf - well + except when it is expecting one... */ +int +my_snprintf(char *buffer, size_t size, netperf_output_elt_t *output_elt) +{ + switch (output_elt->output_type) { + case NETPERF_TYPE_CHAR: + return snprintf(buffer, size, + output_elt->format, + (char *)output_elt->display_value); + break; + case NETPERF_TYPE_INT32: + return snprintf(buffer, size, + output_elt->format, + *(int *)(output_elt->display_value)); + break; + case NETPERF_TYPE_UINT32: + return snprintf(buffer, size, + output_elt->format, + *(unsigned int *)(output_elt->display_value)); + break; + case NETPERF_TYPE_INT64: + return snprintf(buffer, size, + output_elt->format, + *(long long *)(output_elt->display_value)); + break; + case NETPERF_TYPE_UINT64: + return snprintf(buffer, size, + output_elt->format, + *(unsigned long long *)(output_elt->display_value)); + break; + case NETPERF_TYPE_FLOAT: + return snprintf(buffer, size, + output_elt->format, + *(float *)(output_elt->display_value)); + break; + case NETPERF_TYPE_DOUBLE: + return snprintf(buffer, size, + output_elt->format, + *(double *)(output_elt->display_value)); + break; + default: + fprintf(stderr, + "Unknown/unsupported output_elt output_type of %d\n", + output_elt->output_type); + fflush(stderr); + exit(-1); + } +} + +void +print_omni_csv() +{ + + int i,j,k,buflen,vallen; + + char *hdr1 = NULL; + char *val1 = NULL; + char *h1 = NULL; + char *v1 = NULL; + char tmpval[1024]; + + buflen = 0; + for (i = 0; i < NETPERF_MAX_BLOCKS; i++) { + for (j = 0; + ((j < NETPERF_OUTPUT_MAX) && + (output_list[i][j] != OUTPUT_END)); + j++) { + if ((netperf_output_source[output_list[i][j]].format != NULL) && + (netperf_output_source[output_list[i][j]].display_value != NULL)) { + vallen = + my_snprintf(tmpval, + 1024, + &(netperf_output_source[output_list[i][j]])); + if (vallen == -1) { + fprintf(where,"my_snprintf failed on %s with format %s\n", + netperf_output_enum_to_str(j), + netperf_output_source[output_list[i][j]].format); + fflush(where); + } + vallen += 1; /* forget not the terminator */ + } + else + vallen = 0; + + if (vallen > + netperf_output_source[output_list[i][j]].tot_line_len) + netperf_output_source[output_list[i][j]].tot_line_len = vallen; + + buflen += + netperf_output_source[output_list[i][j]].tot_line_len; + } + } + + if (print_headers) hdr1 = malloc(buflen + 1); + val1 = malloc(buflen + 1); + + if (((hdr1 == NULL) && (print_headers)) || + (val1 == NULL)) { + fprintf(where,"unable to allocate output buffers\n"); + fflush(where); + exit(-1); + } + + if (print_headers) memset(hdr1,' ',buflen + 1); + memset(val1,' ',buflen + 1); + + /* ostensibly, we now "know" that we have enough space in all our + strings, and we have spaces where we want them etc */ + h1 = hdr1; + v1 = val1; + for (i = 0; i < NETPERF_MAX_BLOCKS; i++) { + for (j = 0; + ((j < NETPERF_OUTPUT_MAX) && + (output_list[i][j] != OUTPUT_END)); + j++) { + int len; + len = 0; + if (print_headers) { + for (k = 0; ((k < 4) && + (NULL != + netperf_output_source[output_list[i][j]].line[k]) && + (strcmp("",netperf_output_source[output_list[i][j]].line[k]))); k++) { + + len = sprintf(h1, + "%s", + netperf_output_source[output_list[i][j]].line[k]); + *(h1 + len) = ' '; + /* now move to the next starting column. for csv we aren't worried + about alignment between the header and the value lines */ + h1 += len + 1; + } + *(h1 - 1) = ','; + } + if ((netperf_output_source[output_list[i][j]].format != NULL) && + (netperf_output_source[output_list[i][j]].display_value != NULL)) { + /* tot_line_len is bogus here, but should be "OK" ? */ + len = my_snprintf(v1, + netperf_output_source[output_list[i][j]].tot_line_len, + &(netperf_output_source[output_list[i][j]])); + + /* nuke the trailing \n" from the string routine. */ + *(v1 + len) = ','; + v1 += len + 1; + } + else { + /* we need a ',' even if there is no value */ + *v1 = ','; + v1 += 2; + } + } + } + + /* ok, _now_ null terminate each line by nuking the last comma. do + we have an OBOB here? */ + if (print_headers) *(h1-1) = 0; + *(v1-1) = 0; + /* and now spit it out, but only if it is going to have something + in it. we don't want a bunch of blank lines or nulls... */ + if (output_list[0][0] != OUTPUT_END) { + if (print_headers) printf("%s\n",hdr1); + printf("%s\n",val1); + } + + if (hdr1 != NULL) free(hdr1); + if (val1 != NULL) free(val1); + +} + +void +print_omni_keyword() +{ + /* this one should be the simplest of all - no buffers to allocate, + just spit it all out. raj 20080805 */ + + int i,j; + char tmpval[1024]; + int vallen; + + for (i = 0; i < NETPERF_MAX_BLOCKS; i++) { + for (j = 0; + ((j < NETPERF_OUTPUT_MAX) && + (output_list[i][j] != OUTPUT_END)); + j++) { + if ((netperf_output_source[output_list[i][j]].format != NULL) && + (netperf_output_source[output_list[i][j]].display_value != NULL)) { + vallen = + my_snprintf(tmpval, + 1024, + &(netperf_output_source[output_list[i][j]])); + if (vallen == -1) { + snprintf(tmpval, + 1024, + "my_snprintf failed with format %s\n", + netperf_output_source[output_list[i][j]].format); + } + fprintf(where, + "%s=%s\n",netperf_output_enum_to_str(output_list[i][j]), + tmpval); + } + } + } + fflush(where); +} + +void +print_omni_human() +{ + + int i,j,k,buflen,buflen_max; + + char *hdr[4]; + char *val1 = NULL; + char tmpval[1024]; /* excessive, but we may have the command line */ + int vallen; + + for (k = 0; k < 4; k ++) { + hdr[k] = NULL; + } + + /* decisions, decisions... walk the list twice to only need to + allocate the charcter buffers once, or walk it once and possibly + reallocate them as I go... oh, lets walk it twice just for fun to + start. since only now do we know that the values are around to be + printed, we should try the snprintf for the value and see how + much space it wants and update max_line_len accordingly */ + buflen_max = 0; + for (i = 0; i < NETPERF_MAX_BLOCKS; i++) { + buflen = 0; + for (j = 0; + ((j < NETPERF_OUTPUT_MAX) && + (output_list[i][j] != OUTPUT_END)); + j++) { + if ((netperf_output_source[output_list[i][j]].format != NULL) && + (netperf_output_source[output_list[i][j]].display_value != + NULL)) + /* need to count the \n */ + vallen = my_snprintf(tmpval, + 1024, + &(netperf_output_source[output_list[i][j]])) + 1; + else + vallen = 0; + + if (vallen > + netperf_output_source[output_list[i][j]].max_line_len) + netperf_output_source[output_list[i][j]].max_line_len = vallen; + + buflen += + netperf_output_source[output_list[i][j]].max_line_len + 1; + } + + if (buflen > buflen_max) + buflen_max = buflen; + } + + /* more belts and suspenders */ + for (k = 0; (k < 4) && (print_headers); k++) { + hdr[k] = malloc(buflen_max+1); + } + val1 = malloc(buflen_max+1); + + /* we could probably be more succinct here but perhaps the compiler + can figure that out for us :) */ + for (k = 0; (k < 4) && (print_headers); k++) { + if (hdr[k] == NULL) { + fprintf(where,"Unable to allocate output buffers\n"); + fflush(where); + exit(-1); + } + } + + /* ostensibly, we now "know" that we have enough space in all our + strings, and we have spaces where we want them etc */ + for (i = 0; i < NETPERF_MAX_BLOCKS; i++) { + char *h[4]; + char *v1 = val1; + + for (k = 0; k < 4; k++) h[k] = hdr[k]; + + /* we want to blank things out each time since we skip around a lot */ + for (k = 0; (k < 4) && (print_headers); k++) { + memset(hdr[k],' ',buflen_max+1); + } + memset(val1,' ',buflen_max+1); + + + for (j = 0; + ((j < NETPERF_OUTPUT_MAX) && + (output_list[i][j] != OUTPUT_END)); + j++) { + if (print_headers) { + for (k = 0; k < 4; k++) { + memcpy(h[k], + netperf_output_source[output_list[i][j]].line[k], + strlen(netperf_output_source[output_list[i][j]].line[k])); + } + } + if ((netperf_output_source[output_list[i][j]].format != NULL) && + (netperf_output_source[output_list[i][j]].display_value != NULL)) { + int len; + len = my_snprintf(v1, + netperf_output_source[output_list[i][j]].max_line_len, + &(netperf_output_source[output_list[i][j]])); + /* nuke the trailing \n" from the string routine. */ + *(v1 + len) = ' '; + } + /* now move to the next starting column */ + for (k = 0; (k < 4) && (print_headers); k++) { + h[k] += + netperf_output_source[output_list[i][j]].max_line_len + 1; + } + v1 += netperf_output_source[output_list[i][j]].max_line_len + 1; + } + /* ok, _now_ null terminate each line. do we have an OBOB here? */ + for (k = 0; (k < 4) && (print_headers); k++) { + *h[k] = 0; + } + *v1 = 0; + /* and now spit it out, but only if it is going to have something + in it. we don't want a bunch of blank lines or nulls... at some + point we might want to work backwards collapsine whitespace from + the right but for now, we won't bother */ + if (output_list[i][0] != OUTPUT_END) { + if (i > 0) printf("\n"); /* we want a blank line between blocks ? */ + for (k = 0; (k < 4) && (print_headers); k++) { + printf("%s\n",hdr[k]); + } + printf("%s\n",val1); + } + }; + for (k = 0; k < 4; k++) { + if (hdr[k] != NULL) free(hdr[k]); + } +} + +void +print_omni() +{ + + print_omni_init(); + + if (debug > 2) + dump_netperf_output_source(where); + + switch (netperf_output_mode) { + case CSV: + print_omni_csv(); + break; + case KEYVAL: + print_omni_keyword(); + break; + case HUMAN: + print_omni_human(); + break; + default: + fprintf(where,"Yo Rick! There is a bug in netperf_output_mode!\n"); + fflush(where); + exit(-1); + } + +} +/* for the next few routines (connect, accept, send, recv, + disconnect/close) we will use a return of -1 to mean times up, -2 + to mean a transient error (eg ENOBUFS on a UDP send call) and -3 to + mean hard error. this means it is ok for the connect routine to + return a 0 (zero) if that happens to be the fd/SOCKET we get and in + theory we will be able to support zero-length messages on those + protocols which support it. all in theory of course. raj + 2008-01-09 */ + +int +connect_data_socket(SOCKET send_socket, struct addrinfo *remote_res, int dont_give_up) +{ + int ret; + + /* Connect up to the remote port on the data socket */ + if ((ret = connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen)) == INVALID_SOCKET) { + if (SOCKET_EINTR(ret)) { + /* we interpret this to mean that the test is supposed to be + over, so return a value of -1 to the caller */ + return -1; + } + if ((SOCKET_EADDRINUSE(ret)) || SOCKET_EADDRNOTAVAIL(ret) || dont_give_up) { + /* likely something our explicit bind() would have caught in + the past, so go get another port, via create_data_socket. + yes, this is a bit more overhead than before, but the + condition should be rather rare. we only get a new port if + this was a connection-including test like TCP_CRR or + TCP_CC. Otherwise we need to return an error. raj + 2008-01-08 */ + return -2; + } + else + /* -3 means there was an error */ + return -3; + } + return 0; +} + +static +int send_pktinfo(SOCKET data_socket, char *buffer, int len, struct sockaddr *destination, int destlen) { +#ifdef IP_PKTINFO + struct msghdr msg; + struct iovec iovec[1]; + char msg_control[512]; + struct cmsghdr *cmsg; + int cmsg_space = 0; + + iovec[0].iov_base = buffer; + iovec[0].iov_len = len; + msg.msg_name = destination; + msg.msg_namelen = destlen; + msg.msg_iov = iovec; + msg.msg_iovlen = 1; + msg.msg_control = msg_control; + msg.msg_controllen = sizeof(msg_control); + msg.msg_flags = 0; + + cmsg = CMSG_FIRSTHDR(&msg); + if (have_pktinfo) { + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); + *(struct in_pktinfo*)CMSG_DATA(cmsg) = in_pktinfo; + cmsg_space += CMSG_SPACE(sizeof(in_pktinfo)); + } + msg.msg_controllen = cmsg_space; + return sendmsg(data_socket, &msg, 0); +#else + return -1; +#endif /* IP_PKTINFO */ +} + + +int +send_data(SOCKET data_socket, struct ring_elt *send_ring, uint32_t bytes_to_send, struct sockaddr *destination, int destlen, int protocol) { + + int len; + + /* if the user has supplied a destination, we use sendto, otherwise + we use send. we ass-u-me blocking operations always, so no need + to check for eagain or the like. */ + + if (debug > 2) { + fprintf(where, + "%s sock %d, ring elt %p, bytes %d, dest %p, len %d\n", + __FUNCTION__, + data_socket, + send_ring, + bytes_to_send, + destination, + destlen); + fflush(where); + } + + if (destination) { + if (have_pktinfo) { + len = send_pktinfo(data_socket, + send_ring->buffer_ptr, + bytes_to_send, + destination, + destlen); + } + else { + len = sendto(data_socket, + send_ring->buffer_ptr, + bytes_to_send, +#if defined(MSG_FASTOPEN) + (use_fastopen && protocol == IPPROTO_TCP) ? MSG_FASTOPEN : 0, +#else + 0, +#endif + destination, + destlen); + } + } + else { + if (!use_write) { + len = send(data_socket, + send_ring->buffer_ptr, + bytes_to_send, + 0); + } + else { +#ifndef WIN32 + len = write(data_socket, + send_ring->buffer_ptr, + bytes_to_send); +#else + fprintf(where,"I'm sorry Dave I cannot write() under Windows\n"); + fflush(where); + return -3; +#endif + } + } + if(len != bytes_to_send) { + /* don't forget that some platforms may do a partial send upon + receipt of the interrupt and not return an EINTR... */ + if (SOCKET_EINTR(len) || (len >= 0)) + { + /* we hit the end of a timed test. */ + return -1; + } + /* if this is UDP it is possible to receive an ENOBUFS on the send + call and it would not be a fatal error. of course if we were + to return 0 then it would make the test think it was over when + it really wasn't. the question becomes what to do. for the + time being, the answer will likely be to return something like + -2 to indicate a non-fatal error happened on the send and let + the caller figure it out :) we won't actually check to see if + this is UDP - it is the author's experience in many, Many, MANY + years that the only time an ENOBUFS has been returned in a + netperf test has been with UDP. famous last words :) */ + if (errno == ENOBUFS) + return -2; + else { + fprintf(where,"%s: data send error: %s (errno %d)\n", + __FUNCTION__, strerror(errno), errno); + return -3; + } + } + return len; +} + +#if defined(__linux) +static int +recv_data_no_copy(SOCKET data_socket, struct ring_elt *recv_ring, uint32_t bytes_to_recv, struct sockaddr *source, netperf_socklen_t *sourcelen, uint32_t flags, uint32_t *num_receives) { + +#ifndef SPLICE_F_MOVE +# define SPLICE_F_MOVE 0x01 +#endif +#ifndef SPLICE_F_NONBLOCK +# define SPLICE_F_NONBLOCK 0x02 +#endif + + static int pfd[2] = {-1, -1}; + static int fdnull = -1; + + + char *temp_message_ptr; + int bytes_left; + int bytes_recvd; + int my_recvs; + int my_flags = SPLICE_F_MOVE | SPLICE_F_NONBLOCK; /* values + suggested by + Eric Dumazet */ + int ret; + + if (pfd[0] == -1) { + if (pipe(pfd)) { + fprintf(where, + "%s pipe call failed with errno %d '%s'\n", + __FUNCTION__, + errno, + strerror(errno)); + return -4; /* this will cause recv_data to do things the + old-fashioned way for the test */ + } + if ((fdnull = open("/dev/null",O_WRONLY)) == -1) { + fprintf(where, + "%s open call failed with errno %d '%s'\n", + __FUNCTION__, + errno, + strerror(errno)); + return -4; + } + } + + /* receive data off the data_socket, ass-u-me-ing a blocking socket + all the way!-) 2008-01-08 */ + my_recvs = 0; + bytes_left = bytes_to_recv; + + if (debug > 1) { + fprintf(where, + "%s sock %d, ring elt %p, bytes %d, source %p, srclen %d, flags %x, num_recv %p\n", + __FUNCTION__, + data_socket, + recv_ring, + bytes_to_recv, + source, + (source != NULL) ? *sourcelen : -1, + flags, + num_receives); + fflush(where); + } + do { + + bytes_recvd = splice(data_socket, + NULL, + pfd[1], + NULL, + bytes_left, + my_flags); + + + if (bytes_recvd > 0) { + /* per Eric Dumazet, we should just let this second splice call + move as many bytes as it can and not worry about how much. + this should make the call more robust when made on a system + under memory pressure */ + splice(pfd[0], NULL, fdnull, NULL, 1 << 30, my_flags); + bytes_left -= bytes_recvd; + } + else { + break; + } + my_recvs++; /* should the pair of splices count as one? */ + } while ((bytes_left > 0) && (flags & NETPERF_WAITALL)); + + *num_receives = my_recvs; + + /* OK, we are out of the loop - now what? */ + if (bytes_recvd < 0) { + /* did the timer hit, or was there an error? */ + if (SOCKET_EINTR(bytes_recvd)) + { + /* We hit the end of a timed test. */ + return -1; + } + /* it was a hard error */ + return -3; + } + + + /* this looks a little funny, but should be correct. if we had + NETPERF_WAITALL set and we got here, it means we got all the + bytes of the request/response. otherwise we would have hit the + error or end of test cases. if NETPERF_WAITALL isn't set, this + is a STREAM test, and we will have only made one call to recv, so + bytes_recvd will be accurate. */ + if (bytes_left) + return bytes_recvd; + else + return bytes_to_recv; + +} + +#endif + +static +int recv_pktinfo(SOCKET data_socket, char *message_ptr, int bytes_to_recv, int my_flags, struct sockaddr *source, netperf_socklen_t *sourcelen) { + +#ifdef IP_PKTINFO + struct iovec my_iovec; + struct msghdr my_header; + struct cmsghdr *cmsg; + struct in_pktinfo *pktinfo; + + char control_buf[512]; + int onoff = 1; + int ret; + + my_iovec.iov_base = message_ptr; + my_iovec.iov_len = bytes_to_recv; + + my_header.msg_name = source; + my_header.msg_namelen = *sourcelen; + my_header.msg_iov = &my_iovec; + my_header.msg_iovlen = 1; + my_header.msg_control = control_buf; + my_header.msg_controllen = sizeof(control_buf); + + /* not going to bother checking, if it doesn't work we are no + worse-off than we were before. we are going to ignore IPv6 for + the time being */ + setsockopt(data_socket, IPPROTO_IP, IP_PKTINFO, &onoff, sizeof(onoff)); + + ret = recvmsg(data_socket, &my_header, 0); + + if (ret >= 0) { + struct sockaddr_in me; + struct sockaddr_in clear; + netperf_socklen_t melen = sizeof(me); + for (cmsg = CMSG_FIRSTHDR(&my_header); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&my_header, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + in_pktinfo = *(struct in_pktinfo *)CMSG_DATA(cmsg); + have_pktinfo = 1; + } + } + } + + onoff = 0; + setsockopt(data_socket, IPPROTO_IP, IP_PKTINFO, &onoff, sizeof(onoff)); + + return ret; +#else + return -1; +#endif +} + + +int +recv_data(SOCKET data_socket, struct ring_elt *recv_ring, uint32_t bytes_to_recv, struct sockaddr *source, netperf_socklen_t *sourcelen, uint32_t flags, uint32_t *num_receives) { + + char *temp_message_ptr; + int bytes_left; + int bytes_recvd; + int my_recvs; + int my_flags = 0; /* will we one day want to set MSG_WAITALL? */ + +#if defined(__linux) + int ret; + if (loc_rcvavoid == 1) { + ret = recv_data_no_copy(data_socket, recv_ring, bytes_to_recv, source, sourcelen, flags, num_receives); + if (ret != -4) + return ret; + else + loc_rcvavoid = 0; + } +#endif + + /* receive data off the data_socket, ass-u-me-ing a blocking socket + all the way!-) 2008-01-08 */ + my_recvs = 0; + bytes_left = bytes_to_recv; + temp_message_ptr = recv_ring->buffer_ptr; + + if (debug > 1) { + fprintf(where, + "%s sock %d, ring elt %p, bytes %d, source %p, srclen %d, flags %x, num_recv %p\n", + __FUNCTION__, + data_socket, + recv_ring, + bytes_to_recv, + source, + (source != NULL) ? *sourcelen : -1, + flags, + num_receives); + fflush(where); + } + do { + if (source) { + /* call recvfrom it does look a little silly here inside the do + while, but I think it is ok - a UDP or other DGRAM or + SEQPACKET (?) socket, which should be the only time we + pass-in a source pointer will have a semantic that should get + us out of the dowhile on the first call anyway. if it + turns-out not to be the case, then we can hoist the if above + the do and put the dowhile in the else. */ + if (use_pktinfo) { + bytes_recvd = recv_pktinfo(data_socket, + temp_message_ptr, + bytes_left, + my_flags, + source, + sourcelen); + use_pktinfo = 0; + } + else { + bytes_recvd = recvfrom(data_socket, + temp_message_ptr, + bytes_left, + my_flags, + source, + sourcelen); + } + } + else { + /* just call recv */ + bytes_recvd = recv(data_socket, + temp_message_ptr, + bytes_left, + my_flags); + } + if (bytes_recvd > 0) { + bytes_left -= bytes_recvd; + temp_message_ptr += bytes_recvd; + } + else { + break; + } + my_recvs++; + } while ((bytes_left > 0) && (flags & NETPERF_WAITALL)); + + *num_receives = my_recvs; + + /* OK, we are out of the loop - now what? */ + if (bytes_recvd < 0) { + /* did the timer hit, or was there an error? */ + if (SOCKET_EINTR(bytes_recvd)) + { + /* We hit the end of a timed test. */ + return -1; + } + if (SOCKET_EAGAIN(bytes_recvd) || + SOCKET_EWOULDBLOCK(bytes_recvd)) { + return -2; + } + /* it was a hard error */ + return -3; + } + + + /* this looks a little funny, but should be correct. if we had + NETPERF_WAITALL set and we got here, it means we got all the + bytes of the request/response. otherwise we would have hit the + error or end of test cases. if NETPERF_WAITALL isn't set, this + is a STREAM test, and we will have only made one call to recv, so + bytes_recvd will be accurate. */ + if (bytes_left) + return bytes_recvd; + else + return bytes_to_recv; + +} + + +int +close_data_socket(SOCKET data_socket, struct sockaddr *peer, int peerlen, int protocol) +{ + + int ret; + char buffer[4]; + + if (debug) { + fprintf(where, + "%s sock %d peer %p peerlen %d protocol %d\n", + __FUNCTION__, + data_socket, + peer, + peerlen, + protocol); + fflush(where); + } + + if (protocol == IPPROTO_UDP) { + /* try to give the remote a signal. what this means if we ever + wanted to actually send zero-length messages remains to be seen + :) */ + int i; + for (i = 0; i < 3; i++) { + if (peer) + ret = sendto(data_socket, + buffer, + 0, + 0, + peer, + peerlen); + else + ret = send(data_socket, + buffer, + 0, + 0); + if (SOCKET_EINTR(ret)) { + close(data_socket); + return -1; + } + } + } + ret = close(data_socket); + + if (SOCKET_EINTR(ret)) { + /* end of test */ + return -1; + } + else if (ret == 0) { + return ret; + } + else + return -3; + +} + +int +disconnect_data_socket(SOCKET data_socket, int initiate, int do_close, struct sockaddr *peer, int peerlen) +{ + + char buffer[4]; + int bytes_recvd; + + if (debug) { + fprintf(where, + "%s sock %d init %d do_close %d protocol %d\n", + __FUNCTION__, + data_socket, + initiate, + do_close, + protocol); + fflush(where); + } + + /* at some point we'll need to abstract this a little. for now, if + the protocol is UDP, we try to send some number of zero-length + datagrams to allow the remote to get out of its loop without + having to wait for the padded timer to expire. if it isn't UDP, + we assume a reliable connection and can do the usual graceful + shutdown thing */ + + /* this needs to be revisited for the netperf receiving case when + the test is terminated by a Ctrl-C. raj 2012-01-24 */ + + if (protocol != IPPROTO_UDP) { + if (initiate) + shutdown(data_socket, SHUT_WR); + + /* we are expecting to get either a return of zero indicating + connection close, or an error. of course, we *may* never + receive anything from the remote which means we probably really + aught to have a select here but until we are once bitten we + will remain twice bold. */ + bytes_recvd = recv(data_socket, + buffer, + 1, + 0); + + if (bytes_recvd != 0) { + /* connection close, call close. we assume that the requisite + number of bytes have been received */ + if (SOCKET_EINTR(bytes_recvd)) + { + /* We hit the end of a timed test. */ + return -1; + } + return -3; + } + } + else { + int i; + for (i = 0; i < 3; i++) { + if (peer) + bytes_recvd = sendto(data_socket, + buffer, + 0, + 0, + peer, + peerlen); + else + bytes_recvd = send(data_socket, + buffer, + 0, + 0); + /* we only really care if the timer expired on us */ + if (SOCKET_EINTR(bytes_recvd)) { + if (do_close) + close(data_socket); + return -1; + } + } + } + + if (do_close) + close(data_socket); + + return 0; +} + +#ifdef HAVE_LINUX_TCP_H +static void +dump_tcp_info(struct tcp_info *tcp_info) +{ + + fprintf(stderr, + "tcpi_rto %d tcpi_ato %d tcpi_pmtu %d tcpi_rcv_ssthresh %d\n" + "tcpi_rtt %d tcpi_rttvar %d tcpi_snd_ssthresh %d tpci_snd_cwnd %d\n" + "tcpi_reordering %d tcpi_total_retrans %d\n", + tcp_info->tcpi_rto, + tcp_info->tcpi_ato, + tcp_info->tcpi_pmtu, + tcp_info->tcpi_rcv_ssthresh, + tcp_info->tcpi_rtt, + tcp_info->tcpi_rttvar, + tcp_info->tcpi_snd_ssthresh, + tcp_info->tcpi_snd_cwnd, + tcp_info->tcpi_reordering, + tcp_info->tcpi_total_retrans); + + return; +} + +#endif + +static int +get_transport_retrans(SOCKET socket, int protocol) { + +#ifdef HAVE_LINUX_TCP_H + struct tcp_info tcp_info; + + int ret; + netperf_socklen_t infosize; + + if (protocol != IPPROTO_TCP) + return -1; + + infosize = sizeof(struct tcp_info); + + if ((ret = getsockopt(socket,protocol,TCP_INFO,&tcp_info,&infosize)) < 0) { + if (debug) { + fprintf(where, + "%s: getsockopt errno %d %s\n", + __FUNCTION__, + errno, + strerror(errno)); + fflush(where); + } + return -1; + } + else { + + /* we assume that if we have LINUX_TCP_H we also have getenv */ + if (debug > 1 || getenv("DUMP_TCP_INFO")) { + dump_tcp_info(&tcp_info); + } + return tcp_info.tcpi_total_retrans; + } + + +#else + return -1; +#endif +} + + +static void +get_transport_info(SOCKET socket, int *mss, int protocol) +{ + + netperf_socklen_t sock_opt_len; + int option; + sock_opt_len = sizeof(netperf_socklen_t); + + switch (protocol) { +#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) + case IPPROTO_TCP: + option = TCP_MAXSEG; + break; +#endif + +#if defined(IPPROTO_SCTP) && defined(SCTP_MAXSEG) + case IPPROTO_SCTP: + option = SCTP_MAXSEG; + break; +#endif + default: + *mss = -1; + return; + } + + if (getsockopt(socket, + protocol, + option, + (char *)mss, + &sock_opt_len) == SOCKET_ERROR) { + fprintf(where, + "%s: getsockopt: errno %d\n", + __FUNCTION__, + errno); + fflush(where); + *mss = -1; + } + +} + +static void +get_transport_cong_control(SOCKET socket, int protocol, char cong_control[], int len) +{ +#ifdef TCP_CONGESTION + int my_len = len; + if (protocol != IPPROTO_TCP) { + strncpy(cong_control,"TCP Only",len); + } + else if (getsockopt(socket, + protocol, TCP_CONGESTION, cong_control, &my_len) == + SOCKET_ERROR) { + snprintf(cong_control,len,"%d errno",errno); + } +#else + strncpy(cong_control,"Unavailable",len); +#endif + cong_control[len-1] = '\0'; +} + +static void +set_transport_cong_control(SOCKET socket, int protocol, char cong_control[], int len) +{ +#ifdef TCP_CONGESTION + if (protocol == IPPROTO_TCP) { + /* if it fails, we'll pick that up via the subsequent "get" */ + setsockopt(socket, protocol, TCP_CONGESTION, cong_control, len); + } +#endif +} + +static void +set_receive_timeout(SOCKET sock, int timeout) +{ +#ifdef SO_RCVTIMEO +#ifndef WIN32 + struct timeval foo; + + foo.tv_sec = timeout; + foo.tv_usec = 0; + + if (setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,&foo,sizeof(foo)) < 0) { + if (debug) { + fprintf(where,"Note - attempt to set a receive timeout on the data socket failed with errno %d (%s)\n", + errno, + strerror(errno)); + fflush(where); + } + } +#endif +#endif +} + +static SOCKET +omni_create_data_socket(struct addrinfo *res) +{ + SOCKET temp_socket; + + temp_socket = create_data_socket(res); + + if (temp_socket != SOCKET_ERROR) { + if (local_cong_control_req[0] != '\0') { + set_transport_cong_control(temp_socket, + res->ai_protocol, + local_cong_control_req, + sizeof(local_cong_control_req)); + } + + if ((res->ai_protocol == IPPROTO_UDP) && + (receive_timeout != -1)) { + set_receive_timeout(temp_socket, receive_timeout); + } + + if (socket_debug) { + int one = 1; + setsockopt(temp_socket, + SOL_SOCKET, + SO_DEBUG, + &one, + sizeof(one)); + } + } + return temp_socket; +} +/* choosing the default send size is a trifle more complicated than it + used to be as we have to account for different protocol limits */ + +#define UDP_LENGTH_MAX (0xFFFF - 28) + +static int +choose_send_size(int lss, int protocol) { + + int send_size; + + if (lss > 0) { + send_size = lss_size; + + /* we will assume that everyone has IPPROTO_UDP and thus avoid an + issue with Windows using an enum */ + if ((protocol == IPPROTO_UDP) && (send_size > UDP_LENGTH_MAX)) + send_size = UDP_LENGTH_MAX; + + } + else { + send_size = 4096; + } + return send_size; +} + +/* brain dead simple way to get netperf to emit a uuid. sadly, by this + point we will have already established the control connection but + those are the breaks. we do _NOT_ include a trailing newline + because we want to be able to use this in a script */ + +void +print_uuid(char remote_host[]) +{ + printf("%s",test_uuid); +} +#if defined(__linux) +/* + * Linux has this odd behavior where if the socket buffers are larger + * than a device's txqueuelen, the kernel will silently drop transmits + * which would not fit into the tx queue, and not pass an ENOBUFS + * error back to the application. As a result, a UDP stream test can + * report absurd transmit bandwidths (like 20Gb/s on a 1GbE NIC). + * This behavior can be avoided if you request extended error + * reporting on the socket. This is done by setting the IP_RECVERR + * socket option at the IP level. + */ +static void +enable_enobufs(int s) +{ + struct protoent *pr; + int on = 1; + + if ((pr = getprotobyname("ip")) == NULL) { + fprintf(where, "%s failed: getprotobyname\n",__FUNCTION__); + fflush(where); + return; + } + if (setsockopt(s, pr->p_proto, IP_RECVERR, (char *)&on, sizeof(on)) < 0) { + fprintf(where, "%s failed: setsockopt (errno %d)\n",__FUNCTION__,errno); + fflush(where); + return; + } +} +#endif + +void +set_omni_request_flags(struct omni_request_struct *omni_request) { + + /* we have no else clauses here because we previously set flags + to zero above raj 20090803 */ + if (rem_nodelay) + omni_request->flags |= OMNI_NO_DELAY; + + if (remote_use_sendfile) + omni_request->flags |= OMNI_USE_SENDFILE; + + if (connection_test) + omni_request->flags |= OMNI_CONNECT_TEST; + + if (remote_checksum_off) + omni_request->flags |= OMNI_CHECKSUM_OFF; + + if (remote_cpu_usage) + omni_request->flags |= OMNI_MEASURE_CPU; + + if (routing_allowed) + omni_request->flags |= OMNI_ROUTING_ALLOWED; + + if (desired_output_groups & OMNI_WANT_REM_IFNAME) + omni_request->flags |= OMNI_WANT_IFNAME; + + if (desired_output_groups & OMNI_WANT_REM_IFSLOT) + omni_request->flags |= OMNI_WANT_IFSLOT; + + if (desired_output_groups & OMNI_WANT_REM_IFIDS) + omni_request->flags |= OMNI_WANT_IFIDS; + + if (desired_output_groups & OMNI_WANT_REM_DRVINFO) + omni_request->flags |= OMNI_WANT_DRVINFO; + + if (desired_output_groups & OMNI_WANT_REM_CONG) + omni_request->flags |= OMNI_WANT_REM_CONG; + + if (use_fastopen) + omni_request->flags |= OMNI_FASTOPEN; + + if (want_use_pktinfo) + omni_request->flags |= OMNI_USE_PKTINFO; + + if (want_defer_accept) + omni_request->flags |= OMNI_WANT_DEFER_ACCEPT; + +} + + +/* this code is intended to be "the two routines to run them all" for + BSDish sockets. it comes about as part of a desire to shrink the + code footprint of netperf and to avoid having so many blessed + routines to alter as time goes by. the downside is there will be + more "ifs" than there were before. raj 2008-01-07 */ + +void +send_omni_inner(char remote_host[], unsigned int legacy_caller, char header_str[]) +{ + + int ret,rret; + int connected = 0; + int timed_out = 0; + int pad_time = 0; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct sockaddr_storage remote_addr; + struct sockaddr_storage my_addr; + int remote_addr_len = sizeof(remote_addr); + netperf_socklen_t my_addr_len = sizeof(my_addr); + + SOCKET data_socket; + int need_socket; + + uint32_t temp_recvs; + char tmpfmt; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + struct omni_request_struct *omni_request; + struct omni_response_struct *omni_response; + struct omni_results_struct *omni_result; + +#ifdef WANT_FIRST_BURST + int requests_outstanding = 0; +#endif + + omni_request = + (struct omni_request_struct *)netperf_request.content.test_specific_data; + omni_response = + (struct omni_response_struct *)netperf_response.content.test_specific_data; + omni_result = + (struct omni_results_struct *)netperf_response.content.test_specific_data; + + + if (keep_histogram) { + if (first_burst_size > 0) + time_hist = HIST_new_n(first_burst_size + 1); + else + time_hist = HIST_new_n(1); + } + + /* since we are now disconnected from the code that established the + control socket, and since we want to be able to use different + protocols and such, we are passed the name of the remote host and + must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + socket_type, + protocol, + 0); + + if ( print_headers ) { + print_top_test_header(header_str,local_res,remote_res); + } + + /* initialize a few counters */ + + need_socket = 1; + + if (connection_test) + pick_next_port_number(local_res,remote_res); + + + /* If the user has requested cpu utilization measurements, we must + calibrate the cpu(s). We will perform this task within the tests + themselves. If the user has specified the cpu rate, then + calibrate_local_cpu will return rather quickly as it will have + nothing to do. If local_cpu_rate is zero, then we will go through + all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + confidence_iteration = 1; + init_stat(); + + send_ring = NULL; + recv_ring = NULL; + + /* you will keep running the test until you get it right! :) */ + while (((confidence < 0) && (confidence_iteration <= iteration_max)) || + (confidence_iteration <= iteration_min)) { + + trans_completed = 0; + bytes_xferd = 0.0; + remote_bytes_xferd = 0.0; + times_up = 0; + bytes_sent = 0; + bytes_received = 0; + local_send_calls = 0; + local_receive_calls = 0; + + /* since we are tracking the number of outstanding requests for + timestamping purposes, and since the previous iteration of + using confidence intervals may not have completed all of them, + we now need to forget about them or we will mistakenly fill our + tracking array. raj 2011-03-14 */ + if (keep_histogram) { + HIST_purge(time_hist); + } + +#ifdef WANT_FIRST_BURST + /* we have to remember to reset the number of transactions + outstanding and the "congestion window for each new + iteration. raj 2006-01-31. */ + requests_outstanding = 0; +#endif + + /* if the command-line included requests to randomize the IP + addresses, then honor it. of course, this may not work all that + well for some tests... raj 20101129 */ + if (local_mask_len) + random_ip_address(local_res, local_mask_len); + if (remote_mask_len) + random_ip_address(remote_res, remote_mask_len); + + data_socket = omni_create_data_socket(local_res); + + if (data_socket == INVALID_SOCKET) { + perror("netperf: send_omni: unable to create data socket"); + exit(1); + } +#if defined(__linux) + /* we really only need this for a UDP_STREAM test. we particularly + do not want it for a CC or CRR test. raj 2012-08-06 */ + if ((protocol == IPPROTO_UDP) && + NETPERF_XMIT_ONLY(direction)) { + enable_enobufs(data_socket); + } +#endif + need_socket = 0; + + /* we need to consider if this is a request/response test, if we + are receiving, if we are sending, etc, when setting-up our recv + and send buffer rings. we should only need to do this once, and + that would be when the relevant _ring variable is NULL. raj + 2008-01-18 */ + if (direction & NETPERF_XMIT) { + if (is_multicast_addr(remote_res)) { + set_multicast_ttl(data_socket); + } + + if (NULL == send_ring) { + if (req_size > 0) { + /* request/response test */ + if (send_width == 0) send_width = 1; + bytes_to_send = req_size; + } + else { + /* stream test */ + if (send_size == 0) { + send_size = choose_send_size(lss_size,protocol); + } + if (send_width == 0) + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + bytes_to_send = send_size; + } + + send_ring = allocate_buffer_ring(send_width, + bytes_to_send, + local_send_align, + local_send_offset); + if (debug) { + fprintf(where, + "%s: %d entry send_ring obtained...\n", + __FUNCTION__, + send_width); + } + } + } + + if (direction & NETPERF_RECV) { + /* do we need to join a multicast group? */ + if (is_multicast_addr(local_res)) { + join_multicast_addr(data_socket, local_res); + } + + /* do we need to allocate a recv_ring? */ + if (NULL == recv_ring) { + if (rsp_size > 0) { + if (recv_width == 0) recv_width = 1; + bytes_to_recv = rsp_size; + } + else { + /* stream test */ + if (recv_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + bytes_to_recv = recv_size; + } + + recv_ring = allocate_buffer_ring(recv_width, + bytes_to_recv, + local_recv_align, + local_recv_offset); + if (debug) { + fprintf(where, + "%s: %d entry recv_ring obtained...\n", + __FUNCTION__, + recv_width); + } + } + } + + if (!no_control) { + + /* Tell the remote end to do a listen or otherwise prepare for + what is to come. The server alters the socket paramters on the + other side at this point, hence the reason for all the values + being passed in the setup message. If the user did not specify + any of the parameters, they will be passed as values which will + indicate to the remote that no changes beyond the system's + default should be used. Alignment is the exception, it will + default to 8, which will probably be no alignment + alterations. */ + + netperf_request.content.request_type = DO_OMNI; + omni_request->flags = 0; + omni_request->send_buf_size = rss_size_req; + omni_request->send_size = remote_send_size_req; + omni_request->send_alignment = remote_send_align; + omni_request->send_offset = remote_send_offset; + omni_request->send_width = send_width; + omni_request->request_size = req_size; + + omni_request->recv_buf_size = rsr_size_req; + omni_request->receive_size = remote_recv_size_req; + omni_request->recv_alignment = remote_recv_align; + omni_request->recv_offset = remote_recv_offset; + omni_request->recv_width = recv_width; + omni_request->response_size = rsp_size; + omni_request->socket_prio = remote_socket_prio; + omni_request->socket_tos = remote_socket_tos; + + set_omni_request_flags(omni_request); + + /* perhaps this should be made conditional on + remote_cong_control_req[0] not being NULL? */ + strncpy(omni_request->cong_control, + remote_cong_control_req, + sizeof(omni_request->cong_control)); + omni_request->cong_control[sizeof(omni_request->cong_control) - 1] = + '\0'; + + if (want_keepalive) + omni_request->flags |= OMNI_WANT_KEEPALIVE; + + omni_request->cpu_rate = remote_cpu_rate; + if (test_time) + omni_request->test_length = test_time; + else + omni_request->test_length = test_trans * -1; + omni_request->so_rcvavoid = rem_rcvavoid; + omni_request->so_sndavoid = rem_sndavoid; + omni_request->send_dirty_count = rem_dirty_count; + omni_request->recv_dirty_count = rem_dirty_count; + omni_request->recv_clean_count = rem_clean_count; + + omni_request->data_port = atoi(remote_data_port); + omni_request->ipfamily = af_to_nf(remote_res->ai_family); + omni_request->socket_type = hst_to_nst(socket_type); + omni_request->protocol = protocol; + + omni_request->interval_burst = remote_interval_burst; + omni_request->interval_usecs = remote_interval_usecs; + + omni_request->direction = 0; + /* yes, the sense here is correct - if we are transmitting, they + receive, if we are receiving, they are transmitting... */ + if (direction & NETPERF_XMIT) + omni_request->direction |= NETPERF_RECV; + if (direction & NETPERF_RECV) + omni_request->direction |= NETPERF_XMIT; + + /* some tests may require knowledge of our local addressing. such + tests will for the time being require that the user specify a + local IP/name so we can extract them from the data_socket. */ + getsockname(data_socket, (struct sockaddr *)&my_addr, &my_addr_len); + + ret = get_sockaddr_family_addr_port(&my_addr, + nf_to_af(omni_request->ipfamily), + omni_request->netperf_ip, + &(omni_request->netperf_port)); + ret = get_sockaddr_family_addr_port((struct sockaddr_storage *)remote_res->ai_addr, + nf_to_af(omni_request->ipfamily), + omni_request->netserver_ip, + &(omni_request->data_port)); + /* if the user didn't explicitly set the remote data address we + don't want to pass along the one we picked implicitly, or a + netserver sitting behind a (BLETCH) NAT will be asked to try + to bind to the "public" IP. */ + if (!explicit_data_address) { + omni_request->netserver_ip[0] = 0; + omni_request->netserver_ip[1] = 0; + omni_request->netserver_ip[2] = 0; + omni_request->netserver_ip[3] = 0; + } + if (debug > 1) { + fprintf(where,"netperf: %s: requesting OMNI test\n",__FUNCTION__); + } + + strncpy(omni_request->fill_file, + remote_fill_file, + sizeof(omni_request->fill_file)); + + send_request_n(OMNI_REQUEST_CONV_CUTOFF); + + + /* the response from the remote should contain all the relevant + socket and other parameters we need to know for this test. + so, we can shove them back into the relevant variables here + and be on our way. */ + + recv_response_n(OMNI_RESPONSE_CONV_CUTOFF); /* brittle, but functional */ + + if (!netperf_response.content.serv_errno) { + rsr_size = omni_response->recv_buf_size; + remote_recv_size = omni_response->receive_size; + rss_size = omni_response->send_buf_size; + remote_send_size = omni_response->send_size; + rem_nodelay = omni_response->flags & OMNI_NO_DELAY; + remote_use_sendfile = omni_response->flags & OMNI_USE_SENDFILE; + remote_cpu_usage = omni_response->flags & OMNI_MEASURE_CPU; + remote_cpu_rate = omni_response->cpu_rate; + remote_send_width = omni_response->send_width; + remote_recv_width = omni_response->recv_width; + remote_socket_prio = omni_response->socket_prio; + remote_socket_tos = omni_response->socket_tos; + + /* make sure that port numbers are in network order because + recv_response will have put everything into host order */ + set_port_number(remote_res, + (unsigned short)omni_response->data_port); + + if (debug) { + fprintf(where,"remote listen done.\n"); + fprintf(where,"remote port is %u\n",get_port_number(remote_res)); + fflush(where); + } + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(-1); + } + + } + +#ifdef WANT_DEMO + /* at some point we will have to be more clever about this, but + for now we won't */ + + demo_rr_setup(100); +#endif + + /* if we are not a connectionless protocol, we need to connect. at + some point even if we are a connectionless protocol, we may + still want to "connect" for convenience raj 2008-01-14 */ + need_to_connect = (protocol != IPPROTO_UDP) || local_connected; + + /* possibly wait just a moment before actually starting - used + mainly when one is doing many many many concurrent netperf + tests */ + WAIT_BEFORE_DATA_TRAFFIC(); + + /* Set-up the test end conditions. For tests over a + "reliable/connection-oriented" transport (eg TCP, SCTP, etc) + this can be either time or byte/transaction count based. for + unreliable transport or connection tests it can only be time + based. having said that, we rely entirely on other code to + enforce this before we even get here. raj 2008-01-08 */ + + /* enable a test_time of 0 to mean just keep running until + something other than alarm() generates a signal. raj + 2012-02-01 */ + if ((test_time) || ((test_trans == 0) && (test_bytes == 0))) { + /* The user wanted to end the test after a period of time. if + we are a recv-only test, we need to protect ourself against + the remote going poof, but we want to make sure we don't + give-up before they finish, so we will add a PAD_TIME to the + timer. if we are RR or XMIT, there should be no need for + padding */ + times_up = 0; + units_remaining = 0; + if ((!no_control) && + (NETPERF_RECV_ONLY(direction)) && + ((test_trans == 0) && (test_bytes == 0))) + pad_time = 0; + start_timer(test_time + pad_time); + } + else { + /* The tester wanted to send a number of bytes or exchange a + number of transactions. */ + if (NETPERF_IS_RR(direction)) + units_remaining = test_trans; + else + units_remaining = test_bytes; + times_up = 1; + } + + /* grab the current time, and if necessary any starting information + for the gathering of CPU utilization at this end. */ + cpu_start(local_cpu_usage); + +#if defined(WANT_INTERVALS) + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + +#ifdef WANT_DEMO + if (demo_mode) { + demo_first_timestamp(); + } +#endif + + /* the "OR" here allows us to control test length by either + byte/transaction count or by timer. when the test is + byte/transaction count based the time test will always evaluate + false. when the test is controlled by time, the byte/transaction + count will always evaluate to false. when the test is finished + the whole expression will go false and we will stop sending + data. at least that is the plan :) raj 2008-01-08 */ + + while ((!times_up) || (units_remaining > 0)) { + + /* we need to be careful about when we snap a timestamp + depending on the test parameters. this one *should* cover + everything but the burst request/response test - famous last + words of course. raj 20110111 */ + + if (keep_histogram) { + HIST_timestamp_start(time_hist); + } + + + again: + + if (need_socket) { + if (connection_test) + pick_next_port_number(local_res,remote_res); + + data_socket = omni_create_data_socket(local_res); + + if (data_socket == INVALID_SOCKET) { + perror("netperf: send_omni: unable to create data socket"); + exit(1); + } + need_socket = 0; +#if defined(__linux) + if ((protocol == IPPROTO_UDP) && + (direction & NETPERF_XMIT)) { + enable_enobufs(data_socket); + } +#endif + } + + /* only connect if and when we need to */ + if (need_to_connect && !use_fastopen) { + /* assign to data_socket since connect_data_socket returns + SOCKET and not int thanks to Windows. */ + ret = connect_data_socket(data_socket,remote_res,dont_give_up); + if (ret == 0) { + connected = 1; + need_to_connect = 0; + } + else if (ret == -1) { + times_up = 1; + timed_out = 1; + break; + } + else if ((ret == -2) && connection_test) { + /* transient error on a connection test means go around and + try again with another local port number */ + if (debug) { + fprintf(where,"transient! transient! torpedo in the water!\n"); + fflush(where); + } + close(data_socket); + connected = 0; /* probably redundant but what the heck... */ + need_socket = 1; + need_to_connect = 1; + /* this will stuff the next local port number within bounds + into our local res, and then when the goto has us + allocating a new socket it will do the right thing with the + bind() call */ + pick_next_port_number(local_res,remote_res); + /* yes Virginia, a goto. perhaps one day we will rewrite + the code to avoid it but for now, a goto... raj */ + goto again; + } + else { + /* either this was a hard failure (-3) or a soft failure on + something other than a connection test */ + perror("netperf: send_omni: connect_data_socket failed"); + exit(1); + } + } + +#ifdef WANT_FIRST_BURST + /* Long ago and far away, on just about any *nix, one could + avoid having multiple requests bundled into the same TCP + segment simply by setting TCP_NODELAY and perhaps not trying + to have more outstanding at one time than our guesstimate as + to the TCP congestion window. In that way one could use a + burst mode TCP_RR test as part of trying to measure maximum + packets per second (PPS) on a system or through a NIC (well, + assuming there weren't many retransmissions anyway) These + days with Linux the dominant *nix and with it having made it + virtually impossible to do any longer, it is no longer worth + it to try the application-layer backflips. So, I am removing + them. At some point we'll simply have to enhance this code + to deal with multiple connections at one time, each with just + the one transaction in flight for our PPS testing. Multiple + netperfs, each with one connection and one transaction in + flight rapidly becomes a context-switching benchmark rather + than "networking". raj 2015-04-20 */ + + while ((first_burst_size > 0) && + (requests_outstanding < first_burst_size) && + (NETPERF_IS_RR(direction)) && + (!connection_test)) { + if (debug > 1) { + fprintf(where, + "injecting, req_outstanding %d burst %d\n", + requests_outstanding, + first_burst_size); + } + + if ((ret = send_data(data_socket, + send_ring, + bytes_to_send, + (connected) ? NULL : remote_res->ai_addr, + remote_res->ai_addrlen, + protocol)) != bytes_to_send) { + /* in theory, we should never hit the end of the test in the + first burst. however, in practice I have indeed seen + some ENOBUFS happen in some aggregate tests. so we need + to be a bit more sophisticated in how we handle it. raj + 20130516 */ + if (ret != -2) { + perror("send_omni: initial burst data send error"); + exit(-1); + } + failed_sends++; + } + else { + local_send_calls += 1; + requests_outstanding += 1; + } + + /* yes, it seems a trifle odd having this *after* the send() + just above, but really this is for the next send() or + recv() call below or in the iteration of this loop, and the + first HIST_timestamp_start() call at the top of the + outermost loop will be for the first send() call here in + the burst code. clear ain't it?-) raj 20110111 */ + + if (keep_histogram) { + HIST_timestamp_start(time_hist); + } + } + +#endif /* WANT_FIRST_BURST */ + + /* if we should try to send something, then by all means, let us + try to send something. */ + if (direction & NETPERF_XMIT) { + + ret = send_data(data_socket, + send_ring, + bytes_to_send, + (connected) ? NULL : remote_res->ai_addr, + /* if the destination above is NULL, this is ignored */ + remote_res->ai_addrlen, + protocol); + /* the order of these if's will seem a triffle strange, but they + are my best guess as to order of probabilty and/or importance + to the overhead raj 2008-01-09*/ + if (ret == bytes_to_send) { + /* if this is a send-only test controlled by byte count we + decrement units_remaining by the bytes sent */ + if (!(direction & NETPERF_RECV) && (units_remaining > 0)) { + units_remaining -= ret; + } + bytes_sent += ret; + send_ring = send_ring->next; + local_send_calls++; + } + else if (ret == -2) { + /* what to do here -2 means a non-fatal error - probably + ENOBUFS and so our send didn't happen. in the old code for + UDP_STREAM we would just continue in the while loop. it + isn't clear that is what to do here, so we will simply + increment the failed_sends stat and fall-through. If this + is a UDP_STREAM style of test, the net effect should be the + same. if this is a UDP_RR with a really-big burst count, I + don't think we were checking for ENOBUFS there anyway and + so would have failed. Here we can just let things + slide. */ + failed_sends++; + } + else if (ret == 0) { + /* was this a zero-byte send? if it was, then ostensibly we + would hit the ret == bytes_to_send case which means we'd + never get here as we are using blocking semantics */ + fprintf(where,"HOW DID I GET HERE?\n"); + fflush(where); + } + else if (ret == -1) { + times_up = 1; + timed_out = 1; + break; + } + else { + perror("netperf: send_omni: send_data failed"); + exit(1); + } + + } + +#ifdef WANT_FIRST_BURST + /* it isn't clear we need to check the directions here. the + increment should be cheaper than the conditional, and it + shouldn't hurt the other directions because they'll never + look at them. famous last words of raj 2008-01-25 */ + requests_outstanding += 1; +#endif + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out + from under us, which to date is the easiest/cleanest/least + Windows-specific way I can find to force the winsock calls to + return WSAEINTR with the test is over. anything that will run + on 95 and NT and is closer to what netperf expects from Unix + signals and such would be appreciated raj 1/96 */ + win_kludge_socket = data_socket; +#endif /* WIN32 */ + + if (direction & NETPERF_RECV) { + rret = recv_data(data_socket, + recv_ring, + bytes_to_recv, + (connected) ? NULL : (struct sockaddr *)&remote_addr, + /* if remote_addr NULL this is ignored */ + &remote_addr_len, + /* if XMIT also set this is RR so waitall */ + (direction & NETPERF_XMIT) ? NETPERF_WAITALL: 0, + &temp_recvs); + if (rret > 0) { + /* if this is a recv-only test controlled by byte count we + decrement the units_remaining by the bytes received */ + if (!(direction & NETPERF_XMIT) && (units_remaining > 0)) { + units_remaining -= rret; + } + bytes_received += rret; + local_receive_calls += temp_recvs; + } + else if (rret == 0) { + /* is this the end of a test, just a zero-byte recv, or + something else? that is an exceedingly good question and + one for which I don't presently have a good answer, but + that won't stop me from guessing :) raj 2008-01-09 */ + if (!((connection_test) || (null_message_ok))) { + /* if it is neither a connection_test nor null_message_ok it + must be the end of the test */ + times_up = 1; /* ostensibly the signal handler did this */ + break; + } + local_receive_calls += temp_recvs; + } + else if (rret == -1) { + /* test timed-out */ + times_up = 1; + timed_out = 1; + break; + } + else if (rret == -2) { + /* we timed-out on a data receive. this is only allowed for + a UDP_RR test. we want to set things up so we start + ramping up again like we were at the beginning. if we + actually timeout it means that all has been lost. or at + least we assume so */ + if (debug) { + fprintf(where,"Timeout receiving resonse from remote\n"); + fflush(where); + } +#ifdef WANT_FIRST_BURST + if (first_burst_size) { + requests_outstanding = 0; + } +#endif + if (keep_histogram) { + HIST_purge(time_hist); + } +#ifdef WANT_DEMO + /* "start over" on a demo interval. we will forget about + everything that happened in the demo interval up to the + timeout and begin fresh. */ + demo_reset(); +#endif /* WANT_DEMO */ + + continue; + } + else { + /* anything else is bad */ + perror("netperf: send_omni: recv_data failed"); + exit(1); + } + recv_ring = recv_ring->next; + +#ifdef WANT_FIRST_BURST + /* so, since we've gotten a response back, update the + bookkeeping accordingly. there is one less request + outstanding and we can put one more out there than + before. */ + requests_outstanding -= 1; +#endif + + } + + /* if this is a connection test, we want to do some stuff about + connection close here in the test loop. raj 2008-01-08 */ + if (connection_test) { + +#ifdef __linux + /* so, "Linux" with autotuning likes to alter the socket buffer + sizes over the life of the connection, but only does so when + one takes the defaults at time of socket creation. if we + took those defaults, we should inquire as to what the values + ultimately became. raj 2008-01-15 */ + /* however annoying having to do this might be, it really + shouldn't be done over and over again. instead we will + assume it does not change, which is fine since we would + have only reported one of them anyway. raj 20100917 */ + if ((lsr_size_req < 0) && (-1 == lsr_size_end)) + get_sock_buffer(data_socket, RECV_BUFFER, &lsr_size_end); + else + lsr_size_end = lsr_size; + if ((lss_size_req < 0) && (-1 == lss_size_end)) + get_sock_buffer(data_socket, SEND_BUFFER, &lss_size_end); + else + lss_size_end = lss_size; +#else + lsr_size_end = lsr_size; + lss_size_end = lss_size; +#endif + + /* we will only make this call the one time - after the first + call, the value will be real or -1. if this is a connection + test we want to do this here because later we won't be + connected and the data may no longer be available. */ + if (transport_mss == -2) + get_transport_info(data_socket, + &transport_mss, + local_res->ai_protocol); + + + ret = disconnect_data_socket(data_socket, + (no_control) ? 1 : 0, + 1, + NULL, + 0); + if (ret == 0) { + /* we will need a new connection to be established next time + around the loop. However, the next time around the loop + will already be picking the next port number */ + need_to_connect = 1; + connected = 0; + need_socket = 1; + } + else if (ret == -1) { + times_up = 1; + timed_out = 1; + break; + } + else { + perror("netperf: send_omni: disconnect_data_socket failed"); + exit(1); + } + } + + + if (keep_histogram) { + HIST_timestamp_stop_add(time_hist); + } + +#ifdef WANT_DEMO + if (NETPERF_IS_RR(direction)) { + if (libfmt == 'x') { + demo_interval_tick(1); + } + else { + demo_interval_tick(req_size + rsp_size); + } + } + else if (NETPERF_XMIT_ONLY(direction)) { + demo_interval_tick(bytes_to_send); + } + else { + demo_interval_tick(rret); + } +#endif + +#if defined(WANT_INTERVALS) + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + + /* was this a "transaction" test? */ + if (NETPERF_IS_RR(direction)) { + trans_completed++; + if (units_remaining) { + units_remaining--; + } + } + + + } + + /* we are now, ostensibly, at the end of this iteration */ + +#if defined(WANT_DEMO) + /* if we were in demo mode this will ensure one final interim + result, which, naturally might be a bit early :) */ + demo_interval_final(); +#endif + + if (transport_mss == -2) + get_transport_info(data_socket, + &transport_mss, + local_res->ai_protocol); + local_transport_retrans = get_transport_retrans(data_socket, + local_res->ai_protocol); + + + /* so, if we have/had a data connection, we will want to close it + now, and this will be independent of whether there is a control + connection. */ + + if (connected) { + +#ifdef __linux + /* so, "Linux" with autotuning likes to alter the socket buffer + sizes over the life of the connection, but only does so when + one takes the defaults at time of socket creation. if we took + those defaults, we should inquire as to what the values + ultimately became. raj 2008-01-15 */ + if (lsr_size_req < 0) + get_sock_buffer(data_socket, RECV_BUFFER, &lsr_size_end); + else + lsr_size_end = lsr_size; + if (lss_size_req < 0) + get_sock_buffer(data_socket, SEND_BUFFER, &lss_size_end); + else + lss_size_end = lss_size; +#else + lsr_size_end = lsr_size; + lss_size_end = lss_size; +#endif + if ((desired_output_groups & OMNI_WANT_LOC_CONG) && + (local_cong_control[0] == '\0')) { + get_transport_cong_control(data_socket, + local_res->ai_protocol, + local_cong_control, + sizeof(local_cong_control)); + } + + /* CHECK PARMS HERE; */ + ret = disconnect_data_socket(data_socket, + 1, + 1, + NULL, + 0); + connected = 0; + need_socket = 1; + + } + else { + /* this is the UDP case at present */ + ret = disconnect_data_socket(data_socket, + 1, + 1, + remote_res->ai_addr, + remote_res->ai_addrlen); + need_socket = 1; + lsr_size_end = lsr_size; + lss_size_end = lss_size; + } + + /* this call will always give us the elapsed time for the test, and + will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); + + /* if we timed-out, and had padded the timer, we need to subtract + the pad_time from the elapsed time on the assumption that we + were essentially idle for pad_time and just waiting for a timer + to expire on something like a UDP test. if we have not padded + the timer, pad_time will be zero. if we have not timed out + then we want to make sure we stop the timer. */ + if (timed_out) { + if (debug) { + fprintf(where,"Adjusting elapsed_time by %d seconds\n",pad_time); + fflush(where); + } + elapsed_time -= (float)pad_time; + } + else { + stop_timer(); + } + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting things. If + it wasn't supposed to care, it will return obvious values. */ + + recv_response_n(OMNI_RESULTS_CONV_CUTOFF); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + remote_cpu_method = format_cpu_method(omni_result->cpu_method); + lib_num_rem_cpus = omni_result->num_cpus; + lib_remote_cpu_stats.cpu_util = omni_result->cpu_util; + lib_remote_cpu_stats.cpu_user = omni_result->cpu_percent_user; + lib_remote_cpu_stats.cpu_system = omni_result->cpu_percent_system; + lib_remote_cpu_stats.cpu_iowait = omni_result->cpu_percent_iowait; + lib_remote_cpu_stats.cpu_irq = omni_result->cpu_percent_irq; + lib_remote_cpu_stats.cpu_swintr = omni_result->cpu_percent_swintr; + lib_remote_cpu_stats.peak_cpu_util = omni_result->peak_cpu_util; + lib_remote_cpu_stats.peak_cpu_id = omni_result->peak_cpu_id; + /* why? because some stacks want to be clever and autotune their + socket buffer sizes, which means that if we accept the defaults, + the size we get from getsockopt() at the beginning of a + connection may not be what we would get at the end of the + connection... */ + rsr_size_end = omni_result->recv_buf_size; + rss_size_end = omni_result->send_buf_size; + remote_bytes_sent = (uint64_t)omni_result->bytes_sent_hi << 32; + remote_bytes_sent += omni_result->bytes_sent_lo; + remote_send_calls = omni_result->send_calls; + remote_bytes_received = (uint64_t)omni_result->bytes_received_hi << 32; + remote_bytes_received += omni_result->bytes_received_lo; + remote_receive_calls = omni_result->recv_calls; + remote_bytes_xferd = (double) remote_bytes_received + + remote_bytes_sent; + if (omni_result->recv_calls > 0) + remote_bytes_per_recv = (double) remote_bytes_received / + (double) omni_result->recv_calls; + else + remote_bytes_per_recv = 0.0; + if (omni_result->send_calls > 0) + remote_bytes_per_send = (double) remote_bytes_sent / + (double) omni_result->send_calls; + else + remote_bytes_per_send = 0.0; + + remote_transport_retrans = omni_result->transport_retrans; + /* what was the congestion control? */ + if (desired_output_groups & OMNI_WANT_REM_CONG) { + strncpy(remote_cong_control, + omni_result->cong_control, + sizeof(remote_cong_control)); + remote_cong_control[sizeof(remote_cong_control) - 1] = '\0'; + } + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(-1); + } + } + else { + /* when we are sending, in a no_control test, we have to + ass-u-me that everything we sent was received, otherwise, we + will report a transfer rate of zero. */ + remote_bytes_xferd = (double) bytes_sent; + } + + /* so, what was the end result? */ + local_cpu_method = format_cpu_method(cpu_method); + + if (local_send_calls > 0) + bytes_per_send = (double) bytes_sent / (double) local_send_calls; + else bytes_per_send = 0.0; + + if (local_receive_calls > 0) + bytes_per_recv = (double) bytes_received / (double) local_receive_calls; + else + bytes_per_recv = 0.0; + + bytes_xferd = (double) bytes_sent + bytes_received; + + /* if the output format is 'x' we know the test was + request/response. if the libfmt is something else, it could be + xmit, recv or bidirectional. if we were the receiver then we + can use our byte totals even if it is + UDP/unreliable. otherwise, we use the remote totals - they + should be the same if the protocol is reliable, and if it is + unreliable then we want what was actually received */ + if ('x' == libfmt) + /* it was a request/response test */ + thruput = calc_thruput((double)trans_completed); + else if (NETPERF_RECV_ONLY(direction)) + thruput = calc_thruput(bytes_xferd); + else + thruput = calc_thruput(remote_bytes_xferd); + + if (NETPERF_IS_RR(direction)) { + float rtt_elapsed_time = elapsed_time; + +#ifdef WANT_INTERVALS + /* if the test was paced, we need to subtract the time we were + sitting paced from the time we use to calculate the average + rtt_latency. Of course, won't really know how long we were + sitting unless we bracket the sit with timing calls, which + will be additional overhead affecting CPU utilization. but, + there is no such thing as a free lunch is there :) raj + 20110121 */ + if (interval_burst) { + rtt_elapsed_time -= (float)(interval_wait_microseconds / 1000000.0); + } +#endif /* WANT_INTERVALS */ + + if (!connection_test) { + /* calculate the round trip latency, using the transaction rate + whether or not the user was asking for thruput to be in 'x' + units please... however... a connection_test only ever has + one transaction in flight at one time */ + rtt_latency = + (((double)1.0/(trans_completed/rtt_elapsed_time)) * + (double)1000000.0) * + (double) (1 + ((first_burst_size > 0) ? first_burst_size : 0)); + } + else { + rtt_latency = ((double)1.0/(trans_completed/rtt_elapsed_time)) * + (double)1000000.0; + } + tmpfmt = libfmt; + libfmt = 'x'; + transaction_rate = calc_thruput((double)trans_completed); + libfmt = tmpfmt; + } + + /* ok, time to possibly calculate cpu util and/or service demand */ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(elapsed_time); + + /* we need to decide what to feed the service demand beast, + which will, ultimately, depend on what sort of test it is and + whether or not the user asked for something specific - as in + per KB even on a TCP_RR test if it is being (ab)used as a + bidirectional bulk-transfer test. raj 2008-01-14 */ + local_service_demand = + calc_service_demand_fmt(('x' == libfmt) ? (double)trans_completed: bytes_xferd, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = omni_result->cpu_util; + + remote_service_demand = + calc_service_demand_fmt(('x' == libfmt) ? (double) trans_completed: bytes_xferd, + 0.0, + remote_cpu_utilization, + omni_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* time to calculate our confidence */ + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + /* this this is the end of the confidence while loop? */ + confidence_iteration++; + } + + /* we end with confidence_iteration one larger than the number of + iterations. if we weren't doing confidence intervals this will + still be reported as one */ + confidence_iteration--; + +#if defined(WANT_INTERVALS) +#ifdef WIN32 + stop_itimer(); +#endif +#endif /* WANT_INTERVALS */ + +/* at some point we may want to actually display some results :) */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* a kludge for omni printing because I don't know how to tell that + something is a float vs a double in my_snprintf() given what it + is passed and I'm not ready to force all the netlib.c stuff to + use doubles rather than floats. help there would be + appreciated. raj 2008-01-28 */ + elapsed_time_double = (double) elapsed_time; + local_service_demand_double = (double)local_service_demand; + remote_service_demand_double = (double)remote_service_demand; + + if ('x' == libfmt) sd_str = "usec/Tran"; + else sd_str = "usec/KB"; + + if (iteration_max > 1) { + result_confid_pct = get_result_confid(); + loc_cpu_confid_pct = get_loc_cpu_confid(); + rem_cpu_confid_pct = get_rem_cpu_confid(); + interval_pct = interval * 100.0; + } + + /* at some point we need to average these during a confidence + interval run, and when we do do that, we need to make sure we + restore the value of libfmt correctly */ + tmpfmt = libfmt; + if ('x' == libfmt) { + libfmt = 'm'; + } + local_send_thruput = calc_thruput((double)bytes_sent); + local_recv_thruput = calc_thruput((double)bytes_received); + remote_send_thruput = calc_thruput((double)remote_bytes_sent); + remote_recv_thruput = calc_thruput((double)remote_bytes_received); + + libfmt = tmpfmt; + + /* were we tracking possibly expensive statistics? */ + if (keep_statistics) { + HIST_get_stats(time_hist, + &min_latency, + &max_latency, + &mean_latency, + &stddev_latency); + p50_latency = HIST_get_percentile(time_hist, 0.50); + p90_latency = HIST_get_percentile(time_hist, 0.90); + p99_latency = HIST_get_percentile(time_hist, 0.99); + + } + + /* if we are running a legacy test we do not do the nifty new omni + output stuff */ + if (!legacy) { + print_omni(); + } + +#if defined(DEBUG_OMNI_OUTPUT) + { + /* just something quick to sanity check the output selectors. this + should be gone for "production" :) */ + int i; + print_omni_init(); + output_list[0][1] = OUTPUT_END; + for (i = OUTPUT_NONE; i < NETPERF_OUTPUT_MAX; i++) { + output_list[0][0] = i; + print_omni_csv(); + } + } +#endif + + /* likely as not we are going to do something slightly different here */ + if ((verbosity > 1) && (!legacy)) { + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of "); + if (NETPERF_RECV_ONLY(direction)) + fprintf(where,"recv"); + if (NETPERF_XMIT_ONLY(direction)) + fprintf(where,"send"); + if (NETPERF_IS_RR(direction)) { + if (connection_test) { + if (NETPERF_CC(direction)) { + fprintf(where,"connect/close"); + } + else { + fprintf(where,"connect/request/response/close"); + } + } + else { + fprintf(where,"request/response"); + } + } + fprintf(where," times\n"); + HIST_report(time_hist); + fflush(where); +#endif /* WANT_HISTOGRAM */ + + } + +} + + +void +send_omni(char remote_host[]) +{ + char name_buf[32]; + snprintf(name_buf,sizeof(name_buf),"OMNI %s TEST",direction_str); + name_buf[31] = '\0'; + send_omni_inner(remote_host, 0, name_buf); +} + +#if defined(WIN32) +#if !defined(InetNtop) +/* +*+ Why isn't this in the winsock headers yet? */ +const char * +inet_ntop(int af, const void *src, char *dst, size_t size); +#endif +#endif + +static void +set_hostname_and_port_2(void *addr, char *hostname, char *portstr, int family, int port) +{ + + inet_ntop(family, addr, hostname, BUFSIZ); + + sprintf(portstr, "%u", port); + +} + + + +/* the name is something of a misnomer since this test could send, or + receive, or both, but it matches the historical netperf routine + naming convention for what runs in the netserver context. */ +void +recv_omni() +{ + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_storage myaddr_in, peeraddr_in; + int peeraddr_set = 0; + SOCKET s_listen, data_socket; + netperf_socklen_t addrlen; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int timed_out = 0; + int pad_time = 0; + int need_to_connect = 0; + int need_to_accept; + int connected; + int ret; + uint32_t temp_recvs; + + struct omni_request_struct *omni_request; + struct omni_response_struct *omni_response; + struct omni_results_struct *omni_results; + + omni_request = + (struct omni_request_struct *)netperf_request.content.test_specific_data; + omni_response = + (struct omni_response_struct *)netperf_response.content.test_specific_data; + omni_results = + (struct omni_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: %s: entered...\n",__FUNCTION__); + fflush(where); + } + + /* netserver has no good way of knowing where the conversion cutoff + point is, so we have to fix it after the fact */ + fixup_request_n(OMNI_REQUEST_CONV_CUTOFF); + + /* thus fixed-up, we can extract the requested congestion control + algorithm */ + strncpy(local_cong_control_req, + omni_request->cong_control, + sizeof(local_cong_control_req)); + + /* based on what we have been told by the remote netperf, we want to + setup our endpoint for the "data connection" and let the remote + netperf know the situation. */ + + if (debug) { + fprintf(where,"%s: setting the response type...\n",__FUNCTION__); + fflush(where); + } + + netperf_response.content.response_type = OMNI_RESPONSE; + + if (debug) { + fprintf(where,"%s: the response type is set...\n",__FUNCTION__); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"%s: grabbing a socket...\n",__FUNCTION__); + fflush(where); + } + + /* create_data_socket expects to find some things in the global + variables, so set the globals based on the values in the request. + once the socket has been created, we will set the response values + based on the updated value of those globals. raj 7/94 */ + lss_size_req = omni_request->send_buf_size; + lsr_size_req = omni_request->recv_buf_size; + loc_nodelay = (omni_request->flags) & OMNI_NO_DELAY; + loc_rcvavoid = omni_request->so_rcvavoid; + loc_sndavoid = omni_request->so_sndavoid; + routing_allowed = (omni_request->flags) & OMNI_ROUTING_ALLOWED; + want_keepalive = (omni_request->flags) & OMNI_WANT_KEEPALIVE; + local_socket_prio = omni_request->socket_prio; + local_socket_tos = omni_request->socket_tos; + want_defer_accept = omni_request->flags & OMNI_WANT_DEFER_ACCEPT; + +#ifdef WANT_INTERVALS + interval_usecs = omni_request->interval_usecs; + interval_wate = interval_usecs / 1000; + interval_burst = omni_request->interval_burst; +#else + interval_usecs = 0; + interval_wate = 1; + interval_burst = 0; +#endif + + connection_test = omni_request->flags & OMNI_CONNECT_TEST; +#ifdef TCP_FASTOPEN + use_fastopen = omni_request->flags & OMNI_FASTOPEN; +#endif + direction = omni_request->direction; + use_pktinfo = (omni_request->flags) & OMNI_USE_PKTINFO; + + /* let's be quite certain the fill file string is null terminated */ + omni_request->fill_file[sizeof(omni_request->fill_file) - 1] = '\0'; + strncpy(local_fill_file, + omni_request->fill_file, + sizeof(local_fill_file)); + + /* kludgy, because I have no way at present to say how many bytes + needed to be swapped around for the request from which this is + pulled, and it is probably all wrong for IPv6 :( */ + switch (nf_to_af(omni_request->ipfamily)) { + case AF_INET6: + /* yes indeed it is, do nothing, bz */ + break; + case AF_INET: + default: + for (ret=0; ret < 4; ret++) { + omni_request->netserver_ip[ret] = htonl(omni_request->netserver_ip[ret]); + omni_request->netperf_ip[ret] = htonl(omni_request->netperf_ip[ret]); + } + break; + } + + set_hostname_and_port_2(omni_request->netserver_ip, + local_name, + port_buffer, + nf_to_af(omni_request->ipfamily), + omni_request->data_port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(omni_request->ipfamily), + nst_to_hst(omni_request->socket_type), + omni_request->protocol, + 0); + + s_listen = omni_create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + if (debug) { + fprintf(where,"could not create data socket\n"); + fflush(where); + } + exit(-1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_omni: requested recv alignment of %d offset %d\n" + "recv_omni: requested send alignment of %d offset %d\n", + omni_request->recv_alignment, + omni_request->recv_offset, + omni_request->send_alignment, + omni_request->send_offset); + fflush(where); + } + + omni_response->send_size = omni_request->send_size; + omni_response->send_width = omni_request->send_width; + omni_response->socket_prio = local_socket_prio; + omni_response->socket_tos = local_socket_tos; + + if (omni_request->direction & NETPERF_XMIT) { +#ifdef fo + /* do we need to set multicast ttl? */ + if (is_multicast_addr(remote_res)) { + /* yes, s_listen - for a UDP test we will be copying it to + data_socket but that hasn't happened yet. raj 20100315 */ + set_multicast_ttl(s_listen); + } +#endif + + if (omni_request->response_size > 0) { + /* request/response_test */ + bytes_to_send = omni_request->response_size; + if (omni_request->send_width == 0) send_width = 1; + else send_width = omni_request->send_width; + } + else { + if (omni_request->send_size == -1) { + bytes_to_send = choose_send_size(lss_size,omni_request->protocol); + } + else bytes_to_send = omni_request->send_size; + /* set the send_width */ + if (omni_request->send_width == 0) { + send_width = (lss_size/bytes_to_send) + 1; + if (send_width == 1) send_width++; + } + else + send_width = omni_request->send_width; + } + send_ring = allocate_buffer_ring(send_width, + bytes_to_send, + omni_request->send_alignment, + omni_request->send_offset); + + omni_response->send_width = send_width; + omni_response->send_size = bytes_to_send; + } + + omni_response->receive_size = omni_request->receive_size; + omni_response->recv_width = omni_request->recv_width; + if (omni_request->direction & NETPERF_RECV) { + + /* do we need to join a multicast group? */ + if (is_multicast_addr(local_res)) { + /* yes, s_listen - for a UDP test we will be copying it to + data_socket but that hasn't happened yet. raj 20100315 */ + join_multicast_addr(s_listen, local_res); + } + + if (omni_request->request_size > 0) { + /* request/response test */ + bytes_to_recv = omni_request->request_size; + if (omni_request->recv_width == 0) recv_width = 1; + else recv_width = omni_request->recv_width; + } + else { + if (omni_request->receive_size == -1) { + if (lsr_size > 0) bytes_to_recv = lsr_size; + else bytes_to_recv = 4096; + } + else { + bytes_to_recv = omni_request->receive_size; + } + /* set the recv_width */ + if (omni_request->recv_width == 0) { + recv_width = (lsr_size/bytes_to_recv) + 1; + if (recv_width == 1) recv_width++; + } + else + recv_width = omni_request->recv_width; + } + recv_ring = allocate_buffer_ring(recv_width, + bytes_to_recv, + omni_request->recv_alignment, + omni_request->recv_offset); + + omni_response->receive_size = bytes_to_recv; + omni_response->recv_width = recv_width; + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + need_to_accept = (omni_request->protocol != IPPROTO_UDP); + + /* we need to hang a listen for everything that needs at least one + accept. the age-old constant of 5 is probably OK for our purposes + but does not necessarily represent best practice */ + if (need_to_accept) { + int backlog = 5; +#ifdef TCP_FASTOPEN + /* one of these days I will have to go find-out what the backlog + is supposed to be here. until then, I'll just set it to five + like the listen() call does - it is classic, and was what was + used in the online example I found */ + if (use_fastopen && + (setsockopt(s_listen,IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog)) == + SOCKET_ERROR)) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"netperfserver: %s could not fastopen\n",__FUNCTION__); + fflush(where); + } + exit(1); + } +#endif /* TCP_FASTOPEN */ +#ifdef TCP_DEFER_ACCEPT + if (want_defer_accept && + (setsockopt(s_listen, IPPROTO_TCP, TCP_DEFER_ACCEPT, &backlog, sizeof(backlog)) == SOCKET_ERROR)) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where, + "netperfserver: %s could not defer accept\n",__FUNCTION__); + fflush(where); + } + exit(1); + } +#endif /* TCP_DEFER_ACCEPT */ + if (listen(s_listen, backlog) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"netperfserver: %s could not listen\n",__FUNCTION__); + fflush(where); + } + exit(1); + } + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not getsockname\n"); + fflush(where); + } + exit(-1); + } + + /* Now myaddr_in contains the port and the internet address this is + returned to the sender also implicitly telling the sender that + the socket buffer sizing has been done. likely as not, the IP + address will be the wildcard - so we only really need to extract + the port number. since send_response is going to call htonl on + all the fields, we want to initially put the port number in there + in host order. */ + + omni_response->data_port = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + if (debug) { + fprintf(where,"telling the remote to call me at %d\n", + omni_response->data_port); + fflush(where); + } + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + omni_response->cpu_rate = (float)0.0; /* assume no cpu */ + omni_response->flags &= ~OMNI_MEASURE_CPU; + if (omni_request->flags & OMNI_MEASURE_CPU) { + omni_response->flags |= OMNI_MEASURE_CPU; + omni_response->cpu_rate = + calibrate_local_cpu(omni_request->cpu_rate); + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + omni_response->send_buf_size = lss_size; + omni_response->recv_buf_size = lsr_size; + if (loc_nodelay) + omni_response->flags |= OMNI_NO_DELAY; + else + omni_response->flags &= ~OMNI_NO_DELAY; + + omni_response->so_rcvavoid = loc_rcvavoid; + omni_response->so_sndavoid = loc_sndavoid; + omni_response->interval_usecs = interval_usecs; + omni_response->interval_burst = interval_burst; + + send_response_n(OMNI_RESPONSE_CONV_CUTOFF); /* brittle, but functional */ + + local_send_calls = 0; + local_receive_calls = 0; + + addrlen = sizeof(peeraddr_in); + memset(&peeraddr_in,0,sizeof(peeraddr_in)); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(omni_request->flags & OMNI_MEASURE_CPU); + + /* if the test is timed, set a timer of suitable length. if the + test is by byte/transaction count, we don't need a timer - or + rather we rely on the netperf to only ask us to do transaction + counts over "reliable" protocols. perhaps at some point we + should add a check herebouts to verify that... */ + + if (omni_request->test_length >= 0) { + times_up = 0; + units_remaining = 0; + test_time=omni_request->test_length; + /* if we are the sender and only sending, then we don't need/want + the padding, otherwise, we need the padding */ + if (!(NETPERF_XMIT_ONLY(omni_request->direction)) && + (omni_request->test_length > 0)) + pad_time = PAD_TIME; + start_timer(omni_request->test_length + pad_time); + } + else { + times_up = 1; + units_remaining = omni_request->test_length * -1; + } + +#if defined(WANT_INTERVALS) + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + + trans_completed = 0; + bytes_sent = 0; + bytes_received = 0; + connected = 0; + + while ((!times_up) || (units_remaining > 0)) { + + if (need_to_accept) { + /* accept a connection from the remote */ +#ifdef WIN32 + /* The test timer will probably fire during this accept, + so to make the start_timer above work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket = s_listen; +#endif + if ((data_socket=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + if (errno == EINTR) { + /* the timer popped */ + times_up = 1; /* ostensibly the signal hander dealt with this?*/ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + fprintf(where,"%s: accept: errno = %d\n",__FUNCTION__,errno); + fflush(where); + close(s_listen); + + exit(-1); + } + + if (debug) { + fprintf(where,"%s: accepted data connection.\n",__FUNCTION__); + fflush(where); + } + need_to_accept = 0; + connected = 1; + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass + attributes across an accept() call. Including this goes + against my better judgement :( raj 11/95 */ + + kludge_socket_options(data_socket); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + } + else { + /* I wonder if duping would be better here? we also need to set + peeraddr_in so we can send to netperf if this isn't a + request/response test or if we are going to connect() the + socket, but we only need to do it once. */ + if ((omni_request->protocol == IPPROTO_UDP) && + (!peeraddr_set)) { + peeraddr_set = 1; + data_socket = s_listen; + set_sockaddr_family_addr_port(&peeraddr_in, + nf_to_af(omni_request->ipfamily), + omni_request->netperf_ip, + omni_request->netperf_port); + } + } + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from + under us, which to date is the easiest/cleanest/least + Windows-specific way I can find to force the winsock calls to + return WSAEINTR with the test is over. anything that will run on + 95 and NT and is closer to what netperf expects from Unix signals + and such would be appreciated raj 1/96 */ + win_kludge_socket = data_socket; +#endif /* WIN32 */ + + /* in recv_omni, we check recv first, and _then_ send, otherwise, + a request/response test will be all messed-up :) and that then + is why there are two routines to rule them all rather than just + one :) */ + if ((omni_request->direction & NETPERF_RECV) && + ((!times_up) || (units_remaining > 0))) { + ret = recv_data(data_socket, + recv_ring, + bytes_to_recv, + (connected) ? NULL : (struct sockaddr *)&peeraddr_in, + &addrlen, + /* if XMIT also, then this is RR test so waitall */ + (direction & NETPERF_XMIT) ? NETPERF_WAITALL: 0, + &temp_recvs); + if (ret > 0) { + /* if this is a recv-only test controlled by byte count we + decrement the units_remaining by the bytes received */ + if (!(direction & NETPERF_XMIT) && (units_remaining > 0)) { + units_remaining -= ret; + } + bytes_received += ret; + local_receive_calls += temp_recvs; + } + else if (ret == 0) { + /* is this the end of a test, just a zero-byte recv, or + something else? that is an exceedingly good question and + one for which I don't presently have a good answer, but + that won't stop me from guessing :) raj 2008-01-09 */ + if (!((connection_test) || (null_message_ok))) { + /* if it is neither a connection_test nor null_message_ok it + must be the end of the test */ + times_up = 1; + break; + } + local_receive_calls += temp_recvs; + } + else if (ret == -1) { + /* test timed-out */ + times_up = 1; + timed_out = 1; + break; + } + else { + /* presently at least, -2 and -3 are equally bad on recv */ + /* we need a response message here for the control connection + before we exit! */ + netperf_response.content.serv_errno = errno; + send_response(); + exit(-1); + } + recv_ring = recv_ring->next; + } + + /* if we should try to send something, then by all means, let us + try to send something. */ + if ((omni_request->direction & NETPERF_XMIT) && + ((!times_up) || (units_remaining > 0))) { + + /* there used to be some code here looking sched_yield() until + there was no more queued, unsent data on the socket but + frankly, I've no idea what that was all about so I have + removed it. It may have been part of a kludge to try to avoid + coalescing requests and responses */ + + if (omni_request->protocol == IPPROTO_UDP && need_to_connect && + !connected) { + if (connect(data_socket, + (struct sockaddr*)&peeraddr_in, + addrlen) == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + close(data_socket); + exit(-1); + } + connected = 1; + } + + ret = send_data(data_socket, + send_ring, + bytes_to_send, + (connected) ? NULL : (struct sockaddr *)&peeraddr_in, + addrlen, + omni_request->protocol); + + /* the order of these if's will seem a triffle strange, but they + are my best guess as to order of probabilty and/or importance + to the overhead raj 2008-01-09*/ + if (ret == bytes_to_send) { + /* if this is a send-only test controlled by byte count we + decrement units_remaining by the bytes sent */ + if (!(direction & NETPERF_RECV) && (units_remaining > 0)) { + units_remaining -= ret; + } + bytes_sent += ret; + send_ring = send_ring->next; + local_send_calls++; + } + else if (ret == -2) { + /* what to do here -2 means a non-fatal error - probably + ENOBUFS and so our send didn't happen. in the old code for + UDP_STREAM we would just continue in the while loop. it + isn't clear that is what to do here, so we will simply + increment the failed_sends stat and fall-through. If this + is a UDP_STREAM style of test, the net effect should be the + same. if this is a UDP_RR with a really-big burst count, I + don't think we were checking for ENOBUFS there anyway and + so would have failed. Here we can just let things + slide. */ + failed_sends++; + } + else if (ret == 0) { + /* was this a zero-byte send? if it was, then ostensibly we + would hit the ret == bytes_to_send case which means we'd + never get here as we are using blocking semantics */ + } + else if (ret == -1) { + times_up = 1; + timed_out = 1; + break; + } + else { + /* we need a response message back to netperf here before we + exit */ + /* NEED RESPONSE; */ + netperf_response.content.serv_errno = errno; + send_response(); + exit(-1); + } + + } + + if (connection_test) { +#ifdef __linux + /* so, "Linux" with autotuning likes to alter the socket buffer + sizes over the life of the connection, but only does so when + one takes the defaults at time of socket creation. if we + took those defaults, we should inquire as to what the values + ultimately became. raj 2008-01-15 */ + /* but as annoying as it is to have to make these calls, don't + penalize linux by calling them over and over again. instead + we will simply ass-u-me that it will become the same value + over and over again. raj 20100917 */ + if ((lsr_size_req < 0) && (-1 == lsr_size_end)) + get_sock_buffer(data_socket, RECV_BUFFER, &lsr_size_end); + else + lsr_size_end = lsr_size; + if ((lss_size_req < 0) && (-1 == lss_size_end)) + get_sock_buffer(data_socket, SEND_BUFFER, &lss_size_end); + else + lss_size_end = lss_size; +#else + lsr_size_end = lsr_size; + lss_size_end = lss_size; +#endif + ret = close_data_socket(data_socket,NULL,0,omni_request->protocol); + if (ret == -1) { + times_up = 1; + timed_out = 1; + break; + } + else if (ret < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + perror("netperf: recv_omni: close_data_socket failed"); + fflush(where); + exit(-1); + } + /* we will need a new connection to be established */ + need_to_accept = 1; + connected = 0; + } + +#if defined(WANT_INTERVALS) + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + /* was this a "transaction" test? don't for get that a TCP_CC + style test will have no xmit or recv :) so, we check for either + both XMIT and RECV set, or neither XMIT nor RECV set */ + if (NETPERF_IS_RR(omni_request->direction)) { + trans_completed++; + if (units_remaining) { + units_remaining--; + } + } + } + + /* The current iteration loop now exits due to timeout or unit count + being reached */ + stop_timer(); + cpu_stop(omni_request->flags & OMNI_MEASURE_CPU,&elapsed_time); + close(s_listen); + +#if defined(WANT_INTERVALS) +#ifdef WIN32 + stop_itimer(); +#endif +#endif /* WANT_INTERVALS */ + + if (timed_out) { + /* we ended the test by time, which may have been PAD_TIME seconds + longer than we wanted to run. so, we want to subtract pad_time + from the elapsed_time. if we didn't pad the timer pad_time will + be 0 so we can just subtract it anyway :) */ + if (debug) { + fprintf(where,"Adjusting elapsed time by %d seconds\n",pad_time); + fflush(where); + } + elapsed_time -= pad_time; + } + + remote_transport_retrans = get_transport_retrans(data_socket, + omni_request->protocol); + + if (connected) { +#ifdef __linux + /* so, "Linux" with autotuning likes to alter the socket buffer + sizes over the life of the connection, but only does so when + one takes the defaults at time of socket creation. if we took + those defaults, we should inquire as to what the values + ultimately became. raj 2008-01-15 */ + if (lsr_size_req < 0) + get_sock_buffer(data_socket, RECV_BUFFER, &lsr_size_end); + else + lsr_size_end = lsr_size; + if (lss_size_req < 0) + get_sock_buffer(data_socket, SEND_BUFFER, &lss_size_end); + else + lss_size_end = lss_size; +#else + lsr_size_end = lsr_size; + lss_size_end = lss_size; +#endif + if (omni_request->flags & OMNI_WANT_REM_CONG) { + get_transport_cong_control(data_socket, + local_res->ai_protocol, + omni_results->cong_control, + sizeof(omni_results->cong_control)); + } + else { + strncpy(omni_results->cong_control,"",sizeof(omni_results->cong_control)); + } + + + close_data_socket(data_socket,NULL,0,omni_request->protocol); + } + else { + close_data_socket(data_socket,(struct sockaddr *)&peeraddr_in,addrlen,omni_request->protocol); + lsr_size_end = lsr_size; + lss_size_end = lss_size; + } + + /* send the results to the sender */ + + omni_results->send_calls = (uint32_t) local_send_calls; + omni_results->bytes_received_lo = bytes_received & 0x00000000FFFFFFFFULL; + omni_results->bytes_received_hi = (bytes_received & 0xFFFFFFFF00000000ULL) >> 32; + omni_results->recv_buf_size = lsr_size_end; + omni_results->recv_calls = (uint32_t) local_receive_calls; + omni_results->bytes_sent_lo = bytes_sent & 0x00000000FFFFFFFFULL; + omni_results->bytes_sent_hi = (bytes_sent & 0xFFFFFFFF00000000ULL) >> 32; + omni_results->send_buf_size = lss_size_end; + omni_results->trans_received = (uint32_t) trans_completed; + omni_results->elapsed_time = elapsed_time; + omni_results->transport_retrans = remote_transport_retrans; + omni_results->cpu_method = cpu_method; + omni_results->num_cpus = lib_num_loc_cpus; + if (omni_request->flags & OMNI_MEASURE_CPU) { + omni_results->cpu_util = calc_cpu_util(elapsed_time); + omni_results->cpu_percent_user = lib_local_cpu_stats.cpu_user; + omni_results->cpu_percent_system = lib_local_cpu_stats.cpu_system; + omni_results->cpu_percent_iowait = lib_local_cpu_stats.cpu_iowait; + omni_results->cpu_percent_irq = lib_local_cpu_stats.cpu_irq; + omni_results->cpu_percent_swintr = lib_local_cpu_stats.cpu_swintr; + omni_results->peak_cpu_util = lib_local_cpu_stats.peak_cpu_util; + omni_results->peak_cpu_id = lib_local_cpu_stats.peak_cpu_id; + } + +#if defined(WANT_INTERVALS) +#ifdef WIN32 + stop_itimer(); +#endif +#endif /* WANT_INTERVALS */ + + if (debug) { + fprintf(where, + "%s: test complete, sending results.\n", + __FUNCTION__); + fflush(where); + } + + send_response_n(OMNI_RESULTS_CONV_CUTOFF); + +} + + +#ifdef WANT_MIGRATION +void +send_tcp_stream(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %s\n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6"PRId64" %6.2f %6"PRId64" %6.2f %6"PRId64"\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + send_omni_inner(remote_host, legacy, "MIGRATED TCP STREAM TEST"); + + + if (legacy) { + + /* We are now ready to print all the information, but only if we + are truly acting as a legacy test. If the user has specified + zero-level verbosity, we will just print the local service + demand, or the remote service demand. If the user has requested + verbosity level 1, he will get the basic "streamperf" + numbers. If the user has specified a verbosity of greater than + 1, we will display a veritable plethora of background + information from outside of this block as it it not + cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput, /* how fast did it go */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)local_send_calls, + local_send_calls, + bytes_sent / (double)remote_receive_calls, + remote_receive_calls); + fprintf(where, + ksink_fmt2, + transport_mss); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + fflush(where); + } + + } +} + +void +send_tcp_maerts(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %s\n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Recv Send Recv Send\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Recvs %-8.8s Sends\n\ +Local Remote Local Remote Xfered Per Per\n\ +Recv Send Recv Send Recv (avg) Send (avg)\n\ +%5d %5d %5d %5d %6"PRId64" %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + send_omni_inner(remote_host, legacy, "MIGRATED TCP MAERTS TEST"); + + + /* We are now ready to print all the information, but only if we are + truly acting as a leacy test. If the user has specified + zero-level verbosity, we will just print the local service + demand, or the remote service demand. If the user has requested + verbosity level 1, he will get the basic "streamperf" numbers. If + the user has specified a verbosity of greater than 1, we will + display a veritable plethora of background information from + outside of this block as it it not cpu_measurement + specific... */ + + if (legacy) { + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + remote_send_size, /* how large were the recvs */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + lsr_size, /* local recvbuf size */ + rss_size, /* remot sendbuf size */ + remote_send_size, /* how large were the recvs */ + elapsed_time, /* how long did it take */ + thruput, /* how fast did it go */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_recv_align, + remote_recv_align, + local_recv_offset, + remote_recv_offset, + bytes_received, + bytes_received / (double)local_receive_calls, + local_receive_calls, + remote_bytes_sent / (double)remote_send_calls, + remote_send_calls); + fprintf(where, + ksink_fmt2, + transport_mss); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in recv() call.\n"); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + fflush(where); + } + } +} + + +void +send_tcp_rr(char remote_host[]) { + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_title_band = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed \n\ +Send Recv Size Size Time Throughput \n\ +bytes Bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f %s\n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_title_tput = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Tput CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time %-8.8s local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_title_latency = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Latency CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time usecs local remote local remote\n\ +bytes bytes bytes bytes secs. per tran %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset RoundTrip Trans Throughput\n\ +Local Remote Local Remote Latency Rate %-8.8s/s\n\ +Send Recv Send Recv usec/Tran per sec Outbound Inbound\n\ +%5d %5d %5d %5d %-6.3f %-6.3f %-6.3f %-6.3f\n"; + + send_omni_inner(remote_host, legacy, "MIGRATED TCP REQUEST/RESPONSE TEST"); + + if (legacy) { + /* We are now ready to print all the information. If the user has + specified zero-level verbosity, we will just print the local + service demand, or the remote service demand. If the user has + requested verbosity level 1, he will get the basic "streamperf" + numbers. If the user has specified a verbosity of greater than 1, + we will display a veritable plethora of background information + from outside of this block as it it not cpu_measurement + specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + if ('x' == libfmt) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + else { + fprintf(where, + cpu_title_tput, + format_units(), + local_cpu_method, + remote_cpu_method); + } + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + ('x' == libfmt) ? tput_title : tput_title_band, + format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + /* are we trans or do we need to convert to bytes then + bits? at this point, thruput is in our "confident" + transactions per second. we can convert to a + bidirectional bitrate by multiplying that by the sum + of the req_size and rsp_size. we pass that to + calc_thruput_interval_omni with an elapsed time of + 1.0 s to get it converted to [kmg]bits/s or + [KMG]Bytes/s */ + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* normally, you might think that if we were messing about with + the value of libfmt we would need to put it back again, but + since this is basically the last thing we are going to do with + it, it does not matter. so there :) raj 2007-06-08 */ + /* if the user was asking for transactions, then we report + megabits per second for the unidirectional throughput, + otherwise we use the desired units. */ + if ('x' == libfmt) { + libfmt = 'm'; + } + + fprintf(where, + ksink_fmt, + format_units(), + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset, + /* if the user has enable burst mode, we have to remember + to account for that in the number of transactions + outstanding at any one time. otherwise we will + underreport the latency of individual + transactions. learned from saf by raj 2007-06-08 */ + (((double)1.0/transaction_rate)*(double)1000000.0) * + (double) (1 + ((first_burst_size > 0) ? first_burst_size : 0)), + transaction_rate, + calc_thruput_interval_omni(transaction_rate * (double)req_size, + 1.0), + calc_thruput_interval_omni(transaction_rate * (double)rsp_size, + 1.0)); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + fflush(where); + } + } +} + + +void +send_tcp_conn_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\n\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + send_omni_inner(remote_host, + legacy, + "MIGRATED TCP Connect/Request/Response TEST"); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (legacy) { + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + fflush(where); + } + } +} + +void +send_udp_stream(char remote_host[]) +{ + /**********************************************************************/ + /* */ + /* UDP Unidirectional Send Test */ + /* */ + /**********************************************************************/ + + char *tput_title = "\ +Socket Message Elapsed Messages \n\ +Size Size Time Okay Errors Throughput\n\ +bytes bytes secs # # %s/sec\n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = "\ +%6d %6d %-7.2f %7"PRIu64" %6d %7.2f\n\ +%6d %-7.2f %7"PRIu64" %7.2f\n\n"; + + + char *cpu_title = "\ +Socket Message Elapsed Messages CPU Service\n\ +Size Size Time Okay Errors Throughput Util Demand\n\ +bytes bytes secs # # %s/sec %% %c%c us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.2f %c\n"; + + char *cpu_fmt_1 = "\ +%6d %6d %-7.2f %7"PRIu64" %6d %7.1f %-6.2f %-6.3f\n\ +%6d %-7.2f %7"PRIu64" %7.1f %-6.2f %-6.3f\n\n"; + + + send_omni_inner(remote_host, legacy, "MIGRATED UDP STREAM TEST"); + + if (legacy) { + /* We are now ready to print all the information. If the user has + specified zero-level verbosity, we will just print the local + service demand, or the remote service demand. If the user has + requested verbosity level 1, he will get the basic "streamperf" + numbers. If the user has specified a verbosity of greater than + 1, we will display a veritable plethora of background + information from outside of this block as it it not + cpu_measurement specific... */ + + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + local_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + local_send_calls, + failed_sends, + local_send_thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + local_service_demand, /* local service demand */ + rsr_size, + elapsed_time, + remote_receive_calls, + remote_recv_thruput, + remote_cpu_utilization, /* remote cpu */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + local_send_thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + local_send_calls, + failed_sends, + local_send_thruput, + rsr_size, /* remote recvbuf size */ + elapsed_time, + remote_receive_calls, + remote_recv_thruput); + break; + } + } + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + fprintf(where,"\nHistogram of time spent in send() call\n"); + HIST_report(time_hist); + } +#endif /* WANT_HISTOGRAM */ + fflush(where); + } +} + +void +send_udp_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_title_band = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed \n\ +Send Recv Size Size Time Throughput \n\ +bytes Bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f %s\n"; + + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_title_tput = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Tput CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time %-8.8s local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + send_omni_inner(remote_host, legacy, "MIGRATED UDP REQUEST/RESPONSE TEST"); + + if (legacy) { + /* We are now ready to print all the information. If the user has + specified zero-level verbosity, we will just print the local + service demand, or the remote service demand. If the user has + requested verbosity level 1, he will get the basic "streamperf" + numbers. If the user has specified a verbosity of greater than + 1, we will display a veritable plethora of background + information from outside of this block as it it not + cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + + } + break; + case 1: + case 2: + if (print_headers) { + if ('x' == libfmt) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + else { + fprintf(where, + cpu_title_tput, + format_units(), + local_cpu_method, + remote_cpu_method); + } + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + ('x' == libfmt) ? tput_title : tput_title_band, + format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* UDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/reponse times.\n"); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + fflush(where); + } +} + + +#endif /* WANT_MIGRATION */ + + +/* using legacy test names will cause certain default settings to be + made before we scan the test-specific arguments. raj 2010-07-20 */ +static void +set_omni_defaults_by_legacy_testname() { + + /* the uber defaults are for a unidirectional test using TCP */ + protocol = IPPROTO_TCP; + socket_type = SOCK_STREAM; + connection_test = 0; + req_size = rsp_size = -1; + was_legacy = 1; + legacy = 1; + implicit_direction = 0; /* do we allow certain options to + implicitly affect the test direction? */ + if (strcasecmp(test_name,"TCP_STREAM") == 0) { + direction = NETPERF_XMIT; + } + else if (strcasecmp(test_name,"TCP_MAERTS") == 0) { + direction = NETPERF_RECV; + } + else if (strcasecmp(test_name,"TCP_RR") == 0) { + req_size = rsp_size = 1; + direction = 0; + direction |= NETPERF_XMIT; + direction |= NETPERF_RECV; + } + else if (strcasecmp(test_name,"UDP_STREAM") == 0) { + protocol = IPPROTO_UDP; + socket_type = SOCK_DGRAM; + } + else if (strcasecmp(test_name,"UDP_RR") == 0) { + protocol = IPPROTO_UDP; + socket_type = SOCK_DGRAM; + direction = 0; + direction |= NETPERF_XMIT; + direction |= NETPERF_RECV; + req_size = rsp_size = 1; + } + else if (strcasecmp(test_name,"TCP_CC") == 0) { + direction = 0; + connection_test = 1; + } + else if (strcasecmp(test_name,"TCP_CRR") == 0) { + direction = 0; + direction |= NETPERF_XMIT; + direction |= NETPERF_RECV; + req_size = rsp_size = 1; + connection_test = 1; + } + else if (strcasecmp(test_name,"omni") == 0) { + /* there is not much to do here but clear the legacy flag */ + was_legacy = 0; + legacy = 0; + implicit_direction = 1; + } + socket_type_str = hst_to_str(socket_type); +} + +char omni_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +OMNI and Migrated BSD Sockets Test Options:\n\ + -b number Send number requests at start of _RR tests\n\ + -c Explicitly declare this a connection test such as\n\ + TCP_CRR or TCP_CC\n\ + -C Set TCP_CORK when available\n\ + -d direction Explicitly set test direction based on bitwise OR\n\ + of 0x2 for transmit and 0x4 for receive. Default:\n\ + based on test type\n\ + -D [L][,R] Set TCP_NODELAY locally and/or remotely (TCP_*)\n\ + -h Display this text\n\ + -H name[/mask],fam Use name (or IP) and family as target of data connection\n\ + A mask value will cause randomization of the IP used\n\ + -k [file] Generate keyval output optionally based on file\n\ + Use filename of '?' to get the list of choices\n\ + -K loc[,rem] Set the local and/or remote congestion control\n\ + algorithm to use on those platforms where it can\n\ + be set.\n\ + -L name[/mask],fam Use name (or IP) and family as source of data connection\n\ + A mask value will cause randomization of the IP used\n\ + -m local,remote Set the send size for _STREAM/_MAERTS tests\n\ + -M local,remote Set the recv size for _STREAM/_MAERTS tests\n\ + -n Use the connected socket for UDP locally\n\ + -N Use the connected socket for UDP remotely\n\ + -o [file] Generate CSV output optionally based on file\n\ + Use filename of '?' to get the list of choices\n\ + -O [file] Generate classic-style output based on file\n\ + Use filename of '?' to get the list of choices\n\ + -p min[,max] Set the min/max port numbers for TCP_CRR, TCP_TRR\n\ + -P local[,remote] Set the local/remote port for the data socket\n\ + -r req,[rsp] Set request/response sizes (TCP_RR, UDP_RR)\n\ + -R 0/1 Allow routing of traffic on data connection.\n\ + Default: 0 (off) for UDP_STREAM, 1 (on) otherwise\n\ + -s send[,recv] Set local socket send/recv buffer sizes\n\ + -S send[,recv] Set remote socket send/recv buffer sizes\n\ + -t type Explicitly set socket type. Default is implicit\n\ + based on other settings\n\ + -T protocol Explicitly set data connection protocol. Default is\n\ + implicit based on other settings\n\ + -u uuid Use the supplied string as the UUID for this test.\n\ + -4 Use AF_INET (eg IPv4) on both ends of the data conn\n\ + -6 Use AF_INET6 (eg IPv6) on both ends of the data conn\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + +void +print_omni_usage() +{ + + fwrite(omni_usage, sizeof(char), strlen(omni_usage), stdout); + exit(1); + +} + + +void +scan_omni_args(int argc, char *argv[]) + +{ + +#define OMNI_ARGS "aBb:cCd:De:FgG:hH:i:Ij:kK:l:L:m:M:nNoOp:P:r:R:s:S:t:T:u:UVw:W:46" + + extern char *optarg; /* pointer to option string */ + + int c; + int have_uuid = 0; + int have_R_option = 0; + + char + arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ], + arg3[BUFSIZ]; + + if (debug) { + int i; + printf("%s called with the following argument vector\n", + __FUNCTION__); + for (i = 0; i< argc; i++) { + printf("%s ",argv[i]); + } + printf("\n"); + } + + /* double-check struct sizes */ + { + const union netperf_request_struct * u = (const union netperf_request_struct *)0; + if (debug) { + fprintf(where, "sizeof(omni_request_struct)=%d/%d\n", + (int)sizeof(struct omni_request_struct), + (int)sizeof(u->content.test_specific_data)); + fprintf(where, "sizeof(omni_response_struct)=%d/%d\n", + (int)sizeof(struct omni_response_struct), + (int)sizeof(u->content.test_specific_data)); + fprintf(where, "sizeof(omni_results_struct)=%d/%d\n", + (int)sizeof(struct omni_results_struct), + (int)sizeof(u->content.test_specific_data)); + } + assert(sizeof(struct omni_request_struct) + <= sizeof(u->content.test_specific_data)); + assert(sizeof(struct omni_response_struct) + <= sizeof(u->content.test_specific_data)); + assert(sizeof(struct omni_results_struct) + <= sizeof(u->content.test_specific_data)); + } + + strncpy(local_data_port,"0",sizeof(local_data_port)); + strncpy(remote_data_port,"0",sizeof(remote_data_port)); + + /* this will handle setting default settings based on test name */ + set_omni_defaults_by_legacy_testname(); + + /* Go through all the command line arguments and break them out. For + those options that take two parms, specifying only the first will + set both to that value. Specifying only the second will leave the + first untouched. To change only the first, use the form "first," + (see the routine break_args.. */ + + while ((c= getopt(argc, argv, OMNI_ARGS)) != EOF) { + switch (c) { + case '?': + case '4': + remote_data_family = AF_INET; + local_data_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + remote_data_family = AF_INET6; + local_data_family = AF_INET6; +#else + fprintf(stderr, + "This netperf was not compiled on an IPv6 capable host!\n"); + fflush(stderr); + exit(-1); +#endif + break; + case 'h': + print_omni_usage(); + exit(1); + case 'a': + want_defer_accept = 1; + break; + case 'B': + want_use_pktinfo = 1; + break; + case 'b': +#ifdef WANT_FIRST_BURST + first_burst_size = atoi(optarg); +#else /* WANT_FIRST_BURST */ + printf("Initial request burst functionality not compiled-in!\n"); +#endif /* WANT_FIRST_BURST */ + break; + case 'c': + /* this is a connection test */ + connection_test = 1; + break; + case 'C': +#ifdef TCP_CORK + /* set TCP_CORK */ + loc_tcpcork = 1; + rem_tcpcork = 1; /* however, at first, we ony have cork affect loc */ +#else + printf("WARNING: TCP_CORK not available on this platform!\n"); +#endif /* TCP_CORK */ + break; + case 'd': + /* arbitrarily set the direction variable, but only for an + actual omni test and then disable implicit setting of + direction */ + if (!was_legacy) { + direction = parse_direction(optarg); + implicit_direction = 0; + } + break; + case 'D': + /* set the TCP nodelay flag */ + loc_nodelay = 1; + rem_nodelay = 1; + break; + case 'F': +#if defined(MSG_FASTOPEN) + use_fastopen = 1; +#else + printf("WARNING: TCP FASTOPEN not available on this platform!\n"); +#endif + break; + case 'e': + /* set the rEceive timeout */ + receive_timeout = atoi(optarg); + break; + case 'g': + /* enable SO_DEBUG, or at least make the attempt, on the data socket */ + socket_debug = 1; + break; + case 'G': + /* set the value for a tcp_maxseG call*/ + transport_mss_req = atoi(optarg); + break; + case 'H': + break_args_explicit_sep(optarg,',',arg1,arg2); + if (arg1[0]) { + /* check to see if there was a width, which we would want to + be arg3. for simplicities sake, we will assume the width + must follow the address and not the address family - ie + 1.2.3.4/24,inet. This means we can just pass optarg again + as the source rather than have to shuffle arg values. */ + break_args_explicit_sep(optarg,'/',arg1,arg3); + if (arg1[0]) { + remote_data_address = malloc(strlen(arg1)+1); + strcpy(remote_data_address,arg1); + explicit_data_address = 1; + } + if (arg3[0]) { + remote_mask_len = convert(arg3); + } + } + if (arg2[0]) { + remote_data_family = parse_address_family(arg2); + } + break; + case 'i': + fprintf(stderr,"The support for check_interval has been removed because the contributing editor no longer knew what it was for\n"); + fflush(stderr); + break; + case 'I': + use_write = 1; + break; + case 'j': + parallel_connections = atoi(optarg); + break; + case 'k': + netperf_output_mode = KEYVAL; + legacy = 0; + /* obliterate any previous file name */ + if (output_selection_spec) { + free(output_selection_spec); + output_selection_spec = NULL; + } + if (argv[optind] && ((unsigned char)argv[optind][0] != '-')) { + /* we assume that what follows is the name of a file with the + list of desired output values. */ + output_selection_spec = strdup(argv[optind]); + optind++; + /* special case - if the file name is "?" then we will emit a + list of the available outputs */ + if (strcmp(output_selection_spec,"?") == 0) { + dump_netperf_output_choices(stdout,1); + exit(1); + } + } + break; + case 'K': + /* "Kongestion Kontrol */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strncpy(local_cong_control_req,arg1,sizeof(local_cong_control_req)); + if (arg2[2]) + strncpy(remote_cong_control_req,arg2,sizeof(remote_cong_control_req)); + break; + case 'l': + multicast_ttl = atoi(optarg); + break; + case 'L': + break_args_explicit_sep(optarg,',',arg1,arg2); + if (arg1[0]) { + /* check to see if there was a width, which we would want to + be arg3. for simplicities sake, we will assume the width + must follow the address and not the address family - ie + 1.2.3.4/24,inet. This means we can just pass optarg again + as the source rather than have to shuffle arg values. */ + break_args_explicit_sep(optarg,'/',arg1,arg3); + if (arg1[0]) { + local_data_address = malloc(strlen(arg1)+1); + strcpy(local_data_address,arg1); + } + if (arg3[0]) { + local_mask_len = convert(arg3); + } + } + if (arg2[0]) { + local_data_family = parse_address_family(arg2); + } + break; + case 'm': + /* set the send size. if we set the local send size it will add + XMIT to direction. if we set the remote send size it will + add RECV to the direction. likely as not this will need some + additional throught */ + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + send_size = convert(arg1); + if (implicit_direction) + direction |= NETPERF_XMIT; + } + if (arg2[0]) { + remote_send_size_req = convert(arg2); + if (implicit_direction) + direction |= NETPERF_RECV; + } + break; + case 'M': + /* set the recv sizes. if we set the local recv size it will + add RECV to direction. if we set the remote recv size it + will add XMIT to direction */ + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + remote_recv_size_req = convert(arg1); + if (implicit_direction) + direction |= NETPERF_XMIT; + } + if (arg2[0]) { + recv_size = convert(arg2); + if (implicit_direction) + direction |= NETPERF_RECV; + } + break; + case 'n': + /* set the local socket type */ + local_connected = 1; + break; + case 'N': + /* set the remote socket type */ + remote_connected = 1; + break; + case 'o': + netperf_output_mode = CSV; + legacy = 0; + /* obliterate any previous file name */ + if (output_selection_spec) { + free(output_selection_spec); + output_selection_spec = NULL; + } + if (output_selection_spec) { + free(output_selection_spec); + output_selection_spec = NULL; + } + if (argv[optind] && ((unsigned char)argv[optind][0] != '-')) { + /* we assume that what follows is the name of a file with the + list of desired output values. */ + output_selection_spec = strdup(argv[optind]); + optind++; + /* special case - if the file name is "?" then we will emit a + list of the available outputs */ + if (strcmp(output_selection_spec,"?") == 0) { + dump_netperf_output_choices(stdout,1); + exit(1); + } + } + break; + case 'O': + netperf_output_mode = HUMAN; + legacy = 0; + /* obliterate any previous file name */ + if (output_selection_spec) { + free(output_selection_spec); + output_selection_spec = NULL; + } + if (argv[optind] && ((unsigned char)argv[optind][0] != '-')) { + /* we assume that what follows is the name of a file with the + list of desired output values */ + output_selection_spec = strdup(argv[optind]); + optind++; + if (strcmp(output_selection_spec,"?") == 0) { + dump_netperf_output_choices(stdout,0); + exit(1); + } + } + break; + case 'p': + /* set the min and max port numbers for the TCP_CRR and TCP_TRR */ + /* tests. */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + client_port_min = atoi(arg1); + if (arg2[0]) + client_port_max = atoi(arg2); + break; + case 'P': + /* set the local and remote data port numbers for the tests to + allow them to run through those blankety blank end-to-end + breaking firewalls. raj 2004-06-15 */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strncpy(local_data_port,arg1,sizeof(local_data_port)); + if (arg2[0]) + strncpy(remote_data_port,arg2,sizeof(remote_data_port)); + break; + case 'r': + /* set the request/response sizes. setting request/response + sizes implicitly sets direction to XMIT and RECV */ + if (implicit_direction) { + direction |= NETPERF_XMIT; + direction |= NETPERF_RECV; + } + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = convert(arg1); + if (arg2[0]) + rsp_size = convert(arg2); + break; + case 'R': + routing_allowed = atoi(optarg); + have_R_option = 1; + break; + case 's': + /* set local socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lss_size_req = convert(arg1); + if (arg2[0]) + lsr_size_req = convert(arg2); + break; + case 'S': + /* set remote socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rss_size_req = convert(arg1); + if (arg2[0]) + rsr_size_req = convert(arg2); + break; + case 't': + /* set the socket type */ + socket_type = parse_socket_type(optarg); + break; + case 'T': + /* set the protocol - aka "Transport" */ + protocol = parse_protocol(optarg); + break; + case 'u': + /* use the supplied string as the UUID for this test. at some + point we may want to sanity check the string we are given but + for now we won't worry about it */ + strncpy(test_uuid,optarg,sizeof(test_uuid)); + /* strncpy may leave us with a string without a null at the end */ + test_uuid[sizeof(test_uuid) - 1] = 0; + have_uuid = 1; + break; + case 'U': + /* we don't want to give-up on the failure of a connect() call */ + dont_give_up = 1; + break; + case 'W': + /* set the "width" of the user space data */ + /* buffer. This will be the number of */ + /* send_size buffers malloc'd in the */ + /* *_STREAM test. It may be enhanced to set */ + /* both send and receive "widths" but for now */ + /* it is just the sending *_STREAM. */ + send_width = convert(optarg); + break; + case 'V' : + /* we want to do copy avoidance and will set */ + /* it for everything, everywhere, if we really */ + /* can. of course, we don't know anything */ + /* about the remote... */ + loc_sndavoid = 1; + loc_rcvavoid = 1; + rem_sndavoid = 1; + rem_rcvavoid = 1; + break; + }; + } + + /* generate the UUID for this test if the user has not supplied it */ + if (!have_uuid) + get_uuid_string(test_uuid,sizeof(test_uuid)); + + protocol_str = protocol_to_str(protocol); + /* ok, if we have gone through all that, and direction is still + zero, let us see if it needs to be set to something else. */ + if ((0 == direction) && (!connection_test)) direction = NETPERF_XMIT; + direction_str = direction_to_str(direction); + + /* to cover the backside of blithering idiots who run unidirectional + UDP tests on test setups where they might trash their corporate + WAN, we grudgingly provide a safety latch. unless explicitly + enabled, UDP_STREAM/UDP_MAERTS sockets will not allow themselves + to be routed via a gateway. raj 20091026 */ + + if ((!have_R_option) && + (protocol == IPPROTO_UDP) && + (!NETPERF_IS_RR(direction))) { + routing_allowed = 0; + } + + /* some other sanity checks we need to make would include stuff when + the user has set -m and -M such that both XMIT and RECV are set + and has not set -r. initially we will not allow that. at some + point we might allow that if the user has also set -r, but until + then the code will simply ignore the values from -m and -M when + -r is set. */ + +#if defined(WANT_HISTOGRAM) + if (verbosity > 1) keep_histogram = 1; +#endif + + /* did the user use -d 6 but not set -r? */ + if (NETPERF_IS_RR(direction) && !NETPERF_CC(direction)) { + if (req_size == -1) + req_size = 1; + if (rsp_size == -1) + rsp_size = 1; + } + + /* ok, time to sanity check the output units */ + if ('?' == libfmt) { + /* if this is a RR test then set it to 'x' for transactions */ + if (NETPERF_IS_RR(direction)) { + libfmt = 'x'; + } + else { + libfmt = 'm'; + } + } + else if ('x' == libfmt) { + /* now, a format of 'x' makes no sense for anything other than + an RR test. if someone has been silly enough to try to set + that, we will reset it silently to default - namely 'm' */ + if (!NETPERF_IS_RR(direction)) { + libfmt = 'm'; + } + } + + /* this needs to be strdup :) */ + thruput_format_str = strdup(format_units()); + + /* so, if there is to be no control connection, we want to have some + different settings for a few things */ + + if (no_control) { + if (strcmp(remote_data_port,"0") == 0) { + /* we need to select either the discard port, echo port or + chargen port dedepending on the test direction. raj + 20101220 */ + if (NETPERF_XMIT_ONLY(direction)) { + strncpy(remote_data_port,"discard",sizeof(remote_data_port)); + recv_size = -1; + } + else if (NETPERF_RECV_ONLY(direction)) { + strncpy(remote_data_port,"chargen",sizeof(remote_data_port)); + send_size = -1; + } + else if (NETPERF_IS_RR(direction) || NETPERF_CC(direction)) { + strncpy(remote_data_port,"echo",sizeof(remote_data_port)); + rsp_size = req_size; + } + else { + printf("No default port known for the %s test, please set one yourself\n",test_name); + exit(-1); + } + } + remote_data_port[sizeof(remote_data_port) - 1] = '\0'; + + /* I go back and forth on whether these should become -1 or if + they should become 0 for a no_control test. what do you think? + raj 2006-02-08 */ + + rem_rcvavoid = -1; + rem_sndavoid = -1; + rss_size_req = -1; + rsr_size_req = -1; + rem_nodelay = -1; + + } + /* so, did the user request a few things implicitly via output selection? */ + if (!legacy) + print_omni_init(); + + if (desired_output_groups & OMNI_WANT_STATS) { + keep_statistics = 1; + keep_histogram = 1; + } + +} + +#endif /* WANT_OMNI */ |