diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-02-12 08:40:57 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-02-12 08:40:57 +0000 |
commit | e2fdf6c58545799d7522fabf7a2999f113bf3541 (patch) | |
tree | 4eb7533bf42fa54fcc6a1b2396f1abdfe4102484 /src | |
parent | Adding upstream version 2.10.0. (diff) | |
download | dnsperf-e2fdf6c58545799d7522fabf7a2999f113bf3541.tar.xz dnsperf-e2fdf6c58545799d7522fabf7a2999f113bf3541.zip |
Adding upstream version 2.11.0.upstream/2.11.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/Makefile.in | 77 | ||||
-rw-r--r-- | src/buffer.h | 2 | ||||
-rw-r--r-- | src/config.h.in | 3 | ||||
-rw-r--r-- | src/datafile.c | 2 | ||||
-rw-r--r-- | src/datafile.h | 2 | ||||
-rw-r--r-- | src/dns.c | 2 | ||||
-rw-r--r-- | src/dns.h | 2 | ||||
-rw-r--r-- | src/dnsperf.1.in | 31 | ||||
-rw-r--r-- | src/dnsperf.c | 264 | ||||
-rw-r--r-- | src/edns.c | 2 | ||||
-rw-r--r-- | src/edns.h | 2 | ||||
-rw-r--r-- | src/ext/hg64.c | 327 | ||||
-rw-r--r-- | src/ext/hg64.h | 88 | ||||
-rw-r--r-- | src/ext/parse_uri.c (renamed from src/parse_uri.c) | 0 | ||||
-rw-r--r-- | src/ext/parse_uri.h (renamed from src/parse_uri.h) | 0 | ||||
-rw-r--r-- | src/list.h | 2 | ||||
-rw-r--r-- | src/log.c | 2 | ||||
-rw-r--r-- | src/log.h | 2 | ||||
-rw-r--r-- | src/net.c | 2 | ||||
-rw-r--r-- | src/net.h | 2 | ||||
-rw-r--r-- | src/net_doh.c | 6 | ||||
-rw-r--r-- | src/net_dot.c | 4 | ||||
-rw-r--r-- | src/net_tcp.c | 4 | ||||
-rw-r--r-- | src/net_udp.c | 2 | ||||
-rw-r--r-- | src/opt.c | 40 | ||||
-rw-r--r-- | src/opt.h | 2 | ||||
-rw-r--r-- | src/os.c | 2 | ||||
-rw-r--r-- | src/os.h | 2 | ||||
-rw-r--r-- | src/qtype.c | 2 | ||||
-rw-r--r-- | src/qtype.h | 2 | ||||
-rwxr-xr-x | src/resperf-report | 2 | ||||
-rw-r--r-- | src/resperf.1.in | 2 | ||||
-rw-r--r-- | src/resperf.c | 2 | ||||
-rw-r--r-- | src/result.h | 2 | ||||
-rw-r--r-- | src/strerror.c | 2 | ||||
-rw-r--r-- | src/strerror.h | 2 | ||||
-rwxr-xr-x | src/test/test1.sh | 5 | ||||
-rw-r--r-- | src/tsig.c | 2 | ||||
-rw-r--r-- | src/tsig.h | 2 | ||||
-rw-r--r-- | src/util.h | 2 |
41 files changed, 801 insertions, 114 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 119e187..810f99b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -# Copyright 2019-2022 OARC, Inc. +# Copyright 2019-2023 OARC, Inc. # Copyright 2017-2018 Akamai Technologies # Copyright 2006-2016 Nominum, Inc. # All rights reserved. @@ -30,9 +30,15 @@ bin_PROGRAMS = dnsperf resperf dist_bin_SCRIPTS = resperf-report _libperf_sources = datafile.c dns.c log.c net.c opt.c os.c strerror.c qtype.c \ - edns.c tsig.c net_udp.c net_tcp.c net_dot.c net_doh.c parse_uri.c + edns.c tsig.c net_udp.c net_tcp.c net_dot.c net_doh.c ext/parse_uri.c + _libperf_headers = datafile.h dns.h log.h net.h opt.h os.h util.h strerror.h \ - list.h result.h buffer.h qtype.h edns.h tsig.h parse_uri.h + list.h result.h buffer.h qtype.h edns.h tsig.h ext/parse_uri.h + +if HAVE_STDATOMIC +_libperf_sources += ext/hg64.c +_libperf_headers += ext/hg64.h +endif dnsperf_SOURCES = $(_libperf_sources) dnsperf.c dist_dnsperf_SOURCES = $(_libperf_headers) diff --git a/src/Makefile.in b/src/Makefile.in index 1772980..1b510c7 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -14,7 +14,7 @@ @SET_MAKE@ -# Copyright 2019-2022 OARC, Inc. +# Copyright 2019-2023 OARC, Inc. # Copyright 2017-2018 Akamai Technologies # Copyright 2006-2016 Nominum, Inc. # All rights reserved. @@ -107,6 +107,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = dnsperf$(EXEEXT) resperf$(EXEEXT) +@HAVE_STDATOMIC_TRUE@am__append_1 = ext/hg64.c +@HAVE_STDATOMIC_TRUE@am__append_2 = ext/hg64.h subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_flag.m4 \ @@ -127,14 +129,23 @@ CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(man1dir)" PROGRAMS = $(bin_PROGRAMS) -am__objects_1 = datafile.$(OBJEXT) dns.$(OBJEXT) log.$(OBJEXT) \ +am__dnsperf_SOURCES_DIST = datafile.c dns.c log.c net.c opt.c os.c \ + strerror.c qtype.c edns.c tsig.c net_udp.c net_tcp.c net_dot.c \ + net_doh.c ext/parse_uri.c ext/hg64.c dnsperf.c +am__dirstamp = $(am__leading_dot)dirstamp +@HAVE_STDATOMIC_TRUE@am__objects_1 = ext/hg64.$(OBJEXT) +am__objects_2 = datafile.$(OBJEXT) dns.$(OBJEXT) log.$(OBJEXT) \ net.$(OBJEXT) opt.$(OBJEXT) os.$(OBJEXT) strerror.$(OBJEXT) \ qtype.$(OBJEXT) edns.$(OBJEXT) tsig.$(OBJEXT) \ net_udp.$(OBJEXT) net_tcp.$(OBJEXT) net_dot.$(OBJEXT) \ - net_doh.$(OBJEXT) parse_uri.$(OBJEXT) -am_dnsperf_OBJECTS = $(am__objects_1) dnsperf.$(OBJEXT) -am__objects_2 = -dist_dnsperf_OBJECTS = $(am__objects_2) + net_doh.$(OBJEXT) ext/parse_uri.$(OBJEXT) $(am__objects_1) +am_dnsperf_OBJECTS = $(am__objects_2) dnsperf.$(OBJEXT) +am__dist_dnsperf_SOURCES_DIST = datafile.h dns.h log.h net.h opt.h \ + os.h util.h strerror.h list.h result.h buffer.h qtype.h edns.h \ + tsig.h ext/parse_uri.h ext/hg64.h +am__objects_3 = +am__objects_4 = $(am__objects_3) +dist_dnsperf_OBJECTS = $(am__objects_4) dnsperf_OBJECTS = $(am_dnsperf_OBJECTS) $(dist_dnsperf_OBJECTS) am__DEPENDENCIES_1 = dnsperf_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -144,8 +155,14 @@ AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = -am_resperf_OBJECTS = $(am__objects_1) resperf.$(OBJEXT) -dist_resperf_OBJECTS = $(am__objects_2) +am__resperf_SOURCES_DIST = datafile.c dns.c log.c net.c opt.c os.c \ + strerror.c qtype.c edns.c tsig.c net_udp.c net_tcp.c net_dot.c \ + net_doh.c ext/parse_uri.c ext/hg64.c resperf.c +am_resperf_OBJECTS = $(am__objects_2) resperf.$(OBJEXT) +am__dist_resperf_SOURCES_DIST = datafile.h dns.h log.h net.h opt.h \ + os.h util.h strerror.h list.h result.h buffer.h qtype.h edns.h \ + tsig.h ext/parse_uri.h ext/hg64.h +dist_resperf_OBJECTS = $(am__objects_4) resperf_OBJECTS = $(am_resperf_OBJECTS) $(dist_resperf_OBJECTS) resperf_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -198,9 +215,9 @@ am__depfiles_remade = ./$(DEPDIR)/datafile.Po ./$(DEPDIR)/dns.Po \ ./$(DEPDIR)/net.Po ./$(DEPDIR)/net_doh.Po \ ./$(DEPDIR)/net_dot.Po ./$(DEPDIR)/net_tcp.Po \ ./$(DEPDIR)/net_udp.Po ./$(DEPDIR)/opt.Po ./$(DEPDIR)/os.Po \ - ./$(DEPDIR)/parse_uri.Po ./$(DEPDIR)/qtype.Po \ - ./$(DEPDIR)/resperf.Po ./$(DEPDIR)/strerror.Po \ - ./$(DEPDIR)/tsig.Po + ./$(DEPDIR)/qtype.Po ./$(DEPDIR)/resperf.Po \ + ./$(DEPDIR)/strerror.Po ./$(DEPDIR)/tsig.Po \ + ext/$(DEPDIR)/hg64.Po ext/$(DEPDIR)/parse_uri.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -222,8 +239,9 @@ am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(dnsperf_SOURCES) $(dist_dnsperf_SOURCES) \ $(resperf_SOURCES) $(dist_resperf_SOURCES) -DIST_SOURCES = $(dnsperf_SOURCES) $(dist_dnsperf_SOURCES) \ - $(resperf_SOURCES) $(dist_resperf_SOURCES) +DIST_SOURCES = $(am__dnsperf_SOURCES_DIST) \ + $(am__dist_dnsperf_SOURCES_DIST) $(am__resperf_SOURCES_DIST) \ + $(am__dist_resperf_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ @@ -440,12 +458,12 @@ AM_CFLAGS = -I$(srcdir) \ EXTRA_DIST = dnsperf.1.in resperf-report resperf.1.in dist_bin_SCRIPTS = resperf-report -_libperf_sources = datafile.c dns.c log.c net.c opt.c os.c strerror.c qtype.c \ - edns.c tsig.c net_udp.c net_tcp.c net_dot.c net_doh.c parse_uri.c - -_libperf_headers = datafile.h dns.h log.h net.h opt.h os.h util.h strerror.h \ - list.h result.h buffer.h qtype.h edns.h tsig.h parse_uri.h - +_libperf_sources = datafile.c dns.c log.c net.c opt.c os.c strerror.c \ + qtype.c edns.c tsig.c net_udp.c net_tcp.c net_dot.c net_doh.c \ + ext/parse_uri.c $(am__append_1) +_libperf_headers = datafile.h dns.h log.h net.h opt.h os.h util.h \ + strerror.h list.h result.h buffer.h qtype.h edns.h tsig.h \ + ext/parse_uri.h $(am__append_2) dnsperf_SOURCES = $(_libperf_sources) dnsperf.c dist_dnsperf_SOURCES = $(_libperf_headers) dnsperf_LDADD = $(PTHREAD_LIBS) $(libssl_LIBS) $(libcrypto_LIBS) \ @@ -555,6 +573,15 @@ clean-binPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list +ext/$(am__dirstamp): + @$(MKDIR_P) ext + @: > ext/$(am__dirstamp) +ext/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ext/$(DEPDIR) + @: > ext/$(DEPDIR)/$(am__dirstamp) +ext/parse_uri.$(OBJEXT): ext/$(am__dirstamp) \ + ext/$(DEPDIR)/$(am__dirstamp) +ext/hg64.$(OBJEXT): ext/$(am__dirstamp) ext/$(DEPDIR)/$(am__dirstamp) dnsperf$(EXEEXT): $(dnsperf_OBJECTS) $(dnsperf_DEPENDENCIES) $(EXTRA_dnsperf_DEPENDENCIES) @rm -f dnsperf$(EXEEXT) @@ -601,6 +628,7 @@ uninstall-dist_binSCRIPTS: mostlyclean-compile: -rm -f *.$(OBJEXT) + -rm -f ext/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @@ -617,11 +645,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_udp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_uri.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qtype.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resperf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strerror.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@ext/$(DEPDIR)/hg64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@ext/$(DEPDIR)/parse_uri.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @@ -893,6 +922,8 @@ clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f ext/$(DEPDIR)/$(am__dirstamp) + -rm -f ext/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -916,11 +947,12 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/net_udp.Po -rm -f ./$(DEPDIR)/opt.Po -rm -f ./$(DEPDIR)/os.Po - -rm -f ./$(DEPDIR)/parse_uri.Po -rm -f ./$(DEPDIR)/qtype.Po -rm -f ./$(DEPDIR)/resperf.Po -rm -f ./$(DEPDIR)/strerror.Po -rm -f ./$(DEPDIR)/tsig.Po + -rm -f ext/$(DEPDIR)/hg64.Po + -rm -f ext/$(DEPDIR)/parse_uri.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-tags @@ -982,11 +1014,12 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/net_udp.Po -rm -f ./$(DEPDIR)/opt.Po -rm -f ./$(DEPDIR)/os.Po - -rm -f ./$(DEPDIR)/parse_uri.Po -rm -f ./$(DEPDIR)/qtype.Po -rm -f ./$(DEPDIR)/resperf.Po -rm -f ./$(DEPDIR)/strerror.Po -rm -f ./$(DEPDIR)/tsig.Po + -rm -f ext/$(DEPDIR)/hg64.Po + -rm -f ext/$(DEPDIR)/parse_uri.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/src/buffer.h b/src/buffer.h index 52bfef2..1cc5962 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/config.h.in b/src/config.h.in index 49e5f44..bdd273f 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -36,6 +36,9 @@ /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT +/* Define to 1 if you have the <stdatomic.h> header file. */ +#undef HAVE_STDATOMIC_H + /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H diff --git a/src/datafile.c b/src/datafile.c index 06ea0ea..85e34b8 100644 --- a/src/datafile.c +++ b/src/datafile.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/datafile.h b/src/datafile.h index a1b737e..bebc1e9 100644 --- a/src/datafile.h +++ b/src/datafile.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/dnsperf.1.in b/src/dnsperf.1.in index 9ae24bd..c44a5f3 100644 --- a/src/dnsperf.1.in +++ b/src/dnsperf.1.in @@ -1,4 +1,4 @@ -.\" Copyright 2019-2022 OARC, Inc. +.\" Copyright 2019-2023 OARC, Inc. .\" Copyright 2017-2018 Akamai Technologies .\" Copyright 2006-2016 Nominum, Inc. .\" All rights reserved. @@ -341,8 +341,9 @@ The default is the loopback address, 127.0.0.1. \fB-S \fIstats_interval\fR .br .RS -If this parameter is specified, a count of the number of queries per second -during the interval will be printed out every stats_interval seconds. +If this parameter is specified, a count of the number of answers received +per second during the interval will be printed out every \fIstats_interval\fR +seconds. .RE \fB-t \fItimeout\fR @@ -458,7 +459,6 @@ Following type are available. .br \fBunexpected\fR: Suppress messages about answers with an unexpected message ID .RE - \fBnum-queries-per-conn=\fINUMBER\fR .br .RS @@ -474,6 +474,29 @@ Waiting for responses may timeout and the timeout used for this is the same as specified by \fB-t\fR. Note that this option is only useful for connection oriented protocols. .RE + +\fBverbose-interval-stats\fR +.br +.RS +Change the statistics format of \fB-S\fR to that shown at end of run. + +\fIPlease note\fR: Min/max values for latency and connections are not +available in interval statistics. +Number of answers received within \fIstats_interval\fR can legitimately +exceed number of queries sent, depending on answer latency, configured +\fItimeout\fR, and \fIstats_interval\fR. +.RE + +\fBlatency-histogram\fR +.br +.RS +Print detailed latency histograms for DNS answers and connections. +Latency is quantized into bins with roughly 3 % resolution, and latency +range for individual bins increases logarithmically. +This is done to to limit amount of memory required for histograms +and also allows to visualize latency using logarithmic percentile histograms +with minimal postprocessing. +.RE .SH "SEE ALSO" \fBresperf\fR(1) .SH AUTHOR diff --git a/src/dnsperf.c b/src/dnsperf.c index 7d10212..90b7874 100644 --- a/src/dnsperf.c +++ b/src/dnsperf.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -32,6 +32,10 @@ #include "util.h" #include "list.h" #include "buffer.h" +#if HAVE_STDATOMIC_H +#include "ext/hg64.h" +#define USE_HISTOGRAMS +#endif #include <inttypes.h> #include <errno.h> @@ -49,6 +53,8 @@ #include <openssl/conf.h> #include <openssl/err.h> +#define HISTOGRAM_SIGBITS 5 /* about 3 % latency precision */ + #define DEFAULT_SERVER_NAME "127.0.0.1" #define DEFAULT_SERVER_PORT 53 #define DEFAULT_SERVER_DOT_PORT 853 @@ -85,6 +91,7 @@ typedef struct { uint32_t max_outstanding; uint32_t max_qps; uint64_t stats_interval; + bool verbose_interval_stats; bool updates; bool binary_input; perf_input_format_t input_format; @@ -92,6 +99,9 @@ typedef struct { enum perf_net_mode mode; perf_suppress_t suppress; size_t num_queries_per_conn; +#ifdef USE_HISTOGRAMS + bool latency_histogram; +#endif } config_t; typedef struct { @@ -101,8 +111,9 @@ typedef struct { struct timespec stop_time_ns; } times_t; +#define DNSPERF_STATS_RCODECOUNTS 16 typedef struct { - uint64_t rcodecounts[16]; + uint64_t rcodecounts[DNSPERF_STATS_RCODECOUNTS]; uint64_t num_sent; uint64_t num_interrupted; @@ -124,6 +135,11 @@ typedef struct { uint64_t conn_latency_sum_squares; uint64_t conn_latency_min; uint64_t conn_latency_max; + +#ifdef USE_HISTOGRAMS + hg64* latency; + hg64* conn_latency; +#endif } stats_t; typedef perf_list(struct query_info) query_list; @@ -257,7 +273,86 @@ stddev(uint64_t sum_of_squares, uint64_t sum, uint64_t total) } static void -print_statistics(const config_t* config, const times_t* times, stats_t* stats) +diff_stats(const config_t* config, stats_t* last, stats_t* now, stats_t* diff) +{ + int i = 0; + for (; i < DNSPERF_STATS_RCODECOUNTS; i++) { + diff->rcodecounts[i] = now->rcodecounts[i] - last->rcodecounts[i]; + } + + diff->num_sent = now->num_sent - last->num_sent; + diff->num_interrupted = now->num_interrupted - last->num_interrupted; + diff->num_timedout = now->num_timedout - last->num_timedout; + diff->num_completed = now->num_completed - last->num_completed; + + diff->total_request_size = now->total_request_size - last->total_request_size; + diff->total_response_size = now->total_response_size - last->total_response_size; + + diff->latency_sum = now->latency_sum - last->latency_sum; + diff->latency_sum_squares = now->latency_sum_squares - last->latency_sum_squares; + diff->latency_min = 0; /* not enough data */ + diff->latency_max = 0; + + diff->num_conn_reconnect = now->num_conn_reconnect - last->num_conn_reconnect; + diff->num_conn_completed = now->num_conn_completed - last->num_conn_completed; + + diff->conn_latency_sum = now->conn_latency_sum - last->conn_latency_sum; + diff->conn_latency_sum_squares = now->conn_latency_sum_squares - last->conn_latency_sum_squares; + diff->conn_latency_min = 0; + diff->conn_latency_max = 0; + +#ifdef USE_HISTOGRAMS + if (config->latency_histogram) { + free(diff->latency); + diff->latency = hg64_create(HISTOGRAM_SIGBITS); + if (last->latency) { + hg64_diff(now->latency, last->latency, diff->latency); + } else { /* first sample */ + hg64_merge(diff->latency, now->latency); + } + hg64_get(diff->latency, hg64_min_key(diff->latency), &diff->latency_min, NULL, NULL); + hg64_get(diff->latency, hg64_max_key(diff->latency), NULL, &diff->latency_max, NULL); + + free(diff->conn_latency); + diff->conn_latency = hg64_create(HISTOGRAM_SIGBITS); + if (last->conn_latency) { + hg64_diff(now->conn_latency, last->conn_latency, diff->conn_latency); + } else { /* first sample */ + hg64_merge(diff->conn_latency, now->conn_latency); + } + hg64_get(diff->conn_latency, hg64_min_key(diff->conn_latency), &diff->conn_latency_min, NULL, NULL); + hg64_get(diff->conn_latency, hg64_max_key(diff->conn_latency), NULL, &diff->conn_latency_max, NULL); + } +#endif +} + +#ifdef USE_HISTOGRAMS +static void +print_histogram(hg64* histogram, const char* const desc) +{ + printf(" Latency bucket (s): %s\n", desc); + uint64_t pmin, pmax, pcount; + for (unsigned key = 0; + hg64_get(histogram, key, &pmin, &pmax, &pcount) == true; + key = hg64_next(histogram, key)) { + if (pcount == 0) + continue; + printf(" %" PRIu64 ".%06" PRIu64 " - %" PRIu64 ".%06" PRIu64 ": %" PRIu64 "\n", + pmin / MILLION, + pmin % MILLION, + pmax / MILLION, + pmax % MILLION, + pcount); + }; +} +#endif + +/* + * now != 0 is call to print stats in the middle of test run. + * min-max values are not available on per-interval basis, so skip them. + */ +static void +print_statistics(const config_t* config, const times_t* times, stats_t* stats, uint64_t now, uint64_t interval_time) { const char* units; uint64_t run_time; @@ -267,9 +362,12 @@ print_statistics(const config_t* config, const times_t* times, stats_t* stats) units = config->updates ? "Updates" : "Queries"; - run_time = times->end_time - times->start_time; + if (now) + run_time = now - times->start_time; + else + run_time = times->end_time - times->start_time; - printf("Statistics:\n\n"); + printf("%sStatistics:\n\n", now ? "Interval " : ""); printf(" %s sent: %" PRIu64 "\n", units, stats->num_sent); @@ -287,7 +385,7 @@ print_statistics(const config_t* config, const times_t* times, stats_t* stats) printf(" Response codes: "); first_rcode = true; - for (i = 0; i < 16; i++) { + for (i = 0; i < DNSPERF_STATS_RCODECOUNTS; i++) { if (stats->rcodecounts[i] == 0) continue; if (first_rcode) @@ -308,23 +406,33 @@ print_statistics(const config_t* config, const times_t* times, stats_t* stats) (unsigned int)(run_time / MILLION), (unsigned int)(run_time % MILLION)); printf(" %s per second: %.6lf\n", units, - PERF_SAFE_DIV(stats->num_completed, (((double)run_time) / MILLION))); + PERF_SAFE_DIV(stats->num_completed, (((double)(now ? interval_time : run_time) / MILLION)))); printf("\n"); latency_avg = PERF_SAFE_DIV(stats->latency_sum, stats->num_completed); - printf(" Average Latency (s): %u.%06u (min %u.%06u, max %u.%06u)\n", + printf(" Average Latency (s): %u.%06u", (unsigned int)(latency_avg / MILLION), - (unsigned int)(latency_avg % MILLION), - (unsigned int)(stats->latency_min / MILLION), - (unsigned int)(stats->latency_min % MILLION), - (unsigned int)(stats->latency_max / MILLION), - (unsigned int)(stats->latency_max % MILLION)); + (unsigned int)(latency_avg % MILLION)); + if (!now) { + printf(" (min %u.%06u, max %u.%06u)\n", + (unsigned int)(stats->latency_min / MILLION), + (unsigned int)(stats->latency_min % MILLION), + (unsigned int)(stats->latency_max / MILLION), + (unsigned int)(stats->latency_max % MILLION)); + } else { + printf("\n"); + } + if (stats->num_completed > 1) { printf(" Latency StdDev (s): %f\n", stddev(stats->latency_sum_squares, stats->latency_sum, stats->num_completed) / MILLION); +#ifdef USE_HISTOGRAMS + if (config->latency_histogram) + print_histogram(stats->latency, "answer count"); +#endif } printf("\n"); @@ -336,32 +444,56 @@ print_statistics(const config_t* config, const times_t* times, stats_t* stats) printf("Connection Statistics:\n\n"); printf(" Reconnections: %" PRIu64 "\n\n", stats->num_conn_reconnect); latency_avg = PERF_SAFE_DIV(stats->conn_latency_sum, stats->num_conn_completed); - printf(" Average Latency (s): %u.%06u (min %u.%06u, max %u.%06u)\n", + printf(" Average Latency (s): %u.%06u", (unsigned int)(latency_avg / MILLION), - (unsigned int)(latency_avg % MILLION), - (unsigned int)(stats->conn_latency_min / MILLION), - (unsigned int)(stats->conn_latency_min % MILLION), - (unsigned int)(stats->conn_latency_max / MILLION), - (unsigned int)(stats->conn_latency_max % MILLION)); + (unsigned int)(latency_avg % MILLION)); + if (!now) { + printf(" (min %u.%06u, max %u.%06u)\n", + (unsigned int)(stats->conn_latency_min / MILLION), + (unsigned int)(stats->conn_latency_min % MILLION), + (unsigned int)(stats->conn_latency_max / MILLION), + (unsigned int)(stats->conn_latency_max % MILLION)); + } else { + printf("\n"); + } if (stats->num_conn_completed > 1) { printf(" Latency StdDev (s): %f\n", stddev(stats->conn_latency_sum_squares, stats->conn_latency_sum, stats->num_conn_completed) / MILLION); +#ifdef USE_HISTOGRAMS + if (config->latency_histogram) + print_histogram(stats->latency, "connection count"); +#endif } printf("\n"); } +/* + * Caller must free() stats->latency and stats->conn_latency. + */ static void sum_stats(const config_t* config, stats_t* total) { unsigned int i, j; memset(total, 0, sizeof(*total)); +#ifdef USE_HISTOGRAMS + if (config->latency_histogram) { + total->latency = hg64_create(HISTOGRAM_SIGBITS); + total->conn_latency = hg64_create(HISTOGRAM_SIGBITS); + } +#endif for (i = 0; i < config->threads; i++) { stats_t* stats = &threads[i].stats; +#ifdef USE_HISTOGRAMS + if (config->latency_histogram) { + hg64_merge(total->latency, stats->latency); + hg64_merge(total->conn_latency, stats->conn_latency); + } +#endif - for (j = 0; j < 16; j++) + for (j = 0; j < DNSPERF_STATS_RCODECOUNTS; j++) total->rcodecounts[j] += stats->rcodecounts[j]; total->num_sent += stats->num_sent; @@ -499,6 +631,12 @@ setup(int argc, char** argv, config_t* config) "suppress messages/warnings, see man-page for list of message types", NULL, &local_suppress); perf_long_opt_add("num-queries-per-conn", perf_opt_uint, "queries", "Number of queries to send per connection", NULL, &config->num_queries_per_conn); + perf_long_opt_add("verbose-interval-stats", perf_opt_boolean, NULL, + "print detailed statistics for each stats_interval", NULL, &config->verbose_interval_stats); +#ifdef USE_HISTOGRAMS + perf_long_opt_add("latency-histogram", perf_opt_boolean, NULL, + "collect and print detailed latency histograms", NULL, &config->latency_histogram); +#endif bool log_stdout = false; perf_opt_add('W', perf_opt_boolean, NULL, "log warnings and errors to stdout instead of stderr", NULL, &log_stdout); @@ -574,14 +712,18 @@ setup(int argc, char** argv, config_t* config) * If we run more threads than max-qps, some threads will have * ->max_qps set to 0, and be unlimited. */ - if (config->max_qps > 0 && config->threads > config->max_qps) + if (config->max_qps > 0 && config->threads > config->max_qps) { + perf_log_warning("requested max QPS limit (-Q %u) is lower than number of threads (-T %u), lowering number of threads", config->max_qps, config->threads); config->threads = config->max_qps; + } /* * We also can't run more threads than clients. */ - if (config->threads > config->clients) + if (config->threads > config->clients) { + perf_log_warning("requested number of threads (-T %u) exceeds number of clients (-c %u), lowering number of threads\n", config->threads, config->clients); config->threads = config->clients; + } #ifndef HAVE_LDNS if (config->updates) { @@ -636,6 +778,10 @@ query_move(threadinfo_t* tinfo, query_info* q, query_move_op op) static inline uint64_t num_outstanding(const stats_t* stats) { + /* make sure negative values aren't returned */ + if (stats->num_completed + stats->num_timedout > stats->num_sent) { + return 0; + } return stats->num_sent - stats->num_completed - stats->num_timedout; } @@ -920,7 +1066,10 @@ recv_one(threadinfo_t* tinfo, int which_sock, } if (!n) { // Treat connection closed like try again until reconnection features are in - *saved_errnop = EAGAIN; + if (!*saved_errnop) { + // only set this if there was no error before to allow above error check to overwrite EAGAIN + *saved_errnop = EAGAIN; + } return false; } recvd->sock = tinfo->socks[which_sock]; @@ -1006,8 +1155,6 @@ do_recv(void* arg) break; } bit_set(socketbits, current_socket); - if (saved_errno != EAGAIN) - break; } if (j == tinfo->nsocks) break; @@ -1039,11 +1186,13 @@ do_recv(void* arg) perf_log_warning("received short response"); continue; } - if (recvd[i].unexpected && !tinfo->config->suppress.unexpected) { - perf_log_warning("received a response with an " - "unexpected (maybe timed out) " - "id: %u", - recvd[i].qid); + if (recvd[i].unexpected) { + if (!tinfo->config->suppress.unexpected) { + perf_log_warning("received a response with an " + "unexpected (maybe timed out) " + "id: %u", + recvd[i].qid); + } continue; } latency = recvd[i].when - recvd[i].sent; @@ -1061,6 +1210,11 @@ do_recv(void* arg) stats->total_response_size += recvd[i].size; stats->rcodecounts[recvd[i].rcode]++; stats->latency_sum += latency; +#ifdef USE_HISTOGRAMS + if (stats->latency) { + hg64_inc(stats->latency, latency); + } +#endif stats->latency_sum_squares += (latency * latency); if (latency < stats->latency_min || stats->num_completed == 1) stats->latency_min = latency; @@ -1097,18 +1251,17 @@ static void* do_interval_stats(void* arg) { threadinfo_t* tinfo; - stats_t total; + stats_t total = {}; + stats_t last = {}; + stats_t diff = {}; uint64_t now; uint64_t last_interval_time; - uint64_t last_completed; uint64_t interval_time; - uint64_t num_completed; double qps; struct perf_net_socket sock = { .mode = sock_pipe, .fd = threadpipe[0] }; tinfo = arg; last_interval_time = tinfo->times->start_time; - last_completed = 0; wait_for_start(); while (perf_os_waituntilreadable(&sock, threadpipe[0], @@ -1117,13 +1270,23 @@ do_interval_stats(void* arg) now = perf_get_time(); sum_stats(tinfo->config, &total); interval_time = now - last_interval_time; - num_completed = total.num_completed - last_completed; - qps = num_completed / (((double)interval_time) / MILLION); - perf_log_printf("%u.%06u: %.6lf", - (unsigned int)(now / MILLION), - (unsigned int)(now % MILLION), qps); + + if (tinfo->config->verbose_interval_stats) { + diff_stats(tinfo->config, &last, &total, &diff); + print_statistics(tinfo->config, tinfo->times, &diff, now, interval_time); + } else { + qps = (total.num_completed - last.num_completed) / (((double)interval_time) / MILLION); + perf_log_printf("%u.%06u: %.6lf", + (unsigned int)(now / MILLION), + (unsigned int)(now % MILLION), qps); + } + last_interval_time = now; - last_completed = total.num_completed; +#ifdef USE_HISTOGRAMS + free(last.latency); + free(last.conn_latency); +#endif + last = total; } return NULL; @@ -1189,6 +1352,11 @@ static void perf__net_event(struct perf_net_socket* sock, perf_socket_event_t ev case perf_socket_event_connected: stats->num_conn_completed++; +#ifdef USE_HISTOGRAMS + if (stats->conn_latency) { + hg64_inc(stats->conn_latency, elapsed_time); + } +#endif stats->conn_latency_sum += elapsed_time; stats->conn_latency_sum_squares += (elapsed_time * elapsed_time); if (elapsed_time < stats->conn_latency_min || stats->num_conn_completed == 1) @@ -1218,6 +1386,12 @@ threadinfo_init(threadinfo_t* tinfo, const config_t* config, perf_list_init(tinfo->outstanding_queries); perf_list_init(tinfo->unused_queries); +#ifdef USE_HISTOGRAMS + if (config->latency_histogram) { + tinfo->stats.latency = hg64_create(HISTOGRAM_SIGBITS); + tinfo->stats.conn_latency = hg64_create(HISTOGRAM_SIGBITS); + } +#endif for (i = 0; i < NQIDS; i++) { perf_link_init(&tinfo->queries[i]); perf_list_append(tinfo->unused_queries, &tinfo->queries[i]); @@ -1240,11 +1414,15 @@ threadinfo_init(threadinfo_t* tinfo, const config_t* config, /* * We can't have more than 64k outstanding queries per thread. */ - if (tinfo->max_outstanding > NQIDS) + if (tinfo->max_outstanding > NQIDS) { + perf_log_warning("requested number of outstanding queries (-q %u) per single thread (-T) exceeds built-in maximum %u, adjusting\n", tinfo->max_outstanding, NQIDS); tinfo->max_outstanding = NQIDS; + } - if (tinfo->nsocks > MAX_SOCKETS) + if (tinfo->nsocks > MAX_SOCKETS) { + perf_log_warning("requested number of clients (-c %u) per thread (-T) exceeds built-in maximum %u, adjusting\n", tinfo->nsocks, MAX_SOCKETS); tinfo->nsocks = MAX_SOCKETS; + } if (!(tinfo->socks = calloc(tinfo->nsocks, sizeof(*tinfo->socks)))) { perf_log_fatal("out of memory"); @@ -1383,7 +1561,7 @@ int main(int argc, char** argv) print_final_status(&config); sum_stats(&config, &total_stats); - print_statistics(&config, ×, &total_stats); + print_statistics(&config, ×, &total_stats, 0, 0); perf_net_stats_print(config.mode); cleanup(&config); @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/ext/hg64.c b/src/ext/hg64.c new file mode 100644 index 0000000..9cd4b4f --- /dev/null +++ b/src/ext/hg64.c @@ -0,0 +1,327 @@ +/* + * hg64 - 64-bit histograms + * + * Written by Tony Finch <dot@dotat.at> <fanf@isc.org> + * + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <assert.h> +#include <errno.h> +#include <stdatomic.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "hg64.h" + +/* number of bins is same as number of bits in a value */ +#define BINS 64 + +typedef atomic_uint_fast64_t counter; +typedef _Atomic(counter*) bin_ptr; + +struct hg64 { + unsigned sigbits; + bin_ptr bin[BINS]; +}; + +static inline counter* +get_bin(hg64* hg, unsigned b) +{ + /* key_to_new_counter() below has the matching store / release */ + return (atomic_load_explicit(&hg->bin[b], memory_order_acquire)); +} + +/* + * when we only care about the histogram precision + */ +struct hg64p { + unsigned sigbits; +}; + +#ifdef __has_attribute +#if __has_attribute(__transparent_union__) +#define TRANSPARENT __attribute__((__transparent_union__)) +#endif +#endif + +#ifdef TRANSPARENT + +typedef union hg64u { + hg64* hg; + const struct hg64p* hp; +} hg64u TRANSPARENT; + +#define hg64p(hu) ((hu).hp) +#else + +typedef void* hg64u; + +#define hg64p(hu) ((const struct hg64p*)(hu)) +#endif + +/* + * The bins arrays have a static size for simplicity, but that means We + * waste a little extra space that could be saved by omitting the + * exponents that land in the denormal number bin. The following macros + * calculate (at run time) the exact number of keys when we need to do + * accurate bounds checks. + */ +#define DENORMALS(hp) ((hp)->sigbits - 1) +#define EXPONENTS(hp) (BINS - DENORMALS(hp)) +#define MANTISSAS(hp) (1 << (hp)->sigbits) +#define KEYS(hp) (EXPONENTS(hp) * MANTISSAS(hp)) + +#define BINSIZE(hp) MANTISSAS(hp) + +/**********************************************************************/ + +#define OUTARG(ptr, val) (void)(((ptr) != NULL) && (bool)(*(ptr) = (val))) + +/**********************************************************************/ + +hg64* hg64_create(unsigned sigbits) +{ + if (sigbits < 1 || 15 < sigbits) { + return (NULL); + } + hg64* hg = malloc(sizeof(*hg)); + hg->sigbits = sigbits; + /* + * it is probably portable to zero-initialize atomics but the + * C standard says we shouldn't rely on it; but this loop + * should optimize to memset() on most target systems + */ + for (unsigned b = 0; b < BINS; b++) { + atomic_init(&hg->bin[b], NULL); + } + return (hg); +} + +void hg64_destroy(hg64* hg) +{ + for (unsigned b = 0; b < BINS; b++) { + free(get_bin(hg, b)); + } + *hg = (hg64) { 0 }; + free(hg); +} + +/**********************************************************************/ + +static inline uint64_t +key_to_minval(hg64u hu, unsigned key) +{ + unsigned binsize = BINSIZE(hg64p(hu)); + unsigned exponent = (key / binsize) - 1; + uint64_t mantissa = (key % binsize) + binsize; + return (key < binsize ? key : mantissa << exponent); +} + +/* + * don't shift by 64, and don't underflow exponent; instead, + * reduce shift by 1 for each hazard and pre-shift UINT64_MAX + */ +static inline uint64_t +key_to_maxval(hg64u hu, unsigned key) +{ + unsigned binsize = BINSIZE(hg64p(hu)); + unsigned shift = 63 - (key / binsize); + uint64_t range = UINT64_MAX / 4 >> shift; + return (key_to_minval(hu, key) + range); +} + +/* + * This branchless conversion is due to Paul Khuong: see bin_down_of() in + * https://pvk.ca/Blog/2015/06/27/linear-log-bucketing-fast-versatile-simple/ + */ +static inline unsigned +value_to_key(hg64u hu, uint64_t value) +{ + /* fast path */ + const struct hg64p* hp = hg64p(hu); + /* ensure that denormal numbers are all in the same bin */ + uint64_t binned = value | BINSIZE(hp); + int clz = __builtin_clzll((unsigned long long)(binned)); + /* actually 1 less than the exponent except for denormals */ + unsigned exponent = 63 - hp->sigbits - clz; + /* mantissa has leading bit set except for denormals */ + unsigned mantissa = value >> exponent; + /* leading bit of mantissa adds one to exponent */ + return ((exponent << hp->sigbits) + mantissa); +} + +static counter* +key_to_new_counter(hg64* hg, unsigned key) +{ + /* slow path */ + unsigned binsize = BINSIZE(hg); + unsigned b = key / binsize; + unsigned c = key % binsize; + counter* old_bp = NULL; + counter* new_bp = malloc(sizeof(counter) * binsize); + /* see comment in hg64_create() above */ + for (unsigned i = 0; i < binsize; i++) { + atomic_init(new_bp + i, 0); + } + bin_ptr* bpp = &hg->bin[b]; + if (atomic_compare_exchange_strong_explicit(bpp, &old_bp, new_bp, + memory_order_acq_rel, memory_order_acquire)) { + return (new_bp + c); + } else { + /* lost the race, so use the winner's counters */ + free(new_bp); + return (old_bp + c); + } +} + +static inline counter* +key_to_counter(hg64* hg, unsigned key) +{ + /* fast path */ + unsigned binsize = BINSIZE(hg); + unsigned b = key / binsize; + unsigned c = key % binsize; + counter* bp = get_bin(hg, b); + return (bp == NULL ? NULL : bp + c); +} + +static inline uint64_t +get_key_count(hg64* hg, unsigned key) +{ + counter* ctr = key_to_counter(hg, key); + return (ctr == NULL ? 0 : atomic_load_explicit(ctr, memory_order_relaxed)); +} + +static inline void +add_key_count(hg64* hg, unsigned key, uint64_t inc) +{ + if (inc == 0) + return; + counter* ctr = key_to_counter(hg, key); + ctr = ctr ? ctr : key_to_new_counter(hg, key); + atomic_fetch_add_explicit(ctr, inc, memory_order_relaxed); +} + +/**********************************************************************/ + +void hg64_inc(hg64* hg, uint64_t value) +{ + add_key_count(hg, value_to_key(hg, value), 1); +} + +bool hg64_get(hg64* hg, unsigned key, + uint64_t* pmin, uint64_t* pmax, uint64_t* pcount) +{ + if (key < KEYS(hg)) { + OUTARG(pmin, key_to_minval(hg, key)); + OUTARG(pmax, key_to_maxval(hg, key)); + OUTARG(pcount, get_key_count(hg, key)); + return (true); + } else { + return (false); + } +} + +unsigned +hg64_next(hg64* hg, unsigned key) +{ + key++; + while (key < KEYS(hg) && (key & (BINSIZE(hg) - 1)) == 0 && key_to_counter(hg, key) == NULL) { + key += BINSIZE(hg); + } + return (key); +} + +/* + * https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf + */ +void hg64_mean_variance(hg64* hg, double* pmean, double* pvar) +{ + double pop = 0.0; + double mean = 0.0; + double sigma = 0.0; + uint64_t min, max, count; + for (unsigned key = 0; + hg64_get(hg, key, &min, &max, &count); + key = hg64_next(hg, key)) { + double delta = (double)min / 2.0 + (double)max / 2.0 - mean; + if (count != 0) { /* avoid division by zero */ + pop += count; + mean += count * delta / pop; + sigma += count * delta * (min + max - mean); + } + } + OUTARG(pmean, mean); + OUTARG(pvar, sigma / pop); +} + +/**********************************************************************/ + +void hg64_merge(hg64* target, hg64* source) +{ + uint64_t count; + for (unsigned skey = 0; + hg64_get(source, skey, NULL, NULL, &count); + skey = hg64_next(source, skey)) { + uint64_t svmin = key_to_minval(source, skey); + uint64_t svmax = key_to_maxval(source, skey); + unsigned tkmin = value_to_key(target, svmin); + unsigned tkmax = value_to_key(target, svmax); + unsigned keys = tkmax - tkmin + 1; + /* is there a more cunning way to spread out the remainder? */ + uint64_t div = count / keys; + uint64_t rem = count % keys; + for (unsigned tkey = tkmin; tkey <= tkmax; tkey++) { + uint64_t inc = div + (uint64_t)(tkey < rem); + add_key_count(target, tkey, inc); + } + } +} + +void hg64_diff(hg64* a, hg64* b, hg64* diff) +{ + assert((a->sigbits == b->sigbits) && (b->sigbits == diff->sigbits)); + uint64_t count_a = 0; + uint64_t count_b = 0; + for (unsigned key = 0; + hg64_get(a, key, NULL, NULL, &count_a); + key++) { + hg64_get(b, key, NULL, NULL, &count_b); + add_key_count(diff, key, count_a - count_b); + } +} + +unsigned hg64_min_key(hg64* hg) +{ + uint64_t pcount; + for (unsigned key = 0; + hg64_get(hg, key, NULL, NULL, &pcount); + key = hg64_next(hg, key)) { + if (pcount > 0) + return key; + } + return 0; +} + +unsigned hg64_max_key(hg64* hg) +{ + unsigned last_key = 0; + uint64_t pcount; + for (unsigned key = 0; + hg64_get(hg, key, NULL, NULL, &pcount); + key = hg64_next(hg, key)) { + if (pcount > 0) + last_key = key; + } + return last_key; +} diff --git a/src/ext/hg64.h b/src/ext/hg64.h new file mode 100644 index 0000000..21ec3d3 --- /dev/null +++ b/src/ext/hg64.h @@ -0,0 +1,88 @@ +/* + * hg64 - 64-bit histograms + * + * Written by Tony Finch <dot@dotat.at> <fanf@isc.org> + * + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef HG64_H +#define HG64_H 1 + +typedef struct hg64 hg64; + +/* + * Allocate a new histogram. `sigbits` must be between 1 and 15 + * inclusive; it is the number of significant bits of each value + * to use when mapping values to buckets. + */ +hg64* hg64_create(unsigned sigbits); + +/* + * Free the memory used by a histogram + */ +void hg64_destroy(hg64* hg); + +/* + * Add 1 to the value's bucket + */ +void hg64_inc(hg64* hg, uint64_t value); + +/* + * Get information about a bucket. This can be used as an iterator, + * by initializing `key` to zero and incrementing by one or using + * `hg64_next()` until `hg64_get()` returns `false`. The number of + * iterations is a little less than `1 << (6 + sigbits)`. + * + * If `pmin` is non-NULL it is set to the bucket's minimum inclusive value. + * + * If `pmax` is non-NULL it is set to the bucket's maximum inclusive value. + * + * If `pcount` is non-NULL it is set to the bucket's counter, which + * can be zero. (Empty buckets are included in the iterator.) + */ +bool hg64_get(hg64* hg, unsigned key, + uint64_t* pmin, uint64_t* pmax, uint64_t* pcount); + +/* + * Skip to the next key, omitting groups of nonexistent buckets. + */ +unsigned hg64_next(hg64* hg, unsigned key); + +/* + * Get summary statistics about the histogram. + * + * If `pmean` is non-NULL it is set to the mean of the recorded data. + * + * If `pvar` is non-NULL it is set to the variance of the recorded + * data. The standard deviation is the square root of the variance. + */ +void hg64_mean_variance(hg64* hg, double* pmean, double* pvar); + +/* + * Increase the counts in `target` by the counts recorded in `source` + */ +void hg64_merge(hg64* target, hg64* source); + +/* + * diff = a - b + */ +void hg64_diff(hg64* a, hg64* b, hg64* diff); + +/* + * Get highest key with non-zero value. Returns 0 if all values are 0. + */ +unsigned hg64_max_key(hg64* hg); + +/* + * Get lowest key with non-zero value. Returns 0 if all values are 0. + */ +unsigned hg64_min_key(hg64* hg); + +#endif diff --git a/src/parse_uri.c b/src/ext/parse_uri.c index fa74c85..fa74c85 100644 --- a/src/parse_uri.c +++ b/src/ext/parse_uri.c diff --git a/src/parse_uri.h b/src/ext/parse_uri.h index bb69e79..bb69e79 100644 --- a/src/parse_uri.h +++ b/src/ext/parse_uri.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/net_doh.c b/src/net_doh.c index f483e0d..dafd736 100644 --- a/src/net_doh.c +++ b/src/net_doh.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -28,7 +28,7 @@ #include "net.h" #include "edns.h" -#include "parse_uri.h" +#include "ext/parse_uri.h" #include "log.h" #include "strerror.h" #include "util.h" @@ -324,6 +324,8 @@ static ssize_t perf__doh_recv(struct perf_net_socket* sock, void* buf, size_t le return -1; case SSL_ERROR_SYSCALL: switch (errno) { + case EBADF: + // treat this as a retry, can happen if sendto is reconnecting case ECONNREFUSED: case ECONNRESET: case ENOTCONN: diff --git a/src/net_dot.c b/src/net_dot.c index 9fd718d..7e58a73 100644 --- a/src/net_dot.c +++ b/src/net_dot.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -177,6 +177,8 @@ static ssize_t perf__dot_recv(struct perf_net_socket* sock, void* buf, size_t le break; case SSL_ERROR_SYSCALL: switch (errno) { + case EBADF: + // treat this as a retry, can happen if sendto is reconnecting case ECONNREFUSED: case ECONNRESET: case ENOTCONN: diff --git a/src/net_tcp.c b/src/net_tcp.c index 61676fc..026dcce 100644 --- a/src/net_tcp.c +++ b/src/net_tcp.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -171,6 +171,8 @@ static ssize_t perf__tcp_recv(struct perf_net_socket* sock, void* buf, size_t le return 0; } else if (n < 0) { switch (errno) { + case EBADF: + // treat this as a retry, can happen if sendto is reconnecting case ECONNREFUSED: case ECONNRESET: case ENOTCONN: diff --git a/src/net_udp.c b/src/net_udp.c index 5ca100f..2efc384 100644 --- a/src/net_udp.c +++ b/src/net_udp.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -238,39 +238,57 @@ static int perf_opt_long_parse(char* optarg) ssize_t optlen; char* arg; - // TODO: Allow boolean not to have =/value - if (!(arg = strchr(optarg, '='))) { - return -1; - } - optlen = arg - optarg; - if (optlen < 1) { - return -1; + if ((arg = strchr(optarg, '='))) { + arg++; + optlen = arg - optarg; + if (optlen < 1) { + return -1; + } + } else { + optlen = strlen(optarg); } - arg++; long_opt_t* opt = longopts; while (opt) { if (!strncmp(optarg, opt->name, optlen)) { switch (opt->type) { case perf_opt_string: + if (!arg) { + return -1; + } *opt->u.stringp = arg; break; case perf_opt_boolean: *opt->u.boolp = true; break; case perf_opt_uint: + if (!arg) { + return -1; + } *opt->u.uintp = parse_uint(opt->desc, arg, 1, 0xFFFFFFFF); break; case perf_opt_zpint: + if (!arg) { + return -1; + } *opt->u.uintp = parse_uint(opt->desc, arg, 0, 0xFFFFFFFF); break; case perf_opt_timeval: + if (!arg) { + return -1; + } *opt->u.uint64p = parse_timeval(opt->desc, arg); break; case perf_opt_double: + if (!arg) { + return -1; + } *opt->u.doublep = parse_double(opt->desc, arg); break; case perf_opt_port: + if (!arg) { + return -1; + } *opt->u.portp = parse_uint(opt->desc, arg, 0, 0xFFFF); break; } @@ -284,10 +302,10 @@ static int perf_opt_long_parse(char* optarg) void perf_long_opt_usage(void) { - fprintf(stderr, "Usage: %s ... -O <name>=<value> ...\n\nAvailable long options:\n", progname); + fprintf(stderr, "Usage: %s ... -O <name>[=<value>] ...\n\nAvailable long options:\n", progname); long_opt_t* opt = longopts; while (opt) { - fprintf(stderr, " %s: %s", opt->name, opt->help); + fprintf(stderr, " %s%s: %s", opt->name, opt->type != perf_opt_boolean ? "=<val>" : "", opt->help); if (opt->defval) { fprintf(stderr, " (default: %s)", opt->defval); } @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/qtype.c b/src/qtype.c index c41348f..bce24f3 100644 --- a/src/qtype.c +++ b/src/qtype.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/qtype.h b/src/qtype.h index 87e157c..d136870 100644 --- a/src/qtype.h +++ b/src/qtype.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/resperf-report b/src/resperf-report index 2d154bc..7c1431d 100755 --- a/src/resperf-report +++ b/src/resperf-report @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2019-2022 OARC, Inc. +# Copyright 2019-2023 OARC, Inc. # Copyright 2017-2018 Akamai Technologies # Copyright 2006-2016 Nominum, Inc. # All rights reserved. diff --git a/src/resperf.1.in b/src/resperf.1.in index 28f9bbb..1c31b35 100644 --- a/src/resperf.1.in +++ b/src/resperf.1.in @@ -1,4 +1,4 @@ -.\" Copyright 2019-2022 OARC, Inc. +.\" Copyright 2019-2023 OARC, Inc. .\" Copyright 2017-2018 Akamai Technologies .\" Copyright 2006-2016 Nominum, Inc. .\" All rights reserved. diff --git a/src/resperf.c b/src/resperf.c index aa58dc0..ae4f9cd 100644 --- a/src/resperf.c +++ b/src/resperf.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/result.h b/src/result.h index 7670a91..b50716b 100644 --- a/src/result.h +++ b/src/result.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/strerror.c b/src/strerror.c index 1a771f8..d870cb1 100644 --- a/src/strerror.c +++ b/src/strerror.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/strerror.h b/src/strerror.h index 915e06d..312b148 100644 --- a/src/strerror.h +++ b/src/strerror.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. diff --git a/src/test/test1.sh b/src/test/test1.sh index 945aab3..cd6af3c 100755 --- a/src/test/test1.sh +++ b/src/test/test1.sh @@ -2,3 +2,8 @@ ../dnsperf -h ../resperf -h + +! ../dnsperf -O suppress +! ../dnsperf -O suppress= +! ../resperf -O suppress +! ../resperf -O suppress= @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 OARC, Inc. + * Copyright 2019-2023 OARC, Inc. * Copyright 2017-2018 Akamai Technologies * Copyright 2006-2016 Nominum, Inc. * All rights reserved. |