summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-02-12 08:40:57 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-02-12 08:40:57 +0000
commite2fdf6c58545799d7522fabf7a2999f113bf3541 (patch)
tree4eb7533bf42fa54fcc6a1b2396f1abdfe4102484 /src
parentAdding upstream version 2.10.0. (diff)
downloaddnsperf-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.am12
-rw-r--r--src/Makefile.in77
-rw-r--r--src/buffer.h2
-rw-r--r--src/config.h.in3
-rw-r--r--src/datafile.c2
-rw-r--r--src/datafile.h2
-rw-r--r--src/dns.c2
-rw-r--r--src/dns.h2
-rw-r--r--src/dnsperf.1.in31
-rw-r--r--src/dnsperf.c264
-rw-r--r--src/edns.c2
-rw-r--r--src/edns.h2
-rw-r--r--src/ext/hg64.c327
-rw-r--r--src/ext/hg64.h88
-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.h2
-rw-r--r--src/log.c2
-rw-r--r--src/log.h2
-rw-r--r--src/net.c2
-rw-r--r--src/net.h2
-rw-r--r--src/net_doh.c6
-rw-r--r--src/net_dot.c4
-rw-r--r--src/net_tcp.c4
-rw-r--r--src/net_udp.c2
-rw-r--r--src/opt.c40
-rw-r--r--src/opt.h2
-rw-r--r--src/os.c2
-rw-r--r--src/os.h2
-rw-r--r--src/qtype.c2
-rw-r--r--src/qtype.h2
-rwxr-xr-xsrc/resperf-report2
-rw-r--r--src/resperf.1.in2
-rw-r--r--src/resperf.c2
-rw-r--r--src/result.h2
-rw-r--r--src/strerror.c2
-rw-r--r--src/strerror.h2
-rwxr-xr-xsrc/test/test1.sh5
-rw-r--r--src/tsig.c2
-rw-r--r--src/tsig.h2
-rw-r--r--src/util.h2
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.
diff --git a/src/dns.c b/src/dns.c
index 20bb354..c990d9b 100644
--- a/src/dns.c
+++ b/src/dns.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/dns.h b/src/dns.h
index c126f97..0c0df3c 100644
--- a/src/dns.h
+++ b/src/dns.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/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, &times, &total_stats);
+ print_statistics(&config, &times, &total_stats, 0, 0);
perf_net_stats_print(config.mode);
cleanup(&config);
diff --git a/src/edns.c b/src/edns.c
index fa66cc4..d3a0608 100644
--- a/src/edns.c
+++ b/src/edns.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/edns.h b/src/edns.h
index 8064cc4..bcd6822 100644
--- a/src/edns.h
+++ b/src/edns.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/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
diff --git a/src/list.h b/src/list.h
index d52ad04..47064fb 100644
--- a/src/list.h
+++ b/src/list.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/log.c b/src/log.c
index e807dcc..661e373 100644
--- a/src/log.c
+++ b/src/log.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/log.h b/src/log.h
index e922ca1..040a29a 100644
--- a/src/log.h
+++ b/src/log.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/net.c b/src/net.c
index ca3750f..89e75e0 100644
--- a/src/net.c
+++ b/src/net.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/net.h b/src/net.h
index 89af773..1624be6 100644
--- a/src/net.h
+++ b/src/net.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/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.
diff --git a/src/opt.c b/src/opt.c
index d65a0f8..7a0f346 100644
--- a/src/opt.c
+++ b/src/opt.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.
@@ -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);
}
diff --git a/src/opt.h b/src/opt.h
index e30ea8c..82a15a2 100644
--- a/src/opt.h
+++ b/src/opt.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/os.c b/src/os.c
index 24304bc..a36cbed 100644
--- a/src/os.c
+++ b/src/os.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/os.h b/src/os.h
index b21e6ed..0d9e8c5 100644
--- a/src/os.h
+++ b/src/os.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/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=
diff --git a/src/tsig.c b/src/tsig.c
index ce10eea..b697141 100644
--- a/src/tsig.c
+++ b/src/tsig.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/tsig.h b/src/tsig.h
index 637b348..b69c2d1 100644
--- a/src/tsig.h
+++ b/src/tsig.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/util.h b/src/util.h
index c59e459..ae821b0 100644
--- a/src/util.h
+++ b/src/util.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.