diff -ruN old/compat/Thread.c new/compat/Thread.c --- old/compat/Thread.c 2020-10-03 02:42:49.000000000 +0800 +++ new/compat/Thread.c 2020-11-05 09:06:26.000000000 +0800 @@ -222,7 +222,6 @@ Condition_Unlock(thread_sNum_cond); #if defined(HAVE_POSIX_THREAD) - // pthreads -- spawn new thread if (pthread_create(&thread->mTID, NULL, thread_run_wrapper, thread) != 0) { WARN(1, "pthread_create"); @@ -243,9 +242,7 @@ #if HAVE_THREAD_DEBUG thread_debug("Thread_run_wrapper(%p mode=%x) thread counts tot/trfc=%d/%d", (void *)thread, thread->mThreadMode, thread_sNum, thread_trfc_sNum); #endif - #elif defined(HAVE_WIN32_THREAD) - // Win32 threads -- spawn new thread // Win32 has a thread handle in addition to the thread ID thread->mHandle = CreateThread(NULL, 0, thread_run_wrapper, thread, 0, &thread->mTID); @@ -260,9 +257,7 @@ } Condition_Unlock(thread_sNum_cond); } - #else - // single-threaded -- call Run_Wrapper in this thread thread_run_wrapper(thread); #endif diff -ruN old/compat/delay.c new/compat/delay.c --- old/compat/delay.c 2020-10-03 02:42:49.000000000 +0800 +++ new/compat/delay.c 2020-11-05 09:06:26.000000000 +0800 @@ -54,6 +54,7 @@ #include "headers.h" #include "util.h" #include "delay.h" +#include "Thread.h" #include #define MILLION 1000000 @@ -98,7 +99,11 @@ struct timespec res; res.tv_sec = usec/MILLION; res.tv_nsec = (usec * 1000) % BILLION; + #ifndef WIN32 clock_nanosleep(CLOCK_MONOTONIC, 0, &res, NULL); + #else + clock_nanosleep(0, 0, &res, NULL); + #endif } #else #ifdef HAVE_KALMAN @@ -111,6 +116,73 @@ #endif #endif #endif +} + +int clock_usleep (struct timeval *request) { + int rc = 0; +#if HAVE_THREAD_DEBUG + thread_debug("Thread called clock_usleep() until %ld.%ld", request->tv_sec, request->tv_usec); +#endif +#ifdef HAVE_CLOCK_NANOSLEEP + struct timespec tmp; + tmp.tv_sec = request->tv_sec; + tmp.tv_nsec = request->tv_usec * 1000; + +// Cygwin systems have an issue with CLOCK_MONOTONIC +#if defined(CLOCK_MONOTONIC) && !defined(WIN32) + rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &tmp, NULL); +#else + rc = clock_nanosleep(0, 0, &tmp, NULL); +#endif + if (rc) { + fprintf(stderr, "failed clock_nanosleep()=%d\n", rc); + } +#else + struct timeval now; + struct timeval next = *request; +#ifdef HAVE_CLOCK_GETTIME + struct timespec t1; + clock_gettime(CLOCK_REALTIME, &t1); + now.tv_sec = t1.tv_sec; + now.tv_usec = t1.tv_nsec / 1000; +#else + gettimeofday(&now, NULL); +#endif + double delta_usecs; + if ((delta_usecs = TimeDifference(next, now)) > 0.0) { + delay_loop(delta_usecs); + } +#endif + return rc; +} + +int clock_usleep_abstime (struct timeval *request) { + int rc = 0; +#if defined(HAVE_CLOCK_NANOSLEEP) && defined(TIMER_ABSTIME) && !defined(WIN32) + struct timespec tmp; + tmp.tv_sec = request->tv_sec; + tmp.tv_nsec = request->tv_usec * 1000; + rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tmp, NULL); + if (rc) { + fprintf(stderr, "failed clock_nanosleep()=%d\n", rc); + } +#else + struct timeval now; + struct timeval next = *request; +#ifdef HAVE_CLOCK_GETTIME + struct timespec t1; + clock_gettime(CLOCK_REALTIME, &t1); + now.tv_sec = t1.tv_sec; + now.tv_usec = t1.tv_nsec / 1000; +#else + gettimeofday(&now, NULL); +#endif + double delta_usecs; + if ((delta_usecs = (1e6 * TimeDifference(next, now))) > 0.0) { + delay_loop(delta_usecs); + } +#endif + return rc; } #ifdef HAVE_NANOSLEEP diff -ruN old/config.h.in new/config.h.in --- old/config.h.in 2020-10-03 05:29:30.000000000 +0800 +++ new/config.h.in 2020-11-05 09:06:26.000000000 +0800 @@ -103,6 +103,9 @@ /* Define if fast sampling for report intervals is desired */ #undef HAVE_FASTSAMPLING +/* Define to 1 if you have the `freopen' function. */ +#undef HAVE_FREOPEN + /* Define if syscall(SYS_gettid) available. */ #undef HAVE_GETTID_SYSCALL @@ -193,6 +196,9 @@ /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT +/* Define if role reversal ids are desired */ +#undef HAVE_ROLE_REVERSAL_ID + /* Define to 1 if you have the `sched_setscheduler' function. */ #undef HAVE_SCHED_SETSCHEDULER @@ -220,6 +226,9 @@ /* Define to enable ssm multicast support */ #undef HAVE_SSM_MULTICAST +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -317,6 +326,9 @@ /* Define if winsock2.h exists. */ #undef HAVE_WINSOCK2_H +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + /* Define to disable asserts */ #undef NDEBUG @@ -357,9 +369,6 @@ /* Define to the type of arg 5 for `select'. */ #undef SELECT_TYPE_ARG5 -/* The size of `bool', as computed by sizeof. */ -#undef SIZEOF_BOOL - /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -390,20 +399,11 @@ /* */ #undef _REENTRANT -/* */ -#undef bool - /* Define to empty if `const' does not conform to ANSI C. */ #undef const -/* */ -#undef false - /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to "int" if does not define. */ #undef ssize_t - -/* */ -#undef true diff -ruN old/configure new/configure --- old/configure 2020-10-03 05:29:30.000000000 +0800 +++ new/configure 2020-11-05 09:06:26.000000000 +0800 @@ -757,6 +757,7 @@ enable_debuginfo enable_web100 enable_kalman +enable_role_reversal_id enable_seqno64b enable_fastsampling enable_thread_debug @@ -1407,6 +1408,9 @@ (default is no) --disable-web100 disable web100 support (default is autodetect) --disable-kalman disable kalman delay tuning (default is enable) + --disable-role-reversal-id + disable role reversal special characters (default is + enable) --disable-seqno64b disable 64 bit sequence numer support (default is enable) --enable-fastsampling enable support for 100 microsecond report intervals @@ -2057,189 +2061,6 @@ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member - -# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES -# -------------------------------------------- -# Tries to find the compile-time value of EXPR in a program that includes -# INCLUDES, setting VAR accordingly. Returns whether the value could be -# computed -ac_fn_c_compute_int () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if test "$cross_compiling" = yes; then - # Depending upon the size, compute the lo and hi bounds. -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) >= 0)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_lo=0 ac_mid=0 - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_hi=$ac_mid; break -else - as_fn_arith $ac_mid + 1 && ac_lo=$as_val - if test $ac_lo -le $ac_mid; then - ac_lo= ac_hi= - break - fi - as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) < 0)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_hi=-1 ac_mid=-1 - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) >= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_lo=$ac_mid; break -else - as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val - if test $ac_mid -le $ac_hi; then - ac_lo= ac_hi= - break - fi - as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - ac_lo= ac_hi= -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Binary search between lo and hi bounds. -while test "x$ac_lo" != "x$ac_hi"; do - as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_hi=$ac_mid -else - as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -done -case $ac_lo in #(( -?*) eval "$3=\$ac_lo"; ac_retval=0 ;; -'') ac_retval=1 ;; -esac - else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -static long int longval () { return $2; } -static unsigned long int ulongval () { return $2; } -#include -#include -int -main () -{ - - FILE *f = fopen ("conftest.val", "w"); - if (! f) - return 1; - if (($2) < 0) - { - long int i = longval (); - if (i != ($2)) - return 1; - fprintf (f, "%ld", i); - } - else - { - unsigned long int i = ulongval (); - if (i != ($2)) - return 1; - fprintf (f, "%lu", i); - } - /* Do not output a trailing newline, as this causes \r\n confusion - on some platforms. */ - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. @@ -3253,12 +3074,6 @@ - - - - - - # =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== @@ -3404,6 +3219,12 @@ fi +# Check whether --enable-role_reversal_id was given. +if test "${enable_role_reversal_id+set}" = set; then : + enableval=$enable_role_reversal_id; +fi + + # Check whether --enable-seqno64b was given. if test "${enable_seqno64b+set}" = set; then : enableval=$enable_seqno64b; @@ -7867,7 +7688,7 @@ done -for ac_func in atexit memset select strchr strerror strtol strtoll usleep clock_gettime sched_setscheduler sched_yield mlockall setitimer nanosleep clock_nanosleep +for ac_func in atexit memset select strchr strerror strtol strtoll usleep clock_gettime sched_setscheduler sched_yield mlockall setitimer nanosleep clock_nanosleep freopen do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -8265,95 +8086,100 @@ - - -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of bool" >&5 -$as_echo_n "checking size of bool... " >&6; } -if ${ac_cv_sizeof_bool+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 +$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } +if ${ac_cv_header_stdbool_h+:} false; then : $as_echo_n "(cached) " >&6 else - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (bool))" "ac_cv_sizeof_bool" "$ac_includes_default"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -else - if test "$ac_cv_type_bool" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (bool) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_bool=0 - fi -fi + #include + #ifndef bool + "error: bool is not defined" + #endif + #ifndef false + "error: false is not defined" + #endif + #if false + "error: false is not 0" + #endif + #ifndef true + "error: true is not defined" + #endif + #if true != 1 + "error: true is not 1" + #endif + #ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" + #endif -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_bool" >&5 -$as_echo "$ac_cv_sizeof_bool" >&6; } + struct s { _Bool s: 1; _Bool t; } s; + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + /* See body of main program for 'e'. */ + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_BOOL $ac_cv_sizeof_bool -_ACEOF - - -if test "$ac_cv_sizeof_bool" = 0 ; then - $as_echo "#define bool int" >>confdefs.h - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if true is defined" >&5 -$as_echo_n "checking if true is defined... " >&6; } -if ${ac_cv_have_true+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - int main () { -unsigned int i = true + + bool e = &s; + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); + ; return 0; } _ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_have_true=yes +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdbool_h=yes else - ac_cv_have_true=no + ac_cv_header_stdbool_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 +$as_echo "$ac_cv_header_stdbool_h" >&6; } + ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" +if test "x$ac_cv_type__Bool" = xyes; then : +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_true" >&5 -$as_echo "$ac_cv_have_true" >&6; } -if test "$ac_cv_have_true" != yes ; then - $as_echo "#define true 1" >>confdefs.h - $as_echo "#define false 0" >>confdefs.h +if test $ac_cv_header_stdbool_h = yes; then +$as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h + fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : @@ -8810,6 +8636,12 @@ if test "$enable_kalman" != no; then $as_echo "#define HAVE_KALMAN 1" >>confdefs.h + +fi + +if test "$enable_role_reversal_id" != no; then + +$as_echo "#define HAVE_ROLE_REVERSAL_ID 1" >>confdefs.h fi diff -ruN old/configure.ac new/configure.ac --- old/configure.ac 2020-10-03 05:29:22.000000000 +0800 +++ new/configure.ac 2020-11-05 09:06:26.000000000 +0800 @@ -38,6 +38,9 @@ AC_ARG_ENABLE(kalman, AC_HELP_STRING([--disable-kalman], [disable kalman delay tuning (default is enable)])) +AC_ARG_ENABLE(role_reversal_id, AC_HELP_STRING([--disable-role-reversal-id], + [disable role reversal special characters (default is enable)])) + AC_ARG_ENABLE(seqno64b, AC_HELP_STRING([--disable-seqno64b], [disable 64 bit sequence numer support (default is enable)])) @@ -160,7 +163,7 @@ AC_TYPE_SIGNAL AC_FUNC_STRFTIME AC_FUNC_VPRINTF -AC_CHECK_FUNCS([atexit memset select strchr strerror strtol strtoll usleep clock_gettime sched_setscheduler sched_yield mlockall setitimer nanosleep clock_nanosleep]) +AC_CHECK_FUNCS([atexit memset select strchr strerror strtol strtoll usleep clock_gettime sched_setscheduler sched_yield mlockall setitimer nanosleep clock_nanosleep freopen]) AC_REPLACE_FUNCS(snprintf inet_pton inet_ntop gettimeofday) AC_CHECK_DECLS([ENOBUFS, EWOULDBLOCK],[],[],[#include ]) AC_CHECK_DECLS([pthread_cancel],[],[],[#include ]) @@ -205,7 +208,7 @@ dnl =================================================================== dnl Check for compiler characteristics -DAST_CHECK_BOOL +AC_HEADER_STDBOOL AC_C_BIGENDIAN @@ -302,6 +305,10 @@ if test "$enable_kalman" != no; then AC_DEFINE([HAVE_KALMAN], 1, [Define if Kalman tuning is desired and available]) +fi + +if test "$enable_role_reversal_id" != no; then +AC_DEFINE([HAVE_ROLE_REVERSAL_ID], 1, [Define if role reversal ids are desired ]) fi if test "$enable_seqno64b" != no; then diff -ruN old/include/Client.hpp new/include/Client.hpp --- old/include/Client.hpp 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Client.hpp 2020-11-05 09:06:26.000000000 +0800 @@ -89,15 +89,16 @@ int StartSynch(void); void TxDelay(void); void ConnectPeriodic(void); - void my_connect(void); + int my_connect(int exit_on_fail); bool isConnected(void); - void SendFirstPayload(void); + int SendFirstPayload(void); int BarrierClient(struct BarrierMutex *); struct ReportHeader *myJob; private: inline void WritePacketID(intmax_t); inline void WriteTcpTxHdr(struct ReportStruct *, int, int); + inline double get_delay_target(void); void InitTrafficLoop(void); void SetReportStartTime(void); inline void SetFullDuplexReportStartTime(void); @@ -112,6 +113,7 @@ double delay_lower_bounds; intmax_t totLen; bool one_report; + bool apply_first_udppkt_delay; int udp_payload_minimum; // TCP plain diff -ruN old/include/Listener.hpp new/include/Listener.hpp --- old/include/Listener.hpp 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Listener.hpp 2020-11-05 09:06:26.000000000 +0800 @@ -72,6 +72,7 @@ private: int mClients; + int mBufLen; char* mBuf; // used for UDP packet or TCP messages struct ether_header *eth_hdr; struct iphdr *ip_hdr; @@ -79,6 +80,8 @@ thread_Settings *mSettings; thread_Settings *server; Timestamp mEndTime; + bool apply_client_settings_udp(thread_Settings *server); + bool apply_client_settings_tcp(thread_Settings *server); bool apply_client_settings(thread_Settings *server); int client_test_ack(thread_Settings *server); void my_multicast_join(void); diff -ruN old/include/Locale.h new/include/Locale.h --- old/include/Locale.h 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Locale.h 2020-11-05 09:06:26.000000000 +0800 @@ -139,12 +139,14 @@ extern const char report_bw_header[]; -extern const char report_bw_sumcnt_header[]; +extern const char report_sumcnt_bw_header[]; extern const char report_bw_format[]; extern const char report_sum_bw_format[]; + extern const char report_sumcnt_bw_format[]; + extern const char report_bw_read_format[]; extern const char report_bw_jitter_loss_header[]; @@ -177,13 +179,15 @@ extern const char report_sum_bw_read_enhanced_format[]; +extern const char report_sumcnt_bw_read_enhanced_header[]; + extern const char report_sumcnt_bw_read_enhanced_format[]; extern const char report_triptime_enhanced_format[]; extern const char report_bw_write_enhanced_header[]; -extern const char report_bw_write_sumcnt_enhanced_header[]; +extern const char report_sumcnt_bw_write_enhanced_header[]; extern const char report_bw_write_enhanced_format[]; @@ -241,12 +245,17 @@ extern const char report_udp_fullduplex_enhanced_format[]; +extern const char report_sumcnt_udp_fullduplex_header[]; +extern const char report_sumcnt_udp_fullduplex_format[]; + /* ------------------------------------------------------------------- * Misc reports * ------------------------------------------------------------------- */ extern const char report_outoforder[]; + +extern const char report_sumcnt_outoforder[]; extern const char report_l2statistics[]; diff -ruN old/include/PerfSocket.hpp new/include/PerfSocket.hpp --- old/include/PerfSocket.hpp 2020-10-03 02:42:49.000000000 +0800 +++ new/include/PerfSocket.hpp 2020-11-05 09:06:26.000000000 +0800 @@ -63,17 +63,6 @@ void SetSocketOptionsSendTimeout( struct thread_Settings *inSettings, int timer); void SetSocketOptionsReceiveTimeout( struct thread_Settings *inSettings, int timer); // handle interupts -void Sig_Interupt( int inSigno ); - -#ifdef __cplusplus -extern "C" { -#endif - extern int sInterupted; - extern int groupID; - extern Mutex groupCond; - -#ifdef __cplusplus -} /* end extern "C" */ -#endif +void Sig_Interupt(int inSigno); #endif // PERFSOCKET_H diff -ruN old/include/Reporter.h new/include/Reporter.h --- old/include/Reporter.h 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Reporter.h 2020-11-07 01:34:16.000000000 +0800 @@ -68,11 +68,11 @@ #define NETPOWERCONSTANT 1e-6 #define REPORTTXTMAX 80 #define MINBARRIERTIMEOUT 3 - +#define PARTIALPERCENT 0.25 // used to decide if a final partial report should be displayed // If the minimum latency exceeds the boundaries below // assume the clocks are not synched and suppress the // latency output. Units are seconds -#define UNREALISTIC_LATENCYMINMIN -1 +#define UNREALISTIC_LATENCYMINMIN -0.01 #define UNREALISTIC_LATENCYMINMAX 60 #ifdef __cplusplus @@ -81,6 +81,8 @@ extern struct Condition ReportCond; extern struct Condition ReportsPending; +extern int groupID; +extern Mutex transferid_mutex; /* * @@ -207,6 +209,7 @@ // rather than using references struct ReportCommon { enum ThreadMode ThreadMode; + enum ReportMode ReportMode; int flags; int flags_extend; int threads; @@ -236,6 +239,8 @@ char* Ifrnametx; char* SSMMulticastStr; char* Congestion; + char* transferIDStr; + int transferID; #if WIN32 SOCKET socket; #else @@ -300,10 +305,12 @@ struct ReportTimeStamps { double iStart; double iEnd; + double significant_partial; struct timeval startTime; struct timeval matchTime; struct timeval packetTime; struct timeval prevpacketTime; + struct timeval prevsendTime; struct timeval nextTime; struct timeval intervalTime; struct timeval IPGstart; @@ -313,10 +320,9 @@ struct ReportCommon *common; struct ReportTimeStamps ts; void (*output_handler) (struct TransferInfo *stats); - int transferID; int groupID; int threadcnt; - int sumflag; + int filter_this_sample_ouput; uintmax_t cntBytes; intmax_t cntError; intmax_t cntOutofOrder; @@ -346,6 +352,7 @@ struct TransferInfo info; void (*transfer_protocol_sum_handler) (struct TransferInfo *stats, int final); struct BarrierMutex fullduplex_barrier; + int sum_fd_set; }; struct ReporterData { @@ -384,7 +391,7 @@ typedef void (* report_statistics)( struct TransferInfo* ); typedef void (* report_serverstatistics)( struct ConnectionInfo *, struct TransferInfo* ); -void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport, int fullduplex); +void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport); struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, int fullduplex); struct ReportHeader* InitIndividualReport(struct thread_Settings *inSettings); struct ReportHeader* InitConnectionReport(struct thread_Settings *inSettings, double ct); @@ -462,12 +469,9 @@ void tcp_output_sum_write_enhanced (struct TransferInfo *stats); void tcp_output_sumcnt_write_enhanced (struct TransferInfo *stats); // TCP fullduplex -void tcp_output_fullduplex_write(struct TransferInfo *stats); -void tcp_output_fullduplex_write_enhanced(struct TransferInfo *stats); -void tcp_output_fullduplex_read(struct TransferInfo *stats); -void tcp_output_fullduplex_read_enhanced(struct TransferInfo *stats); void tcp_output_fullduplex(struct TransferInfo *stats); void tcp_output_fullduplex_enhanced(struct TransferInfo *stats); +void tcp_output_fullduplex_sum (struct TransferInfo *stats); // UDP server void udp_output_read(struct TransferInfo *stats); @@ -475,7 +479,8 @@ void udp_output_read_enhanced_triptime(struct TransferInfo *stats); void udp_output_read_enhanced_triptime_isoch(struct TransferInfo *stats); void udp_output_sum_read(struct TransferInfo *stats); -void udp_output_sumcnt_read(struct TransferInfo *stats); +void udp_output_sum_read_enhanced (struct TransferInfo *stats); +void udp_output_sumcnt(struct TransferInfo *stats); void udp_output_sumcnt_read_enhanced (struct TransferInfo *stats); //UDP client @@ -486,11 +491,13 @@ void udp_output_sum_write_enhanced (struct TransferInfo *stats); void udp_output_sumcnt_write(struct TransferInfo *stats); void udp_output_sumcnt_write_enhanced (struct TransferInfo *stats); +void udp_output_sumcnt_enhanced(struct TransferInfo *stats); // UDP full duplex void udp_output_fullduplex(struct TransferInfo *stats); -void udp_output_fullduplex_sum(struct TransferInfo *stats); void udp_output_fullduplex_enhanced(struct TransferInfo *stats); +void udp_output_fullduplex_sum(struct TransferInfo *stats); + // CSV output void udp_output_basic_csv(struct TransferInfo *stats); void tcp_output_basic_csv(struct TransferInfo *stats); @@ -508,6 +515,8 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport); int reporter_process_report (struct ReportHeader *reporthdr); + +int setTransferID(struct thread_Settings *inSettings, int role_reversal); #ifdef __cplusplus } /* end extern "C" */ diff -ruN old/include/Server.hpp new/include/Server.hpp --- old/include/Server.hpp 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Server.hpp 2020-11-05 09:06:26.000000000 +0800 @@ -83,6 +83,7 @@ private: thread_Settings *mSettings; char* mBuf; + int mBufLen; Timestamp mEndTime; Timestamp now; ReportStruct scratchpad; diff -ruN old/include/Settings.hpp new/include/Settings.hpp --- old/include/Settings.hpp 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Settings.hpp 2020-11-05 09:06:26.000000000 +0800 @@ -156,6 +156,7 @@ char* mSSMMulticastStr; // --ssm-host char* mIsochronousStr; // --isochronous char* mRxHistogramStr; // --histograms (packets) + char* mTransferIDStr; // FILE* Extractor_file; struct ReportHeader* reporthdr; struct SumReport* mSumReport; @@ -165,6 +166,8 @@ // int's int mThreads; // -P int mTOS; // -S + int mTransferID; + int mConnectRetries; #if WIN32 SOCKET mSock; #else @@ -202,6 +205,7 @@ bool mSinlgeClient; // -1 */ int flags; int flags_extend; + int skip; // enums (which should be special int's) enum ThreadMode mThreadMode; // -s or -c enum ReportMode mReportMode; @@ -321,7 +325,7 @@ #define FLAG_MODEINFINITE 0x00010000 #define FLAG_CONNECTONLY 0x00020000 #define FLAG_SERVERREVERSE 0x00040000 -#define FLAG_FULLDUPLEX 0x00080000 +#define FLAG_FULLDUPLEX 0x00080000 #define FLAG_WRITEACK 0x00100000 #define FLAG_NOUDPFIN 0x00200000 #define FLAG_NOCONNECTSYNC 0x00400000 @@ -510,7 +514,7 @@ void Settings_GenerateListenerSettings(struct thread_Settings *client, struct thread_Settings **listener); // generate settings for speaker instance - void Settings_GenerateClientSettings(struct thread_Settings *server, struct thread_Settings **client, void * mBuf); +void Settings_GenerateClientSettings(struct thread_Settings *server, struct thread_Settings **client, void * mBuf); // generate client header for server int Settings_GenerateClientHdr(struct thread_Settings *client, void * hdr, struct timeval startTime); diff -ruN old/include/Timestamp.hpp new/include/Timestamp.hpp --- old/include/Timestamp.hpp 2020-10-03 02:42:49.000000000 +0800 +++ new/include/Timestamp.hpp 2020-11-05 09:06:26.000000000 +0800 @@ -68,7 +68,7 @@ /* ------------------------------------------------------------------- * Create a timestamp, with the current time in it. * ------------------------------------------------------------------- */ - Timestamp( void ) { + Timestamp(void) { setnow(); } @@ -83,21 +83,21 @@ /* ------------------------------------------------------------------- * Create a timestamp, with the given seconds/microseconds * ------------------------------------------------------------------- */ - Timestamp( long sec, long usec ) { - set( sec, usec ); + Timestamp(long sec, long usec) { + set(sec, usec); } /* ------------------------------------------------------------------- * Create a timestamp, with the given seconds * ------------------------------------------------------------------- */ - Timestamp( double sec ) { - set( sec ); + Timestamp(double sec) { + set(sec); } /* ------------------------------------------------------------------- * Set timestamp to current time. * ------------------------------------------------------------------- */ - void inline setnow( void ) { + void inline setnow(void) { #ifdef HAVE_CLOCK_GETTIME struct timespec t1; clock_gettime(CLOCK_REALTIME, &t1); @@ -111,9 +111,9 @@ /* ------------------------------------------------------------------- * Set timestamp to the given seconds/microseconds * ------------------------------------------------------------------- */ - void set( long sec, long usec ) { - assert( sec >= 0 ); - assert( usec >= 0 && usec < kMillion ); + void set(long sec, long usec) { + assert(sec >= 0); + assert(usec >= 0 && usec < kMillion); mTime.tv_sec = sec; mTime.tv_usec = usec; @@ -122,7 +122,7 @@ /* ------------------------------------------------------------------- * Set timestamp to the given seconds * ------------------------------------------------------------------- */ - void set( double sec ) { + void set(double sec) { mTime.tv_sec = (long) sec; mTime.tv_usec = (long) ((sec - mTime.tv_sec) * kMillion); } @@ -130,21 +130,21 @@ /* ------------------------------------------------------------------- * return seconds portion of timestamp * ------------------------------------------------------------------- */ - long inline getSecs( void ) { + long inline getSecs(void) { return mTime.tv_sec; } /* ------------------------------------------------------------------- * return microseconds portion of timestamp * ------------------------------------------------------------------- */ - long inline getUsecs( void ) { + long inline getUsecs(void) { return mTime.tv_usec; } /* ------------------------------------------------------------------- * return timestamp as a floating point seconds * ------------------------------------------------------------------- */ - double get( void ) { + double get(void) { return mTime.tv_sec + mTime.tv_usec / ((double) kMillion); } @@ -152,7 +152,7 @@ * subtract the right timestamp from my timestamp. * return the difference in microseconds. * ------------------------------------------------------------------- */ - long subUsec( Timestamp right ) { + long subUsec(Timestamp right) { return(mTime.tv_sec - right.mTime.tv_sec) * kMillion + (mTime.tv_usec - right.mTime.tv_usec); } @@ -161,7 +161,7 @@ * subtract the right timestamp from my timestamp. * return the difference in microseconds. * ------------------------------------------------------------------- */ - long subUsec( timeval right ) { + long subUsec(timeval right) { return(mTime.tv_sec - right.tv_sec) * kMillion + (mTime.tv_usec - right.tv_usec); } @@ -170,7 +170,7 @@ * subtract my timestamp from the right timestamp * return the difference in microseconds. * ------------------------------------------------------------------- */ - long mysubUsec( timeval right ) { + long mysubUsec(timeval right) { return(right.tv_sec - mTime.tv_sec) * kMillion + (right.tv_usec - mTime.tv_usec); } @@ -189,7 +189,7 @@ * subtract the right timestamp from my timestamp. * return the difference in seconds as a floating point. * ------------------------------------------------------------------- */ - double subSec( Timestamp right ) { + double subSec(Timestamp right) { return(mTime.tv_sec - right.mTime.tv_sec) + (mTime.tv_usec - right.mTime.tv_usec) / ((double) kMillion); } @@ -197,46 +197,67 @@ /* ------------------------------------------------------------------- * add the right timestamp to my timestamp. * ------------------------------------------------------------------- */ - void add( Timestamp right ) { + void add(Timestamp right) { mTime.tv_sec += right.mTime.tv_sec; mTime.tv_usec += right.mTime.tv_usec; // watch for under- and overflow - if ( mTime.tv_usec < 0 ) { + if (mTime.tv_usec < 0) { mTime.tv_usec += kMillion; mTime.tv_sec--; } - if ( mTime.tv_usec >= kMillion ) { + if (mTime.tv_usec >= kMillion) { mTime.tv_usec -= kMillion; mTime.tv_sec++; } - assert( mTime.tv_usec >= 0 && - mTime.tv_usec < kMillion ); + assert(mTime.tv_usec >= 0 && + mTime.tv_usec < kMillion); } /* ------------------------------------------------------------------- + * add the right timestamp to my timestamp. + * ------------------------------------------------------------------- */ + void add (struct timeval *right) { + mTime.tv_sec += right->tv_sec; + mTime.tv_usec += right->tv_usec; + + // watch for under- and overflow + if (mTime.tv_usec < 0) { + mTime.tv_usec += kMillion; + mTime.tv_sec--; + } + if (mTime.tv_usec >= kMillion) { + mTime.tv_usec -= kMillion; + mTime.tv_sec++; + } + + assert(mTime.tv_usec >= 0 && + mTime.tv_usec < kMillion); + } + + /* ------------------------------------------------------------------- * add the seconds to my timestamp. * TODO optimize? * ------------------------------------------------------------------- */ - void add( double sec ) { + void add(double sec) { mTime.tv_sec += (long) sec; - mTime.tv_usec += (long) ((sec - ((long) sec )) * kMillion); + mTime.tv_usec += (long) ((sec - ((long) sec)) * kMillion); // watch for overflow - if ( mTime.tv_usec >= kMillion ) { + if (mTime.tv_usec >= kMillion) { mTime.tv_usec -= kMillion; mTime.tv_sec++; } - assert( mTime.tv_usec >= 0 && - mTime.tv_usec < kMillion ); + assert(mTime.tv_usec >= 0 && + mTime.tv_usec < kMillion); } /* ------------------------------------------------------------------- * add micro seconds to my timestamp. * ------------------------------------------------------------------- */ - void add(unsigned int usec ) { + void add(unsigned int usec) { mTime.tv_usec += usec; mTime.tv_sec += mTime.tv_usec / kMillion; mTime.tv_usec = mTime.tv_usec % kMillion; @@ -246,36 +267,35 @@ /* ------------------------------------------------------------------- * return true if my timestamp is before the right timestamp. * ------------------------------------------------------------------- */ - bool before( timeval right ) { + bool before(timeval right) { return mTime.tv_sec < right.tv_sec || (mTime.tv_sec == right.tv_sec && mTime.tv_usec < right.tv_usec); } - bool before( Timestamp right ) { return before(right.mTime); } + bool before(Timestamp right) { return before(right.mTime); } /* ------------------------------------------------------------------- * return true if my timestamp is after the right timestamp. * ------------------------------------------------------------------- */ - bool after( timeval right ) { + bool after(timeval right) { return mTime.tv_sec > right.tv_sec || (mTime.tv_sec == right.tv_sec && mTime.tv_usec > right.tv_usec); } - bool after( Timestamp right ) { return after(right.mTime); } + bool after(Timestamp right) { return after(right.mTime); } /** * This function returns the fraction of time elapsed after the beginning * till the end */ double fraction(Timestamp currentTime, Timestamp endTime) { - if ( (currentTime.after(*this)) && (endTime.after(currentTime)) ) { + if ((currentTime.after(*this)) && (endTime.after(currentTime))) { return(((double)currentTime.subUsec(*this)) / ((double)endTime.subUsec(*this))); } else { return -1.0; } } - protected: enum { diff -ruN old/include/delay.h new/include/delay.h --- old/include/delay.h 2020-10-03 02:42:49.000000000 +0800 +++ new/include/delay.h 2020-11-05 09:06:26.000000000 +0800 @@ -49,15 +49,17 @@ * ------------------------------------------------------------------- * accurate microsecond delay * ------------------------------------------------------------------- */ - #ifndef DELAY_H #define DELAY_H #ifdef __cplusplus extern "C" { #endif +#include void delay_loop( unsigned long usecs ); void delay_busyloop(unsigned long usecs); void delay_nanosleep(unsigned long usecs); +int clock_usleep(struct timeval *request); +int clock_usleep_abstime(struct timeval *request); #ifdef HAVE_KALMAN // Kalman filter states struct kalman_state { diff -ruN old/include/headers.h new/include/headers.h --- old/include/headers.h 2020-10-03 02:42:49.000000000 +0800 +++ new/include/headers.h 2020-11-05 09:06:26.000000000 +0800 @@ -85,6 +85,22 @@ #include #include +#ifdef HAVE_STDBOOL_H +# include +#else +# ifndef HAVE__BOOL +# ifdef __cplusplus +typedef bool _Bool; +# else +# define _Bool signed char +# endif +# endif +# define bool _Bool +# define false 0 +# define true 1 +# define __bool_true_false_are_defined 1 +#endif + // AF_PACKET HEADERS #if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET) #include diff -ruN old/include/packet_ring.h new/include/packet_ring.h --- old/include/packet_ring.h 2020-10-03 02:42:49.000000000 +0800 +++ new/include/packet_ring.h 2020-11-05 09:06:26.000000000 +0800 @@ -60,6 +60,7 @@ intmax_t packetID; intmax_t packetLen; struct timeval packetTime; + struct timeval prevPacketTime; struct timeval sentTime; struct timeval prevSentTime; int errwrite; diff -ruN old/include/payloads.h new/include/payloads.h --- old/include/payloads.h 2020-10-03 05:12:16.000000000 +0800 +++ new/include/payloads.h 2020-11-05 09:06:26.000000000 +0800 @@ -59,8 +59,11 @@ #define HEADER_VERSION1 0x80000000 #define HEADER_EXTEND 0x40000000 #define HEADER_UDPTESTS 0x20000000 +#define HEADER_UNUSED 0x10000000 #define HEADER_SEQNO64B 0x08000000 #define HEADER_VERSION2 0x04000000 +#define HEADER_AVOID2 0x02000000 +#define HEADER_AVOID1 0x01000000 #define HEADER_V2PEERDETECT 0x02000000 #define HEADER_LEN_BIT 0x00010000 #define SERVER_HEADER_EXTEND 0x40000000 @@ -74,13 +77,14 @@ #define HEADER_L2LENCHECK 0x0004 #define HEADER_NOUDPFIN 0x0008 #define HEADER_TRIPTIME 0x0010 -#define HEADER_TOSSTARTTIME 0x0020 +#define HEADER_UNUSED2 0x0020 #define HEADER_ISOCH_SETTINGS 0x0040 #define HEADER_UNITS_PPS 0x0080 #define HEADER_BWSET 0x0100 #define HEADER_FQRATESET 0x0200 #define HEADER_REVERSE 0x0400 #define HEADER_FULLDUPLEX 0x0800 +#define HEADER_EPOCH_START 0x1000 // later features #define HDRXACKMAX 2500000 // default 2.5 seconds, units microseconds diff -ruN old/include/util.h new/include/util.h --- old/include/util.h 2020-10-03 02:42:49.000000000 +0800 +++ new/include/util.h 2020-11-05 09:06:26.000000000 +0800 @@ -61,6 +61,8 @@ extern "C" { #endif +extern int sInterupted; + /* ------------------------------------------------------------------- * set/getsockopt wrappers for SO_RCVBUF and SO_SNDBUF; TCP_MAXSEG * socket.c diff -ruN old/include/version.h new/include/version.h --- old/include/version.h 2020-10-03 05:17:37.000000000 +0800 +++ new/include/version.h 2020-11-12 03:24:12.000000000 +0800 @@ -1,4 +1,4 @@ #define IPERF_VERSION "2.0.14a" -#define IPERF_VERSION_DATE "2 October 2020" +#define IPERF_VERSION_DATE "11 Nov 2020" #define IPERF_VERSION_MAJORHEX 0x00020000 #define IPERF_VERSION_MINORHEX 0x000E0000 diff -ruN old/m4/dast.m4 new/m4/dast.m4 --- old/m4/dast.m4 2020-10-03 02:42:49.000000000 +0800 +++ new/m4/dast.m4 2020-11-05 09:06:26.000000000 +0800 @@ -1,37 +1,7 @@ -dnl DAST_CHECK_BOOL -dnl Check for bool support. Defines bool, true, and false. - AH_TEMPLATE([HAVE_POSIX_THREAD], []) AH_TEMPLATE([_REENTRANT], []) AH_TEMPLATE([ssize_t], [Define to "int" if does not define.]) -AH_TEMPLATE([bool]) -AH_TEMPLATE([true]) -AH_TEMPLATE([false]) - -AC_DEFUN(DAST_CHECK_BOOL, [ - -AC_CHECK_SIZEOF(bool) -if test "$ac_cv_sizeof_bool" = 0 ; then - AC_DEFINE(bool, int) -fi - -AC_CACHE_CHECK(if true is defined, ac_cv_have_true, - [AC_LANG_SAVE - AC_LANG_CPLUSPLUS - AC_TRY_COMPILE([], - [unsigned int i = true], - ac_cv_have_true=yes, - ac_cv_have_true=no) - AC_LANG_RESTORE - ]) - -if test "$ac_cv_have_true" != yes ; then - AC_DEFINE(true, 1) - AC_DEFINE(false, 0) -fi - -]) dnl =================================================================== dnl DAST_REPLACE_TYPE( type, sizeof ) diff -ruN old/man/iperf.1 new/man/iperf.1 --- old/man/iperf.1 2020-10-03 02:42:49.000000000 +0800 +++ new/man/iperf.1 2020-11-05 09:06:26.000000000 +0800 @@ -138,11 +138,14 @@ .BR -c ", " --client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" run in client mode, connecting to \fIhost\fR where the optional %dev will SO_BINDTODEVICE that output interface (requires root and see NOTES) .TP -.BR " --connect-only" -only perform a TCP connect without any data transfer - useful to measure TCP connect() times +.BR " --connect-only[=" \fIn\fR "]" +only perform a TCP connect (or 3WHS) without any data transfer, useful to measure TCP connect() times. Optional value of n is the total number of connects to do (zero is run forever.) Note that -i will rate limit the connects where -P will create bursts and -t will end the client and hence end its connect attempts. .TP +.BR " --connect-retries " \fIn\fR "]" +number of times to retry a TCP connect at the application level. See operating system information on the details of TCP connect related settings. +.TP .BR -d ", " --dualtest " " -Do a bidirectional test simultanous test using two unidirectional sockets +Do a bidirectional test simultaneous test using two unidirectional sockets .TP .BR " --fq-rate n[kmgKMG]" Set a rate to be used with fair-queueing based socket-level pacing, in bytes or bits per second. Only available on platforms supporting the SO_MAX_PACING_RATE socket option. (Note: Here the suffixes indicate bytes/sec or bits/sec per use of uppercase or lowercase, respectively) @@ -154,7 +157,7 @@ increment the destination ip address when using the parallel (-P) option .TP .BR " --ipg "\fIn\fR -set the interpacket gap to \fIn\fR (units of seconds) for packets or within a frame/burst when --isochronous is set +set the inter-packet gap to \fIn\fR (units of seconds) for packets or within a frame/burst when --isochronous is set .TP .BR " --isochronous[=" \fIfps\fR:\fImean\fR,\fIstdev\fR "]" send isochronous traffic with frequency frames per second and load defined by mean and standard deviation using a log normal distribution, defaults to 60:20m,0. (Note: Here the suffixes indicate bytes/sec or bits/sec per use of uppercase or lowercase, respectively. Also the p suffix is supported to set the burst size in packets, e.g. isochronous=2:25p will send two 25 packet bursts every second, or one 25 packet burst every 0.5 seconds.) @@ -186,7 +189,7 @@ time in seconds to hold back or delay after the TCP connect and prior to the socket writes. For UDP it's the delay between the traffic thread starting and the first write. .TP .BR " --txstart-time "\fIn\fR.\fIn\fR -set the txstart-time to \fIn\fR.\fIn\fR using unix or epoch time format (supports microsecond resolution, e.g 1536014418.123456) An example to delay one second using command subsitution is iperf -c 192.168.1.10 --txstart-time $(expr $(date +%s) + 1).$(date +%N) +set the txstart-time to \fIn\fR.\fIn\fR using unix or epoch time format (supports microsecond resolution, e.g 1536014418.123456) An example to delay one second using command substitution is iperf -c 192.168.1.10 --txstart-time $(expr $(date +%s) + 1).$(date +%N) .TP .BR -B ", " --bind " \fIip\fR | \fIip\fR:\fIport\fR | \fIipv6 -V\fR | \fI[ipv6]\fR:\fIport -V\fR" bind src ip addr and optional port as the source of traffic (see notes) @@ -709,7 +712,7 @@ See https://sourceforge.net/p/iperf2/tickets/ .SH AUTHORS Iperf2, based from iperf (originally written by Mark Gates and Alex -Warshavsky), has a goal of maintainence with some feature enhancement. +Warshavsky), has a goal of maintenance with some feature enhancement. Other contributions from Ajay Tirumala, Jim Ferguson, Jon Dugan , Feng Qin, Kevin Gibbs, diff -ruN old/src/Client.cpp new/src/Client.cpp --- old/src/Client.cpp 2020-10-03 05:30:52.000000000 +0800 +++ new/src/Client.cpp 2020-11-11 06:27:23.000000000 +0800 @@ -50,7 +50,6 @@ * A client thread initiates a connect to the server and handles * sending and receiving data, then closes the socket. * ------------------------------------------------------------------- */ - #include #include "headers.h" #include "Client.hpp" @@ -76,23 +75,21 @@ Client::Client (thread_Settings *inSettings) { #ifdef HAVE_THREAD_DEBUG - thread_debug("Client in constructor (%x/%x) %p", inSettings->flags, inSettings->flags_extend, (void *)inSettings->mSumReport); + thread_debug("Client constructor with thread %p sum=%p (flags=%x)", (void *) inSettings, (void *)inSettings->mSumReport, inSettings->flags); #endif - mSettings = inSettings; mBuf = NULL; myJob = NULL; myReport = NULL; one_report = false; udp_payload_minimum = 1; + apply_first_udppkt_delay = false; memset(&scratchpad, 0, sizeof(struct ReportStruct)); reportstruct = &scratchpad; reportstruct->packetID = 1; mySocket = isServerReverse(mSettings) ? mSettings->mSock : INVALID_SOCKET; connected = isServerReverse(mSettings); - if (!isServerReverse(mSettings)) - SockAddr_remoteAddr(mSettings); if (isCompat(mSettings) && isPeerVerDetect(mSettings)) { fprintf(stderr, "%s", warn_compat_and_peer_exchange); unsetPeerVerDetect(mSettings); @@ -136,7 +133,7 @@ * If inLocalhost is not null, bind to that address, specifying * which outgoing interface to use. * ------------------------------------------------------------------- */ -void Client::my_connect (void) { +int Client::my_connect (int exit_on_fail) { int rc; double connecttime = -1.0; // create an internet socket @@ -155,33 +152,55 @@ mSettings->mSock=mySocket; SetSocketOptions(mSettings); SockAddr_localAddr(mSettings); + SockAddr_remoteAddr(mSettings); if (mSettings->mLocalhost != NULL) { // bind socket to local address rc = bind(mySocket, (sockaddr*) &mSettings->local, - SockAddr_get_sizeof_sockaddr(&mSettings->local)); + SockAddr_get_sizeof_sockaddr(&mSettings->local)); WARN_errno(rc == SOCKET_ERROR, "bind"); } - // Bound the TCP connect() to the -t value (if it was given on the command line) - // otherwise let TCP use its defaul timeouts fo the connect() - if (isModeTime(mSettings) && !isUDP(mSettings)) { - SetSocketOptionsSendTimeout(mSettings, (mSettings->mAmount * 10000)); - } - // connect socket + connected = false; if (!isUDP(mSettings)) { - connect_start.setnow(); - rc = connect(mySocket, (sockaddr*) &mSettings->peer, - SockAddr_get_sizeof_sockaddr(&mSettings->peer)); - connect_done.setnow(); - connecttime = 1e3 * connect_done.subSec(connect_start); - mSettings->connecttime = connecttime; + int trycnt = mSettings->mConnectRetries + 1; + while (trycnt > 0) { + connect_start.setnow(); + rc = connect(mySocket, (sockaddr*) &mSettings->peer, + SockAddr_get_sizeof_sockaddr(&mSettings->peer)); + if (rc == SOCKET_ERROR) { + WARN_errno(rc == SOCKET_ERROR, "tcp connect"); + if ((--trycnt) <= 0) { + if (exit_on_fail) { + close(mySocket); + _exit(1); + } else { + return 0; + } + } else { + delay_loop(200000); + } + } else { + connect_done.setnow(); + connecttime = 1e3 * connect_done.subSec(connect_start); + mSettings->connecttime = connecttime; + connected = true; + break; + } + } } else { rc = connect(mySocket, (sockaddr*) &mSettings->peer, - SockAddr_get_sizeof_sockaddr(&mSettings->peer)); + SockAddr_get_sizeof_sockaddr(&mSettings->peer)); + WARN_errno(rc == SOCKET_ERROR, "udp connect"); + if (rc != SOCKET_ERROR) + connected = true; } - WARN_errno(rc == SOCKET_ERROR, "connect"); - if (rc != SOCKET_ERROR) { + if (mSettings->mInterval > 0) { + SetSocketOptionsSendTimeout(mSettings, (mSettings->mInterval * 1000000) / 2); + } else { + SetSocketOptionsSendTimeout(mSettings, (mSettings->mAmount * 10000) / 2); + } + if (connected) { getsockname(mySocket, (sockaddr*) &mSettings->local, &mSettings->size_local); getpeername(mySocket, (sockaddr*) &mSettings->peer, &mSettings->size_peer); SockAddr_Ifrname(mSettings); @@ -194,14 +213,19 @@ mySocket = INVALID_SOCKET; } } + if (isUDP(mSettings) && !isIsochronous(mSettings) && !isIPG(mSettings)) { + mSettings->mBurstIPG = get_delay_target() / 1e3; // this is being set for the settings report only + } if (isReport(mSettings) && isSettingsReport(mSettings)) { struct ReportHeader *tmp = InitSettingsReport(mSettings); assert(tmp!=NULL); PostReport(tmp); + setNoSettReport(mSettings); } // Post the connect report unless peer version exchange is set if (connected && isConnectionReport(mSettings) && !isSumOnly(mSettings) && !isPeerVerDetect(mSettings)) PostReport(InitConnectionReport(mSettings, connecttime)); + return connected; } // end Connect bool Client::isConnected (void) { @@ -213,21 +237,7 @@ void Client::TxDelay (void) { if (isTxHoldback(mSettings)) { -#ifdef HAVE_CLOCK_NANOSLEEP - timespec tmp; - tmp.tv_sec = mSettings->txholdback_timer.tv_sec; - tmp.tv_nsec = mSettings->txholdback_timer.tv_usec * 1000; - // See if this a delay between connect and data - int rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &tmp, NULL); - if (rc) { - fprintf(stderr, "txholdback failed clock_nanosleep()=%d\n", rc); - } -#else - now.setnow(); - Timestamp tmp; - tmp.set(mSettings->txholdback_timer.tv_sec, mSettings->txholdback_timer.tv_usec); - delay_loop(tmp.subUsec(now)); -#endif + clock_usleep(&mSettings->txholdback_timer); } } @@ -242,37 +252,27 @@ myJob = InitIndividualReport(mSettings); myReport = (struct ReporterData *)myJob->this_report; myReport->info.common->socket=mySocket; - myReport->info.transferID=mySocket; - // Perform delays, usually between connect() and data xfer though before connect // Two delays are supported: // o First is an absolute start time per unix epoch format // o Second is a holdback, a relative amount of seconds between the connect and data xfers // check for an epoch based start time - now.setnow(); - if (isTxStartTime(mSettings)) { - if (isIsochronous(mSettings)) { - Timestamp tmp; - tmp.set(mSettings->txstart_epoch.tv_sec, mSettings->txstart_epoch.tv_usec); - framecounter = new Isochronous::FrameCounter(mSettings->mFPS, tmp); - } else { - // RJM move to compat/clock_nanonsleep.c -#ifdef HAVE_CLOCK_NANOSLEEP - timespec tmp; - tmp.tv_sec = mSettings->txstart_epoch.tv_sec; - tmp.tv_nsec = mSettings->txstart_epoch.tv_usec * 1000; - int rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tmp, NULL); - if (rc) { - fprintf(stderr, "txstart failed clock_nanosleep()=%d\n", rc); - } -#else - now.setnow(); - Timestamp tmp; - tmp.set(mSettings->txstart_epoch.tv_sec, mSettings->txstart_epoch.tv_usec); - delay_loop(tmp.subUsec(now)); -#endif + if (!isServerReverse(mSettings)) { + if (isTxStartTime(mSettings)) { + clock_usleep_abstime(&mSettings->txstart_epoch); + } else if (isTxHoldback(mSettings)) { + TxDelay(); } + reportstruct->packetLen = SendFirstPayload(); + } else { + reportstruct->packetLen = 0; } + + if (isIsochronous(mSettings)) { + Timestamp tmp; + tmp.set(mSettings->txstart_epoch.tv_sec, mSettings->txstart_epoch.tv_usec); + framecounter = new Isochronous::FrameCounter(mSettings->mFPS, tmp); + } int setfullduplexflag = 0; if (isFullDuplex(mSettings) && !isServerReverse(mSettings)) { assert(mSettings->mFullDuplexReport != NULL); @@ -280,8 +280,20 @@ return -1; } SetReportStartTime(); - if (setfullduplexflag) + if (reportstruct->packetLen > 0) { + now.setnow(); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->sentTime = reportstruct->packetTime; + reportstruct->prevSentTime = reportstruct->packetTime; + reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime; + ReportPacket(myReport, reportstruct); + myReport->info.ts.prevpacketTime = reportstruct->packetTime; + reportstruct->packetID++; + } + if (setfullduplexflag) { SetFullDuplexReportStartTime(); + } // Full duplex sockets need to be syncronized #ifdef HAVE_THREAD_DEBUG thread_debug("Client start sync exited"); @@ -325,7 +337,7 @@ sumstats->ts.nextTime = myReport->info.ts.nextTime; } #ifdef HAVE_THREAD_DEBUG - thread_debug("Client fullduplex report start=%ld.%ld next=%ld.%ld", sumstats->ts.startTime.tv_sec, sumstats->ts.startTime.tv_usec, sumstats->ts.nextTime.tv_sec, sumstats->ts.nextTime.tv_usec); + thread_debug("Client group sum report start=%ld.%ld next=%ld.%ld", sumstats->ts.startTime.tv_sec, sumstats->ts.startTime.tv_usec, sumstats->ts.nextTime.tv_sec, sumstats->ts.nextTime.tv_usec); #endif } Mutex_Unlock(&myReport->GroupSumReport->reference.lock); @@ -337,44 +349,43 @@ void Client::ConnectPeriodic (void) { Timestamp end; + Timestamp next; unsigned int amount_usec = 1000000; if (isModeTime(mSettings)) { amount_usec = (mSettings->mAmount * 10000); + end.add(amount_usec); // add in micro seconds } - end.add(amount_usec); // add in micro seconds - Timestamp next = now; setNoConnectSync(mSettings); - while (!sInterupted && (mSettings->mInterval && isModeTime(mSettings) && now.before(end) && next.before(end))) { - while (!now.before(next)) { - next.add(mSettings->mInterval); + int num_connects = -1; + if (!(mSettings->mInterval > 0)) { + if (mSettings->connectonly_count < 0) + num_connects = 10; + else if (mSettings->connectonly_count > 0) + num_connects = mSettings->connectonly_count; + } + + do { + if (my_connect(0)){ + int rc = close(mySocket); + WARN_errno(rc == SOCKET_ERROR, "client close"); + mySocket = INVALID_SOCKET; } - if (next.before(end)) { - timespec tmp; - tmp.tv_sec = next.getSecs(); - tmp.tv_nsec = next.getUsecs() * 1000; -#if defined(HAVE_CLOCK_NANOSLEEP) - int rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tmp, NULL); - if (rc) { - fprintf(stderr, "ConnectPeriodic() failed clock_nanosleep()=%d\n", rc); - } -#else + if (mSettings->mInterval > 0) { now.setnow(); - delay_loop(next.subUsec(now)); -#endif - if (isReport(mSettings)) { - // Post a settings report now - PostReport(InitSettingsReport(mSettings)); - unsetReport(mSettings); + do { + next.add(mSettings->mInterval); + } while (next.before(now)); + if (next.before(end)) { + struct timeval tmp; + tmp.tv_sec = next.getSecs(); + tmp.tv_usec = next.getUsecs(); + clock_usleep_abstime(&tmp); } - if (isConnected()) { - int rc = close(mySocket); - WARN_errno(rc == SOCKET_ERROR, "client close"); - mySocket = INVALID_SOCKET; - } - my_connect(); - now.setnow(); } - } + if (num_connects > 0) { + --num_connects; + } + } while (num_connects && !sInterupted && (next.before(end) || (isModeTime(mSettings) && !(mSettings->mInterval > 0)))); } /* ------------------------------------------------------------------- * Common traffic loop intializations @@ -415,12 +426,10 @@ // Finally, post this thread's "job report" which the reporter thread // will continuously process as long as there are packets flowing // right now the ring is empty - if (!isReverse(mSettings)) { - if (isDataReport(mSettings)) { - assert(myJob!=NULL); - assert(myReport!=NULL); - PostReport(myJob); - } + if (!isReverse(mSettings) && !isSingleUDP(mSettings) && isDataReport(mSettings)) { + assert(myJob!=NULL); + assert(myReport!=NULL); + PostReport(myJob); } one_report = ((!isUDP(mSettings) && !isEnhanced(mSettings) && (mSettings->mIntervalMode != kInterval_Time) \ && !isIsochronous(mSettings) && !isTripTime(mSettings) && !isReverse(mSettings)) ? true : false); @@ -440,7 +449,7 @@ // Peform common traffic setup InitTrafficLoop(); /* - * UDP specific setup + * UDP */ if (isUDP(mSettings)) { if (isFileInput(mSettings)) { @@ -450,8 +459,6 @@ Extractor_reduceReadSize(sizeof(struct UDP_datagram), mSettings); readAt += sizeof(struct UDP_datagram); } - } - if (isUDP(mSettings)) { // Launch the approprate UDP traffic loop if (isIsochronous(mSettings)) { RunUDPIsochronous(); @@ -474,7 +481,6 @@ int burst_size = mSettings->mBufLen; int burst_remaining = 0; int burst_id = 1; - struct timeval prevsend = myReport->info.ts.startTime; // RJM, consider moving this into the constructor if (isIsochronous(mSettings)) { @@ -507,7 +513,7 @@ reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); reportstruct->sentTime = reportstruct->packetTime; - reportstruct->prevSentTime = prevsend; + reportstruct->prevSentTime = myReport->info.ts.prevsendTime; ReportPacket(myReport, reportstruct); } reportstruct->transit_ready = 0; @@ -518,7 +524,7 @@ reportstruct->packetTime.tv_usec = now.getUsecs(); WriteTcpTxHdr(reportstruct, burst_size, burst_id++); reportstruct->sentTime = reportstruct->packetTime; - prevsend = reportstruct->packetTime; + myReport->info.ts.prevsendTime = reportstruct->packetTime; // perform write n = writen(mySocket, mBuf, sizeof(struct TCP_burst_payload)); assert(n == sizeof(struct TCP_burst_payload)); @@ -661,11 +667,7 @@ reportstruct->packetTime.tv_sec = time2.getSecs(); reportstruct->packetTime.tv_usec = time2.getUsecs(); reportstruct->sentTime = reportstruct->packetTime; - - if (isEnhanced(mSettings) || (mSettings->mIntervalMode == kInterval_Time)) { - ReportPacket(myReport, reportstruct); - } - + ReportPacket(myReport, reportstruct); if (isModeAmount(mSettings)) { /* mAmount may be unsigned, so don't let it underflow! */ if (mSettings->mAmount >= (unsigned long) reportstruct->packetLen) { @@ -685,15 +687,8 @@ /* * UDP send loop */ -void Client::RunUDP (void) { - struct timeval prevsend = myReport->info.ts.startTime; - struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; - int currLen; - - double delay_target = 0; - double delay = 0; - double adjust = 0; - +double Client::get_delay_target (void) { + double delay_target; if (isIPG(mSettings)) { delay_target = mSettings->mBurstIPG * 1000000; // convert from milliseconds to nanoseconds } else { @@ -706,9 +701,24 @@ delay_target = 1e9 / mSettings->mUDPRate; } } + return delay_target; +} + +void Client::RunUDP (void) { + struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; + int currLen; + + double delay_target = get_delay_target(); + double delay = 0; + double adjust = 0; + // Set this to > 0 so first loop iteration will delay the IPG currLen = 1; double variance = mSettings->mVariance; + if (apply_first_udppkt_delay && (delay_target > 100000)) { + //the case when a UDP first packet went out in SendFirstPayload + delay_loop((unsigned long) (delay_target / 1000)); + } while (InProgress()) { // Test case: drop 17 packets and send 2 out-of-order: @@ -729,14 +739,12 @@ long var_rate = lognormal(mSettings->mUDPRate,variance); if (var_rate < 0) var_rate = 0; - - delay_target = (double) (mSettings->mBufLen * ((kSecs_to_nsecs * kBytes_to_Bits) - / var_rate)); + delay_target = (double) (mSettings->mBufLen * ((kSecs_to_nsecs * kBytes_to_Bits) / var_rate)); time3 = now; } } // store datagram ID into buffer - WritePacketID(reportstruct->packetID++); + WritePacketID(reportstruct->packetID); mBuf_UDP->tv_sec = htonl(reportstruct->packetTime.tv_sec); mBuf_UDP->tv_usec = htonl(reportstruct->packetTime.tv_usec); @@ -754,8 +762,7 @@ else adjust = 1000.0 * lastPacketTime.subUsec(reportstruct->packetTime); - lastPacketTime.set(reportstruct->packetTime.tv_sec, - reportstruct->packetTime.tv_usec); + lastPacketTime.set(reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec); // Since linux nanosleep/busyloop can exceed delay // there are two possible equilibriums // 1) Try to perserve inter packet gap @@ -801,9 +808,10 @@ // report packets reportstruct->packetLen = (unsigned long) currLen; - reportstruct->prevSentTime = prevsend; + reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime; ReportPacket(myReport, reportstruct); - prevsend = reportstruct->packetTime; + reportstruct->packetID++; + myReport->info.ts.prevpacketTime = reportstruct->packetTime; // Insert delay here only if the running delay is greater than 100 usec, // otherwise don't delay and immediately continue with the next tx. if (delay >= 100000) { @@ -819,7 +827,6 @@ * UDP isochronous send loop */ void Client::RunUDPIsochronous (void) { - struct timeval prevsend = myReport->info.ts.startTime; struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; // skip over the UDP datagram (seq no, timestamp) to reach the isoch fields struct client_udp_testhdr *udp_payload = (client_udp_testhdr *) mBuf; @@ -864,7 +871,7 @@ reportstruct->sentTime = reportstruct->packetTime; mBuf_UDP->tv_sec = htonl(reportstruct->packetTime.tv_sec); mBuf_UDP->tv_usec = htonl(reportstruct->packetTime.tv_usec); - WritePacketID(reportstruct->packetID++); + WritePacketID(reportstruct->packetID); // Adjustment for the running delay // o measure how long the last loop iteration took @@ -940,9 +947,10 @@ reportstruct->frameID=frameid; reportstruct->packetLen = (unsigned long) currLen; - reportstruct->prevSentTime = prevsend; + reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime; ReportPacket(myReport, reportstruct); - prevsend = reportstruct->packetTime; + reportstruct->packetID++; + myReport->info.ts.prevpacketTime = reportstruct->packetTime; // Insert delay here only if the running delay is greater than 1 usec, // otherwise don't delay and immediately continue with the next tx. if (delay >= 1000) { @@ -953,7 +961,6 @@ } } FinishTrafficActions(); - DELETE_PTR(framecounter); } // end RunUDPIsoch @@ -1094,14 +1101,12 @@ reportstruct->packetLen = 0; } int do_close = EndJob(myJob, reportstruct); - if (isUDP(mSettings)) { - if (!(isMulticast(mSettings) || isNoUDPfin(mSettings))) { - /* - * For UDP, there is a final handshake between the client and the server, - * do that now (unless requested no to) - */ - AwaitServerFinPacket(); - } + if (isUDP(mSettings) && !isMulticast(mSettings) && !isNoUDPfin(mSettings)) { + /* + * For UDP, there is a final handshake between the client and the server, + * do that now (unless requested no to) + */ + AwaitServerFinPacket(); } if (do_close) { #if HAVE_THREAD_DEBUG @@ -1149,20 +1154,21 @@ rc = read(mySocket, mBuf, MAXUDPBUF); WARN_errno(rc < 0, "read"); if (rc > 0) { + ack_success = 1; #ifdef HAVE_THREAD_DEBUG - thread_debug("UDP client post server relay report", -reportstruct->packetID); + thread_debug("UDP client received server relay report ack (%d)", -reportstruct->packetID); #endif - PostReport(InitServerRelayUDPReport(mSettings, (server_hdr*) ((UDP_datagram*)mBuf + 1))); - ack_success = 1; + if (mSettings->mReportMode != kReport_CSV) + PostReport(InitServerRelayUDPReport(mSettings, (server_hdr*) ((UDP_datagram*)mBuf + 1))); break; - } + } } } - if (!ack_success) + if ((!ack_success) && (mSettings->mReportMode != kReport_CSV)) fprintf(stderr, warn_no_ack, mySocket, (isModeTime(mSettings) ? 10 : 1)); } -// end write_UDP_FIN + void Client::PostNullEvent (void) { assert(myReport!=NULL); // push a nonevent into the packet ring @@ -1200,41 +1206,48 @@ #endif } -void Client::SendFirstPayload (void) { - if (!isCompat(mSettings) && !isConnectOnly(mSettings)) { - int pktlen = 0; - if (myReport && !TimeZero(myReport->info.ts.startTime)) { +int Client::SendFirstPayload (void) { + int pktlen = 0; + if (!isConnectOnly(mSettings)) { + if (myReport && !TimeZero(myReport->info.ts.startTime) && !(mSettings->mMode == kTest_TradeOff)) { reportstruct->packetTime = myReport->info.ts.startTime; } else { now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); } - pktlen = Settings_GenerateClientHdr(mSettings, (void *) mBuf, reportstruct->packetTime); + if (isTxStartTime(mSettings)) { + pktlen = Settings_GenerateClientHdr(mSettings, (void *) mBuf, mSettings->txstart_epoch); + } else { + pktlen = Settings_GenerateClientHdr(mSettings, (void *) mBuf, reportstruct->packetTime); + } if (pktlen > 0) { if (isUDP(mSettings)) { struct client_udp_testhdr *tmphdr = (struct client_udp_testhdr *) mBuf; - WritePacketID(reportstruct->packetID++); + WritePacketID(reportstruct->packetID); tmphdr->seqno_ts.tv_sec = htonl(reportstruct->packetTime.tv_sec); tmphdr->seqno_ts.tv_usec = htonl(reportstruct->packetTime.tv_usec); udp_payload_minimum = pktlen; - } #if HAVE_DECL_MSG_DONTWAIT - reportstruct->packetLen = send(mySocket, mBuf, pktlen, MSG_DONTWAIT); + pktlen = send(mySocket, mBuf, (pktlen > mSettings->mBufLen) ? pktlen : mSettings->mBufLen, MSG_DONTWAIT); #else - reportstruct->packetLen = send(mySocket, mBuf, pktlen, 0); + pktlen = send(mySocket, mBuf, (pktlen > mSettings->mBufLen) ? pktlen : mSettings->mBufLen, 0); #endif - WARN_errno(reportstruct->packetLen < 0, "send_hdr"); - if (reportstruct->packetLen > 0) { - ReportPacket(myReport, reportstruct); - if (!isUDP(mSettings) && isPeerVerDetect(mSettings) && !isServerReverse(mSettings)) { + apply_first_udppkt_delay = true; + } else { +#if HAVE_DECL_MSG_DONTWAIT + pktlen = send(mySocket, mBuf, pktlen, MSG_DONTWAIT); +#else + pktlen = send(mySocket, mBuf, pktlen, 0); +#endif + if (isPeerVerDetect(mSettings) && !isServerReverse(mSettings)) { PeerXchange(); } } - } else { - WARN_errno(1, "send first fail"); + WARN_errno(pktlen < 0, "send_hdr"); } } + return pktlen; } void Client::PeerXchange (void) { diff -ruN old/src/Launch.cpp new/src/Launch.cpp --- old/src/Launch.cpp 2020-10-03 02:42:49.000000000 +0800 +++ new/src/Launch.cpp 2020-11-12 05:22:57.000000000 +0800 @@ -61,6 +61,8 @@ #include "Server.hpp" #include "PerfSocket.hpp" #include "active_hosts.h" +#include "SocketAddr.h" +#include "delay.h" static int fullduplex_startstop_barrier (struct BarrierMutex *barrier) { int rc = 0; @@ -97,7 +99,7 @@ return rc; } int fullduplex_start_barrier (struct BarrierMutex *barrier) { - int rc=fullduplex_startstop_barrier(barrier); + int rc = fullduplex_startstop_barrier(barrier); #ifdef HAVE_THREAD_DEBUG thread_debug("Fullduplex start barrier done on condition %p rc=%d", (void *)&barrier->await, rc); #endif @@ -122,7 +124,6 @@ setReport(thread); // start up a listener theListener = new Listener(thread); - // Start listening theListener->Run(); DELETE_PTR(theListener); @@ -145,6 +146,9 @@ #endif // Start up the server theServer = new Server(thread); + if (isTxStartTime(thread)) { + clock_usleep_abstime(&thread->txstart_epoch); + } // Run the test if (isUDP(thread)) { theServer->RunUDP(); @@ -155,7 +159,9 @@ } static void clientside_client_basic (struct thread_Settings *thread, Client *theClient) { - theClient->my_connect(); + setTransferID(thread, 0); + SockAddr_remoteAddr(thread); + theClient->my_connect(1); #ifdef HAVE_THREAD_DEBUG thread_debug("Client spawn thread basic (sock=%d)", thread->mSock); #endif @@ -166,17 +172,14 @@ if (thread->mThreads > 1) Iperf_push_host(&thread->peer, thread); theClient->StartSynch(); - if (!isCompat(thread)) { - theClient->SendFirstPayload(); - } - if (isTxHoldback(thread)) - theClient->TxDelay(); theClient->Run(); } } static void clientside_client_reverse (struct thread_Settings *thread, Client *theClient) { - theClient->my_connect(); + setTransferID(thread, 0); + SockAddr_remoteAddr(thread); + theClient->my_connect(1); #ifdef HAVE_THREAD_DEBUG thread_debug("Client spawn thread reverse (sock=%d)", thread->mSock); #endif @@ -187,8 +190,8 @@ struct thread_Settings *reverse_client = NULL; Settings_Copy(thread, &reverse_client, 0); FAIL((!reverse_client || !(thread->mSock > 0)), "Reverse test failed to start per thread settings or socket problem", thread); + setTransferID(reverse_client, 1); theClient->StartSynch(); - theClient->SendFirstPayload(); reverse_client->mSock = thread->mSock; // use the same socket for both directions reverse_client->mThreadMode = kMode_Server; setReverse(reverse_client); @@ -213,10 +216,17 @@ static void clientside_client_fullduplex (struct thread_Settings *thread, Client *theClient) { struct thread_Settings *reverse_client = NULL; + setTransferID(thread, 0); + SockAddr_remoteAddr(thread); thread->mFullDuplexReport = InitSumReport(thread, -1, 1); Settings_Copy(thread, &reverse_client, 0); + if ((thread->mThreads > 1) || (!(thread->mThreads > 1) && !isEnhanced(thread))) { + Iperf_push_host(&thread->peer, thread); + Iperf_push_host(&reverse_client->peer, reverse_client); + } assert(reverse_client != NULL); - theClient->my_connect(); + setTransferID(reverse_client, 1); + theClient->my_connect(1); #ifdef HAVE_THREAD_DEBUG thread_debug("Client spawn thread fullduplex (sock=%d)", thread->mSock); #endif @@ -224,12 +234,7 @@ // When -P > 1 then all threads finish connect before starting traffic theClient->BarrierClient(thread->connects_done); if (theClient->isConnected()) { - if (thread->mThreads > 1) { - Iperf_push_host(&thread->peer, thread); - Iperf_push_host(&reverse_client->peer, reverse_client); - } thread->mFullDuplexReport->info.common->socket = thread->mSock; - thread->mFullDuplexReport->info.transferID = thread->mSock; FAIL((!reverse_client || !(thread->mSock > 0)), "Reverse test failed to start per thread settings or socket problem", thread); reverse_client->mSumReport = thread->mSumReport; reverse_client->mSock = thread->mSock; // use the same socket for both directions @@ -239,31 +244,35 @@ } thread_start(reverse_client); if (theClient->StartSynch() != -1) { - theClient->SendFirstPayload(); theClient->Run(); } } } -static void serverside_client_reverse (struct thread_Settings *thread, Client *theClient) { +static void serverside_client_fullduplex (struct thread_Settings *thread, Client *theClient) { #ifdef HAVE_THREAD_DEBUG - thread_debug("Listener spawn client reverse thread (sock=%d)", thread->mSock); + thread_debug("Listener spawn client thread (fd sock=%d)", thread->mSock); #endif + setTransferID(thread, 1); if (theClient->StartSynch() != -1) { - if (isTripTime(thread) || isIsochronous(thread)) - theClient->SendFirstPayload(); theClient->Run(); } } -static void serverside_client_fullduplex(struct thread_Settings *thread, Client *theClient) { +static void serverside_client_bidir (struct thread_Settings *thread, Client *theClient) { #ifdef HAVE_THREAD_DEBUG - thread_debug("Listener spawn client fullduplex thread (sock=%d)", thread->mSock); + thread_debug("Listener spawn client thread (bidir sock=%d)", thread->mSock); #endif - if (theClient->StartSynch() != -1) { - if (isTripTime(thread) || isIsochronous(thread)) - theClient->SendFirstPayload(); - theClient->Run(); + setTransferID(thread, 1); + SockAddr_remoteAddr(thread); + unsetNoSettReport(thread); + setReport(thread); + theClient->my_connect(1); + if (theClient->isConnected()) { + Iperf_push_host(&thread->peer, thread); + if (theClient->StartSynch() != -1) { + theClient->Run(); + } } } @@ -291,6 +300,7 @@ thread_setscheduler(thread); #endif // start up the client + setTransferID(thread, 0); theClient = new Client(thread); // let the reporter thread go first in the case of -P greater than 1 Condition_Lock(reporter_state.await); @@ -314,14 +324,12 @@ _exit(0); } } else { - // These are the server or listener side spawning of clients - if (!isFullDuplex(thread)) { - serverside_client_reverse(thread, theClient); - } else if (isFullDuplex(thread)) { - serverside_client_fullduplex(thread, theClient); + if (thread->mMode != kTest_Normal) { + setCompat(thread); + // These are the server or listener side spawning of clients + serverside_client_bidir(thread, theClient); } else { - fprintf(stdout, "Program error in server side client_spawn"); - _exit(0); + serverside_client_fullduplex(thread, theClient); } } // Call the client's destructor @@ -345,7 +353,6 @@ setReport(clients); // See if we need to start a listener as well Settings_GenerateListenerSettings(clients, &next); - #ifdef HAVE_THREAD if (next != NULL) { // We have threads and we need to start a listener so @@ -373,51 +380,3 @@ } #endif } - -#ifdef WRITEACK_DONE -/* - * writeack_server_spawn - */ -void writeack_server_spawn(struct thread_Settings *thread) { - WriteAck *theServerAck = NULL; -#ifdef HAVE_THREAD_DEBUG - thread_debug("Write ack server spawn settings=%p sock=%d", (void *) thread, thread->mSock); -#endif - // set traffic thread to realtime if needed -#if HAVE_SCHED_SETSCHEDULER - thread_setscheduler(thread); -#endif - // Start up the server - theServerAck = new WriteAck(thread); - // Run the thread - theServerAck->RunServer(); - DELETE_PTR( theServerAck); -} - -/* - * writeack_client_spawn - */ -void writeack_client_spawn(struct thread_Settings *thread) { - Server *theServer = NULL; - WriteAck *theClientAck = NULL; -#ifdef HAVE_THREAD_DEBUG - thread_debug("Write ack client spawn settings=%p sock=%d", (void *) thread, thread->mSock); -#endif -#if HAVE_SCHED_SETSCHEDULER - // set traffic thread to realtime if needed - thread_setscheduler(thread); -#endif - // the client side server doesn't do write acks - unsetWriteAck(thread); - // Start up the server - theServer = new Server( thread ); - // Run the test - if ( isUDP( thread ) ) { - theServer->RunUDP(); - } else { - theServer->RunTCP(); - } - DELETE_PTR( theServer); -} - -#endif diff -ruN old/src/Listener.cpp new/src/Listener.cpp --- old/src/Listener.cpp 2020-10-03 04:28:03.000000000 +0800 +++ new/src/Listener.cpp 2020-11-11 06:18:07.000000000 +0800 @@ -87,6 +87,7 @@ #include #endif /* ------------------------------------------------------------------- + * Stores local hostname and socket info. * ------------------------------------------------------------------- */ @@ -102,7 +103,8 @@ */ mSettings = inSettings; // alloc and initialize the buffer (mBuf) used for test messages in the payload - mBuf = new char[(mSettings->mBufLen > MINMBUFALLOCSIZE) ? mSettings->mBufLen : MINMBUFALLOCSIZE]; // defined in payloads.h + mBufLen = (mSettings->mBufLen > MINMBUFALLOCSIZE) ? mSettings->mBufLen : MINMBUFALLOCSIZE; + mBuf = new char[mBufLen]; // defined in payloads.h FAIL_errno(mBuf == NULL, "No memory for buffer\n", mSettings); } // end Listener @@ -184,26 +186,33 @@ // UDP needs a new listen per every new socket my_listen(); // This will set ListenSocket to a new sock fd } - // Use a select() with a timeout if -t is set - if (mMode_Time) { + // Use a select() with a timeout if -t is set or if this is a v1 -r or -d test + fd_set set; + if ((mMode_Time) || isCompat(mSettings)) { // Hang a select w/timeout on the listener socket struct timeval timeout; timeout.tv_sec = mSettings->mAmount / 100; timeout.tv_usec = (mSettings->mAmount % 100) * 10000; - fd_set set; + if (isTxStartTime(mSettings)) { + now.setnow(); + long adjsecs = (mSettings->txstart_epoch.tv_sec - now.getSecs()); + if (adjsecs > 0) + timeout.tv_sec += adjsecs + 1; + } FD_ZERO(&set); FD_SET(ListenSocket, &set); - if (select(ListenSocket + 1, &set, NULL, NULL, &timeout) > 0) { - if (!setsock_blocking(mSettings->mSock, 0)) { - WARN(1, "Failed setting socket to non-blocking mode"); - } - } else { + if (!(select(ListenSocket + 1, &set, NULL, NULL, &timeout) > 0)) { #ifdef HAVE_THREAD_DEBUG thread_debug("Listener select timeout"); #endif - continue; + if (isCompat(mSettings)) { + fprintf(stderr, "ERROR: expected reverse connect did not occur\n"); + break; + } else + continue; } - } else if (!setsock_blocking(mSettings->mSock, 1)) { + } + if (!setsock_blocking(mSettings->mSock, 1)) { WARN(1, "Failed setting socket to blocking mode"); } // Instantiate another settings object to be used by the server thread @@ -223,8 +232,9 @@ Settings_Destroy(server); continue; } + #ifdef HAVE_THREAD_DEBUG - thread_debug("Listener thread accepted server sock=%d", server->mSock); + thread_debug("Listener thread accepted server sock=%d transferID", server->mSock, server->mTransferID); #endif // Decrement the -P counter, commonly usd to kill the listener // after one test, i.e. -s -P 1 @@ -263,84 +273,96 @@ // the first message to convey test request and test settings information. This flag // is also used for threads that are children so-to-speak, e.g. a -d or -r client, // which cannot have test flags otherwise there would be "test setup recursion" - if (!isCompat(mSettings)) { - // Time to read the very first packet received (per UDP) or the test flags (TCP) - // to get the client's requested test information. - // Note 1: It's important to know that this will also populate mBuf with - // enough information for the listener to perform test info exchange later in the code - // Note 2: The mBuf read is a peek so the server's traffic thread started later - // will also process the first message from an accounting perspective. - // This is required for accurate traffic statistics - if (apply_client_settings(server) <= 0) { - if (isConnectionReport(server) && !isSumOnly(server)) { - PostReport(InitConnectionReport(server, 0)); - } + // Time to read the very first packet received (per UDP) or the test flags (TCP) + // to get the client's requested test information. + // Note 1: It's important to know that this will also populate mBuf with + // enough information for the listener to perform test info exchange later in the code + // Note 2: The mBuf read is a peek so the server's traffic thread started later + // will also process the first message from an accounting perspective. + // This is required for accurate traffic statistics + if (!isCompat(server) && !apply_client_settings(server)) { + if (isConnectionReport(server) && !isSumOnly(server)) { + PostReport(InitConnectionReport(server, 0)); + } + Iperf_remove_host(&server->peer); + if (DecrSumReportRefCounter(server->mSumReport) <= 0) { + FreeSumReport(server->mSumReport); + } + close(server->mSock); + assert(server != mSettings); + Settings_Destroy(server); + continue; + } + // server settings flags should now be set per the client's first message exchange + // so the server setting's flags per the client can now be checked + if (isUDP(server) && !isCompat(mSettings) && (isL2LengthCheck(mSettings) || isL2LengthCheck(server))) { + if (!L2_setup(server, server->mSock)) { + // Requested L2 testing but L2 setup failed Iperf_remove_host(&server->peer); if (DecrSumReportRefCounter(server->mSumReport) <= 0) { FreeSumReport(server->mSumReport); } - if (!isUDP(server)) - close(server->mSock); assert(server != mSettings); Settings_Destroy(server); continue; } - // server settings flags should now be set per the client's first message exchange - // so the server setting's flags per the client can now be checked - if (isUDP(server) && (isL2LengthCheck(mSettings) || isL2LengthCheck(server))) { - if (!L2_setup(server, server->mSock)) { - // Requested L2 testing but L2 setup failed - Iperf_remove_host(&server->peer); - if (DecrSumReportRefCounter(server->mSumReport) <= 0) { - FreeSumReport(server->mSumReport); - } - assert(server != mSettings); - Settings_Destroy(server); - continue; - } - } - if (isConnectionReport(server) && !isSumOnly(server)) { - PostReport(InitConnectionReport(server, 0)); - } - // Read any more test settings and test values (not just the flags) and instantiate - // any settings objects for client threads (e.g. bidir or full duplex) - // This will set the listener_client_settings to NULL if - // there is no need for the Listener to start a client - // - // Note: the packet payload pointer for this information has different - // offsets per TCP or UDP. Basically, TCP starts at byte 0 but UDP - // has to skip over the UDP seq no, etc. - // - if (isFullDuplex(server) || isServerReverse(server) || (server->mMode != kTest_Normal)) { - thread_Settings *listener_client_settings = NULL; - // read client header for reverse settings - Settings_GenerateClientSettings(server, &listener_client_settings, mBuf); - if (listener_client_settings) { - if (isFullDuplex(listener_client_settings) || isReverse(listener_client_settings)) - Iperf_push_host(&listener_client_settings->peer, listener_client_settings); - if (isFullDuplex(server)) { - server->mFullDuplexReport = InitSumReport(server, server->mSock, 1); - listener_client_settings->mFullDuplexReport = server->mFullDuplexReport; -#if HAVE_THREAD_DEBUG - thread_debug("BiDir report client=%p/%p server=%p/%p", (void *) listener_client_settings, (void *) listener_client_settings->mFullDuplexReport, (void *) server, (void *) server->mFullDuplexReport); -#endif + } + // Force compat mode to use 64 bit seq numbers + if (isUDP(server) && isCompat(mSettings)) { + setSeqNo64b(server); + } + + setTransferID(server, isCompat(mSettings)); + if (isConnectionReport(server) && !isSumOnly(server)) { + PostReport(InitConnectionReport(server, 0)); + } + // Read any more test settings and test values (not just the flags) and instantiate + // any settings objects for client threads (e.g. bidir or full duplex) + // This will set the listener_client_settings to NULL if + // there is no need for the Listener to start a client + // + // Note: the packet payload pointer for this information has different + // offsets per TCP or UDP. Basically, TCP starts at byte 0 but UDP + // has to skip over the UDP seq no, etc. + // + if (!isCompat(server) && !isCompat(mSettings) && \ + (isFullDuplex(server) || isServerReverse(server) || (server->mMode != kTest_Normal))) { + thread_Settings *listener_client_settings = NULL; + // read client header for reverse settings + Settings_GenerateClientSettings(server, &listener_client_settings, mBuf); + if (listener_client_settings) { + if (server->mMode != kTest_Normal) + listener_client_settings->mTransferID = 0; + setTransferID(listener_client_settings, 1); + if (isFullDuplex(listener_client_settings) || isReverse(listener_client_settings)) + Iperf_push_host(&listener_client_settings->peer, listener_client_settings); + if (isFullDuplex(server)) { + assert(server->mSumReport != NULL); + if (!server->mSumReport->sum_fd_set) { // Reset the sum output routine for the server sum report // now that it's know to be full duplex. This wasn't known // during accept() - assert(server->mSumReport != NULL); - SetSumHandlers(server, server->mSumReport, 0); - server->runNow = listener_client_settings; - } else if (server->mMode != kTest_Normal) { - client_init(listener_client_settings); - if (listener_client_settings->mMode == kTest_DualTest) { + SetSumHandlers(server, server->mSumReport); + server->mSumReport->sum_fd_set = 1; + } + server->mFullDuplexReport = InitSumReport(server, server->mSock, 1); + listener_client_settings->mFullDuplexReport = server->mFullDuplexReport; +#if HAVE_THREAD_DEBUG + thread_debug("FullDuplex report client=%p/%p server=%p/%p", (void *) listener_client_settings, (void *) listener_client_settings->mFullDuplexReport, (void *) server, (void *) server->mFullDuplexReport); +#endif + server->runNow = listener_client_settings; + } else if (server->mMode != kTest_Normal) { +#if HAVE_THREAD_DEBUG + thread_debug("V1 test (-d or -r) sum report client=%p/%p server=%p/%p", (void *) listener_client_settings, (void *) listener_client_settings->mFullDuplexReport, (void *) server, (void *) server->mFullDuplexReport); +#endif + if (listener_client_settings->mMode == kTest_DualTest) { #ifdef HAVE_THREAD - server->runNow = listener_client_settings; + server->runNow = listener_client_settings; #else - server->runNext = listener_client_settings; + server->runNext = listener_client_settings; #endif - } else { - server->runNext = listener_client_settings; - } + } else { + server->runNext = listener_client_settings; } } } @@ -431,11 +453,6 @@ if (SockAddr_isMulticast(&mSettings->local)) { #ifdef HAVE_MULTICAST my_multicast_join(); -#if HAVE_DECL_IP_MULTICAST_ALL - int mc_all = 0; - rc = setsockopt(ListenSocket, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all)); - WARN_errno(rc == SOCKET_ERROR, "ip_multicast_all"); -#endif #else fprintf(stderr, "Multicast not supported"); #endif // HAVE_MULTICAST @@ -492,6 +509,11 @@ int rc = setsockopt(ListenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)); WARN_errno(rc == SOCKET_ERROR, "multicast join"); +#if HAVE_DECL_IP_MULTICAST_ALL + int mc_all = 0; + rc = setsockopt(ListenSocket, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all)); + WARN_errno(rc == SOCKET_ERROR, "ip_multicast_all"); +#endif } else { #ifdef HAVE_IPV6_MULTICAST struct ipv6_mreq mreq; @@ -797,7 +819,7 @@ // The INVALID socket is used to keep the while loop going server->mSock = INVALID_SOCKET; // Hang a 0 byte read with MSG_PEEK to get the sock addr struct populated - rc = recvfrom(ListenSocket, NULL, 0, MSG_PEEK, \ + rc = recvfrom(ListenSocket, mBuf, mBufLen, MSG_PEEK, \ (struct sockaddr*) &server->peer, &server->size_peer); #if HAVE_THREAD_DEBUG { @@ -877,26 +899,25 @@ // Read deep enough into the packet to get the client settings // Read the headers but don't pull them from the queue in order to // preserve server thread accounting, i.e. these exchanges will -// be part of traffic accounting -int Listener::apply_client_settings (thread_Settings *server) { +// be part of traffic accounting. Return false if it's determined +// this traffic shouldn't be accepted for a test run +// Description of bits and fields is in include/payloads.h +bool Listener::apply_client_settings (thread_Settings *server) { assert(server != NULL); assert(mBuf != NULL); - int n, peeklen; - uint32_t flags = 0; - uint16_t upperflags = 0; - int rc = 1; + bool rc; + // Set the receive timeout for the very first read based upon the -t // and not -i. - #ifdef WIN32 - int sorcvtimer = 2000; + int sorcvtimer = 4000; DWORD timeout; if (isServerModeTime(server)) { // Windows SO_RCVTIMEO uses ms timeout = (double) sorcvtimer / 1e3; } #else - struct timeval timeout = {2, 0}; + struct timeval timeout = {4, 0}; if (isServerModeTime(server)) { timeout.tv_sec = server->mAmount / 100; timeout.tv_usec = (server->mAmount % 100) * 10000; @@ -910,34 +931,32 @@ server->mMode = kTest_Normal; if (isUDP(server)) { - n = recvn(server->mSock, mBuf, sizeof(uint32_t) + sizeof(struct UDP_datagram), MSG_PEEK); - if (n == 0) { - //peer closed the socket, with no writes e.g. a connect-only test - return -1; + rc = apply_client_settings_udp(server); + } else { + rc = apply_client_settings_tcp(server); + } + return rc; +} + +bool Listener::apply_client_settings_udp (thread_Settings *server) { + struct client_udp_testhdr *hdr = (struct client_udp_testhdr *) mBuf; + uint32_t flags = ntohl(hdr->base.flags); + uint16_t upperflags = 0; + if (flags & HEADER_SEQNO64B) { + setSeqNo64b(server); + } + if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND)) { + if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { + if (flags & RUN_NOW) + server->mMode = kTest_DualTest; + else + server->mMode = kTest_TradeOff; } - FAIL_errno(n != (sizeof(uint32_t) + sizeof(struct UDP_datagram)), "read udp flags", server); - struct client_udp_testhdr *hdr = (struct client_udp_testhdr *) mBuf; - flags = ntohl(hdr->base.flags); - if (flags & HEADER_SEQNO64B) { - setSeqNo64b(server); - } - // figure out the length of the test header - if ((peeklen = Settings_ClientHdrPeekLen(flags) + sizeof(struct UDP_datagram)) > 0) { - // read the test settings passed to the server by the client - n = recvn(server->mSock, mBuf, peeklen, MSG_PEEK); - FAIL_errno((n < peeklen), "read udp test hdr", server); - if (flags & HEADER_VERSION1) { - if (flags & RUN_NOW) - server->mMode = kTest_DualTest; - else - server->mMode = kTest_TradeOff; - } - if (flags & (HEADER_EXTEND | HEADER_VERSION2)) { - upperflags = htons(hdr->extend.upperflags); - server->mTOS = ntohs(hdr->extend.tos); - server->peer_version_u = ntohl(hdr->extend.version_u); - server->peer_version_l = ntohl(hdr->extend.version_l); - } + if (flags & HEADER_EXTEND) { + upperflags = htons(hdr->extend.upperflags); + server->mTOS = ntohs(hdr->extend.tos); + server->peer_version_u = ntohl(hdr->extend.version_u); + server->peer_version_l = ntohl(hdr->extend.version_l); if (flags & HEADER_UDPTESTS) { // Handle stateless flags if (upperflags & HEADER_ISOCH) { @@ -955,84 +974,124 @@ setNoUDPfin(server); } } - if (flags & HEADER_VERSION2) { - if (upperflags & HEADER_FULLDUPLEX) { - setFullDuplex(server); - setServerReverse(server); + if (upperflags & HEADER_EPOCH_START) { + server->txstart_epoch.tv_sec = ntohl(hdr->start_fq.start_tv_sec); + server->txstart_epoch.tv_usec = ntohl(hdr->start_fq.start_tv_usec); + Timestamp now; + if ((abs(now.getSecs() - server->txstart_epoch.tv_sec)) > (MAXDIFFTXSTART + 1)) { + fprintf(stdout,"WARN: ignore --txstart-time because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTXSTART); + unsetTxStartTime(server); + } else { + setTxStartTime(server); } - if (upperflags & HEADER_REVERSE) { - server->mThreadMode=kMode_Client; - setServerReverse(server); - setNoUDPfin(server); - unsetReport(server); + } + if (upperflags & HEADER_TRIPTIME) { + server->triptime_start.tv_sec = ntohl(hdr->start_fq.start_tv_sec); + server->triptime_start.tv_usec = ntohl(hdr->start_fq.start_tv_usec); + Timestamp now; + if (!isTxStartTime(server) && ((abs(now.getSecs() - server->triptime_start.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { + fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + } else { + setTripTime(server); + setEnhanced(server); } - if (upperflags & HEADER_TRIPTIME) { - server->triptime_start.tv_sec = ntohl(hdr->start_fq.start_tv_sec); - server->triptime_start.tv_usec = ntohl(hdr->start_fq.start_tv_usec); - Timestamp now; - if ((abs(now.getSecs() - server->triptime_start.tv_sec)) > MAXDIFFTIMESTAMPSECS) { - fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); - } else { - setTripTime(server); - setEnhanced(server); - } - } } } - } else { - n = recvn(server->mSock, mBuf, sizeof(uint32_t), MSG_PEEK); - if (n == 0) { - //peer closed the socket, with no writes e.g. a connect-only test - return -1; + if (flags & HEADER_VERSION2) { + upperflags = htons(hdr->extend.upperflags); + if (upperflags & HEADER_FULLDUPLEX) { + setFullDuplex(server); + setServerReverse(server); + } + if (upperflags & HEADER_REVERSE) { + server->mThreadMode=kMode_Client; + setServerReverse(server); + setNoUDPfin(server); + unsetReport(server); + } } + } + return true; +} +bool Listener::apply_client_settings_tcp (thread_Settings *server) { + bool rc; + int n = recvn(server->mSock, mBuf, sizeof(uint32_t), MSG_PEEK); + if (n == 0) { + //peer closed the socket, with no writes e.g. a connect-only test + rc = false; + } else { FAIL_errno((n < (int) sizeof(uint32_t)), "read tcp flags", server); + rc = true; struct client_tcp_testhdr *hdr = (struct client_tcp_testhdr *) mBuf; - flags = ntohl(hdr->base.flags); - // figure out the length of the test header - if ((peeklen = Settings_ClientHdrPeekLen(flags)) > 0) { - // read the test settings passed to the server by the client - n = recvn(server->mSock, mBuf, peeklen, MSG_PEEK); - FAIL_errno((n < peeklen), "read tcp test info", server); - struct client_tcp_testhdr *hdr = (struct client_tcp_testhdr *) mBuf; - if (flags & HEADER_VERSION1) { - if (flags & RUN_NOW) - server->mMode = kTest_DualTest; - else - server->mMode = kTest_TradeOff; - } - if (flags & (HEADER_EXTEND | HEADER_VERSION2)) { - upperflags = htons(hdr->extend.upperflags); - server->mTOS = ntohs(hdr->extend.tos); - server->peer_version_u = ntohl(hdr->extend.version_u); - server->peer_version_l = ntohl(hdr->extend.version_l); - if (upperflags & HEADER_TRIPTIME) { - setTripTime(server); - setEnhanced(server); + uint32_t flags = ntohl(hdr->base.flags); + uint16_t upperflags = 0; + int peeklen; + if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND)) { + // figure out the length of the test header + if ((peeklen = Settings_ClientHdrPeekLen(flags)) > 0) { + // read the test settings passed to the server by the client + int n = recvn(server->mSock, mBuf, peeklen, MSG_PEEK); + FAIL_errno((n < peeklen), "read tcp test info", server); + struct client_tcp_testhdr *hdr = (struct client_tcp_testhdr *) mBuf; + if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { + if (flags & RUN_NOW) + server->mMode = kTest_DualTest; + else + server->mMode = kTest_TradeOff; } - if (upperflags & HEADER_ISOCH) { - setIsochronous(server); - } - if (flags & HEADER_VERSION2) { - if (upperflags & HEADER_FULLDUPLEX) { - setFullDuplex(server); - setServerReverse(server); + if (flags & HEADER_EXTEND) { + upperflags = htons(hdr->extend.upperflags); + server->mTOS = ntohs(hdr->extend.tos); + server->peer_version_u = ntohl(hdr->extend.version_u); + server->peer_version_l = ntohl(hdr->extend.version_l); + if (upperflags & HEADER_ISOCH) { + setIsochronous(server); } - if (upperflags & HEADER_REVERSE) { - server->mThreadMode=kMode_Client; - setServerReverse(server); + if (upperflags & HEADER_EPOCH_START) { + server->txstart_epoch.tv_sec = ntohl(hdr->start_fq.start_tv_sec); + server->txstart_epoch.tv_usec = ntohl(hdr->start_fq.start_tv_usec); + Timestamp now; + if ((abs(now.getSecs() - server->txstart_epoch.tv_sec)) > (MAXDIFFTXSTART + 1)) { + fprintf(stdout,"WARN: ignore --txstart-time because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTXSTART); + unsetTxStartTime(server); + } else { + setTxStartTime(server); + } } + if (upperflags & HEADER_TRIPTIME) { + server->skip = peeklen; + server->triptime_start.tv_sec = ntohl(hdr->start_fq.start_tv_sec); + server->triptime_start.tv_usec = ntohl(hdr->start_fq.start_tv_usec); + Timestamp now; + if (!isTxStartTime(server) && ((abs(now.getSecs() - server->triptime_start.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { + fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + } else { + setTripTime(server); + setEnhanced(server); + } + } + if (flags & HEADER_VERSION2) { + if (upperflags & HEADER_FULLDUPLEX) { + setFullDuplex(server); + setServerReverse(server); + } + if (upperflags & HEADER_REVERSE) { + server->mThreadMode=kMode_Client; + setServerReverse(server); + } + } } } + // Handle case that requires an ack back to the client + // Signaled by not UDP (only supported by TCP) + // and either 2.0.13 flags or the newer 2.0.14 flag of + // V2PEERDETECT + if (!isUDP(server) && !isCompat(mSettings) && \ + ((!(flags & HEADER_VERSION2) && (flags & HEADER_EXTEND)) || \ + (flags & HEADER_V2PEERDETECT))) { + client_test_ack(server); + } } - } - // Handle case that requires an ack back to the client - // Signaled by not UDP (only supported by TCP) - // and either 2.0.13 flags or the newer 2.0.14 flag of - // V2PEERDETECT - if (!isUDP(server) && \ - ((!(flags & HEADER_VERSION2) && (flags & HEADER_EXTEND)) || \ - (flags & HEADER_V2PEERDETECT))) { - client_test_ack(server); } return rc; } diff -ruN old/src/Locale.c new/src/Locale.c --- old/src/Locale.c 2020-10-03 02:42:49.000000000 +0800 +++ new/src/Locale.c 2020-11-05 09:06:26.000000000 +0800 @@ -70,7 +70,7 @@ \n\ Client/Server:\n\ -b, --bandwidth #[kmgKMG | pps] bandwidth to send at in bits/sec or packets per second\n\ - -e, --enhancedreports use enhanced reporting giving more tcp/udp and traffic information\n\ + -e, --enhanced use enhanced reporting giving more tcp/udp and traffic information\n\ -f, --format [kmgKMG] format to report: Kbits, Mbits, KBytes, MBytes\n\ -i, --interval # seconds between periodic bandwidth reports\n\ -l, --len #[kmKM] length of buffer in bytes to read or write (Defaults: TCP=128K, v4 UDP=1470, v6 UDP=1450)\n\ @@ -87,9 +87,13 @@ -M, --mss # set TCP maximum segment size (MTU - 40 bytes)\n\ -N, --nodelay set TCP no delay, disabling Nagle's Algorithm\n\ -S, --tos # set the socket's IP_TOS (byte) field\n\ + -Z, --tcp-congestion set TCP congestion control algorithm (Linux only)\n\ \n\ Server specific:\n\ -s, --server run in server mode\n\ + -1, --singleclient run one server at a time\n\ + -b, --bandwidth #[kmgKMG] bandwidth to read at in bits/sec or packets per second\n\ + --histograms enable latency histograms\n\ -t, --time # time in seconds to listen for new connections as well as to receive traffic (default not set)\n\ --udp-histogram #,# enable UDP latency histogram(s) with bin width and count, e.g. 1,1000=1(ms),1000(bins)\n\ -B, --bind [%] bind to multicast address and optional device\n\ @@ -106,14 +110,21 @@ \n\ Client specific:\n\ -c, --client run in client mode, connecting to \n\ + --connect-only run a connect only test\n\ -d, --dualtest Do a bidirectional test simultaneously (multiple sockets)\n\ - --fullduplex run fullduplexectional test over same socket (full duplex mode)\n\ + --fq-rate #[kmgKMG] bandwidth to socket pacing\n\ + --full-duplex run full duplex test using same socket\n\ --ipg set the the interpacket gap (milliseconds) for packets within an isochronous frame\n\ --isochronous :, send traffic in bursts (frames - emulate video traffic)\n\ --incr-dstip Increment the destination ip with parallel (-P) traffic threads\n\ + --no-connect-sync No sychronization after connect when -P or parallel traffic threads\n\ + --no-udp-fin No final server to client stats at end of UDP test\n\ -n, --num #[kmgKMG] number of bytes to transmit (instead of -t)\n\ -r, --tradeoff Do a fullduplexectional test individually\n\ -t, --time # time in seconds to transmit for (default 10 secs)\n\ + --trip-times enable end to end measurements (requires client and server clock sync)\n\ + --txdelay-time time in seconds to hold back after connect and before first write\n\ + --txstart-time unix epoch time to schedule first write and start traffic\n\ -B, --bind [ | ] bind ip (and optional port) from which to source traffic\n\ -F, --fileinput input the data to be transmitted from a file\n\ -I, --stdin input the data to be transmitted from stdin\n\ @@ -125,10 +136,10 @@ " -R Remove the windows service\n" " --reverse reverse the test (client receives, server sends)\n" #endif -" -T, --ttl # time-to-live, for multicast (default 1)\n\ +" -S, --tos IP DSCP or tos settings\n\ + -T, --ttl # time-to-live, for multicast (default 1)\n\ -V, --ipv6_domain Set the domain to IPv6 (send packets over IPv6)\n\ -X, --peer-detect perform server version detection and version exchange\n\ - -Z, --linux-congestion set TCP congestion control algorithm (Linux only)\n\ \n\ Miscellaneous:\n\ -x, --reportexclude [CDMSV] exclude C(connection) D(data) M(multicast) S(settings) V(server) reports\n\ @@ -240,7 +251,7 @@ "[ ID] Interval Transfer Bandwidth\n"; const char report_bw_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec\n"; const char report_sum_bw_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec\n"; @@ -250,8 +261,9 @@ const char report_bw_jitter_loss_header[] = "[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams\n"; + const char report_bw_jitter_loss_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%)\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%)\n"; const char report_sum_bw_jitter_loss_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%)\n"; @@ -262,7 +274,7 @@ /* ------------------------------------------------------------------- * Enhanced reports (per -e) * ------------------------------------------------------------------- */ -const char report_bw_sumcnt_header[] = +const char report_sumcnt_bw_header[] = "[SUM-cnt] Interval Transfer Bandwidth\n"; const char client_report_epoch_start[] = @@ -278,7 +290,7 @@ "Read buffer size"; const char report_bw_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec\n"; const char report_sum_bw_enhanced_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec\n"; @@ -287,65 +299,68 @@ "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Reads=Dist\n"; const char report_bw_read_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; +const char report_sumcnt_bw_read_enhanced_header[] = +"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Reads=Dist\n"; + +const char report_sumcnt_bw_read_enhanced_format[] = +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; + const char report_bw_read_enhanced_netpwr_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Burst Latency avg/min/max/stdev (cnt/size) inP NetPwr Reads=Dist\n"; const char report_bw_read_enhanced_netpwr_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f/%6.3f/%6.3f/%6.3f ms (%d/%d) %s %4.2f %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f/%6.3f/%6.3f/%6.3f ms (%d/%d) %s %s %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; const char report_sum_bw_read_enhanced_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; const char report_triptime_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " trip-time (3WHS done->fin+finack) = %4.4f sec\n"; +"%s" IPERFTimeFrmt " trip-time (3WHS done->fin+finack) = %4.4f sec\n"; #ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS const char report_bw_write_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT NetPwr\n"; -const char report_bw_write_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d %8dK/%u us %4.2f\n"; - -const char report_bw_write_enhanced_nocwnd_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d NA/%u us %4.2f\n"; - const char report_sum_bw_write_enhanced_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d%10d\n"; -const char report_sumcnt_bw_write_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d%10d\n"; +const char report_bw_write_enhanced_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d %8dK/%u us %s\n"; +const char report_bw_write_enhanced_nocwnd_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d NA/%u us %s\n"; + #else const char report_bw_write_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err\n"; const char report_bw_write_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d\n"; const char report_sum_bw_write_enhanced_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d\n"; -const char report_sumcnt_bw_write_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d\n"; - #endif -const char report_bw_write_sumcnt_enhanced_header[] = +const char report_sumcnt_bw_write_enhanced_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry\n"; +const char report_sumcnt_bw_write_enhanced_format[] = +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d\n"; + const char report_bw_pps_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS\n"; const char report_bw_pps_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d %8.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %8.0f pps\n"; const char report_bw_pps_enhanced_isoch_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS frames:tx/missed/slips\n"; const char report_bw_pps_enhanced_isoch_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d %8.0f pps %3d/%d/%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %8.0f pps %3d/%d/%d\n"; const char report_sum_bw_pps_enhanced_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %d/%d %8.0f pps\n"; @@ -354,21 +369,21 @@ "[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams PPS\n"; const char report_bw_jitter_loss_pps_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %8.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %8.0f pps\n"; const char report_bw_jitter_loss_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ Latency avg/min/max/stdev PPS inP NetPwr\n"; const char report_bw_jitter_loss_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %6.3f/%6.3f/%6.3f/%6.3f ms %4.0f pps %s %4.2f\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %6.3f/%6.3f/%6.3f/%6.3f ms %4.0f pps %s %s\n"; const char report_bw_jitter_loss_enhanced_isoch_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ Latency avg/min/max/stdev PPS inP NetPwr Frames/Lost\n"; const char report_bw_jitter_loss_enhanced_isoch_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %6.3f/%6.3f/%6.3f/%6.3f ms %4.0f pps %s %4.2f %3d/%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %6.3f/%6.3f/%6.3f/%6.3f ms %4.0f pps %s %s %3d/%d\n"; const char report_sum_bw_jitter_loss_enhanced_format[] = "[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %4.0f pps\n"; @@ -377,7 +392,7 @@ "[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %4.0f pps\n"; const char report_bw_jitter_loss_suppress_enhanced_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) -/-/-/- ms %4.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) -/-/-/- ms %4.0f pps\n"; /* * Frame interval reports @@ -389,10 +404,10 @@ Latency avg/min/max/stdev PPS inP NetPwr\n"; const char report_frame_jitter_loss_enhanced_format[] = -"[%3d] " IPERFFTimeFrmt "(%0.4f) sec %ss %ss/sec %4" PRIdMAX " %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %6.3f/%6.3f/%6.3f/%6.3f ms %4.0f pps %2.0f pkts %4.2f\n"; +"%s" IPERFFTimeFrmt "(%0.4f) sec %ss %ss/sec %4" PRIdMAX " %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) %6.3f/%6.3f/%6.3f/%6.3f ms %4.0f pps %2.0f pkts %s\n"; const char report_frame_jitter_loss_suppress_enhanced_format[] = -"[%3d] " IPERFTimeFrmt "(%0.4f) sec %ld %ss %ss/sec %4" PRIdMAX " %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) -/-/-/- ms %4.0f pps\n"; +"%s" IPERFTimeFrmt "(%0.4f) sec %ld %ss %ss/sec %4" PRIdMAX " %6.3f ms %4" PRIdMAX "/%5" PRIdMAX " (%.2g%%) -/-/-/- ms %4.0f pps\n"; const char report_frame_tcp_enhanced_header[] = "[ ID] Interval(f-transit)" IPERFFTimeSpace "Transfer Bandwidth FrameID\n"; @@ -401,7 +416,7 @@ * Fullduplex reports * ------------------------------------------------------------------- */ const char report_bw_sum_fullduplex_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec\n"; const char report_bw_sum_fullduplex_enhanced_format[] = "[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec\n"; @@ -409,8 +424,14 @@ const char report_udp_fullduplex_header[] = "[ ID] Interval Transfer Bandwidth Datagrams PPS\n"; +const char report_sumcnt_udp_fullduplex_header[] = +"[SUM-cnt] Interval Transfer Bandwidth Datagrams PPS\n"; + +const char report_sumcnt_udp_fullduplex_format[] = +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %5" PRIdMAX "%8.0f pps\n"; + const char report_udp_fullduplex_format[] = -"[%3d] " IPERFTimeFrmt " sec %ss %ss/sec %5" PRIdMAX "%8.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %5" PRIdMAX "%8.0f pps\n"; const char report_udp_fullduplex_enhanced_format[] = "[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec %5" PRIdMAX "%8.0f pps\n"; @@ -422,10 +443,13 @@ * Misc reports * ------------------------------------------------------------------- */ const char report_outoforder[] = -"[%3d] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +const char report_sumcnt_outoforder[] = +"[SUM-%d] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; + const char report_l2statistics[] = -"[%3d] " IPERFTimeFrmt " sec L2 processing detected errors, total(length/checksum/unknown) = %" PRIdMAX "(%" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX ")\n"; +"%s" IPERFTimeFrmt " sec L2 processing detected errors, total(length/checksum/unknown) = %" PRIdMAX "(%" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX ")\n"; const char report_sum_outoforder[] = "[SUM] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; @@ -458,9 +482,11 @@ "%s,%u,%s,%u"; const char report_l2length_error[] = -"[%3d] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; - +/*------------------------------------------------------------------- + * CSV outputs + *------------------------------------------------------------------*/ const char reportCSV_bw_format[] = "%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX "\n"; diff -ruN old/src/PerfSocket.cpp new/src/PerfSocket.cpp --- old/src/PerfSocket.cpp 2020-10-03 02:42:49.000000000 +0800 +++ new/src/PerfSocket.cpp 2020-11-05 09:06:26.000000000 +0800 @@ -77,11 +77,11 @@ #if HAVE_DECL_SO_BINDTODEVICE #include #endif + /* ------------------------------------------------------------------- * Set socket options before the listen() or connect() calls. * These are optional performance tuning factors. * ------------------------------------------------------------------- */ - void SetSocketOptions (struct thread_Settings *inSettings) { // set the TCP window size (socket buffer sizes) // also the UDP buffer size @@ -108,7 +108,7 @@ if ((inSettings->mThreadMode == kMode_Client) && inSettings->mIfrnametx) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), inSettings->mIfrnametx); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", inSettings->mIfrnametx); if (setsockopt(inSettings->mSock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { char *buf; int len = snprintf(NULL, 0, "%s %s", "bind to device", inSettings->mIfrnametx); diff -ruN old/src/ReportOutputs.c new/src/ReportOutputs.c --- old/src/ReportOutputs.c 2020-10-03 04:32:33.000000000 +0800 +++ new/src/ReportOutputs.c 2020-11-12 01:52:58.000000000 +0800 @@ -51,15 +51,17 @@ #include "Locale.h" #include "SocketAddr.h" +// These static variables are not thread safe but ok to use becase only +// the repoter thread usses them #define SNBUFFERSIZE 256 #define SNBUFFEREXTENDSIZE 512 static char outbuffer[SNBUFFERSIZE]; // Buffer for printing static char outbufferext[SNBUFFEREXTENDSIZE]; // Buffer for printing static char outbufferext2[SNBUFFEREXTENDSIZE]; // Buffer for printing static char llaw_buf[100]; +static char netpower_buf[100]; static int HEADING_FLAG(report_bw) = 0; -static int HEADING_FLAG(report_bw_sumcnt) = 0; static int HEADING_FLAG(report_bw_jitter_loss) = 0; static int HEADING_FLAG(report_bw_read_enhanced) = 0; static int HEADING_FLAG(report_bw_read_enhanced_netpwr) = 0; @@ -72,12 +74,16 @@ static int HEADING_FLAG(report_bw_jitter_loss_enhanced_isoch) = 0; static int HEADING_FLAG(report_frame_jitter_loss_enhanced) = 0; static int HEADING_FLAG(report_frame_tcp_enhanced) = 0; -static int HEADING_FLAG(report_bw_write_sumcnt_enhanced) = 0; static int HEADING_FLAG(report_udp_fullduplex) = 0; +static int HEADING_FLAG(report_sumcnt_bw) = 0; +static int HEADING_FLAG(report_sumcnt_udp_fullduplex) = 0; +static int HEADING_FLAG(report_sumcnt_bw_read_enhanced) = 0; +static int HEADING_FLAG(report_sumcnt_bw_write_enhanced) = 0; void reporter_default_heading_flags (int flag) { HEADING_FLAG(report_bw) = flag; - HEADING_FLAG(report_bw_sumcnt) = flag; + HEADING_FLAG(report_sumcnt_bw) = flag; + HEADING_FLAG(report_sumcnt_udp_fullduplex) = flag; HEADING_FLAG(report_bw_jitter_loss) = flag; HEADING_FLAG(report_bw_read_enhanced) = flag; HEADING_FLAG(report_bw_read_enhanced_netpwr) = flag; @@ -90,12 +96,14 @@ HEADING_FLAG(report_bw_jitter_loss_enhanced_isoch) = flag; HEADING_FLAG(report_frame_jitter_loss_enhanced) = flag; HEADING_FLAG(report_frame_tcp_enhanced) = flag; - HEADING_FLAG(report_bw_write_sumcnt_enhanced) = flag; + HEADING_FLAG(report_sumcnt_bw_read_enhanced) = flag; + HEADING_FLAG(report_sumcnt_bw_write_enhanced) = flag; HEADING_FLAG(report_udp_fullduplex) = flag; } - static inline void _print_stats_common (struct TransferInfo *stats) { assert(stats!=NULL); + outbuffer[0] = '\0'; + outbufferext[0] = '\0'; byte_snprintf(outbuffer, sizeof(outbuffer), (double) stats->cntBytes, toupper((int)stats->common->Format)); if (stats->ts.iEnd < SMALLEST_INTERVAL_SEC) { stats->cntBytes = 0; @@ -109,12 +117,12 @@ static inline void _output_outoforder(struct TransferInfo *stats) { if (stats->cntOutofOrder > 0) { printf(report_outoforder, - stats->transferID, stats->ts.iStart, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, stats->cntOutofOrder); } if (stats->l2counts.cnt) { printf(report_l2statistics, - stats->transferID, stats->ts.iStart, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, stats->l2counts.cnt, stats->l2counts.lengtherr, stats->l2counts.udpcsumerr, stats->l2counts.unknown); } @@ -124,36 +132,61 @@ // Little's law is L = lambda * W, where L is queue depth, // lambda the arrival rate and W is the processing time // -static inline void set_llawbuf(double lambda, double meantransit) { +#define LLAW_LOWERBOUNDS -1e7 +static inline void set_llawbuf(double lambda, double meantransit, struct TransferInfo *stats) { double L = lambda * meantransit; - byte_snprintf(llaw_buf, sizeof(llaw_buf), L, 'A'); - llaw_buf[sizeof(llaw_buf)-1] = '\0'; + if (L < LLAW_LOWERBOUNDS) { + strcpy(llaw_buf, "OBL"); + } else { + //force to adpative bytes for human readable + byte_snprintf(llaw_buf, sizeof(llaw_buf), L, 'A'); + llaw_buf[sizeof(llaw_buf)-1] = '\0'; + } } +#define NETPWR_LOWERBOUNDS -1e7 +static inline void set_netpowerbuf(double meantransit, struct TransferInfo *stats) { + if (meantransit == 0.0) { + strcpy(netpower_buf, "NAN"); + } else { + double netpwr = (NETPOWERCONSTANT * ((double) stats->cntBytes) / (double) (stats->ts.iEnd - stats->ts.iStart) / meantransit); + if (netpwr < NETPWR_LOWERBOUNDS) { + strcpy(netpower_buf, "OBL"); + } else { + snprintf(netpower_buf, sizeof(netpower_buf), "%.0f", netpwr); + } + } +} //TCP Output void tcp_output_fullduplex (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_sum_fullduplex_format, stats->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); + printf(report_bw_sum_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } +void tcp_output_fullduplex_sum (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_bw); + _print_stats_common(stats); + printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); +} + void tcp_output_fullduplex_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_sum_fullduplex_enhanced_format, stats->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); + printf(report_bw_sum_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } void tcp_output_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_format, stats->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); + printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } //TCP read or server output void tcp_output_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_read_enhanced); _print_stats_common(stats); printf(report_bw_read_enhanced_format, - stats->transferID, stats->ts.iStart, stats->ts.iEnd, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.read.cntRead, stats->sock_callstats.read.bins[0], @@ -169,11 +202,12 @@ HEADING_PRINT_COND(report_bw_read_enhanced_netpwr); double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; double lambda = (stats->IPGsum > 0.0) ? ((double)stats->cntBytes / stats->IPGsum) : 0.0; - set_llawbuf(lambda, meantransit); + set_llawbuf(lambda, meantransit, stats); _print_stats_common(stats); if (stats->cntBytes) { + set_netpowerbuf(meantransit, stats); printf(report_bw_read_enhanced_netpwr_format, - stats->transferID, stats->ts.iStart, stats->ts.iEnd, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (meantransit * 1e3), stats->transit.minTransit*1e3, @@ -182,7 +216,7 @@ stats->transit.cntTransit, stats->transit.cntTransit ? (long) ((double)stats->cntBytes / (double) stats->transit.cntTransit) : 0, llaw_buf, - (meantransit > 0.0) ? (NETPOWERCONSTANT * ((double) stats->cntBytes) / (double) (stats->ts.iEnd - stats->ts.iStart) / meantransit) : NAN, + netpower_buf, stats->sock_callstats.read.cntRead, stats->sock_callstats.read.bins[0], stats->sock_callstats.read.bins[1], @@ -193,8 +227,9 @@ stats->sock_callstats.read.bins[6], stats->sock_callstats.read.bins[7]); } else { + set_netpowerbuf(meantransit, stats); printf(report_bw_read_enhanced_format, - stats->transferID, stats->ts.iStart, stats->ts.iEnd, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (meantransit * 1e3), stats->transit.minTransit*1e3, @@ -203,7 +238,7 @@ stats->transit.cntTransit, stats->transit.cntTransit ? (long) ((double)stats->cntBytes / (double) stats->transit.cntTransit) : 0, llaw_buf, - (meantransit > 0.0) ? (NETPOWERCONSTANT * ((double) stats->cntBytes) / (double) (stats->ts.iEnd - stats->ts.iStart) / meantransit) : NAN, + netpower_buf, stats->sock_callstats.read.cntRead, stats->sock_callstats.read.bins[0], stats->sock_callstats.read.bins[1], @@ -223,7 +258,7 @@ void tcp_output_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_format, stats->transferID, + printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } @@ -233,35 +268,32 @@ _print_stats_common(stats); #ifndef HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS printf(report_bw_write_enhanced_format, - stats->transferID, stats->ts.iStart, stats->ts.iEnd, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.TCPretry); #else - double netpower = 0; - if (stats->sock_callstats.write.rtt > 0) { - netpower = NETPOWERCONSTANT * (((double)stats->cntBytes / (double) (stats->ts.iEnd - stats->ts.iStart)) / (1e-6 * stats->sock_callstats.write.rtt)); - } + set_netpowerbuf(stats->sock_callstats.write.rtt * 1e-6, stats); if (stats->sock_callstats.write.cwnd > 0) { printf(report_bw_write_enhanced_format, - stats->transferID, stats->ts.iStart, stats->ts.iEnd, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.TCPretry, stats->sock_callstats.write.cwnd, stats->sock_callstats.write.rtt, - netpower); + netpower_buf); } else { printf(report_bw_write_enhanced_nocwnd_format, - stats->transferID, stats->ts.iStart, stats->ts.iEnd, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.TCPretry, stats->sock_callstats.write.rtt, - netpower); + netpower_buf); } #endif } @@ -270,14 +302,14 @@ void udp_output_fullduplex (struct TransferInfo *stats) { HEADING_PRINT_COND(report_udp_fullduplex); _print_stats_common(stats); - printf(report_udp_fullduplex_format, stats->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ + printf(report_udp_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); } void udp_output_fullduplex_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_udp_fullduplex); _print_stats_common(stats); - printf(report_udp_fullduplex_enhanced_format, stats->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ + printf(report_udp_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); } @@ -292,7 +324,7 @@ void udp_output_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss); _print_stats_common(stats); - printf(report_bw_jitter_loss_format, stats->transferID, + printf(report_bw_jitter_loss_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, @@ -303,7 +335,7 @@ void udp_output_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss_pps); _print_stats_common(stats); - printf(report_bw_jitter_loss_pps_format, stats->transferID, + printf(report_bw_jitter_loss_pps_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, @@ -316,28 +348,39 @@ HEADING_PRINT_COND(report_bw_jitter_loss_enhanced); _print_stats_common(stats); if (!stats->cntIPG) { - printf(report_bw_jitter_loss_suppress_enhanced_format, stats->transferID, + printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, 0.0, stats->cntError, stats->cntDatagrams, 0.0,0.0,0.0,0.0,0.0,0.0); } else { - double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; - double lambda = (stats->IPGsum > 0.0) ? ((double)stats->cntBytes / stats->IPGsum) : 0.0; - set_llawbuf(lambda, meantransit); - printf(report_bw_jitter_loss_enhanced_format, stats->transferID, - stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, - (100.0 * stats->cntError) / stats->cntDatagrams, - (meantransit * 1e3), - stats->transit.minTransit*1e3, - stats->transit.maxTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, - (stats->cntIPG / stats->IPGsum), - llaw_buf, - (meantransit > 0.0) ? (NETPOWERCONSTANT * ((double)stats->cntBytes) / (double) (stats->ts.iEnd - stats->ts.iStart) / meantransit) : 0); + if ((stats->transit.minTransit > UNREALISTIC_LATENCYMINMAX) || + (stats->transit.minTransit < UNREALISTIC_LATENCYMINMIN)) { + printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (100.0 * stats->cntError) / stats->cntDatagrams, + (stats->cntIPG / stats->IPGsum)); + } else { + double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; + double lambda = (stats->IPGsum > 0.0) ? ((double)stats->cntBytes / stats->IPGsum) : 0.0; + set_llawbuf(lambda, meantransit, stats); + set_netpowerbuf(meantransit, stats); + printf(report_bw_jitter_loss_enhanced_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (100.0 * stats->cntError) / stats->cntDatagrams, + (meantransit * 1e3), + stats->transit.minTransit*1e3, + stats->transit.maxTransit*1e3, + (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, + (stats->cntIPG / stats->IPGsum), + llaw_buf, + netpower_buf); + } } if (stats->latency_histogram) { histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); @@ -348,7 +391,7 @@ HEADING_PRINT_COND(report_bw_jitter_loss_enhanced_isoch); _print_stats_common(stats); if (!stats->cntIPG) { - printf(report_bw_jitter_loss_suppress_enhanced_format, stats->transferID, + printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, 0.0, stats->cntError, @@ -361,17 +404,18 @@ // latency output if ((stats->transit.minTransit > UNREALISTIC_LATENCYMINMAX) || (stats->transit.minTransit < UNREALISTIC_LATENCYMINMIN)) { - printf(report_bw_jitter_loss_suppress_enhanced_format, stats->transferID, + printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, (stats->cntIPG / stats->IPGsum)); } else { - double meantransit = (stats->transit.sumTransit / stats->transit.cntTransit); + double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; double lambda = (stats->IPGsum > 0.0) ? ((double)stats->cntBytes / stats->IPGsum) : 0.0; - set_llawbuf(lambda, meantransit); - printf(report_bw_jitter_loss_enhanced_isoch_format, stats->transferID, + set_llawbuf(lambda, meantransit, stats); + set_netpowerbuf(meantransit, stats); + printf(report_bw_jitter_loss_enhanced_isoch_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->jitter*1e3, stats->cntError, stats->cntDatagrams, @@ -381,8 +425,8 @@ stats->transit.maxTransit*1e3, (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, (stats->cntIPG / stats->IPGsum), - ((stats->IPGsum > 0.0) ? llaw_buf : 0), - ((meantransit > 0.0) ? (NETPOWERCONSTANT * ((double) stats->cntBytes) / (double) (stats->ts.iEnd - stats->ts.iStart) / meantransit) : 0), + llaw_buf, + netpower_buf, stats->isochstats.framecnt, stats->isochstats.framelostcnt); } } @@ -397,7 +441,7 @@ void udp_output_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_format, stats->transferID, + printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } @@ -405,7 +449,7 @@ void udp_output_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_pps_enhanced); _print_stats_common(stats); - printf(report_bw_pps_enhanced_format, stats->transferID, + printf(report_bw_pps_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, @@ -415,7 +459,7 @@ void udp_output_write_enhanced_isoch (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_pps_enhanced_isoch); _print_stats_common(stats); - printf(report_bw_pps_enhanced_isoch_format, stats->transferID, + printf(report_bw_pps_enhanced_isoch_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, @@ -440,29 +484,70 @@ stats->ts.iEnd, stats->cntOutofOrder); } } -void udp_output_sumcnt_read (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_sumcnt); +void udp_output_sumcnt (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); - printf(report_sumcnt_bw_jitter_loss_format, stats->threadcnt, + printf(report_sumcnt_bw_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext); + if (stats->cntOutofOrder > 0) { + if (isSumOnly(stats->common)) { + printf(report_sumcnt_outoforder, + stats->threadcnt, + stats->ts.iStart, + stats->ts.iEnd, stats->cntOutofOrder); + } else { + printf(report_outoforder, + stats->common->transferIDStr, stats->ts.iStart, + stats->ts.iEnd, stats->cntOutofOrder); + } + } +} +void udp_output_sumcnt_enhanced (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_sumcnt_udp_fullduplex); + _print_stats_common(stats); + printf(report_sumcnt_udp_fullduplex_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ + stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); + if (stats->cntOutofOrder > 0) { + if (isSumOnly(stats->common)) { + printf(report_sumcnt_outoforder, + stats->threadcnt, + stats->ts.iStart, + stats->ts.iEnd, stats->cntOutofOrder); + } else { + printf(report_outoforder, + stats->common->transferIDStr, stats->ts.iStart, + stats->ts.iEnd, stats->cntOutofOrder); + } + } +} + +void udp_output_sumcnt_read_enhanced (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_sumcnt_bw_read_enhanced); + _print_stats_common(stats); + printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt, + stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams); if (stats->cntOutofOrder > 0) { - printf(report_sum_outoforder, - stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + if (isSumOnly(stats->common)) { + printf(report_sumcnt_outoforder, + stats->threadcnt, + stats->ts.iStart, + stats->ts.iEnd, stats->cntOutofOrder); + } } } + void udp_output_sum_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } - void udp_output_sumcnt_write (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_sumcnt); + HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); printf(report_sumcnt_bw_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, @@ -487,6 +572,17 @@ stats->sock_callstats.write.WriteErr, ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0)); } +void udp_output_sumcnt_write_enhanced (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_sumcnt_bw_write_enhanced); + _print_stats_common(stats); + printf(report_sumcnt_bw_write_enhanced_format, stats->threadcnt, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0)); +} + void tcp_output_sum_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); @@ -511,21 +607,27 @@ stats->sock_callstats.read.bins[7]); } void tcp_output_sumcnt_read (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_sumcnt); + HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); printf(report_sumcnt_bw_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } void tcp_output_sumcnt_read_enhanced (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_write_sumcnt_enhanced); + HEADING_PRINT_COND(report_sumcnt_bw_read_enhanced); _print_stats_common(stats); - printf(report_sumcnt_bw_write_enhanced_format, stats->threadcnt, + printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry); + stats->sock_callstats.read.cntRead, + stats->sock_callstats.read.bins[0], + stats->sock_callstats.read.bins[1], + stats->sock_callstats.read.bins[2], + stats->sock_callstats.read.bins[3], + stats->sock_callstats.read.bins[4], + stats->sock_callstats.read.bins[5], + stats->sock_callstats.read.bins[6], + stats->sock_callstats.read.bins[7]); } void tcp_output_sum_write (struct TransferInfo *stats) { @@ -536,7 +638,7 @@ outbuffer, outbufferext); } void tcp_output_sumcnt_write (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_sumcnt); + HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); printf(report_sumcnt_bw_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, @@ -553,7 +655,7 @@ stats->sock_callstats.write.TCPretry); } void tcp_output_sumcnt_write_enhanced (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_write_sumcnt_enhanced); + HEADING_PRINT_COND(report_sumcnt_bw_write_enhanced); _print_stats_common(stats); printf(report_sumcnt_bw_write_enhanced_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, @@ -631,7 +733,7 @@ printf(reportCSV_bw_jitter_loss_format, __timestring, stats->csv_peer, - stats->transferID, + stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, stats->cntBytes, @@ -653,7 +755,7 @@ printf(reportCSV_bw_format, __timestring, stats->csv_peer, - stats->transferID, + stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, stats->cntBytes, @@ -725,6 +827,8 @@ fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion); } if (isSingleClient(report->common)) { + fprintf(stdout, "WARN: Suggested to use lower case -u instead of -U (to avoid serialize & bypass of reporter thread)\n"); + } else if (isSingleClient(report->common)) { fprintf(stdout, "Server set to single client traffic mode per -U (serialize traffic tests)\n"); } if (isMulticast(report->common)) { @@ -750,8 +854,8 @@ (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid, \ report->common->Ifrnametx, (!report->common->threads ? 1 : report->common->threads)); } - if (isEnhanced(report->common)) { - byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, toupper((int)report->common->Format)); + if (isEnhanced(report->common) && !isUDP(report->common)) { + byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, 'B'); outbuffer[(sizeof(outbuffer)-1)] = '\0'; printf("%s: %s\n", client_write_size, outbuffer); } @@ -771,11 +875,17 @@ if (isCongestionControl(report->common) && report->common->Congestion) { fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion); } - if (isIPG(report->common) && !isIsochronous(report->common)) { - byte_snprintf(outbuffer, sizeof(outbuffer), report->common->FQPacingRate, 'a'); + if (isSingleClient(report->common)) { + fprintf(stdout, "WARN: Client set to bypass reporter thread per -U (suggest use lower case -u instead)\n"); + } + if ((isIPG(report->common) || isUDP(report->common)) && !isIsochronous(report->common)) { + byte_snprintf(outbuffer, sizeof(outbuffer), report->common->pktIPG, 'a'); outbuffer[(sizeof(outbuffer)-1)] = '\0'; - fprintf(stdout, "IPG set to %0.6f seconds (%0.3f pps), UDP payload len %d bytes\n", \ - report->common->pktIPG, (1.0 / report->common->pktIPG), report->common->BufLen); +#ifdef HAVE_KALMAN + printf(client_datagram_size_kalman, report->common->BufLen, report->common->pktIPG); +#else + printf(client_datagram_size, report->common->BufLen, report->common->pktIPG); +#endif } output_window_size(report); printf("\n"); @@ -804,6 +914,14 @@ outbufferext[0]='\0'; outbufferext2[0]='\0'; char *b = &outbuffer[0]; + if (!isUDP(report->common) && (report->common->socket > 0) && (isPrintMSS(report->common) || isEnhanced(report->common))) { + if (isPrintMSS(report->common) && (report->MSS <= 0)) { + printf(report_mss_unsupported, report->MSS); + } else { + snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "MSS=", report->MSS); + b += strlen(b); + } + } if (isIsochronous(report->common)) { snprintf(b, SNBUFFERSIZE-strlen(b), " (isoch)"); b += strlen(b); @@ -819,8 +937,8 @@ b += strlen(b); } } - if (isTripTime(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (trip-times)"); + if (isTxStartTime(report->common)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (epoch-start)"); b += strlen(b); } if (isL2LengthCheck(report->common)) { @@ -831,14 +949,15 @@ snprintf(b, SNBUFFERSIZE-strlen(b), " (no-udp-fin)"); b += strlen(b); } - if (!isUDP(report->common) && (report->common->socket > 0) && (isPrintMSS(report->common) || isEnhanced(report->common))) { - if (isPrintMSS(report->common) && (report->MSS <= 0)) { - printf(report_mss_unsupported, report->MSS); - } else { - snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "MSS=", report->MSS); - b += strlen(b); - } + if (isTripTime(report->common)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (trip-times)"); + b += strlen(b); } + + if (isEnhanced(report->common)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (sock=%d)", report->common->socket);; + b += strlen(b); + } if (isEnhanced(report->common) || isPeerVerDetect(report->common)) { if (report->peerversion[0] != '\0') { snprintf(b, SNBUFFERSIZE-strlen(b), "%s", report->peerversion); @@ -872,13 +991,13 @@ #endif #ifdef HAVE_IPV6 if (isEnhanced(report->common) && report->common->Ifrname && (strlen(report->common->Ifrname) < SNBUFFERSIZE-strlen(b))) { - printf(report_peer_dev, report->common->socket, local_addr, report->common->Ifrname, \ + printf(report_peer_dev, report->common->transferID, local_addr, report->common->Ifrname, \ (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : \ ntohs(((struct sockaddr_in6*)local)->sin6_port)), \ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : \ ntohs(((struct sockaddr_in6*)peer)->sin6_port)), outbuffer); } else { - printf(report_peer, report->common->socket, local_addr, \ + printf(report_peer, report->common->transferID, local_addr, \ (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : \ ntohs(((struct sockaddr_in6*)local)->sin6_port)), \ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : \ @@ -886,12 +1005,12 @@ } #else if (isEnhanced(report->common) && report->common->Ifrname && (strlen(report->common->Ifrname) < SNBUFFERSIZE-strlen(b))) { - printf(report_peer_dev, report->common->socket, local_addr, report->common->Ifrname, \ + printf(report_peer_dev, report->common->transferID, local_addr, report->common->Ifrname, \ local_addr, (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : 0), \ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : 0), \ outbuffer); } else { - printf(report_peer, report->common->socket, \ + printf(report_peer, report->common->transferID, \ local_addr, (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : 0), \ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : 0), \ outbuffer); @@ -909,12 +1028,12 @@ ts = *localtime(&t1.tv_sec); char now_timebuf[80]; strftime(now_timebuf, sizeof(now_timebuf), "%Y-%m-%d %H:%M:%S (%Z)", &ts); - printf(client_report_epoch_start_current, report->common->socket, start_timebuf, report->epochStartTime.tv_sec, report->epochStartTime.tv_usec, now_timebuf); + printf(client_report_epoch_start_current, report->common->transferID, start_timebuf, report->epochStartTime.tv_sec, report->epochStartTime.tv_usec, now_timebuf); #else // Format time, "ddd yyyy-mm-dd hh:mm:ss zzz" ts = *localtime(&report->epochStartTime.tv_sec); strftime(start_timebuf, sizeof(start_timebuf), "%Y-%m-%d %H:%M:%S (%Z)", &ts); - printf(client_report_epoch_start, report->common->socket, start_timebuf, report->epochStartTime.tv_sec, report->epochStartTime.tv_usec); + printf(client_report_epoch_start, report->common->transferID, start_timebuf, report->epochStartTime.tv_sec, report->epochStartTime.tv_usec); #endif fflush(stdout); } @@ -962,6 +1081,6 @@ } void reporter_print_server_relay_report (struct ServerRelay *report) { - printf(server_reporting, report->info.transferID); + printf(server_reporting, report->info.common->transferID); udp_output_read(&report->info); } diff -ruN old/src/Reporter.c new/src/Reporter.c --- old/src/Reporter.c 2020-10-03 02:42:49.000000000 +0800 +++ new/src/Reporter.c 2020-11-12 02:37:02.000000000 +0800 @@ -128,6 +128,7 @@ */ void ReportPacket (struct ReporterData* data, struct ReportStruct *packet) { assert(data != NULL); + struct TransferInfo *stats = &data->info; #ifdef HAVE_THREAD_DEBUG if (packet->packetID < 0) { thread_debug("Reporting last packet for %p qdepth=%d sock=%d", (void *) data, packetring_getcount(data->packetring), data->info.common->socket); @@ -135,14 +136,25 @@ #endif #ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS // tcpi stats are only sampled on the report interval - struct TransferInfo *stats = &data->info; if (isEnhanced(stats->common) && (stats->common->ThreadMode == kMode_Client) && \ (TimeDifference(stats->ts.nextTime, packet->packetTime) < 0)) { gettcpistats(data, 0); } #endif + // Note for threaded operation all that needs + // to be done is to enqueue the packet data + // into the ring. packetring_enqueue(data->packetring, packet); -#ifndef HAVE_THREAD + // The traffic thread calls the reporting process + // directly forr non-threaded operation + // These defeats the puropse of separating + // traffic i/o from user i/o and really + // should be avoided. +#ifdef HAVE_THREAD + // bypass the reporter thread here for single UDP + if (isSingleUDP(stats->common)) + reporter_process_transfer_report(data); +#else /* * Process the report in this thread */ @@ -287,8 +299,10 @@ if (ReportRoot == NULL) { // The reporter is starting from an empty state // so set the load detect to trigger an initial delay - reset_consumption_detector(); - reporter_default_heading_flags((inSettings->mReportMode == kReport_CSV)); + if (!isSingleUDP(inSettings)) { + reset_consumption_detector(); + reporter_default_heading_flags((inSettings->mReportMode == kReport_CSV)); + } if (!ReportPendingHead) { Condition_TimedWait(&ReportCond, 1); #ifdef HAVE_THREAD_DEBUG @@ -404,21 +418,22 @@ // Store a cached pointer for the linked list maitenance struct ReportHeader *tmp = (*work_item)->next; if (reporter_process_report(*work_item)) { +#ifdef HAVE_THREAD_DEBUG + thread_debug("Jobq *REMOVE* %p", (void *) (*work_item)); +#endif // memory for *work_item is gone by now *work_item = tmp; if (!tmp) break; } -#ifdef HAVE_THREAD_DEBUG -// thread_debug( "Jobq *REMOVE* (%p)=%p (%p)=%p", (void *) work_item, (void *) (*work_item), (void *) &(*work_item)->next, (void *) *(&(*work_item)->next)); -#endif work_item = &(*work_item)->next; } } } if (myConnectionReport) { - if (myConnectionReport->connect_times.cnt > 1) + if (myConnectionReport->connect_times.cnt > 1) { reporter_connect_printf_tcp_final(myConnectionReport); + } FreeConnectionReport(myConnectionReport); } #ifdef HAVE_THREAD_DEBUG @@ -504,8 +519,7 @@ } if (DecrSumReportRefCounter(this_ireport->GroupSumReport) == 0) { if ((this_ireport->GroupSumReport->transfer_protocol_sum_handler) && \ - (this_ireport->GroupSumReport->reference.maxcount > (fullduplexstats ? 2 : 1))) { - sumstats->sumflag = 1; + (this_ireport->GroupSumReport->reference.maxcount > 1)) { (*this_ireport->GroupSumReport->transfer_protocol_sum_handler)(&this_ireport->GroupSumReport->info, 1); } FreeSumReport(this_ireport->GroupSumReport); @@ -541,7 +555,7 @@ { struct ConnectionInfo *creport = (struct ConnectionInfo *)reporthdr->this_report; assert(creport!=NULL); - if (creport->common->ThreadMode == kMode_Client) { + if (!isCompat(creport->common) && (creport->common->ThreadMode == kMode_Client)) { // Clients' connect times will be inputs to the overall connect stats reporter_update_connect_time(creport->connecttime); } @@ -591,12 +605,13 @@ stats->total.IPG.current++; } stats->ts.IPGstart = packet->packetTime; - stats->IPGsum += TimeDifference(packet->sentTime, packet->prevSentTime); + stats->IPGsum += TimeDifference(packet->packetTime, packet->prevPacketTime); #ifdef DEBUG_PPS - printf("*** IPGsum = %f cnt=%ld ipg=%ld.%ld pt=%ld.%ld id=%ld empty=%d transit=%f %ld.%ld\n", stats->IPGsum, stats->cntIPG, stats->ts.IPGstart.tv_sec, stats->ts.IPGstart.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->packetID, packet->emptyreport, TimeDifference(packet->sentTime, packet->prevSentTime),packet->prevSentTime.tv_sec, packet->prevSentTime.tv_usec); + printf("*** IPGsum = %f cnt=%ld ipg=%ld.%ld pkt=%ld.%ld id=%ld empty=%d transit=%f prev=%ld.%ld\n", stats->IPGsum, stats->cntIPG, stats->ts.IPGstart.tv_sec, stats->ts.IPGstart.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->packetID, packet->emptyreport, TimeDifference(packet->packetTime, packet->prevPacketTime), packet->prevPacketTime.tv_sec, packet->prevPacketTime.tv_usec); #endif } +// Variance uses the Welford inline algorithm, mean is also inline static inline double reporter_handle_packet_oneway_transit (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; // Transit or latency updates done inline below @@ -913,7 +928,7 @@ emptystats.ts.iStart = stats->ts.iStart; emptystats.ts.iEnd = stats->ts.iEnd; emptystats.common = stats->common; - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(&emptystats); } } @@ -1005,6 +1020,8 @@ stats->cntError = 0; stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev; stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; + if (stats->total.Datagrams.current == 1) + stats->jitter = 0; if (sumstats) { sumstats->total.OutofOrder.current += stats->total.OutofOrder.current - stats->total.OutofOrder.prev; // assume most of the time out-of-order packets are not @@ -1015,6 +1032,7 @@ sumstats->total.IPG.current += stats->cntIPG; if (sumstats->IPGsum < stats->IPGsum) sumstats->IPGsum = stats->IPGsum; + sumstats->threadcnt++; } if (fullduplexstats) { fullduplexstats->total.Bytes.current += stats->cntBytes; @@ -1033,10 +1051,11 @@ if (stats->cntError < 0) stats->cntError = 0; stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev; - if (final) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - if (stats->output_handler) - (*stats->output_handler)(stats); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + } } reporter_set_timestamps_time(&stats->ts, TOTAL); stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime); @@ -1069,44 +1088,44 @@ stats->framelatency_histogram->final = 1; } } - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); if (!final) reporter_reset_transfer_stats_server_udp(stats); } void reporter_transfer_protocol_sum_server_udp (struct TransferInfo *stats, int final) { - if (stats->sumflag) { - if (final) { - reporter_set_timestamps_time(&stats->ts, TOTAL); - stats->cntOutofOrder = stats->total.OutofOrder.current; - // assume most of the time out-of-order packets are not - // duplicate packets, so conditionally subtract them from the lost packets. - stats->cntError = stats->total.Lost.current; - stats->cntError -= stats->cntOutofOrder; - if (stats->cntError < 0) - stats->cntError = 0; - stats->cntDatagrams = stats->total.Datagrams.current; - stats->cntBytes = stats->total.Bytes.current; - stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime); - stats->cntIPG = stats->total.IPG.current; - } else { - stats->cntOutofOrder = stats->total.OutofOrder.current - stats->total.OutofOrder.prev; - // assume most of the time out-of-order packets are not - // duplicate packets, so conditionally subtract them from the lost packets. - stats->cntError = stats->total.Lost.current - stats->total.Lost.prev; - stats->cntError -= stats->cntOutofOrder; - if (stats->cntError < 0) - stats->cntError = 0; - stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev; - stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; - stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; - } - if (stats->output_handler) - (*stats->output_handler)(stats); - if (!final) - reporter_reset_transfer_stats_server_udp(stats); + if (final) { + reporter_set_timestamps_time(&stats->ts, TOTAL); + stats->cntOutofOrder = stats->total.OutofOrder.current; + // assume most of the time out-of-order packets are not + // duplicate packets, so conditionally subtract them from the lost packets. + stats->cntError = stats->total.Lost.current; + stats->cntError -= stats->cntOutofOrder; + if (stats->cntError < 0) + stats->cntError = 0; + stats->cntDatagrams = stats->total.Datagrams.current; + stats->cntBytes = stats->total.Bytes.current; + stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime); + stats->cntIPG = stats->total.IPG.current; + } else { + stats->cntOutofOrder = stats->total.OutofOrder.current - stats->total.OutofOrder.prev; + // assume most of the time out-of-order packets are not + // duplicate packets, so conditionally subtract them from the lost packets. + stats->cntError = stats->total.Lost.current - stats->total.Lost.prev; + stats->cntError -= stats->cntOutofOrder; + if (stats->cntError < 0) + stats->cntError = 0; + stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev; + stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; + stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; } + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) + (*stats->output_handler)(stats); + if (!final) { + stats->threadcnt = 0; + reporter_reset_transfer_stats_server_udp(stats); + } } void reporter_transfer_protocol_sum_client_udp (struct TransferInfo *stats, int final) { if (final) { @@ -1123,13 +1142,13 @@ stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev; } - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); if (!final) { stats->threadcnt = 0; reporter_reset_transfer_stats_client_udp(stats); - } else + } else if ((stats->common->ReportMode != kReport_CSV) && !(stats->filter_this_sample_ouput)) printf(report_sumcnt_datagrams, stats->threadcnt, stats->total.Datagrams.current); } @@ -1174,10 +1193,10 @@ stats->cntIPG = 0; } } - if (stats->output_handler) { + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { (*stats->output_handler)(stats); - if (final) - printf(report_datagrams, stats->transferID, stats->total.Datagrams.current); + if (final && (stats->common->ReportMode != kReport_CSV)) + printf(report_datagrams, stats->common->transferID, stats->total.Datagrams.current); } reporter_reset_transfer_stats_client_udp(stats); } @@ -1204,9 +1223,12 @@ if (final) { if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { // print a partial interval report if enable and this a final - reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - (*stats->output_handler)(stats); - reporter_reset_transfer_stats_server_tcp(stats); + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + reporter_reset_transfer_stats_server_tcp(stats); + } } reporter_set_timestamps_time(&stats->ts, TOTAL); stats->cntBytes = stats->total.Bytes.current; @@ -1224,7 +1246,7 @@ stats->framelatency_histogram->final = 1; } } - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); if (!final) reporter_reset_transfer_stats_server_tcp(stats); @@ -1251,9 +1273,12 @@ if (final) { if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { // print a partial interval report if enable and this a final - reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - (*stats->output_handler)(stats); - reporter_reset_transfer_stats_server_tcp(stats); + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + reporter_reset_transfer_stats_client_tcp(stats); + } } stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr; stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt; @@ -1261,7 +1286,7 @@ stats->cntBytes = stats->total.Bytes.current; reporter_set_timestamps_time(&stats->ts, TOTAL); } - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); if (!final) reporter_reset_transfer_stats_client_tcp(stats); @@ -1273,12 +1298,17 @@ void reporter_transfer_protocol_sum_client_tcp (struct TransferInfo *stats, int final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; - if (final) - reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - if (stats->output_handler) + if (final) { + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + reporter_reset_transfer_stats_client_tcp(stats); + } + } else if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { (*stats->output_handler)(stats); - if (!final) stats->threadcnt = 0; + } reporter_reset_transfer_stats_client_tcp(stats); } if (final) { @@ -1286,53 +1316,60 @@ stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt; stats->sock_callstats.write.TCPretry = stats->sock_callstats.write.totTCPretry; stats->cntBytes = stats->total.Bytes.current; - reporter_set_timestamps_time(&stats->ts, TOTAL); - if (stats->output_handler) + reporter_set_timestamps_time(&stats->ts, TOTAL); + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); } } void reporter_transfer_protocol_sum_server_tcp (struct TransferInfo *stats, int final) { - if (stats->sumflag) { - if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { - stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; - if (final) - reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - if (stats->output_handler) - (*stats->output_handler)(stats); - if (!final) - stats->threadcnt = 0; - reporter_reset_transfer_stats_server_tcp(stats); - } + if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { + stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; if (final) { - int ix; - stats->cntBytes = stats->total.Bytes.current; - stats->sock_callstats.read.cntRead = stats->sock_callstats.read.totcntRead; - for (ix = 0; ix < TCPREADBINCOUNT; ix++) { - stats->sock_callstats.read.bins[ix] = stats->sock_callstats.read.totbins[ix]; + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); } - stats->cntBytes = stats->total.Bytes.current; - reporter_set_timestamps_time(&stats->ts, TOTAL); - if (stats->output_handler) - (*stats->output_handler)(stats); + } else if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + (*stats->output_handler)(stats); + stats->threadcnt = 0; } + reporter_reset_transfer_stats_server_tcp(stats); } + if (final) { + int ix; + stats->cntBytes = stats->total.Bytes.current; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.totcntRead; + for (ix = 0; ix < TCPREADBINCOUNT; ix++) { + stats->sock_callstats.read.bins[ix] = stats->sock_callstats.read.totbins[ix]; + } + stats->cntBytes = stats->total.Bytes.current; + reporter_set_timestamps_time(&stats->ts, TOTAL); + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) + (*stats->output_handler)(stats); + } } void reporter_transfer_protocol_fullduplex_tcp (struct TransferInfo *stats, int final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; - if (final) - reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - if (stats->output_handler) - (*stats->output_handler)(stats); + if (final) { + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + } + } stats->total.Bytes.prev = stats->total.Bytes.current; } if (final) { stats->cntBytes = stats->total.Bytes.current; reporter_set_timestamps_time(&stats->ts, TOTAL); - if (stats->output_handler) - (*stats->output_handler)(stats); + } else { + reporter_set_timestamps_time(&stats->ts, INTERVAL); } + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) + (*stats->output_handler)(stats); } void reporter_transfer_protocol_fullduplex_udp (struct TransferInfo *stats, int final) { @@ -1340,10 +1377,13 @@ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev; stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; - if (final) - reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); - if (stats->output_handler) - (*stats->output_handler)(stats); + if (final) { + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + } + } stats->total.Bytes.prev = stats->total.Bytes.current; stats->total.IPG.prev = stats->total.IPG.current; stats->total.Datagrams.prev = stats->total.Datagrams.current; @@ -1357,9 +1397,11 @@ stats->cntIPG = stats->total.IPG.current; stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime); reporter_set_timestamps_time(&stats->ts, TOTAL); - if (stats->output_handler) - (*stats->output_handler)(stats); + } else { + reporter_set_timestamps_time(&stats->ts, INTERVAL); } + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) + (*stats->output_handler)(stats); } // Conditional print based on time @@ -1391,8 +1433,11 @@ if (sumstats) { if ((++data->GroupSumReport->threads) == data->GroupSumReport->reference.count) { data->GroupSumReport->threads = 0; - if (data->GroupSumReport->reference.count > (fullduplexstats ? 2 : 1)) - sumstats->sumflag = 1; + if (data->GroupSumReport->reference.count > 1) { + sumstats->filter_this_sample_ouput = 0; + } else { + sumstats->filter_this_sample_ouput = 1; + } reporter_set_timestamps_time(&sumstats->ts, INTERVAL); assert(data->GroupSumReport->transfer_protocol_sum_handler != NULL); (*data->GroupSumReport->transfer_protocol_sum_handler)(sumstats, 0); @@ -1400,7 +1445,7 @@ } // In the (hopefully unlikely event) the reporter fell behind // ouput the missed reports to catch up - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) reporter_transfer_protocol_missed_reports(stats, packet); } return advance_jobq; @@ -1429,7 +1474,7 @@ if (stats->cntError < 0) stats->cntError = 0; stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev; - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); reporter_reset_transfer_stats_server_udp(stats); advance_jobq = 1; @@ -1454,7 +1499,7 @@ stats->ts.packetTime = packet->packetTime; reporter_set_timestamps_time(&stats->ts, FRAME); stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; - if (stats->output_handler) + if ((stats->output_handler) && !(stats->filter_this_sample_ouput)) (*stats->output_handler)(stats); reporter_reset_transfer_stats_client_tcp(stats); advance_jobq = 1; diff -ruN old/src/Reports.c new/src/Reports.c --- old/src/Reports.c 2020-10-03 02:42:49.000000000 +0800 +++ new/src/Reports.c 2020-11-05 09:06:26.000000000 +0800 @@ -58,6 +58,7 @@ #include "Locale.h" #include "active_hosts.h" #include "payloads.h" +static int transferid_counter = 0; static inline void my_str_copy(char **dst, char *src) { if (src) { @@ -87,10 +88,12 @@ my_str_copy(&(*common)->Ifrnametx, inSettings->mIfrnametx); my_str_copy(&(*common)->SSMMulticastStr, inSettings->mSSMMulticastStr); my_str_copy(&(*common)->Congestion, inSettings->mCongestion); + my_str_copy(&(*common)->transferIDStr, inSettings->mTransferIDStr); // copy some relevant settings (*common)->flags = inSettings->flags; (*common)->flags_extend = inSettings->flags_extend; (*common)->ThreadMode = inSettings->mThreadMode; + (*common)->ReportMode = inSettings->mReportMode; (*common)->Format = inSettings->mFormat; (*common)->TTL = inSettings->mTTL; // copy some traffic related settings @@ -104,6 +107,7 @@ (*common)->UDPRate = inSettings->mUDPRate; (*common)->UDPRateUnits = inSettings->mUDPRateUnits; (*common)->socket = inSettings->mSock; + (*common)->transferID = inSettings->mTransferID; (*common)->threads = inSettings->mThreads; (*common)->winsize_requested = inSettings->mTCPWin; #if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET) @@ -137,65 +141,121 @@ free(common->SSMMulticastStr); if (common->Congestion) free(common->Congestion); + if (common->transferIDStr) + free(common->transferIDStr); free(common); } -void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport, int fullduplex) { - if (fullduplex) { +// This will set the transfer id and id string +// on the setting object. If the current id is zero +// this will get the next one. Otherwise it will use +// the value. +int setTransferID (struct thread_Settings *inSettings, int role_reversal) { + if (!inSettings->mTransferID) { + Mutex_Lock(&transferid_mutex); + inSettings->mTransferID = ++transferid_counter; + Mutex_Unlock(&transferid_mutex); + } + int len = 0; + if (inSettings->mTransferIDStr) + free(inSettings->mTransferIDStr); + if (role_reversal) { +#ifdef HAVE_ROLE_REVERSAL_ID + if (inSettings->mTransferID < 10) + len = snprintf(NULL, 0, "[ *%d] ", inSettings->mTransferID); + else + len = snprintf(NULL, 0, "[*%d] ", inSettings->mTransferID); +#endif + } else { + len = snprintf(NULL, 0, "[%3d] ", inSettings->mTransferID); + } + len++; // Trailing null byte + extra + inSettings->mTransferIDStr = (char *) calloc(1, len); + if (role_reversal) { +#ifdef HAVE_ROLE_REVERSAL_ID + if (inSettings->mTransferID < 10) + len = snprintf(inSettings->mTransferIDStr, len, "[ *%d] ", inSettings->mTransferID); + else + len = snprintf(inSettings->mTransferIDStr, len, "[*%d] ", inSettings->mTransferID); +#endif + } else { + len = snprintf(inSettings->mTransferIDStr, len, "[%3d] ", inSettings->mTransferID); + } + inSettings->mTransferIDStr[len] = '\0'; + return inSettings->mTransferID; +} + +void SetFullDuplexHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport) { + if (isUDP(inSettings)) { + sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_udp; + sumreport->info.output_handler = ((inSettings->mReportMode == kReport_CSV) ? NULL : \ + (isEnhanced(inSettings) ? udp_output_fullduplex_enhanced : \ + (isSumOnly(inSettings) ? udp_output_fullduplex : NULL))); + } else { + sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_tcp; + sumreport->info.output_handler = ((inSettings->mReportMode == kReport_CSV) ? NULL : \ + (isEnhanced(inSettings) ? tcp_output_fullduplex_enhanced : + (isSumOnly(inSettings) ? tcp_output_fullduplex : NULL))); + } +} + +void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport) { + switch (inSettings->mThreadMode) { + case kMode_Server : if (isUDP(inSettings)) { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_udp; - sumreport->info.output_handler = ((isSumOnly(inSettings) || (inSettings->mReportMode == kReport_CSV)) ? NULL : \ - (isEnhanced(inSettings) ? udp_output_fullduplex_enhanced : udp_output_fullduplex)); + sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_udp; + if (isSumOnly(inSettings)) { + sumreport->info.output_handler = (isEnhanced(inSettings) \ + ? udp_output_sumcnt_enhanced : udp_output_sumcnt); + } else if (isFullDuplex(inSettings)) { + sumreport->info.output_handler = udp_output_fullduplex_sum; + } else { + sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sum_read_enhanced : udp_output_sum_read); + } } else { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_tcp; - sumreport->info.output_handler = ((isSumOnly(inSettings) || (inSettings->mReportMode == kReport_CSV)) ? NULL : \ - (isEnhanced(inSettings) ? tcp_output_fullduplex_enhanced : tcp_output_fullduplex)); + sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_tcp; + if (isSumOnly(inSettings)) { + sumreport->info.output_handler = ((isEnhanced(inSettings) && !isFullDuplex(inSettings)) ? \ + tcp_output_sumcnt_read_enhanced : tcp_output_sumcnt_read); + } else if (isFullDuplex(inSettings)) { + sumreport->info.output_handler = tcp_output_sum_read; + } else { + sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sum_read_enhanced : tcp_output_sum_read); + } } - } else { - switch (inSettings->mThreadMode) { - case kMode_Server : - if (isUDP(inSettings)) { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_udp; - if (isFullDuplex(inSettings)) { - sumreport->info.output_handler = udp_output_fullduplex_sum; - } else { - sumreport->info.output_handler = (isSumOnly(inSettings) ? udp_output_sumcnt_read : udp_output_sum_read); - } + break; + case kMode_Client : + if (isUDP(inSettings)) { + sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_client_udp; + if (isSumOnly(inSettings)) { + sumreport->info.output_handler = ((isEnhanced(inSettings) && !isFullDuplex(inSettings)) ? \ + udp_output_sumcnt_write_enhanced : udp_output_sumcnt); + } else if (isFullDuplex(inSettings)) { + sumreport->info.output_handler = udp_output_fullduplex_sum; } else { - if (isEnhanced(inSettings)) { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_tcp; - sumreport->info.output_handler = (isSumOnly(inSettings) ? tcp_output_sumcnt_read_enhanced : tcp_output_sum_read_enhanced); - } else { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_tcp; - sumreport->info.output_handler = (isSumOnly(inSettings) ? tcp_output_sumcnt_read : tcp_output_sum_read); - } + sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sum_write_enhanced : udp_output_sum_write); } - break; - case kMode_Client : - if (isUDP(inSettings)) { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_client_udp; - if (isFullDuplex(inSettings)) { - sumreport->info.output_handler = udp_output_fullduplex_sum; - } else { - sumreport->info.output_handler = (isSumOnly(inSettings) ? udp_output_sumcnt_write : \ - (isEnhanced(inSettings) ? udp_output_sum_write_enhanced : udp_output_sum_write)); - } + } else { + sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_client_tcp; + if (isSumOnly(inSettings)) { + sumreport->info.output_handler = ((isEnhanced(inSettings) && !isFullDuplex(inSettings)) ? \ + tcp_output_sumcnt_read_enhanced : tcp_output_sumcnt_read); + } else if (isFullDuplex(inSettings)) { + sumreport->info.output_handler = tcp_output_fullduplex_sum; } else { - sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_client_tcp; - sumreport->info.output_handler = (isSumOnly(inSettings) ? tcp_output_sumcnt_write : tcp_output_sum_write); + sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sum_write_enhanced : tcp_output_sum_write); } - break; - default: - FAIL(1, "SetSumReport", inSettings); } + break; + default: + FAIL(1, "SetSumReport", inSettings); } // overide output handlers when csv reporting set if (inSettings->mReportMode == kReport_CSV) sumreport->info.output_handler = NULL; - } -struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, int fullduplex) { +struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, int fullduplex_report) { struct SumReport *sumreport = (struct SumReport *) calloc(1, sizeof(struct SumReport)); if (sumreport == NULL) { FAIL(1, "Out of Memory!!\n", inSettings); @@ -206,7 +266,6 @@ sumreport->threads = 0; common_copy(&sumreport->info.common, inSettings); sumreport->info.groupID = inID; - sumreport->info.transferID = inSettings->mSock; sumreport->info.threadcnt = 0; // Only initialize the interval time here // The startTime and nextTime for summing reports will be set by @@ -214,9 +273,10 @@ if ((inSettings->mInterval) && (inSettings->mIntervalMode == kInterval_Time)) { sumreport->info.ts.intervalTime.tv_sec = (long) (inSettings->mInterval / rMillion); sumreport->info.ts.intervalTime.tv_usec = (long) (inSettings->mInterval % rMillion); + sumreport->info.ts.significant_partial = ((double) inSettings->mInterval * PARTIALPERCENT / rMillion) ; } - SetSumHandlers(inSettings, sumreport, fullduplex); - if (fullduplex) { + if (fullduplex_report) { + SetFullDuplexHandlers(inSettings, sumreport); if (!isServerReverse(inSettings)) { sumreport->fullduplex_barrier.count = 0; Condition_Initialize(&sumreport->fullduplex_barrier.await); @@ -233,6 +293,8 @@ sumreport->info.ts.nextTime = sumreport->info.ts.startTime; TimeAdd(sumreport->info.ts.nextTime, sumreport->info.ts.intervalTime); } + } else { + SetSumHandlers(inSettings, sumreport); } #ifdef HAVE_THREAD_DEBUG thread_debug("Init sum report %p id=%d", (void *)sumreport, inID); @@ -437,10 +499,10 @@ ireport->info.output_handler = udp_output_read_enhanced_triptime; } else if (isEnhanced(inSettings)) { ireport->info.output_handler = udp_output_read_enhanced_triptime; - } else if (!isFullDuplex(inSettings)) { + } else if (isFullDuplex(inSettings)) { ireport->info.output_handler = udp_output_read; } else { - ireport->info.output_handler = NULL; + ireport->info.output_handler = udp_output_read; } } else { ireport->packet_handler = reporter_handle_packet_server_tcp; @@ -456,7 +518,7 @@ } else if (!isFullDuplex(inSettings)) { ireport->info.output_handler = tcp_output_read; } else { - ireport->info.output_handler = NULL ; + ireport->info.output_handler = tcp_output_read; } } break; @@ -473,7 +535,7 @@ } else if (isEnhanced(inSettings)) { ireport->info.output_handler = udp_output_write_enhanced; } else if (isFullDuplex(inSettings)) { - ireport->info.output_handler = NULL; + ireport->info.output_handler = udp_output_write; } else { ireport->info.output_handler = udp_output_write; } @@ -486,7 +548,7 @@ } else if (isEnhanced(inSettings) || isIsochronous(inSettings)) { ireport->info.output_handler = tcp_output_write_enhanced; } else if (isFullDuplex(inSettings)) { - ireport->info.output_handler = NULL; + ireport->info.output_handler = tcp_output_write; } else { ireport->info.output_handler = tcp_output_write; } @@ -508,13 +570,13 @@ thread_debug("Init data report %p using packetring=%p cond=%p", \ (void *)ireport, (void *)(ireport->packetring), (void *)(ireport->packetring->awake_producer)); #endif - ireport->info.transferID = inSettings->mSock; switch (inSettings->mIntervalMode) { case kInterval_Time : ireport->info.ts.intervalTime.tv_sec = (long) (inSettings->mInterval / rMillion); ireport->info.ts.intervalTime.tv_usec = (long) (inSettings->mInterval % rMillion); ireport->transfer_interval_handler = reporter_condprint_time_interval_report; + ireport->info.ts.significant_partial = (double) inSettings->mInterval * PARTIALPERCENT / rMillion ; break; case kInterval_Frames : if (isUDP(inSettings)) { @@ -536,14 +598,14 @@ char name[] = "T8"; ireport->info.latency_histogram = histogram_init(inSettings->mRXbins,inSettings->mRXbinsize,0,\ pow(10,inSettings->mRXunits), \ - inSettings->mRXci_lower, inSettings->mRXci_upper, ireport->info.transferID, name); + inSettings->mRXci_lower, inSettings->mRXci_upper, ireport->info.common->transferID, name); } - if (isRxHistogram(inSettings) && isIsochronous(inSettings) && isTripTime(inSettings)) { + if (isRxHistogram(inSettings) && (isIsochronous(inSettings) || (!isUDP(inSettings) && isTripTime(inSettings)))) { char name[] = "F8"; // make sure frame bin size min is 100 microsecond ireport->info.framelatency_histogram = histogram_init(inSettings->mRXbins,inSettings->mRXbinsize,0, \ pow(10,inSettings->mRXunits), inSettings->mRXci_lower, \ - inSettings->mRXci_upper, ireport->info.transferID, name); + inSettings->mRXci_upper, ireport->info.common->transferID, name); } } return reporthdr; @@ -674,7 +736,7 @@ struct ServerRelay *sr_report = (struct ServerRelay *)reporthdr->this_report; common_copy(&sr_report->info.common, inSettings); struct TransferInfo *stats = (struct TransferInfo *)&sr_report->info; - stats->transferID = inSettings->mSock; + stats->common->transferID = inSettings->mTransferID; stats->jitter = ntohl(server->base.jitter1); stats->jitter += ntohl(server->base.jitter2) / (double)rMillion; diff -ruN old/src/Server.cpp new/src/Server.cpp --- old/src/Server.cpp 2020-10-03 02:42:49.000000000 +0800 +++ new/src/Server.cpp 2020-11-05 09:06:26.000000000 +0800 @@ -77,7 +77,7 @@ Server::Server (thread_Settings *inSettings) { #ifdef HAVE_THREAD_DEBUG - thread_debug("Server constructor with thread=%p multihdr=%p(sock=%d)", (void *) inSettings, (void *)inSettings->mSumReport, inSettings->mSock); + thread_debug("Server constructor with thread=%p sum=%p (sock=%d)", (void *) inSettings, (void *)inSettings->mSumReport, inSettings->mSock); #endif mSettings = inSettings; mBuf = NULL; @@ -95,8 +95,8 @@ } } #endif - // initialize buffer, length checking done by the Listener - mBuf = new char[(mSettings->mBufLen > MINMBUFALLOCSIZE) ? mSettings->mBufLen : MINMBUFALLOCSIZE]; // defined in payloads.h + mBufLen = (mSettings->mBufLen > MINMBUFALLOCSIZE) ? mSettings->mBufLen : MINMBUFALLOCSIZE; + mBuf = new char[mBufLen]; FAIL_errno(mBuf == NULL, "No memory for buffer\n", mSettings); if (mSettings->mBufLen < (int) sizeof(UDP_datagram)) { fprintf(stderr, warn_buffer_too_small, mSettings->mBufLen); @@ -168,7 +168,7 @@ if (!InitTrafficLoop()) return; - struct timeval prevsend = myReport->info.ts.startTime; + myReport->info.ts.prevsendTime = myReport->info.ts.startTime; int burst_nleft = 0; burst_info.burst_id = 0; @@ -214,7 +214,7 @@ reportstruct->sentTime.tv_sec = now.getSecs(); reportstruct->sentTime.tv_usec = now.getUsecs(); } - prevsend = reportstruct->sentTime; + myReport->info.ts.prevsendTime = reportstruct->sentTime; burst_nleft = burst_info.burst_size - n; currLen += n; readLen = (mSettings->mBufLen < burst_nleft) ? mSettings->mBufLen : burst_nleft; @@ -238,7 +238,7 @@ enqueue_ackring(mSettings->ackring, reportstruct); } #endif - reportstruct->prevSentTime = prevsend; + reportstruct->prevSentTime = myReport->info.ts.prevsendTime; reportstruct->transit_ready = 1; } else { // printf("****currlen = %ld, n=%d, burst_nleft=%d id=%d\n", currLen, n, burst_nleft, burst_info.burst_id); @@ -250,7 +250,7 @@ thread_debug("Server thread detected EOF on socket %d", mSettings->mSock); #endif } else if ((n < 0) && (!NONFATALTCPREADERR(errno))) { - WARN_errno(1, "recv"); + // WARN_errno(1, "recv"); n = 0; } currLen += n; @@ -332,19 +332,24 @@ #endif } inline void Server::SetReportStartTime (void) { + now.setnow(); if (isTripTime(mSettings)) { // Start times come from the sender's timestamp assert(mSettings->triptime_start.tv_sec != 0); assert(mSettings->triptime_start.tv_usec != 0); myReport->info.ts.startTime.tv_sec = mSettings->triptime_start.tv_sec; myReport->info.ts.startTime.tv_usec = mSettings->triptime_start.tv_usec; - myReport->info.ts.prevpacketTime = myReport->info.ts.startTime; - } else if (TimeZero(myReport->info.ts.startTime) && !TimeZero(mSettings->accept_time)) { + if (isUDP(mSettings)) { + myReport->info.ts.prevpacketTime = myReport->info.ts.startTime; + } else { + myReport->info.ts.prevpacketTime.tv_sec = now.getSecs(); + myReport->info.ts.prevpacketTime.tv_usec = now.getUsecs(); + } + } else if (TimeZero(myReport->info.ts.startTime) && !TimeZero(mSettings->accept_time) && !isTxStartTime(mSettings)) { // Servers that aren't full duplex use the accept timestamp for start myReport->info.ts.startTime.tv_sec = mSettings->accept_time.tv_sec; myReport->info.ts.startTime.tv_usec = mSettings->accept_time.tv_usec; } else { - now.setnow(); myReport->info.ts.startTime.tv_sec = now.getSecs(); myReport->info.ts.startTime.tv_usec = now.getUsecs(); } @@ -399,20 +404,16 @@ uint32_t flags = 0; int peeklen = 0; if (isUDP(mSettings)) { - n = recvn(mSettings->mSock, mBuf, sizeof(uint32_t) + sizeof(struct UDP_datagram), MSG_PEEK); + n = recvn(mSettings->mSock, mBuf, mBufLen, MSG_PEEK); if (n == 0) { //peer closed the socket, with no writes e.g. a connect-only test return -1; } - FAIL_errno(n != (sizeof(uint32_t) + sizeof(struct UDP_datagram)), "read udp flags", mSettings); struct client_udp_testhdr *udp_pkt = (struct client_udp_testhdr *) mBuf; flags = ntohl(udp_pkt->base.flags); - if ((peeklen = Settings_ClientHdrPeekLen(flags) + sizeof(struct UDP_datagram)) > 0) { - n = recvn(mSettings->mSock, mBuf, peeklen, MSG_PEEK); - FAIL_errno((n < peeklen), "read udp test hdr", mSettings); - } mSettings->triptime_start.tv_sec = ntohl(udp_pkt->start_fq.start_tv_sec); mSettings->triptime_start.tv_usec = ntohl(udp_pkt->start_fq.start_tv_usec); + reportstruct->packetLen = n; } else { n = recvn(mSettings->mSock, mBuf, sizeof(uint32_t), MSG_PEEK); if (n == 0) { @@ -431,16 +432,19 @@ struct client_tcp_testhdr *tcp_pkt = (struct client_tcp_testhdr *) mBuf; mSettings->triptime_start.tv_sec = ntohl(tcp_pkt->start_fq.start_tv_sec); mSettings->triptime_start.tv_usec = ntohl(tcp_pkt->start_fq.start_tv_usec); + reportstruct->packetLen = n; reportstruct->packetID = n; } } - } - if (isTripTime(mSettings)) { + } else if (isTripTime(mSettings)) { Timestamp now; if ((abs(now.getSecs() - mSettings->triptime_start.tv_sec)) > MAXDIFFTIMESTAMPSECS) { unsetTripTime(mSettings); fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); } + if (mSettings->skip) { + reportstruct->packetLen = recvn(mSettings->mSock, mBuf, mSettings->skip, 0); + } } SetReportStartTime(); if (setfullduplexflag) @@ -502,16 +506,17 @@ peerclose = true; } else if #ifdef WIN32 - (WSAGetLastError() != WSAEWOULDBLOCK) + (WSAGetLastError() != WSAEWOULDBLOCK) #else ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != ECONNREFUSED)) #endif - { - WARN_errno(currLen, "recvmsg"); - currLen= 0; - } + { + WARN_errno(currLen, "recvmsg"); + currLen= 0; } - + } else if (TimeZero(myReport->info.ts.prevpacketTime)) { + myReport->info.ts.prevpacketTime = reportstruct->packetTime; + } if (!tsdone) { now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); @@ -701,7 +706,6 @@ if (!InitTrafficLoop()) return; - struct timeval prevsend = myReport->info.ts.startTime; // Exit loop on three conditions // 1) Fatal read error @@ -732,21 +736,18 @@ } if (!(reportstruct->l2errors & L2UNKNOWN)) { // ReadPacketID returns true if this is the last UDP packet sent by the client - // aslo sets the packet rx time in the reportstruct - reportstruct->prevSentTime = prevsend; + // also sets the packet rx time in the reportstruct + reportstruct->prevSentTime = myReport->info.ts.prevsendTime; + reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime; lastpacket = ReadPacketID(); - prevsend = reportstruct->sentTime; + myReport->info.ts.prevsendTime = reportstruct->sentTime; + myReport->info.ts.prevpacketTime = reportstruct->packetTime; if (isIsochronous(mSettings)) { udp_isoch_processing(rxlen); } } } - if (!isSingleUDP(mSettings)) - ReportPacket(myReport, reportstruct); - else { - packetring_enqueue(myReport->packetring, reportstruct); - reporter_process_transfer_report(myReport); - } + ReportPacket(myReport, reportstruct); } disarm_itimer(); int do_close = EndJob(myJob, reportstruct); diff -ruN old/src/Settings.cpp new/src/Settings.cpp --- old/src/Settings.cpp 2020-10-03 04:55:18.000000000 +0800 +++ new/src/Settings.cpp 2020-11-12 02:01:34.000000000 +0800 @@ -85,14 +85,13 @@ static int writeack = 0; static int infinitetime = 0; static int connectonly = 0; +static int connectretry = 0; static int burstipg = 0; static int isochronous = 0; static int noudpfin = 0; static int numreportstructs = 0; static int sumonly = 0; -extern Mutex groupCond; - void Settings_Interpret(char option, const char *optarg, struct thread_Settings *mExtSettings); // apply compound settings after the command line has been fully parsed void Settings_ModalOptions(struct thread_Settings *mExtSettings); @@ -132,6 +131,7 @@ {"realtime", no_argument, NULL, 'z'}, // more esoteric options +{"awdl", no_argument, NULL, 'A'}, {"bind", required_argument, NULL, 'B'}, {"compatibility", no_argument, NULL, 'C'}, {"daemon", no_argument, NULL, 'D'}, @@ -164,6 +164,7 @@ {"write-ack", optional_argument, &writeack, 1}, {"no-udp-fin", no_argument, &noudpfin, 1}, {"connect-only", optional_argument, &connectonly, 1}, +{"connect-retries", required_argument, &connectretry, 1}, {"no-connect-sync", no_argument, &noconnectsync, 1}, {"full-duplex", no_argument, &fullduplextest, 1}, {"ipg", required_argument, &burstipg, 1}, @@ -223,7 +224,7 @@ #define SHORT_OPTIONS() -const char short_options[] = "1b:c:def:hi:l:mn:o:p:rst:uvw:x:y:zB:CDF:H:IL:M:NP:RS:T:UVWXZ:"; +const char short_options[] = "1b:c:def:hi:l:mn:o:p:rst:uvw:x:y:zAB:CDF:H:IL:M:NP:RS:T:UVWXZ:"; /* ------------------------------------------------------------------- * defaults @@ -236,11 +237,11 @@ const int kDefault_UDPBufLenV6 = 1450; // -u if set, read/write 1470 bytes // v6: 1450 bytes UDP payload will fill one and only one ethernet datagram (IPv6 overhead is 40 bytes) const int kDefault_TCPBufLen = 128 * 1024; // TCP default read/write size + /* ------------------------------------------------------------------- * Initialize all settings to defaults. * ------------------------------------------------------------------- */ - -void Settings_Initialize(struct thread_Settings *main) { +void Settings_Initialize (struct thread_Settings *main) { // Everything defaults to zero or NULL with // this memset. Only need to set non-zero values // below. @@ -291,12 +292,13 @@ } // end Settings -void Settings_Copy(struct thread_Settings *from, struct thread_Settings **into, int copyall) { +void Settings_Copy (struct thread_Settings *from, struct thread_Settings **into, int copyall) { *into = new struct thread_Settings; memset(*into, 0, sizeof(struct thread_Settings)); memcpy(*into, from, sizeof(struct thread_Settings)); + (*into)->mSumReport = NULL; #ifdef HAVE_THREAD_DEBUG - thread_debug("Copy thread settings (malloc) from/to=%p/%p report/multi/fullduplex %p/%p/%p", \ + thread_debug("Copy thread settings (malloc) from/to=%p/%p report/sum/fullduplex %p/%p/%p", \ (void *)from, (void *)*into, (void *)(*into)->reporthdr, (void *)(*into)->mSumReport, (void *)(*into)->mFullDuplexReport); #endif // Some settings don't need to be copied and will confuse things. Don't copy them unless copyall is set @@ -353,6 +355,7 @@ (*into)->mIfrnametx = NULL; (*into)->mIsochronousStr = NULL; (*into)->mCongestion = NULL; + (*into)->mTransferIDStr = NULL; // apply the server side congestion setting to reverse clients if (from->mIsochronousStr != NULL) { (*into)->mIsochronousStr = new char[ strlen(from->mIsochronousStr) + 1]; @@ -380,7 +383,7 @@ * Delete memory: Does not clean up open file pointers or ptr_parents * ------------------------------------------------------------------- */ -void Settings_Destroy(struct thread_Settings *mSettings) { +void Settings_Destroy (struct thread_Settings *mSettings) { #if HAVE_THREAD_DEBUG thread_debug("Free thread settings=%p", mSettings); #endif @@ -394,6 +397,7 @@ DELETE_ARRAY(mSettings->mCongestion); FREE_ARRAY(mSettings->mIfrname); FREE_ARRAY(mSettings->mIfrnametx); + FREE_ARRAY(mSettings->mTransferIDStr); DELETE_ARRAY(mSettings->mIsochronousStr); DELETE_PTR(mSettings); } // end ~Settings @@ -401,7 +405,7 @@ /* ------------------------------------------------------------------- * Parses settings from user's environment variables. * ------------------------------------------------------------------- */ -void Settings_ParseEnvironment(struct thread_Settings *mSettings) { +void Settings_ParseEnvironment (struct thread_Settings *mSettings) { char *theVariable; int i = 0; @@ -418,7 +422,7 @@ * Parse settings from app's command line. * ------------------------------------------------------------------- */ -void Settings_ParseCommandLine(int argc, char **argv, struct thread_Settings *mSettings) { +void Settings_ParseCommandLine (int argc, char **argv, struct thread_Settings *mSettings) { int option; gnu_opterr = 1; // Fail on an unrecognized command line option while ((option = @@ -440,7 +444,7 @@ * or from environment variables. * ------------------------------------------------------------------- */ -void Settings_Interpret(char option, const char *optarg, struct thread_Settings *mExtSettings) { +void Settings_Interpret (char option, const char *optarg, struct thread_Settings *mExtSettings) { char *results; switch (option) { case '1': // Single Client @@ -599,7 +603,7 @@ setModeTime(mExtSettings); setServerModeTime(mExtSettings); if (atof(optarg) > 0.0) - mExtSettings->mAmount = (int) (atof(optarg) * 100.0); + mExtSettings->mAmount = (size_t) (atof(optarg) * 100.0); else infinitetime = 1; break; @@ -669,6 +673,7 @@ break; // more esoteric options + case 'B': // specify bind address if (mExtSettings->mLocalhost == NULL) { mExtSettings->mLocalhost = new char[ strlen(optarg) + 1 ]; @@ -820,16 +825,11 @@ setEnhanced(mExtSettings); match = sscanf(optarg,"%ld.%6ld", &seconds, &usecs); mExtSettings->txstart_epoch.tv_usec = 0; - Timestamp now; switch (match) { case 2: mExtSettings->txstart_epoch.tv_usec = usecs; case 1: mExtSettings->txstart_epoch.tv_sec = seconds; - if ((now.getSecs() - seconds) > 0) { - fprintf(stderr, "WARNING: start time of before now ignored\n"); - unsetTxStartTime(mExtSettings); - } break; default: unsetTxStartTime(mExtSettings); @@ -856,7 +856,6 @@ mExtSettings->txholdback_timer.tv_sec = holdbackdelay.getSecs(); mExtSettings->txholdback_timer.tv_usec = (holdbackdelay.getUsecs()); setTxHoldback(mExtSettings); - setEnhanced(mExtSettings); } } if (triptime) { @@ -877,12 +876,17 @@ if (connectonly) { connectonly = 0; setConnectOnly(mExtSettings); + unsetNoConnReport(mExtSettings); if (optarg) { mExtSettings->connectonly_count = atoi(optarg); } else { - mExtSettings->connectonly_count = 1; + mExtSettings->connectonly_count = -1; } } + if (connectretry) { + connectretry = 0; + mExtSettings->mConnectRetries = atoi(optarg); + } if (sumonly) { sumonly = 0; setSumOnly(mExtSettings); @@ -954,7 +958,7 @@ } } // end Interpret -static void strip_v6_brackets(char *v6addr) { +static void strip_v6_brackets (char *v6addr) { char * results; if (v6addr && (*v6addr == '[') && ((results = strtok(v6addr, "]")) != NULL)) { int len = strlen(v6addr); @@ -964,7 +968,7 @@ } } -static char * isv6_bracketed_port(char *v6addr) { +static char * isv6_bracketed_port (char *v6addr) { char *results = NULL; if (v6addr && (*v6addr == '[') && ((results = strtok(v6addr, "]")) != NULL)) { strip_v6_brackets(v6addr); @@ -974,7 +978,7 @@ } return NULL; } -static char * isv4_port(char *v4addr) { +static char * isv4_port (char *v4addr) { char *results = NULL; if (((results = strtok(v4addr, ":")) != NULL) && ((results = strtok(NULL, ":")) != NULL)) { return results; @@ -997,7 +1001,7 @@ // // Also apply the bail out or exit conditions if the user requested mutually exclusive // or incompatabile options -void Settings_ModalOptions(struct thread_Settings *mExtSettings) { +void Settings_ModalOptions (struct thread_Settings *mExtSettings) { char *results; // Handle default read/write sizes based on v4, v6, UDP or TCP if (!isBuflenSet(mExtSettings)) { @@ -1015,7 +1019,9 @@ if (!isBWSet(mExtSettings) && isUDP(mExtSettings)) { mExtSettings->mUDPRate = kDefault_UDPRate; } - if (isTripTime(mExtSettings) && (isReverse(mExtSettings) || isFullDuplex(mExtSettings))) { + if (isTripTime(mExtSettings) && (isReverse(mExtSettings) || \ + isFullDuplex(mExtSettings) || \ + (mExtSettings->mMode != kTest_Normal))) { setEnhanced(mExtSettings); } // Warnings @@ -1036,7 +1042,7 @@ | isRxHistogram(mExtSettings) | isIsochronous(mExtSettings) \ | isEnhanced(mExtSettings) | (mExtSettings->mMode != kTest_Normal)); if (isCompat(mExtSettings) && compat_nosupport) { - fprintf(stderr, "ERROR: compatability mode not supported with the requested with options\n"); + fprintf(stderr, "ERROR: compatibility mode not supported with the requested with options\n"); bail = true; } if (mExtSettings->mThreadMode == kMode_Client) { @@ -1044,7 +1050,20 @@ fprintf(stderr, "ERROR: option of --sum-only requires -P greater than 1\n"); bail = true; } - if (isUDP(mExtSettings)) { + if (isTxStartTime(mExtSettings)) { + Timestamp now; + long nowsecs = now.getSecs(); + if (((nowsecs - mExtSettings->txstart_epoch.tv_sec) > 0) \ + || ((nowsecs == mExtSettings->txstart_epoch.tv_sec) && (now.getUsecs() > mExtSettings->txstart_epoch.tv_usec))) { + fprintf(stderr, "WARNING: --txstart-time of before now ignored\n"); + unsetTxStartTime(mExtSettings); + } + } + if (isTxHoldback(mExtSettings) && isTxStartTime(mExtSettings)) { + fprintf(stdout,"ERROR: options of --txstart-time and --txdelay-time are mutually exclusive\n"); + bail = true; + } + if (isUDP(mExtSettings)) { if (isPeerVerDetect(mExtSettings)) { fprintf(stderr, "ERROR: option of -X or --peer-detect not supported with -u UDP\n"); bail = true; @@ -1053,17 +1072,18 @@ fprintf(stderr, "ERROR: option of --connect-only not supported with -u UDP\n"); bail = true; } - if (isTxHoldback(mExtSettings)) { - fprintf(stderr, "ERROR: option of --txdelay-time is not supported with -u UDP\n"); - bail = true; - } if (isIPG(mExtSettings) && isBWSet(mExtSettings)) { fprintf(stderr, "ERROR: options of --b and --ipg cannot be applied together\n"); bail = true; } if (mExtSettings->mBurstIPG < 0.0) { - fprintf(stderr, "ERROR: option --ipg must be a postive value\n"); + fprintf(stderr, "ERROR: option --ipg must be a positive value\n"); + bail = true; } + if (mExtSettings->mConnectRetries > 0) { + fprintf(stderr, "ERROR: option --connect-retries not supported with -u UDP\n"); + bail = true; + } { double delay_target; if (isIPG(mExtSettings)) { @@ -1095,11 +1115,11 @@ if (isTxHoldback(mExtSettings)) { Timestamp now; if (mExtSettings->txholdback_timer.tv_sec > MAXDIFFTXDELAY) { - fprintf(stdout,"ERROR: Fail beacuse --txdelay-time is not within %d seconds of now\n", MAXDIFFTXDELAY); + fprintf(stdout,"ERROR: Fail because --txdelay-time is not within %d seconds of now\n", MAXDIFFTXDELAY); bail = true; } if (isConnectOnly(mExtSettings)) { - fprintf(stdout,"ERROR: Fail beacuse --txdelay-time and --connect-only cannot be applied together\n"); + fprintf(stdout,"ERROR: Fail because --txdelay-time and --connect-only cannot be applied together\n"); bail = true; ; } } @@ -1107,7 +1127,7 @@ if (isTxStartTime(mExtSettings)) { Timestamp now; if ((mExtSettings->txstart_epoch.tv_sec- now.getSecs()) > MAXDIFFTXSTART) { - fprintf(stdout,"ERROR: Fail beacuse --txstart-time is not within %d seconds of now\n", MAXDIFFTXSTART); + fprintf(stdout,"ERROR: Fail because --txstart-time is not within %d seconds of now\n", MAXDIFFTXSTART); bail = true; } } @@ -1125,7 +1145,7 @@ one_only++; if (isReverse(mExtSettings)) one_only++; - if (mExtSettings->mMode != kTest_Normal) + if (mExtSettings->mMode != kTest_Normal) one_only++; if (one_only > 1) { fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r are mutually exclusive\n"); @@ -1158,38 +1178,41 @@ bail = true; } if (isIPG(mExtSettings)) { - fprintf(stderr, "ERROR: option of --ipg is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --ipg is not supported on the server\n"); bail = true; } if (isIsochronous(mExtSettings)) { - fprintf(stderr, "ERROR: option of --isochronous is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --isochronous is not supported on the server\n"); bail = true; } if (isFullDuplex(mExtSettings)) { - fprintf(stderr, "ERROR: option of --full-duplex is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --full-duplex is not supported on the server\n"); bail = true; } if (isReverse(mExtSettings)) { - fprintf(stderr, "ERROR: option of --reverse is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --reverse is not supported on the server\n"); bail = true; } if (isIncrDstIP(mExtSettings)) { - fprintf(stderr, "ERROR: option of --incr-dstpip is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --incr-dstpip is not supported on the server\n"); bail = true; } if (isFQPacing(mExtSettings)) { - fprintf(stderr, "ERROR: option of --fq-rate is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --fq-rate is not supported on the server\n"); bail = true; } if (isNoUDPfin(mExtSettings)) { - fprintf(stderr, "ERROR: option of --no-udp-fin is not suppported on the server\n"); + fprintf(stderr, "ERROR: option of --no-udp-fin is not supported on the server\n"); bail = true; } if (isPeerVerDetect(mExtSettings)) { fprintf(stderr, "ERROR: option of -X or --peer-detect not supported on the server\n"); bail = true; } - + if (mExtSettings->mConnectRetries > 0) { + fprintf(stderr, "ERROR: option --connect-retries not supported on the server\n"); + bail = true; + } } if (bail) exit(1); @@ -1336,7 +1359,18 @@ } } if (SockAddr_isMulticast(&tmp)) { - setMulticast(mExtSettings); + bail = false; + if ((mExtSettings->mThreads > 1) && !isIncrDstIP(mExtSettings)) { + fprintf(stderr, "ERROR: client option of -P greater than 1 not supported with multicast address\n"); + bail = true; + } else if (isFullDuplex(mExtSettings) || isReverse(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) { + fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r not supported with multicast addresses\n"); + bail = true; + } + if (bail) + exit(1); + else + setMulticast(mExtSettings); } #ifndef HAVE_DECL_SO_BINDTODEVICE if (mExtSettings->mIfrnametx) { @@ -1348,7 +1382,7 @@ } } -void Settings_GetUpperCaseArg(const char *inarg, char *outarg) { +void Settings_GetUpperCaseArg (const char *inarg, char *outarg) { int len = strlen(inarg); strcpy(outarg,inarg); @@ -1357,7 +1391,7 @@ outarg[len-1]= outarg[len-1]+'A'-'a'; } -void Settings_GetLowerCaseArg(const char *inarg, char *outarg) { +void Settings_GetLowerCaseArg (const char *inarg, char *outarg) { int len = strlen(inarg); strcpy(outarg,inarg); @@ -1374,19 +1408,25 @@ * the struct thread_settings instance generated from the command line * for client side execution */ -void Settings_GenerateListenerSettings(struct thread_Settings *client, struct thread_Settings **listener) { - if (!isCompat(client) && \ - (client->mMode == kTest_DualTest || client->mMode == kTest_TradeOff)) { +#define DUALTIMER_MS 300 +void Settings_GenerateListenerSettings (struct thread_Settings *client, struct thread_Settings **listener) { + if ((client->mMode == kTest_DualTest) || (client->mMode == kTest_TradeOff)) { Settings_Copy(client, listener, 0); - // setCompat((*listener)); RJM, fix me unsetDaemon((*listener)); + setCompat((*listener)); if (client->mListenPort != 0) { (*listener)->mPort = client->mListenPort; } else { (*listener)->mPort = client->mPort; } - if (client->mMode == kTest_TradeOff) - (*listener)->mAmount = (2 * client->mAmount) + 200; + if (client->mMode == kTest_TradeOff) { + (*listener)->mAmount = client->mAmount + DUALTIMER_MS; + } else if (client->mMode == kTest_DualTest) { + (*listener)->mAmount = client->mAmount + (SLOPSECS * 100); + } + if ((client->mMode != kTest_Normal) && ((*listener)->mAmount < DUALTIMER_MS)) { + (*listener)->mAmount = DUALTIMER_MS; + } (*listener)->mFileName = NULL; (*listener)->mHost = NULL; (*listener)->mLocalhost = NULL; @@ -1401,10 +1441,14 @@ (*listener)->mLocalhost = new char[strlen(client->mLocalhost) + 1]; strcpy((*listener)->mLocalhost, client->mLocalhost); } - if (isUDP((*listener))) { - (*listener)->mBufLen = kDefault_UDPBufLen; + if (client->mBufLen <= 0) { + if (isUDP((*listener))) { + (*listener)->mBufLen = kDefault_UDPBufLen; + } else { + (*listener)->mBufLen = kDefault_TCPBufLen; + } } else { - (*listener)->mBufLen = kDefault_TCPBufLen; + (*listener)->mBufLen = client->mBufLen; } setReport((*listener)); } else { @@ -1424,9 +1468,6 @@ } void Settings_ReadClientSettingsV1 (struct thread_Settings **client, struct client_hdr_v1 *hdr) { - if (!isTripTime((*client)) && !isIsochronous((*client))) { - setCompat((*client)); // Disable test headers for this reversed client - } (*client)->mTID = thread_zeroid(); (*client)->mPort = (unsigned short) ntohl(hdr->mPort); (*client)->mThreads = 1; @@ -1455,18 +1496,21 @@ * * Note: mBuf should already be filled out per the Listener's apply_client_settings */ -void Settings_GenerateClientSettings(struct thread_Settings *server, struct thread_Settings **client, void *mBuf) { +void Settings_GenerateClientSettings (struct thread_Settings *server, struct thread_Settings **client, void *mBuf) { assert(server != NULL); assert(mBuf != NULL); uint32_t flags = isUDP(server) ? ntohl(*(uint32_t *)((char *)mBuf + sizeof(struct UDP_datagram))) : ntohl(*(uint32_t *)mBuf); uint16_t upperflags = 0; thread_Settings *reversed_thread = NULL; *client = NULL; + bool v1test = (flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2); #ifdef HAVE_THREAD_DEBUG - if (flags & HEADER_VERSION1) + if (v1test) thread_debug("header set for a version 1 test"); #endif - if (isFullDuplex(server) || (flags & HEADER_VERSION1)) { + if (isFullDuplex(server) || isServerReverse(server)) + setTransferID(server, 0); + if (isFullDuplex(server) || v1test) { Settings_Copy(server, client, 0); reversed_thread = *client; if (isFullDuplex(server) && !(flags & HEADER_VERSION1)) { @@ -1485,73 +1529,82 @@ if (isUDP(server)) { // UDP test information passed in every packet per being stateless struct client_udp_testhdr *hdr = (struct client_udp_testhdr *) mBuf; Settings_ReadClientSettingsV1(&reversed_thread, &hdr->base); - if (flags & HEADER_VERSION1) { - if (flags & RUN_NOW) + if (isFullDuplex(server) || v1test) { + server->mAmount = reversed_thread->mAmount + (SLOPSECS * 100); + } + if (v1test) { + setServerReverse(reversed_thread); + if (flags & RUN_NOW) { reversed_thread->mMode = kTest_DualTest; - else - reversed_thread->mMode = kTest_Normal; - } else if (flags & (HEADER_EXTEND| HEADER_VERSION2)) { + } else { + reversed_thread->mMode = kTest_TradeOff; + } + } + if (flags & HEADER_EXTEND) { reversed_thread->mUDPRate = ntohl(hdr->extend.lRate); #ifdef HAVE_INT64_T reversed_thread->mUDPRate |= ((uint64_t)(ntohl(hdr->extend.uRate) >> 8) << 32); #endif upperflags = ntohs(hdr->extend.upperflags); + if (upperflags & HEADER_NOUDPFIN) { + setNoUDPfin(reversed_thread); + } if ((upperflags & HEADER_UNITS_PPS) == HEADER_UNITS_PPS) { reversed_thread->mUDPRateUnits = kRate_PPS; } else { reversed_thread->mUDPRateUnits = kRate_BW; } - if (flags & HEADER_VERSION2) { - reversed_thread->mTOS = ntohs(hdr->extend.tos); - if (isIsochronous(server)) { - Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings); - } - if (upperflags & HEADER_FQRATESET) { - setFQPacing(reversed_thread); - reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel); + reversed_thread->mTOS = ntohs(hdr->extend.tos); + if (isIsochronous(server)) { + Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings); + } + if (upperflags & HEADER_FQRATESET) { + setFQPacing(reversed_thread); + reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel); #ifdef HAVE_INT64_T - reversed_thread->mFQPacingRate |= ((uint64_t)(ntohl(hdr->start_fq.fqrateu)) << 32); + reversed_thread->mFQPacingRate |= ((uint64_t)(ntohl(hdr->start_fq.fqrateu)) << 32); #endif - } } } } else { //tcp first payload struct client_tcp_testhdr *hdr = (struct client_tcp_testhdr *) mBuf; Settings_ReadClientSettingsV1(&reversed_thread, &hdr->base); - if (flags & HEADER_VERSION1) { + if (isFullDuplex(server) || v1test) { + server->mAmount = reversed_thread->mAmount + (SLOPSECS * 100); + } + if (v1test) { + setServerReverse(reversed_thread); if (flags & RUN_NOW) { reversed_thread->mMode = kTest_DualTest; } else { - reversed_thread->mMode = kTest_Normal; - reversed_thread->mSumReport = NULL; + reversed_thread->mMode = kTest_TradeOff; } - } else if (flags & (HEADER_EXTEND | HEADER_VERSION2)) { + } + if (flags & HEADER_EXTEND) { reversed_thread->mUDPRate = ntohl(hdr->extend.lRate); #ifdef HAVE_INT64_T reversed_thread->mUDPRate |= ((uint64_t)(ntohl(hdr->extend.uRate) >> 8) << 32); #endif upperflags = ntohs(hdr->extend.upperflags); - if (flags & HEADER_VERSION2) { - reversed_thread->mTOS = ntohs(hdr->extend.tos); - if (isIsochronous(server)) { - Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings); - } - if (upperflags & HEADER_FQRATESET) { - setFQPacing(reversed_thread); - reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel); + reversed_thread->mTOS = ntohs(hdr->extend.tos); + + if (isIsochronous(server)) { + Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings); + } + if (upperflags & HEADER_FQRATESET) { + setFQPacing(reversed_thread); + reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel); #ifdef HAVE_INT64_T - reversed_thread->mFQPacingRate |= ((uint64_t)(ntohl(hdr->start_fq.fqrateu)) << 32); + reversed_thread->mFQPacingRate |= ((uint64_t)(ntohl(hdr->start_fq.fqrateu)) << 32); #endif - } } } } - unsetTxStartTime(reversed_thread); unsetTxHoldback(reversed_thread); setNoSettReport(reversed_thread); setNoConnectSync(reversed_thread); // for legacy -d and -r need so set the reversed threads mHost - if (flags & HEADER_VERSION1) { + if (v1test) { reversed_thread->mHost = new char[REPORT_ADDRLEN]; if (((sockaddr*)&server->peer)->sa_family == AF_INET) { inet_ntop(AF_INET, &((sockaddr_in*)&server->peer)->sin_addr, @@ -1563,24 +1616,26 @@ reversed_thread->mHost, REPORT_ADDRLEN); } #endif - } + } else { + reversed_thread->mMode = kTest_Normal; #if HAVE_DECL_SO_MAX_PACING_RATE - if (isFQPacing(reversed_thread)) { - int rc = setsockopt(reversed_thread->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, \ - &reversed_thread->mFQPacingRate, sizeof(reversed_thread->mFQPacingRate)); - WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE"); + if (isFQPacing(reversed_thread)) { + int rc = setsockopt(reversed_thread->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, \ + &reversed_thread->mFQPacingRate, sizeof(reversed_thread->mFQPacingRate)); + WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE"); #ifdef HAVE_THREAD_DEBUG #ifdef HAVE_INT64_T - thread_debug("Set socket %d pacing rate to %ld byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate); + thread_debug("Set socket %d pacing rate to %ld byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate); #else - thread_debug("Set socket %d pacing rate to %d byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate); + thread_debug("Set socket %d pacing rate to %d byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate); #endif #endif + } +#endif // MAX_PACING_RATE } -#endif } -int Settings_GenerateClientHdrV1(struct thread_Settings *client, struct client_hdr_v1 *hdr) { +int Settings_GenerateClientHdrV1 (struct thread_Settings *client, struct client_hdr_v1 *hdr) { if (isBuflenSet(client)) { hdr->mBufLen = htonl(client->mBufLen); } else { @@ -1612,34 +1667,53 @@ * * Returns size of header in bytes */ -int Settings_GenerateClientHdr(struct thread_Settings *client, void *testhdr, struct timeval startTime) { - if (isCompat(client)) - return 0; +int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, struct timeval startTime) { + uint16_t len = 0; uint16_t upperflags = 0; uint16_t lowerflags = 0; - uint16_t len = 0; uint32_t flags = 0; - flags = (HEADER_SEQNO64B | HEADER_VERSION2); // use 64 bit by default - // flags common to both TCP and UDP - if (isReverse(client)) { - flags |= HEADER_UDPTESTS; - upperflags |= HEADER_REVERSE; + if (isReverse(client) && !isCompat(client)) { + upperflags |= HEADER_REVERSE; } - if (isFullDuplex(client)) { - flags |= HEADER_UDPTESTS; - upperflags |= HEADER_FULLDUPLEX; + if (isFullDuplex(client) && !isCompat(client)) { + upperflags |= HEADER_FULLDUPLEX; } + if (isTxStartTime(client) && !TimeZero(startTime)) { + upperflags |= HEADER_EPOCH_START; + } // Now setup UDP and TCP specific passed settings from client to server if (isUDP(client)) { // UDP test information passed in every packet per being stateless struct client_udp_testhdr *hdr = (struct client_udp_testhdr *) testhdr; memset(hdr, 0, sizeof(struct client_udp_testhdr)); + flags |= HEADER_SEQNO64B; // use 64 bit by default + flags |= HEADER_EXTEND; + hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX); + hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX); + hdr->extend.tos = htons(client->mTOS & 0xFF); + if (isBWSet(client)) { + hdr->extend.lRate = htonl((uint32_t)(client->mUDPRate)); +#ifdef HAVE_INT64_T + hdr->extend.uRate = htonl(((uint32_t)(client->mUDPRate >> 32)) << 8); +#endif + } else { + hdr->extend.lRate = htonl(kDefault_UDPRate); + hdr->extend.uRate = 0x0; + } + len += sizeof(struct client_hdrext); + len += Settings_GenerateClientHdrV1(client, &hdr->base); + if (!isCompat(client) && (client->mMode != kTest_Normal)) { + flags |= HEADER_VERSION1; + if (client->mMode == kTest_DualTest) + flags |= RUN_NOW; + hdr->base.flags = htonl(flags); + } /* * set the default offset where underlying "inline" subsystems can write into the udp payload */ if (isL2LengthCheck(client)) { - flags |= (HEADER_UDPTESTS | HEADER_EXTEND); + flags |= HEADER_UDPTESTS; if (isL2LengthCheck(client)) { upperflags |= HEADER_L2LENCHECK; if (isIPV6(client)) @@ -1650,7 +1724,6 @@ flags |= (HEADER_UDPTESTS | HEADER_EXTEND) ; upperflags |= HEADER_ISOCH; if (isFullDuplex(client) || isReverse(client)) { - flags |= HEADER_VERSION2; upperflags |= HEADER_ISOCH_SETTINGS; hdr->isoch_settings.FPSl = htonl((long)(client->mFPS)); hdr->isoch_settings.FPSu = htonl(((client->mFPS - (long)(client->mFPS)) * rMillion)); @@ -1663,16 +1736,20 @@ len += sizeof(struct client_hdrext_isoch_settings); } } - if (isNoUDPfin(client)) { + if (isReverse(client) || isFullDuplex(client)) { flags |= (HEADER_UDPTESTS | HEADER_VERSION2); + } + if (isNoUDPfin(client)) { + flags |= (HEADER_UDPTESTS | HEADER_EXTEND); upperflags |= HEADER_NOUDPFIN; } - if (isTripTime(client) || isFQPacing(client)) { - flags |= (HEADER_UDPTESTS | HEADER_VERSION2); - if (isTripTime(client)) { - upperflags |= HEADER_TRIPTIME; + if (isTripTime(client) || isFQPacing(client) || isTxStartTime(client)) { + flags |= HEADER_UDPTESTS; + if (isTripTime(client) || isTxStartTime(client)) { hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec); hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec); + if (isTripTime(client)) + upperflags |= HEADER_TRIPTIME; } if (isFQPacing(client)) { upperflags |= HEADER_FQRATESET; @@ -1681,36 +1758,11 @@ hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32)); #endif } - len += sizeof(struct client_hdrext_starttime_fq); } - if (isBWSet(client)) { - flags |= (HEADER_EXTEND | HEADER_VERSION2); - hdr->extend.lRate = htonl((uint32_t)(client->mUDPRate)); -#ifdef HAVE_INT64_T - hdr->extend.uRate = htonl(((uint32_t)(client->mUDPRate >> 32)) << 8); -#endif - } - if (flags & (HEADER_EXTEND | HEADER_VERSION2)) { - if (!isBWSet(client)) { - hdr->extend.lRate = htonl(kDefault_UDPRate); - hdr->extend.uRate = 0x0; - } - // Write flags to header so the listener can determine the tests requested - hdr->extend.upperflags = htons(upperflags); - hdr->extend.lowerflags = htons(lowerflags); - hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX); - hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX); - hdr->extend.tos = htons(client->mTOS & 0xFF); - len += sizeof(struct client_hdrext); - } - if (!(flags & HEADER_VERSION2) && (client->mMode != kTest_Normal)) { - flags |= HEADER_VERSION1; - if (client->mMode == kTest_DualTest) - flags |= RUN_NOW; - } - if (flags & (HEADER_VERSION1 | HEADER_VERSION2)) { - len += Settings_GenerateClientHdrV1(client, &hdr->base); - } + // Write flags to header so the listener can determine the tests requested + hdr->extend.upperflags = htons(upperflags); + hdr->extend.lowerflags = htons(lowerflags); + // isoch payload is an enclave field between v 0.13 and v0.14 // so figure out if it's there now - UDP only // will be filled in by the client write @@ -1725,25 +1777,43 @@ } else { // TCP first write with test information struct client_tcp_testhdr *hdr = (struct client_tcp_testhdr *) testhdr; memset(hdr, 0, sizeof(struct client_tcp_testhdr)); - // Set up trip time - if (isTripTime(client) || isFQPacing(client)) { - flags |= HEADER_VERSION2; + flags |= HEADER_EXTEND; + hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX); + hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX); + hdr->extend.tos = htons(client->mTOS & 0xFF); + if (isBWSet(client)) { + hdr->extend.lRate = htonl((uint32_t)(client->mUDPRate)); +#ifdef HAVE_INT64_T + hdr->extend.uRate = htonl(((uint32_t)(client->mUDPRate >> 32)) << 8); +#endif + } + len += sizeof(struct client_hdrext); + len += Settings_GenerateClientHdrV1(client, &hdr->base); + if (!isCompat(client) && (client->mMode != kTest_Normal)) { + flags |= HEADER_VERSION1; + if (client->mMode == kTest_DualTest) + flags |= RUN_NOW; + } + if (isPeerVerDetect(client)) { + flags |= (HEADER_V2PEERDETECT | HEADER_VERSION2); + } + if (isTripTime(client) || isFQPacing(client) || isIsochronous(client) || isTxStartTime(client)) { + hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec); + hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec); + hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate); +#ifdef HAVE_INT64_T + hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32)); +#endif + len += sizeof(struct client_hdrext_starttime_fq); + // Set flags on if (isTripTime(client)) { upperflags |= HEADER_TRIPTIME; - hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec); - hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec); } if (isFQPacing(client)) { upperflags |= HEADER_FQRATESET; - hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate); -#ifdef HAVE_INT64_T - hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32)); -#endif } - len += sizeof(struct client_hdrext_starttime_fq); } if (isIsochronous(client)) { - flags |= HEADER_VERSION2; upperflags |= HEADER_ISOCH; if (isFullDuplex(client) || isReverse(client)) { upperflags |= HEADER_ISOCH_SETTINGS; @@ -1758,32 +1828,11 @@ len += sizeof(struct client_hdrext_isoch_settings); } } - if (isBWSet(client)) { - flags |= (HEADER_EXTEND | HEADER_VERSION2); - hdr->extend.lRate = htonl((uint32_t)client->mUDPRate); -#ifdef HAVE_INT64_T - hdr->extend.uRate = htonl(((uint32_t)(client->mUDPRate >> 32)) << 8); -#endif + if (isReverse(client) || isFullDuplex(client)) { + flags |= HEADER_VERSION2; } - if (isPeerVerDetect(client)) { - flags |= (HEADER_EXTEND | HEADER_V2PEERDETECT); - } - if (flags & (HEADER_EXTEND | HEADER_VERSION2)) { - // Write flags to header so the listener can determine the tests requested - hdr->extend.upperflags = htons(upperflags); - hdr->extend.lowerflags = htons(lowerflags); - hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX); - hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX); - len += sizeof(struct client_hdrext); - } - if (!(flags & HEADER_VERSION2) && (client->mMode != kTest_Normal)) { - flags |= HEADER_VERSION1; - if (client->mMode == kTest_DualTest) - flags |= RUN_NOW; - } - if (flags & (HEADER_VERSION1 | HEADER_VERSION2)) { - len += Settings_GenerateClientHdrV1(client, &hdr->base); - } + hdr->extend.upperflags = htons(upperflags); + hdr->extend.lowerflags = htons(lowerflags); if (len > 0) { flags |= HEADER_LEN_BIT; } diff -ruN old/src/SocketAddr.c new/src/SocketAddr.c --- old/src/SocketAddr.c 2020-10-03 02:42:49.000000000 +0800 +++ new/src/SocketAddr.c 2020-11-05 09:06:26.000000000 +0800 @@ -67,7 +67,7 @@ * if that is what is desired. * ------------------------------------------------------------------- */ -void SockAddr_remoteAddr(struct thread_Settings *inSettings) { +void SockAddr_remoteAddr (struct thread_Settings *inSettings) { SockAddr_zeroAddress(&inSettings->peer); if (inSettings->mHost != NULL) { SockAddr_setHostname(inSettings->mHost, &inSettings->peer, isIPV6(inSettings)); @@ -95,7 +95,7 @@ } // end SocketAddr -void SockAddr_localAddr(struct thread_Settings *inSettings) { +void SockAddr_localAddr (struct thread_Settings *inSettings) { SockAddr_zeroAddress(&inSettings->local); if (inSettings->mLocalhost != NULL) { @@ -176,7 +176,7 @@ * Resolve the hostname address and fill it in. * ------------------------------------------------------------------- */ -void SockAddr_setHostname(const char* inHostname, iperf_sockaddr *inSockAddr, int isIPv6) { +void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, int isIPv6) { // ..I think this works for both ipv6 & ipv4... we'll see #if defined(HAVE_IPV6) { @@ -275,7 +275,7 @@ /* ------------------------------------------------------------------- * Copy the IP address into the string. * ------------------------------------------------------------------- */ -void SockAddr_getHostAddress(iperf_sockaddr *inSockAddr, char* outAddress, +void SockAddr_getHostAddress (iperf_sockaddr *inSockAddr, char* outAddress, size_t len) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) { inet_ntop(AF_INET, &(((struct sockaddr_in*) inSockAddr)->sin_addr), @@ -294,7 +294,7 @@ * Set the address to any (generally all zeros). * ------------------------------------------------------------------- */ -void SockAddr_setAddressAny(iperf_sockaddr *inSockAddr) { +void SockAddr_setAddressAny (iperf_sockaddr *inSockAddr) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) memset(&(((struct sockaddr_in*) inSockAddr)->sin_addr), 0, sizeof(struct in_addr)); @@ -310,7 +310,7 @@ * Incr the address by value * ------------------------------------------------------------------- */ -void SockAddr_incrAddress(iperf_sockaddr *inSockAddr, int value) { +void SockAddr_incrAddress (iperf_sockaddr *inSockAddr, int value) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) ((struct sockaddr_in *)inSockAddr)->sin_addr.s_addr += htonl(value); #if defined(HAVE_IPV6) @@ -327,7 +327,7 @@ * Set the port to the given port. Handles the byte swapping. * ------------------------------------------------------------------- */ -void SockAddr_setPort(iperf_sockaddr *inSockAddr, unsigned short inPort) { +void SockAddr_setPort (iperf_sockaddr *inSockAddr, unsigned short inPort) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) ((struct sockaddr_in*) inSockAddr)->sin_port = htons(inPort); #if defined(HAVE_IPV6) @@ -342,7 +342,7 @@ * Set the port to zero, which lets the OS pick the port. * ------------------------------------------------------------------- */ -void SockAddr_setPortAny(iperf_sockaddr *inSockAddr) { +void SockAddr_setPortAny (iperf_sockaddr *inSockAddr) { SockAddr_setPort(inSockAddr, 0); } // end setPortAny @@ -351,7 +351,7 @@ * Return the port. Handles the byte swapping. * ------------------------------------------------------------------- */ -unsigned short SockAddr_getPort(iperf_sockaddr *inSockAddr) { +unsigned short SockAddr_getPort (iperf_sockaddr *inSockAddr) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) return ntohs(((struct sockaddr_in*) inSockAddr)->sin_port); #if defined(HAVE_IPV6) @@ -367,7 +367,7 @@ * Return the IPv4 Internet Address from the sockaddr_in structure * ------------------------------------------------------------------- */ -struct in_addr* SockAddr_get_in_addr(iperf_sockaddr *inSockAddr) { +struct in_addr* SockAddr_get_in_addr (iperf_sockaddr *inSockAddr) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) return &(((struct sockaddr_in*) inSockAddr)->sin_addr); @@ -379,7 +379,7 @@ * Return the IPv6 Internet Address from the sockaddr_in6 structure * ------------------------------------------------------------------- */ #ifdef HAVE_IPV6 -struct in6_addr* SockAddr_get_in6_addr(iperf_sockaddr *inSockAddr) { +struct in6_addr* SockAddr_get_in6_addr (iperf_sockaddr *inSockAddr) { if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) return &(((struct sockaddr_in6*) inSockAddr)->sin6_addr); @@ -393,8 +393,7 @@ * Return the size of the appropriate address structure. * ------------------------------------------------------------------- */ -Socklen_t SockAddr_get_sizeof_sockaddr(iperf_sockaddr *inSockAddr) { - +Socklen_t SockAddr_get_sizeof_sockaddr (iperf_sockaddr *inSockAddr) { #if defined(HAVE_IPV6) if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) { return(sizeof(struct sockaddr_in6)); @@ -409,8 +408,7 @@ * Return if IPv6 socket * ------------------------------------------------------------------- */ -int SockAddr_isIPv6(iperf_sockaddr *inSockAddr) { - +int SockAddr_isIPv6 (iperf_sockaddr *inSockAddr) { #if defined(HAVE_IPV6) if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) { return 1; @@ -424,8 +422,7 @@ * Return true if the address is multicast ip address. * ------------------------------------------------------------------- */ -int SockAddr_isMulticast(iperf_sockaddr *inSockAddr) { - +int SockAddr_isMulticast (iperf_sockaddr *inSockAddr) { #if defined(HAVE_IPV6) if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) { return(IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr))); @@ -459,23 +456,20 @@ /* ------------------------------------------------------------------- * Zero out the address structure. * ------------------------------------------------------------------- */ - -void SockAddr_zeroAddress(iperf_sockaddr *inSockAddr) { +void SockAddr_zeroAddress (iperf_sockaddr *inSockAddr) { memset(inSockAddr, 0, sizeof(iperf_sockaddr)); } -int SockAddr_isZeroAddress(iperf_sockaddr *inSockAddr) { +int SockAddr_isZeroAddress (iperf_sockaddr *inSockAddr) { iperf_sockaddr zeroSockAddr; memset(&zeroSockAddr, 0, sizeof(iperf_sockaddr)); return(memcmp((void *)inSockAddr, (void *)&zeroSockAddr, sizeof(iperf_sockaddr))); } -// zeroAddress - /* ------------------------------------------------------------------- * Compare two sockaddrs and return true if they are equal * ------------------------------------------------------------------- */ -int SockAddr_are_Equal(iperf_sockaddr *first, iperf_sockaddr *second) { +int SockAddr_are_Equal (iperf_sockaddr *first, iperf_sockaddr *second) { if (((struct sockaddr*)first)->sa_family == AF_INET && ((struct sockaddr*)second)->sa_family == AF_INET) { // compare IPv4 adresses return(((long) ((struct sockaddr_in*)first)->sin_addr.s_addr == (long) ((struct sockaddr_in*)second)->sin_addr.s_addr) @@ -495,7 +489,7 @@ /* ------------------------------------------------------------------- * Compare two sockaddrs and return true if the hosts are equal * ------------------------------------------------------------------- */ -int SockAddr_Hostare_Equal(iperf_sockaddr* first, iperf_sockaddr *second) { +int SockAddr_Hostare_Equal (iperf_sockaddr* first, iperf_sockaddr *second) { if (((struct sockaddr*)first)->sa_family == AF_INET && ((struct sockaddr*)second)->sa_family == AF_INET) { // compare IPv4 adresses return((long) ((struct sockaddr_in*)first)->sin_addr.s_addr == @@ -521,7 +515,7 @@ * Store (and cache) the results in the thread settings structure * Return 0 if set, -1 if not * ------------------------------------------------------------------- */ -int SockAddr_Ifrname(struct thread_Settings *inSettings) { +int SockAddr_Ifrname (struct thread_Settings *inSettings) { #ifdef HAVE_IFADDRS_H if (inSettings->mIfrname == NULL) { struct sockaddr_storage myaddr; diff -ruN old/src/isochronous.cpp new/src/isochronous.cpp --- old/src/isochronous.cpp 2020-10-03 02:42:49.000000000 +0800 +++ new/src/isochronous.cpp 2020-11-05 09:06:26.000000000 +0800 @@ -86,7 +86,11 @@ #endif slip++; } +#ifndef WIN32 int rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &txtime_ts, NULL); +#else + int rc = clock_nanosleep(0, TIMER_ABSTIME, &txtime_ts, NULL); +#endif if (rc) { fprintf(stderr, "txstart failed clock_nanosleep()=%d\n", rc); } diff -ruN old/src/main.cpp new/src/main.cpp --- old/src/main.cpp 2020-10-03 02:42:49.000000000 +0800 +++ new/src/main.cpp 2020-11-05 09:06:26.000000000 +0800 @@ -69,6 +69,7 @@ #include "Listener.hpp" #include "active_hosts.h" #include "util.h" +#include "Reporter.h" #ifdef WIN32 #include "service.h" @@ -90,7 +91,7 @@ // as identifier for SUM reports int groupID = 0; // Mutex to protect access to the above ID - Mutex groupCond; + Mutex transferid_mutex; // Condition used to signal the reporter thread // when a packet ring is full. Shouldn't really // be needed but is "belts and suspeners" @@ -130,7 +131,6 @@ my_signal(SIGINT, Sig_Interupt); #ifndef WIN32 my_signal(SIGALRM, Sig_Interupt); - // Ignore broken pipes signal(SIGPIPE,SIG_IGN); #else @@ -140,7 +140,6 @@ WARN_errno(rc == SOCKET_ERROR, "WSAStartup"); if (rc == SOCKET_ERROR) return 0; - // Tell windows we want to handle our own signals SetConsoleCtrlHandler(sig_dispatcher, true); #endif @@ -153,6 +152,8 @@ Mutex_Initialize(&packetringdebug_mutex); Mutex_Initialize(&thread_debug_mutex); #endif + Mutex_Initialize(&transferid_mutex); + // Initialize reporter thread mutex reporter_state.ready = 0; threads_start.ready = 0; @@ -172,7 +173,6 @@ // Allocate the "global" settings ext_gSettings = new thread_Settings; - // Default reporting mode here to avoid unitialized warnings // this won't be the actual mode ThreadMode ReporterThreadMode = kMode_Reporter; @@ -207,6 +207,21 @@ return 0; } + if (!isSTDOUT(ext_gSettings)) { +#ifdef HAVE_FREOPEN + FILE *fd; + fprintf(stdout, "Output from stdout and stderr will be redirected to file %s\n", ext_gSettings->mOutputFileName); + fflush(stdout); + fd = freopen(ext_gSettings->mOutputFileName, "w", stdout); + FAIL_errno(fd == NULL, "freopen stdout\n", ext_gSettings); + fd = freopen(ext_gSettings->mOutputFileName, "w", stderr); + FAIL_errno(fd == NULL, "freopen stderr\n", ext_gSettings); +#else + fprintf(stderr, "Output to file not supported\n"); +#endif + + } + unsetReport(ext_gSettings); switch (ext_gSettings->mThreadMode) { case kMode_Client : @@ -269,10 +284,8 @@ // No need to make a reporter thread because we don't have threads thread_start(ext_gSettings); #endif - // wait for other (client, server) threads to complete thread_joinall(); - // all done! return 0; } // end main @@ -282,7 +295,7 @@ * respond appropriately.. [static] * ------------------------------------------------------------------- */ -void Sig_Interupt(int inSigno) { +void Sig_Interupt (int inSigno) { #ifdef HAVE_THREAD // We try to not allow a single interrupt handled by multiple threads // to completely kill the app so we save off the first thread ID @@ -314,7 +327,7 @@ * either by exit() or terminating main(). * ------------------------------------------------------------------- */ -void cleanup(void) { +void cleanup (void) { #ifdef WIN32 // Shutdown Winsock WSACleanup(); @@ -332,6 +345,7 @@ Mutex_Destroy(&packetringdebug_mutex); Mutex_Destroy(&thread_debug_mutex); #endif + Mutex_Destroy(&transferid_mutex); // shutdown the thread subsystem thread_destroy(); } // end cleanup @@ -419,7 +433,6 @@ // wait for other (client, server) threads to complete thread_joinall(); } - // // FUNCTION: ServiceStop diff -ruN old/src/packet_ring.c new/src/packet_ring.c --- old/src/packet_ring.c 2020-10-03 02:42:49.000000000 +0800 +++ new/src/packet_ring.c 2020-11-05 09:06:26.000000000 +0800 @@ -86,7 +86,6 @@ return (pr); } - inline void packetring_enqueue (struct PacketRing *pr, struct ReportStruct *metapacket) { while (((pr->producer == pr->maxcount) && (pr->consumer == 0)) || \ ((pr->producer + 1) == pr->consumer)) { diff -ruN old/src/stdio.c new/src/stdio.c --- old/src/stdio.c 2020-10-03 02:42:49.000000000 +0800 +++ new/src/stdio.c 2020-11-07 02:12:55.000000000 +0800 @@ -261,6 +261,7 @@ int conv = 0; const char* suffix; const char* format; + double tmpNum; /* convert to bits for [bkmga] */ if (!isupper((int)inFormat)) { @@ -277,7 +278,7 @@ default: case 'A': { - double tmpNum = inNum; + tmpNum = (inNum < 0.0 ? (-1 * inNum) : inNum); conv = kConv_Unit; if (isupper((int)inFormat)) { @@ -296,21 +297,22 @@ } if (!isupper((int)inFormat)) { - inNum *= kConversionForBits[ conv ]; + inNum *= kConversionForBits[conv]; suffix = kLabel_bit[conv]; } else { - inNum *= kConversion [conv]; - suffix = kLabel_Byte[ conv ]; + inNum *= kConversion[conv]; + suffix = kLabel_Byte[conv]; } /* print such that we always fit in 4 places */ - if (inNum < 0.9995) { /* 9.995 would be rounded to 10.0 */ + tmpNum = (inNum < 0.0 ? (-1 * inNum) : inNum); + if (tmpNum < 0.9995) { /* 9.995 would be rounded to 10.0 */ format = "%4.3f %s"; /* #.## */ - } else if (inNum < 9.995) { /* 9.995 would be rounded to 10.0 */ + } else if (tmpNum < 9.995) { /* 9.995 would be rounded to 10.0 */ format = "%4.2f %s"; /* #.## */ - } else if (inNum < 99.95) { /* 99.95 would be rounded to 100 */ + } else if (tmpNum < 99.95) { /* 99.95 would be rounded to 100 */ format = "%4.1f %s"; /* ##.# */ - } else if (inNum < 999.5) { /* 999.5 would be rounded to 1000 */ + } else if (tmpNum < 999.5) { /* 999.5 would be rounded to 1000 */ format = "%4.0f %s"; /* ### */ } else { /* 1000-1024 fits in 4 places * If not using Adaptive sizes then