1 /* $NetBSD: queryperf.c,v 1.6 2014/12/10 04:37:56 christos Exp $ */
4 * Copyright (C) 2000, 2001 Nominum, Inc.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
11 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
12 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
13 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
15 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
17 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *** DNS Query Performance Testing Tool (queryperf.c)
23 *** Version Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp
25 *** Stephen Jacob <sj@nominum.com>
28 #define BIND_8_COMPAT /* Pull in <arpa/nameser_compat.h> */
31 #include <sys/types.h>
32 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/nameser.h>
48 #ifndef HAVE_GETADDRINFO
49 #include "missing/addrinfo.h"
54 * Configuration defaults
57 #define DEF_MAX_QUERIES_OUTSTANDING 20
58 #define DEF_QUERY_TIMEOUT 5 /* in seconds */
59 #define DEF_SERVER_TO_QUERY "127.0.0.1"
60 #define DEF_SERVER_PORT "53"
61 #define DEF_BUFFER_SIZE 32 /* in k */
63 #define DEF_RTTARRAY_SIZE 50000
64 #define DEF_RTTARRAY_UNIT 100 /* in usec */
67 * Other constants / definitions
70 #define COMMENT_CHAR ';'
71 #define CONFIG_CHAR '#'
72 #define MAX_PORT 65535
73 #define MAX_INPUT_LEN 512
74 #define MAX_DOMAIN_LEN 255
75 #define MAX_BUFFER_LEN 8192 /* in bytes */
76 #define HARD_TIMEOUT_EXTRA 5 /* in seconds */
77 #define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */
79 #define DNS_HEADERLEN 12
84 #define WHITESPACE " \t\n"
86 enum directives_enum
{ V_SERVER
, V_PORT
, V_MAXQUERIES
, V_MAXWAIT
};
87 #define DIRECTIVES { "server", "port", "maxqueries", "maxwait" }
88 #define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }
90 #define QTYPE_STRINGS { \
91 "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR", \
92 "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", "RP", \
93 "AFSDB", "X25", "ISDN", "RT", "NSAP", "NSAP-PTR", "SIG", \
94 "KEY", "PX", "GPOS", "AAAA", "LOC", "NXT", "EID", "NIMLOC", \
95 "SRV", "ATMA", "NAPTR", "KX", "CERT", "A6", "DNAME", "SINK", \
96 "OPT", "APL", "DS", "SSHFP", "IPSECKEY", "RRSIG", "NSEC", \
97 "DNSKEY", "DHCID", "NSEC3", "NSEC3PARAM", "TLSA", "HIP", \
98 "NINFO", "RKEY", "TALINK", "CDS", "SPF", "UINFO", "UID", \
99 "GID", "UNSPEC", "NID", "L32", "L64", "LP", "TKEY", "TSIG", \
100 "IXFR", "AXFR", "MAILB", "MAILA", "URI", "CAA", "*", "ANY", \
104 #define QTYPE_CODES { \
105 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, \
106 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, \
107 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, \
108 49, 50, 51, 52, 55, 56, 57, 58, 59, 99, 100, 101, 102, 103, \
109 104, 105, 106, 107, 249, 250, 251, 252, 253, 254, 255, 255, \
110 256, 257, 32768, 32769 \
113 #define RCODE_STRINGS { \
114 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \
115 "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \
116 "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \
117 "rcode12", "rcode13", "rcode14", "rcode15" \
121 * Data type definitions
124 #define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */
125 #define VALID_QUERY_STATUS(q) ((q) != NULL && \
126 (q)->magic == QUERY_STATUS_MAGIC)
128 struct query_status
{
131 unsigned short int id
;
132 struct timeval sent_timestamp
;
135 char qname
[MAX_DOMAIN_LEN
+ 1];
138 struct query_mininfo
{ /* minimum info for timeout queries */
139 int qtype
; /* use -1 if N/A */
140 struct timeval sent_timestamp
;
141 char qname
[MAX_DOMAIN_LEN
+ 1];
145 * Forward declarations.
147 int is_uint(char *test_int
, unsigned int *result
);
150 * Configuration options (global)
153 unsigned int max_queries_outstanding
; /* init 0 */
154 unsigned int query_timeout
= DEF_QUERY_TIMEOUT
;
155 int ignore_config_changes
= FALSE
;
156 unsigned int socket_bufsize
= DEF_BUFFER_SIZE
;
158 int family
= AF_UNSPEC
;
159 int use_stdin
= TRUE
;
160 char *datafile_name
; /* init NULL */
162 char *server_to_query
; /* init NULL */
163 char *server_port
; /* init NULL */
164 struct addrinfo
*server_ai
; /* init NULL */
166 int run_only_once
= FALSE
;
167 int use_timelimit
= FALSE
;
168 unsigned int run_timelimit
; /* init 0 */
169 unsigned int print_interval
; /* init 0 */
171 unsigned int target_qps
; /* init 0 */
173 int serverset
= FALSE
, portset
= FALSE
;
174 int queriesset
= FALSE
, timeoutset
= FALSE
;
175 int edns
= FALSE
, dnssec
= FALSE
;
176 int countrcodes
= FALSE
;
177 int rcodecounts
[16] = {0};
186 int setup_phase
= TRUE
;
188 FILE *datafile_ptr
; /* init NULL */
189 unsigned int runs_through_file
; /* init 0 */
191 unsigned int num_queries_sent
; /* init 0 */
192 unsigned int num_queries_sent_interval
;
193 unsigned int num_queries_outstanding
; /* init 0 */
194 unsigned int num_queries_timed_out
; /* init 0 */
195 unsigned int num_queries_possiblydelayed
; /* init 0 */
196 unsigned int num_queries_timed_out_interval
;
197 unsigned int num_queries_possiblydelayed_interval
;
199 struct timeval time_of_program_start
;
200 struct timeval time_of_first_query
;
201 double time_of_first_query_sec
;
202 struct timeval time_of_first_query_interval
;
203 struct timeval time_of_end_of_run
;
204 struct timeval time_of_stop_sending
;
206 struct timeval time_of_queryset_start
;
207 double query_interval
;
208 struct timeval time_of_next_queryset
;
211 double rtt_max_interval
= -1;
213 double rtt_min_interval
= -1;
215 double rtt_total_interval
;
216 int rttarray_size
= DEF_RTTARRAY_SIZE
;
217 int rttarray_unit
= DEF_RTTARRAY_UNIT
;
218 unsigned int *rttarray
= NULL
;
219 unsigned int *rttarray_interval
= NULL
;
220 unsigned int rtt_overflows
;
221 unsigned int rtt_overflows_interval
;
222 unsigned int rtt_counted
;
223 unsigned int rtt_counted_interval
;
224 char *rtt_histogram_file
= NULL
;
226 struct query_status
*status
; /* init NULL */
227 unsigned int query_status_allocated
; /* init 0 */
229 int query_socket
= -1;
230 int socket4
= -1, socket6
= -1;
232 static char *rcode_strings
[] = RCODE_STRINGS
;
234 static struct query_mininfo
*timeout_queries
;
238 * Get an unsigned short integer from a buffer (in network order)
240 static unsigned short
241 get_uint16(unsigned char *buf
) {
244 ret
= buf
[0] * 256 + buf
[1];
254 show_startup_info(void) {
256 "DNS Query Performance Testing Tool\n"
257 "Version: Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp \n"
263 * Print out usage/syntax information
269 "Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n"
270 " [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n"
271 " [-i interval] [-r arraysize] [-u unit] [-H histfile]\n"
272 " [-T qps] [-e] [-D] [-R] [-c] [-v] [-h]\n"
273 " -d specifies the input data file (default: stdin)\n"
274 " -s sets the server to query (default: %s)\n"
275 " -p sets the port on which to query the server (default: %s)\n"
276 " -q specifies the maximum number of queries outstanding (default: %d)\n"
277 " -t specifies the timeout for query completion in seconds (default: %d)\n"
278 " -n causes configuration changes to be ignored\n"
279 " -l specifies how a limit for how long to run tests in seconds (no default)\n"
280 " -1 run through input only once (default: multiple iff limit given)\n"
281 " -b set input/output buffer size in kilobytes (default: %d k)\n"
282 " -i specifies interval of intermediate outputs in seconds (default: 0=none)\n"
283 " -f specify address family of DNS transport, inet or inet6 (default: any)\n"
284 " -r set RTT statistics array size (default: %d)\n"
285 " -u set RTT statistics time unit in usec (default: %d)\n"
286 " -H specifies RTT histogram data file (default: none)\n"
287 " -T specify the target qps (default: 0=unspecified)\n"
288 " -e enable EDNS 0\n"
289 " -D set the DNSSEC OK bit (implies EDNS)\n"
290 " -R disable recursion\n"
291 " -c print the number of packets with each rcode\n"
292 " -v verbose: report the RCODE of each response on stdout\n"
293 " -h print this usage\n"
295 DEF_SERVER_TO_QUERY
, DEF_SERVER_PORT
,
296 DEF_MAX_QUERIES_OUTSTANDING
, DEF_QUERY_TIMEOUT
,
297 DEF_BUFFER_SIZE
, DEF_RTTARRAY_SIZE
, DEF_RTTARRAY_UNIT
);
302 * Set the datafile to read
304 * Return -1 on failure
305 * Return a non-negative integer otherwise
308 set_datafile(char *new_file
) {
311 if ((new_file
== NULL
) || (new_file
[0] == '\0')) {
312 fprintf(stderr
, "Error: null datafile name\n");
316 if ((dfname_tmp
= malloc(strlen(new_file
) + 1)) == NULL
) {
317 fprintf(stderr
, "Error allocating memory for datafile name: "
323 datafile_name
= dfname_tmp
;
325 strcpy(datafile_name
, new_file
);
333 * Set the input to be stdin (instead of a datafile)
336 set_input_stdin(void) {
339 datafile_name
= NULL
;
344 * Set the server to be queried
346 * Return -1 on failure
347 * Return a non-negative integer otherwise
350 set_server(char *new_name
) {
351 static struct hostent
*server_he
;
353 /* If no change in server name, don't do anything... */
354 if ((server_to_query
!= NULL
) && (new_name
!= NULL
))
355 if (strcmp(new_name
, server_to_query
) == 0)
358 if ((new_name
== NULL
) || (new_name
[0] == '\0')) {
359 fprintf(stderr
, "Error: null server name\n");
363 free(server_to_query
);
364 server_to_query
= NULL
;
366 if ((server_to_query
= malloc(strlen(new_name
) + 1)) == NULL
) {
367 fprintf(stderr
, "Error allocating memory for server name: "
372 strcpy(server_to_query
, new_name
);
379 * Set the port on which to contact the server
381 * Return -1 if port is invalid
382 * Return a non-negative integer otherwise
385 set_server_port(char *new_port
) {
386 unsigned int uint_val
;
388 if ((is_uint(new_port
, &uint_val
)) != TRUE
)
391 if (uint_val
&& uint_val
> MAX_PORT
)
394 if (server_port
!= NULL
&& new_port
!= NULL
&&
395 strcmp(server_port
, new_port
) == 0)
401 if ((server_port
= malloc(strlen(new_port
) + 1)) == NULL
) {
403 "Error allocating memory for server port: "
408 strcpy(server_port
, new_port
);
415 set_server_sa(void) {
416 struct addrinfo hints
, *res
;
417 static struct protoent
*proto
;
420 if (proto
== NULL
&& (proto
= getprotobyname("udp")) == NULL
) {
421 fprintf(stderr
, "Error: getprotobyname call failed");
425 memset(&hints
, 0, sizeof(hints
));
426 hints
.ai_family
= family
;
427 hints
.ai_socktype
= SOCK_DGRAM
;
428 hints
.ai_protocol
= proto
->p_proto
;
429 if ((error
= getaddrinfo(server_to_query
, server_port
,
430 &hints
, &res
)) != 0) {
431 fprintf(stderr
, "Error: getaddrinfo(%s, %s) failed\n",
432 server_to_query
, server_port
);
436 /* replace the server's addrinfo */
437 if (server_ai
!= NULL
)
438 freeaddrinfo(server_ai
);
445 * Tests if a character is a digit
447 * Return TRUE if it is
448 * Return FALSE if it is not
452 if (d
< '0' || d
> '9')
460 * Tests if a string, test_int, is a valid unsigned integer
462 * Sets *result to be the unsigned integer if it is valid
464 * Return TRUE if it is
465 * Return FALSE if it is not
468 is_uint(char *test_int
, unsigned int *result
) {
469 unsigned long int value
;
472 if (test_int
== NULL
)
475 if (is_digit(test_int
[0]) == FALSE
)
478 value
= strtoul(test_int
, &end
, 10);
480 if ((errno
== ERANGE
) || (*end
!= '\0') || (value
> UINT_MAX
))
483 *result
= (unsigned int)value
;
489 * Set the maximum number of outstanding queries
491 * Returns -1 on failure
492 * Returns a non-negative integer otherwise
495 set_max_queries(unsigned int new_max
) {
496 static unsigned int size_qs
= sizeof(struct query_status
);
497 struct query_status
*temp_stat
;
500 if (new_max
> query_status_allocated
) {
501 temp_stat
= realloc(status
, new_max
* size_qs
);
503 if (temp_stat
== NULL
) {
504 fprintf(stderr
, "Error resizing query_status\n");
510 * Be careful to only initialise between above
511 * the previously allocated space. Note that the
512 * allocation may be larger than the current
513 * max_queries_outstanding. We don't want to
514 * "forget" any outstanding queries! We might
515 * still have some above the bounds of the max.
517 count
= query_status_allocated
;
518 for (; count
< new_max
; count
++) {
519 status
[count
].in_use
= FALSE
;
520 status
[count
].magic
= QUERY_STATUS_MAGIC
;
521 status
[count
].desc
= NULL
;
524 query_status_allocated
= new_max
;
528 max_queries_outstanding
= new_max
;
535 * Parse program arguments and set configuration options
537 * Return -1 on failure
538 * Return a non-negative integer otherwise
541 parse_args(int argc
, char **argv
) {
543 unsigned int uint_arg_val
;
545 while ((c
= getopt(argc
, argv
,
546 "f:q:t:i:nd:s:p:1l:b:eDcvr:RT:u:H:h")) != -1) {
549 if (strcmp(optarg
, "inet") == 0)
552 else if (strcmp(optarg
, "inet6") == 0)
555 else if (strcmp(optarg
, "any") == 0)
558 fprintf(stderr
, "Invalid address family: %s\n",
564 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
565 set_max_queries(uint_arg_val
);
568 fprintf(stderr
, "Option requires a positive "
569 "integer value: -%c %s\n",
576 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
577 query_timeout
= uint_arg_val
;
580 fprintf(stderr
, "Option requires a positive "
581 "integer value: -%c %s\n",
588 ignore_config_changes
= TRUE
;
592 if (set_datafile(optarg
) == -1) {
593 fprintf(stderr
, "Error setting datafile "
594 "name: %s\n", optarg
);
600 if (set_server(optarg
) == -1) {
601 fprintf(stderr
, "Error setting server "
602 "name: %s\n", optarg
);
609 if (is_uint(optarg
, &uint_arg_val
) == TRUE
&&
610 uint_arg_val
< MAX_PORT
)
612 set_server_port(optarg
);
615 fprintf(stderr
, "Option requires a positive "
616 "integer between 0 and %d: -%c %s\n",
617 MAX_PORT
- 1, c
, optarg
);
623 run_only_once
= TRUE
;
627 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
628 use_timelimit
= TRUE
;
629 run_timelimit
= uint_arg_val
;
631 fprintf(stderr
, "Option requires a positive "
639 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
640 socket_bufsize
= uint_arg_val
;
642 fprintf(stderr
, "Option requires a positive "
662 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
663 print_interval
= uint_arg_val
;
665 fprintf(stderr
, "Invalid interval: %s\n",
674 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
675 rttarray_size
= uint_arg_val
;
677 fprintf(stderr
, "Invalid RTT array size: %s\n",
683 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
684 rttarray_unit
= uint_arg_val
;
686 fprintf(stderr
, "Invalid RTT unit: %s\n",
692 rtt_histogram_file
= optarg
;
695 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
696 target_qps
= uint_arg_val
;
698 fprintf(stderr
, "Invalid target qps: %s\n",
706 fprintf(stderr
, "Invalid option: -%c\n", optopt
);
711 if (run_only_once
== FALSE
&& use_timelimit
== FALSE
)
712 run_only_once
= TRUE
;
719 * Open the data file ready for reading
721 * Return -1 on failure
722 * Return non-negative integer on success
725 open_datafile(void) {
726 if (use_stdin
== TRUE
) {
727 datafile_ptr
= stdin
;
730 if ((datafile_ptr
= fopen(datafile_name
, "r")) == NULL
) {
731 fprintf(stderr
, "Error: unable to open datafile: %s\n",
741 * Close the data file if any is open
743 * Return -1 on failure
744 * Return non-negative integer on success, including if not needed
747 close_datafile(void) {
748 if ((use_stdin
== FALSE
) && (datafile_ptr
!= NULL
)) {
749 if (fclose(datafile_ptr
) != 0) {
750 fprintf(stderr
, "Error: unable to close datafile\n");
760 * Open a socket for the queries. When we have an active socket already,
761 * close it and open a new one.
763 * Return -1 on failure
764 * Return the socket identifier
771 struct addrinfo hints
, *res
;
773 memset(&hints
, 0, sizeof(hints
));
774 hints
.ai_family
= server_ai
->ai_family
;
775 hints
.ai_socktype
= server_ai
->ai_socktype
;
776 hints
.ai_protocol
= server_ai
->ai_protocol
;
777 hints
.ai_flags
= AI_PASSIVE
;
779 if ((ret
= getaddrinfo(NULL
, "0", &hints
, &res
)) != 0) {
781 "Error: getaddrinfo for bind socket failed: %s\n",
786 if ((sock
= socket(res
->ai_family
, SOCK_DGRAM
,
787 res
->ai_protocol
)) == -1) {
788 fprintf(stderr
, "Error: socket call failed");
792 #if defined(AF_INET6) && defined(IPV6_V6ONLY)
793 if (res
->ai_family
== AF_INET6
) {
796 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
797 &on
, sizeof(on
)) == -1) {
799 "Warning: setsockopt(IPV6_V6ONLY) failed\n");
804 if (bind(sock
, res
->ai_addr
, res
->ai_addrlen
) == -1)
805 fprintf(stderr
, "Error: bind call failed");
809 bufsize
= 1024 * socket_bufsize
;
811 ret
= setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
,
812 (char *) &bufsize
, sizeof(bufsize
));
814 fprintf(stderr
, "Warning: setsockbuf(SO_RCVBUF) failed\n");
816 ret
= setsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
,
817 (char *) &bufsize
, sizeof(bufsize
));
819 fprintf(stderr
, "Warning: setsockbuf(SO_SNDBUF) failed\n");
831 * Close the query socket(s)
833 * Return -1 on failure
834 * Return a non-negative integer otherwise
839 if (close(socket4
) != 0) {
841 "Error: unable to close IPv4 socket\n");
847 if (close(socket6
) != 0) {
849 "Error: unable to close IPv6 socket\n");
861 * Choose an appropriate socket according to the address family of the
862 * current server. Open a new socket if necessary.
864 * Return -1 on failure
865 * Return the socket identifier
868 change_socket(void) {
871 switch (server_ai
->ai_family
) {
881 fprintf(stderr
, "unexpected address family: %d\n",
882 server_ai
->ai_family
);
887 if ((s
= open_socket()) == -1)
897 * (re)allocate RTT array and zero-clear the whole buffer.
898 * if array is being used, it is freed.
899 * Returns -1 on failure
900 * Returns a non-negative integer otherwise
903 reset_rttarray(int size
) {
904 if (rttarray
!= NULL
)
906 if (rttarray_interval
!= NULL
)
907 free(rttarray_interval
);
910 rttarray_interval
= NULL
;
915 rttarray
= malloc(size
* sizeof(rttarray
[0]));
916 if (rttarray
== NULL
) {
918 "Error: allocating memory for RTT array\n");
921 memset(rttarray
, 0, size
* sizeof(rttarray
[0]));
923 rttarray_interval
= malloc(size
*
924 sizeof(rttarray_interval
[0]));
925 if (rttarray_interval
== NULL
) {
927 "Error: allocating memory for RTT array\n");
931 memset(rttarray_interval
, 0,
932 size
* sizeof(rttarray_interval
[0]));
939 * set_query_interval:
940 * set the interval of consecutive queries if the target qps are specified.
941 * Returns -1 on failure
942 * Returns a non-negative integer otherwise
945 set_query_interval(unsigned int qps
) {
949 query_interval
= (1.0 / (double)qps
);
956 * Set configuration options from command line arguments
957 * Open datafile ready for reading
959 * Return -1 on failure
960 * Return non-negative integer on success
963 setup(int argc
, char **argv
) {
966 if (set_max_queries(DEF_MAX_QUERIES_OUTSTANDING
) == -1) {
967 fprintf(stderr
, "%s: Unable to set default max outstanding "
968 "queries\n", argv
[0]);
972 if (set_server(DEF_SERVER_TO_QUERY
) == -1) {
973 fprintf(stderr
, "%s: Error setting default server name\n",
978 if (set_server_port(DEF_SERVER_PORT
) == -1) {
979 fprintf(stderr
, "%s: Error setting default server port\n",
984 if (parse_args(argc
, argv
) == -1) {
989 if (open_datafile() == -1)
992 if (set_server_sa() == -1)
995 if ((query_socket
= change_socket()) == -1)
998 if (reset_rttarray(rttarray_size
) == -1)
1001 if (set_query_interval(target_qps
) == -1)
1009 * Set a timeval struct to indicate the current time
1012 set_timenow(struct timeval
*tv
) {
1013 if (gettimeofday(tv
, NULL
) == -1) {
1014 fprintf(stderr
, "Error in gettimeofday(). Using inaccurate "
1015 "time() instead\n");
1016 tv
->tv_sec
= time(NULL
);
1023 * add tv1 and tv2, store the result in tv_result.
1026 addtv(struct timeval
*tv1
, struct timeval
*tv2
, struct timeval
*tv_result
) {
1027 tv_result
->tv_sec
= tv1
->tv_sec
+ tv2
->tv_sec
;
1028 tv_result
->tv_usec
= tv1
->tv_usec
+ tv2
->tv_usec
;
1029 if (tv_result
->tv_usec
> 1000000) {
1030 tv_result
->tv_sec
++;
1031 tv_result
->tv_usec
-= 1000000;
1037 * Find the difference in seconds between two timeval structs.
1039 * Return the difference between tv1 and tv2 in seconds in a double.
1042 difftv(struct timeval tv1
, struct timeval tv2
) {
1043 long diff_sec
, diff_usec
;
1046 diff_sec
= tv1
.tv_sec
- tv2
.tv_sec
;
1047 diff_usec
= tv1
.tv_usec
- tv2
.tv_usec
;
1049 diff
= (double)diff_sec
+ ((double)diff_usec
/ 1000000.0);
1055 * timelimit_reached:
1056 * Have we reached the time limit (if any)?
1058 * Returns FALSE if there is no time limit or if we have not reached it
1059 * Returns TRUE otherwise
1062 timelimit_reached(void) {
1063 struct timeval time_now
;
1065 set_timenow(&time_now
);
1067 if (use_timelimit
== FALSE
)
1070 if (setup_phase
== TRUE
) {
1071 if (difftv(time_now
, time_of_program_start
)
1072 < (double)(run_timelimit
+ HARD_TIMEOUT_EXTRA
))
1077 if (difftv(time_now
, time_of_first_query
)
1078 < (double)run_timelimit
)
1087 * Should we keep sending queries or stop here?
1089 * Return TRUE if we should keep on sending queries
1090 * Return FALSE if we should stop
1093 * Rewinds the input and clears reached_end_input if we have reached the
1094 * end of the input, but we are meant to run through it multiple times
1095 * and have not hit the time limit yet (if any is set).
1098 keep_sending(int *reached_end_input
) {
1099 static int stop
= FALSE
;
1104 if ((*reached_end_input
== FALSE
) && (timelimit_reached() == FALSE
))
1106 else if ((*reached_end_input
== TRUE
) && (run_only_once
== FALSE
)
1107 && (timelimit_reached() == FALSE
)) {
1108 rewind(datafile_ptr
);
1109 *reached_end_input
= FALSE
;
1110 runs_through_file
++;
1113 if (*reached_end_input
== TRUE
)
1114 runs_through_file
++;
1115 set_timenow(&time_of_stop_sending
);
1122 * queries_outstanding:
1123 * How many queries are outstanding?
1125 * Returns the number of outstanding queries
1128 queries_outstanding(void) {
1129 return (num_queries_outstanding
);
1134 * Get the next non-comment line from the input file
1136 * Put text in line, up to max of n chars. Skip comment lines.
1139 * Return line length on success
1140 * Return 0 if cannot read a non-comment line (EOF or error)
1143 next_input_line(char *line
, int n
) {
1147 result
= fgets(line
, n
, datafile_ptr
);
1148 } while ((result
!= NULL
) &&
1149 ((line
[0] == COMMENT_CHAR
) || (line
[0] == '\n')));
1154 return (strlen(line
));
1158 * identify_directive:
1159 * Gives us a numerical value equivelant for a directive string
1161 * Returns the value for the directive
1162 * Returns -1 if not a valid directive
1165 identify_directive(char *dir
) {
1166 static char *directives
[] = DIRECTIVES
;
1167 static int dir_values
[] = DIR_VALUES
;
1168 unsigned int index
, num_directives
;
1170 num_directives
= sizeof(directives
) / sizeof(directives
[0]);
1172 if (num_directives
> (sizeof(dir_values
) / sizeof(int)))
1173 num_directives
= sizeof(dir_values
) / sizeof(int);
1175 for (index
= 0; index
< num_directives
; index
++) {
1176 if (strcmp(dir
, directives
[index
]) == 0)
1177 return (dir_values
[index
]);
1185 * Update configuration options from a line from the input file
1188 update_config(char *config_change_desc
) {
1189 char *directive
, *config_value
, *trailing_garbage
;
1190 char conf_copy
[MAX_INPUT_LEN
+ 1];
1191 unsigned int uint_val
;
1192 int directive_number
;
1196 if (ignore_config_changes
== TRUE
) {
1197 fprintf(stderr
, "Ignoring configuration change: %s",
1198 config_change_desc
);
1202 strcpy(conf_copy
, config_change_desc
);
1204 ++config_change_desc
;
1206 if (*config_change_desc
== '\0') {
1207 fprintf(stderr
, "Invalid config: No directive present: %s\n",
1212 if (index(WHITESPACE
, *config_change_desc
) != NULL
) {
1213 fprintf(stderr
, "Invalid config: Space before directive or "
1214 "no directive present: %s\n", conf_copy
);
1218 directive
= strtok(config_change_desc
, WHITESPACE
);
1219 config_value
= strtok(NULL
, WHITESPACE
);
1220 trailing_garbage
= strtok(NULL
, WHITESPACE
);
1222 if ((directive_number
= identify_directive(directive
)) == -1) {
1223 fprintf(stderr
, "Invalid config: Bad directive: %s\n",
1228 if (config_value
== NULL
) {
1229 fprintf(stderr
, "Invalid config: No value present: %s\n",
1234 if (trailing_garbage
!= NULL
) {
1235 fprintf(stderr
, "Config warning: "
1236 "trailing garbage: %s\n", conf_copy
);
1239 switch(directive_number
) {
1242 if (serverset
&& (setup_phase
== TRUE
)) {
1243 fprintf(stderr
, "Config change overridden by command "
1244 "line: %s\n", directive
);
1248 if (set_server(config_value
) == -1) {
1249 fprintf(stderr
, "Set server error: unable to change "
1250 "the server name to '%s'\n", config_value
);
1254 old_af
= server_ai
->ai_family
;
1255 if (set_server_sa() == -1) {
1256 fprintf(stderr
, "Set server error: unable to resolve "
1257 "a new server '%s'\n",
1261 if (old_af
!= server_ai
->ai_family
) {
1262 if ((query_socket
= change_socket()) == -1) {
1263 /* XXX: this is fatal */
1264 fprintf(stderr
, "Set server error: "
1265 "unable to open a new socket "
1266 "for '%s'\n", config_value
);
1274 if (portset
&& (setup_phase
== TRUE
)) {
1275 fprintf(stderr
, "Config change overridden by command "
1276 "line: %s\n", directive
);
1280 check
= is_uint(config_value
, &uint_val
);
1282 if ((check
== TRUE
) && (uint_val
> 0)) {
1283 if (set_server_port(config_value
) == -1) {
1284 fprintf(stderr
, "Invalid config: Bad value for"
1285 " %s: %s\n", directive
, config_value
);
1287 if (set_server_sa() == -1) {
1289 "Failed to set a new port\n");
1294 fprintf(stderr
, "Invalid config: Bad value for "
1295 "%s: %s\n", directive
, config_value
);
1299 if (queriesset
&& (setup_phase
== TRUE
)) {
1300 fprintf(stderr
, "Config change overridden by command "
1301 "line: %s\n", directive
);
1305 check
= is_uint(config_value
, &uint_val
);
1307 if ((check
== TRUE
) && (uint_val
> 0)) {
1308 set_max_queries(uint_val
);
1310 fprintf(stderr
, "Invalid config: Bad value for "
1311 "%s: %s\n", directive
, config_value
);
1315 if (timeoutset
&& (setup_phase
== TRUE
)) {
1316 fprintf(stderr
, "Config change overridden by command "
1317 "line: %s\n", directive
);
1321 check
= is_uint(config_value
, &uint_val
);
1323 if ((check
== TRUE
) && (uint_val
> 0)) {
1324 query_timeout
= uint_val
;
1326 fprintf(stderr
, "Invalid config: Bad value for "
1327 "%s: %s\n", directive
, config_value
);
1331 fprintf(stderr
, "Invalid config: Bad directive: %s\n",
1339 * Parse a query line from the input file
1341 * Set qname to be the domain to query (up to a max of qnlen chars)
1342 * Set qtype to be the type of the query
1344 * Return -1 on failure
1345 * Return a non-negative integer otherwise
1348 parse_query(char *input
, char *qname
, unsigned int qnlen
, int *qtype
) {
1349 static char *qtype_strings
[] = QTYPE_STRINGS
;
1350 static int qtype_codes
[] = QTYPE_CODES
;
1351 unsigned int num_types
, index
;
1353 char incopy
[MAX_INPUT_LEN
+ 1];
1354 char *domain_str
, *type_str
;
1356 num_types
= sizeof(qtype_strings
) / sizeof(qtype_strings
[0]);
1357 if (num_types
> (sizeof(qtype_codes
) / sizeof(int)))
1358 num_types
= sizeof(qtype_codes
) / sizeof(int);
1360 strcpy(incopy
, input
);
1362 domain_str
= strtok(incopy
, WHITESPACE
);
1363 type_str
= strtok(NULL
, WHITESPACE
);
1365 if ((domain_str
== NULL
) || (type_str
== NULL
)) {
1366 fprintf(stderr
, "Invalid query input format: %s\n", input
);
1370 if (strlen(domain_str
) > qnlen
) {
1371 fprintf(stderr
, "Query domain too long: %s\n", domain_str
);
1375 for (index
= 0; (index
< num_types
) && (found
== FALSE
); index
++) {
1376 if (strcasecmp(type_str
, qtype_strings
[index
]) == 0) {
1377 *qtype
= qtype_codes
[index
];
1382 if (found
== FALSE
) {
1383 fprintf(stderr
, "Query type not understood: %s\n", type_str
);
1387 strcpy(qname
, domain_str
);
1394 * Send the query packet for the entry
1396 * Return -1 on failure
1397 * Return a non-negative integer otherwise
1400 dispatch_query(unsigned short int id
, char *dom
, int qt
, u_char
**pktp
,
1403 static u_char packet_buffer
[PACKETSZ
+ 1];
1404 int buffer_len
= PACKETSZ
;
1406 unsigned short int net_id
= htons(id
);
1407 char *id_ptr
= (char *)&net_id
;
1408 HEADER
*hp
= (HEADER
*)packet_buffer
;
1410 buffer_len
= res_mkquery(QUERY
, dom
, C_IN
, qt
, NULL
, 0,
1411 NULL
, packet_buffer
, PACKETSZ
);
1412 if (buffer_len
== -1) {
1413 fprintf(stderr
, "Failed to create query packet: %s %d\n",
1420 if (buffer_len
+ EDNSLEN
>= PACKETSZ
) {
1421 fprintf(stderr
, "Failed to add OPT to query packet\n");
1424 packet_buffer
[11] = 1;
1425 p
= &packet_buffer
[buffer_len
];
1426 *p
++ = 0; /* root name */
1428 *p
++ = 41; /* OPT */
1430 *p
++ = 0; /* UDP payload size (4K) */
1431 *p
++ = 0; /* extended rcode */
1432 *p
++ = 0; /* version */
1434 *p
++ = 0x80; /* upper flag bits - DO set */
1436 *p
++ = 0; /* upper flag bits */
1437 *p
++ = 0; /* lower flag bit */
1439 *p
++ = 0; /* rdlen == 0 */
1440 buffer_len
+= EDNSLEN
;
1443 packet_buffer
[0] = id_ptr
[0];
1444 packet_buffer
[1] = id_ptr
[1];
1446 bytes_sent
= sendto(query_socket
, packet_buffer
, buffer_len
, 0,
1447 server_ai
->ai_addr
, server_ai
->ai_addrlen
);
1448 if (bytes_sent
== -1) {
1449 fprintf(stderr
, "Failed to send query packet: %s %d\n",
1454 if (bytes_sent
!= buffer_len
)
1455 fprintf(stderr
, "Warning: incomplete packet sent: %s %d\n",
1458 *pktp
= packet_buffer
;
1459 *pktlenp
= buffer_len
;
1466 * Send a query based on a line of input
1469 send_query(char *query_desc
) {
1470 static unsigned short int use_query_id
= 0;
1471 static int qname_len
= MAX_DOMAIN_LEN
;
1472 static char domain
[MAX_DOMAIN_LEN
+ 1];
1474 char serveraddr
[NI_MAXHOST
];
1475 int query_type
, qpkt_len
;
1480 if (parse_query(query_desc
, domain
, qname_len
, &query_type
) == -1) {
1481 fprintf(stderr
, "Error parsing query: %s\n", query_desc
);
1485 if (dispatch_query(use_query_id
, domain
, query_type
,
1486 &qpkt
, &qpkt_len
) == -1) {
1489 if (getnameinfo(server_ai
->ai_addr
, server_ai
->ai_addrlen
,
1490 serveraddr
, sizeof(serveraddr
), NULL
, 0,
1491 NI_NUMERICHOST
) == 0) {
1492 addrstr
= serveraddr
;
1494 addrstr
= "???"; /* XXX: this should not happen */
1495 fprintf(stderr
, "Error sending query to %s: %s\n",
1496 addrstr
, query_desc
);
1500 if (setup_phase
== TRUE
) {
1501 set_timenow(&time_of_first_query
);
1502 time_of_first_query_sec
= (double)time_of_first_query
.tv_sec
+
1503 ((double)time_of_first_query
.tv_usec
/ 1000000.0);
1504 setup_phase
= FALSE
;
1505 if (getnameinfo(server_ai
->ai_addr
, server_ai
->ai_addrlen
,
1506 serveraddr
, sizeof(serveraddr
), NULL
, 0,
1507 NI_NUMERICHOST
) != 0) {
1508 fprintf(stderr
, "Error printing server address\n");
1511 printf("[Status] Sending queries (beginning with %s)\n",
1515 /* Find the first slot in status[] that is not in use */
1516 for (count
= 0; (status
[count
].in_use
== TRUE
)
1517 && (count
< max_queries_outstanding
); count
++);
1519 if (status
[count
].in_use
== TRUE
) {
1520 fprintf(stderr
, "Unexpected error: We have run out of "
1521 "status[] space!\n");
1525 /* Register the query in status[] */
1526 status
[count
].id
= use_query_id
;
1528 status
[count
].desc
= strdup(query_desc
);
1529 set_timenow(&status
[count
].sent_timestamp
);
1530 status
[count
].qtype
= query_type
;
1531 if (dn_expand(qpkt
, qpkt
+ qpkt_len
, qpkt
+ DNS_HEADERLEN
,
1532 status
[count
].qname
, MAX_DOMAIN_LEN
) == -1) {
1533 fprintf(stderr
, "Unexpected error: "
1534 "query message doesn't have qname?\n");
1537 status
[count
].in_use
= TRUE
;
1539 if (num_queries_sent_interval
== 0)
1540 set_timenow(&time_of_first_query_interval
);
1543 num_queries_sent_interval
++;
1544 num_queries_outstanding
++;
1548 register_rtt(struct timeval
*timestamp
, char *qname
, int qtype
,
1552 int oldquery
= FALSE
;
1557 rtt
= difftv(now
, *timestamp
);
1559 if (difftv(*timestamp
, time_of_first_query_interval
) < 0)
1562 if (rtt_max
< 0 || rtt_max
< rtt
)
1565 if (rtt_min
< 0 || rtt_min
> rtt
)
1572 if (rtt_max_interval
< 0 || rtt_max_interval
< rtt
)
1573 rtt_max_interval
= rtt
;
1575 if (rtt_min_interval
< 0 || rtt_min_interval
> rtt
)
1576 rtt_min_interval
= rtt
;
1578 rtt_total_interval
+= rtt
;
1579 rtt_counted_interval
++;
1582 if (rttarray
== NULL
)
1585 i
= (int)(rtt
* (1000000.0 / rttarray_unit
));
1586 if (i
< rttarray_size
) {
1589 rttarray_interval
[i
]++;
1591 fprintf(stderr
, "Warning: RTT is out of range: %.6lf "
1592 "[query=%s/%d, rcode=%u]\n", rtt
, qname
, qtype
, rcode
);
1595 rtt_overflows_interval
++;
1600 * register_response:
1601 * Register receipt of a query
1603 * Removes (sets in_use = FALSE) the record for the given query id in
1604 * status[] if any exists.
1607 register_response(unsigned short int id
, unsigned int rcode
, char *qname
,
1610 unsigned int ct
= 0;
1615 if (timeout_queries
!= NULL
) {
1616 struct query_mininfo
*qi
= &timeout_queries
[id
];
1618 if (qi
->qtype
== qtype
&& strcasecmp(qi
->qname
, qname
) == 0) {
1619 register_rtt(&qi
->sent_timestamp
, qname
, qtype
, rcode
);
1625 for (; (ct
< query_status_allocated
) && (found
== FALSE
); ct
++) {
1626 if (status
[ct
].in_use
== TRUE
&& status
[ct
].id
== id
&&
1627 status
[ct
].qtype
== qtype
&&
1628 strcasecmp(status
[ct
].qname
, qname
) == 0) {
1629 status
[ct
].in_use
= FALSE
;
1630 num_queries_outstanding
--;
1633 register_rtt(&status
[ct
].sent_timestamp
, qname
, qtype
,
1636 if (status
[ct
].desc
) {
1637 printf("> %s %s\n", rcode_strings
[rcode
],
1639 free(status
[ct
].desc
);
1644 if (countrcodes
&& (found
== TRUE
|| target_qps
> 0))
1645 rcodecounts
[rcode
]++;
1647 if (found
== FALSE
) {
1648 if (target_qps
> 0) {
1649 num_queries_possiblydelayed
++;
1650 num_queries_possiblydelayed_interval
++;
1653 "Warning: Received a response with an "
1654 "unexpected (maybe timed out) id: %u\n", id
);
1660 * process_single_response:
1661 * Receive from the given socket & process an invididual response packet.
1662 * Remove it from the list of open queries (status[]) and decrement the
1663 * number of outstanding queries if it matches an open query.
1666 process_single_response(int sockfd
) {
1667 struct sockaddr_storage from_addr_ss
;
1668 struct sockaddr
*from_addr
;
1669 static unsigned char in_buf
[MAX_BUFFER_LEN
];
1670 char qname
[MAX_DOMAIN_LEN
+ 1];
1671 int numbytes
, addr_len
, resp_id
, qnamelen
;
1674 memset(&from_addr_ss
, 0, sizeof(from_addr_ss
));
1675 from_addr
= (struct sockaddr
*)&from_addr_ss
;
1676 addr_len
= sizeof(from_addr_ss
);
1678 if ((numbytes
= recvfrom(sockfd
, in_buf
, MAX_BUFFER_LEN
,
1679 0, from_addr
, &addr_len
)) == -1) {
1680 fprintf(stderr
, "Error receiving datagram\n");
1684 if (numbytes
< DNS_HEADERLEN
) {
1686 fprintf(stderr
, "Malformed response\n");
1689 resp_id
= get_uint16(in_buf
);
1690 flags
= get_uint16(in_buf
+ 2);
1691 qnamelen
= dn_expand(in_buf
, in_buf
+ numbytes
, in_buf
+ DNS_HEADERLEN
,
1692 qname
, MAX_DOMAIN_LEN
);
1693 if (qnamelen
== -1) {
1696 "Failed to retrieve qname from response\n");
1699 if (numbytes
< DNS_HEADERLEN
+ qnamelen
+ 2) {
1701 fprintf(stderr
, "Malformed response\n");
1704 qtype
= get_uint16(in_buf
+ DNS_HEADERLEN
+ qnamelen
);
1706 register_response(resp_id
, flags
& 0xF, qname
, qtype
);
1711 * Is there data available on the given file descriptor?
1713 * Return TRUE if there is
1714 * Return FALSE otherwise
1717 data_available(double wait
) {
1721 int available
= FALSE
;
1724 /* Set list of file descriptors */
1726 if (socket4
!= -1) {
1727 FD_SET(socket4
, &read_fds
);
1730 if (socket6
!= -1) {
1731 FD_SET(socket6
, &read_fds
);
1732 if (maxfd
== -1 || maxfd
< socket6
)
1736 if ((wait
> 0.0) && (wait
< (double)LONG_MAX
)) {
1737 tv
.tv_sec
= (long)floor(wait
);
1738 tv
.tv_usec
= (long)(1000000.0 * (wait
- floor(wait
)));
1744 retval
= select(maxfd
+ 1, &read_fds
, NULL
, NULL
, &tv
);
1746 if (socket4
!= -1 && FD_ISSET(socket4
, &read_fds
)) {
1748 process_single_response(socket4
);
1750 if (socket6
!= -1 && FD_ISSET(socket6
, &read_fds
)) {
1752 process_single_response(socket6
);
1759 * process_responses:
1760 * Go through any/all received responses and remove them from the list of
1761 * open queries (set in_use = FALSE for their entry in status[]), also
1762 * decrementing the number of outstanding queries.
1765 process_responses(int adjust_rate
) {
1767 struct timeval now
, waituntil
;
1768 double first_packet_wait
= RESPONSE_BLOCKING_WAIT_TIME
;
1769 unsigned int outstanding
= queries_outstanding();
1771 if (adjust_rate
== TRUE
) {
1774 u
= time_of_first_query_sec
+
1775 query_interval
* num_queries_sent
;
1776 waituntil
.tv_sec
= (long)floor(u
);
1777 waituntil
.tv_usec
= (long)(1000000.0 * (u
- waituntil
.tv_sec
));
1780 * Wait until a response arrives or the specified limit is
1785 wait
= difftv(waituntil
, now
);
1788 if (data_available(wait
) != TRUE
)
1792 * We have reached the limit. Read as many responses
1793 * as possible without waiting, and exit.
1796 while (data_available(0.0) == TRUE
)
1803 * Don't block waiting for packets at all if we aren't
1804 * looking for any responses or if we are now able to send new
1807 if ((outstanding
== 0) ||
1808 (outstanding
< max_queries_outstanding
)) {
1809 first_packet_wait
= 0.0;
1812 if (data_available(first_packet_wait
) == TRUE
) {
1813 while (data_available(0.0) == TRUE
)
1820 * retire_old_queries:
1821 * Go through the list of open queries (status[]) and remove any queries
1822 * (i.e. set in_use = FALSE) which are older than the timeout, decrementing
1823 * the number of queries outstanding for each one removed.
1826 retire_old_queries(int sending
) {
1827 unsigned int count
= 0;
1828 struct timeval curr_time
;
1829 double timeout
= query_timeout
;
1830 int timeout_reduced
= FALSE
;
1833 * If we have target qps and would not be able to send any packets
1834 * due to buffer full, check whether we are behind the schedule.
1835 * If we are, purge some queries more aggressively.
1837 if (target_qps
> 0 && sending
== TRUE
&& count
== 0 &&
1838 queries_outstanding() == max_queries_outstanding
) {
1839 struct timeval next
, now
;
1842 n
= time_of_first_query_sec
+
1843 query_interval
* num_queries_sent
;
1844 next
.tv_sec
= (long)floor(n
);
1845 next
.tv_usec
= (long)(1000000.0 * (n
- next
.tv_sec
));
1848 if (difftv(next
, now
) <= 0) {
1849 timeout_reduced
= TRUE
;
1850 timeout
= 0.001; /* XXX: ad-hoc value */
1854 set_timenow(&curr_time
);
1856 for (; count
< query_status_allocated
; count
++) {
1857 if ((status
[count
].in_use
== TRUE
) &&
1859 status
[count
].sent_timestamp
) >= (double)timeout
))
1861 status
[count
].in_use
= FALSE
;
1862 num_queries_outstanding
--;
1864 if (timeout_queries
!= NULL
) {
1865 struct query_mininfo
*qi
;
1867 qi
= &timeout_queries
[status
[count
].id
];
1868 if (qi
->qtype
!= -1) {
1869 /* now really retire this query */
1870 num_queries_timed_out
++;
1871 num_queries_timed_out_interval
++;
1873 qi
->qtype
= status
[count
].qtype
;
1874 qi
->sent_timestamp
=
1875 status
[count
].sent_timestamp
;
1876 strcpy(qi
->qname
, status
[count
].qname
);
1878 num_queries_timed_out
++;
1879 num_queries_timed_out_interval
++;
1882 if (timeout_reduced
== FALSE
) {
1883 if (status
[count
].desc
) {
1884 printf("> T %s\n", status
[count
].desc
);
1885 free(status
[count
].desc
);
1887 printf("[Timeout] Query timed out: "
1898 * Print RTT histogram to the specified file in the gnuplot format
1901 print_histogram(unsigned int total
) {
1906 if (rtt_histogram_file
== NULL
|| rttarray
== NULL
)
1909 fp
= fopen((const char *)rtt_histogram_file
, "w+");
1911 fprintf(stderr
, "Error opening RTT histogram file: %s\n",
1912 rtt_histogram_file
);
1916 for (i
= 0; i
< rttarray_size
; i
++) {
1917 ratio
= ((double)rttarray
[i
] / (double)total
) * 100;
1918 fprintf(fp
, "%.6lf %.3lf\n",
1919 (double)(i
* rttarray_unit
) +
1920 (double)rttarray_unit
/ 2,
1929 * Print out statistics based on the results of the test
1932 print_statistics(int intermediate
, unsigned int sent
, unsigned int timed_out
,
1933 unsigned int possibly_delayed
,
1934 struct timeval
*first_query
,
1935 struct timeval
*program_start
,
1936 struct timeval
*end_perf
, struct timeval
*end_query
,
1937 unsigned int rcounted
, double rmax
, double rmin
, double rtotal
,
1938 unsigned int roverflows
, unsigned int *rarray
)
1940 unsigned int num_queries_completed
;
1941 double per_lost
, per_completed
, per_lost2
, per_completed2
;
1942 double run_time
, queries_per_sec
, queries_per_sec2
;
1943 double queries_per_sec_total
;
1944 double rtt_average
, rtt_stddev
;
1945 struct timeval start_time
;
1947 num_queries_completed
= sent
- timed_out
;
1949 if (num_queries_completed
== 0) {
1951 per_completed
= 0.0;
1954 per_completed2
= 0.0;
1956 per_lost
= (100.0 * (double)timed_out
) / (double)sent
;
1957 per_completed
= 100.0 - per_lost
;
1959 per_lost2
= (100.0 * (double)(timed_out
- possibly_delayed
))
1961 per_completed2
= 100 - per_lost2
;
1965 start_time
.tv_sec
= program_start
->tv_sec
;
1966 start_time
.tv_usec
= program_start
->tv_usec
;
1968 queries_per_sec
= 0.0;
1969 queries_per_sec2
= 0.0;
1970 queries_per_sec_total
= 0.0;
1972 start_time
.tv_sec
= first_query
->tv_sec
;
1973 start_time
.tv_usec
= first_query
->tv_usec
;
1974 run_time
= difftv(*end_perf
, *first_query
);
1975 queries_per_sec
= (double)num_queries_completed
/ run_time
;
1976 queries_per_sec2
= (double)(num_queries_completed
+
1977 possibly_delayed
) / run_time
;
1979 queries_per_sec_total
= (double)sent
/
1980 difftv(*end_query
, *first_query
);
1987 rtt_average
= rtotal
/ (double)rcounted
;
1988 for (i
= 0; i
< rttarray_size
; i
++) {
1989 if (rarray
[i
] != 0) {
1992 mean
= (double)(i
* rttarray_unit
) +
1993 (double)rttarray_unit
/ 2;
1994 diff
= rtt_average
- (mean
/ 1000000.0);
1995 sum
+= (diff
* diff
) * rarray
[i
];
1998 rtt_stddev
= sqrt(sum
/ (double)rcounted
);
2006 printf("%sStatistics:\n", intermediate
? "Intermediate " : "");
2010 if (!intermediate
) {
2011 printf(" Parse input file: %s\n",
2012 ((run_only_once
== TRUE
) ? "once" : "multiple times"));
2014 printf(" Run time limit: %u seconds\n",
2016 if (run_only_once
== FALSE
)
2017 printf(" Ran through file: %u times\n",
2020 printf(" Ended due to: reaching %s\n",
2021 ((runs_through_file
== 0) ? "time limit"
2027 printf(" Queries sent: %u queries\n", sent
);
2028 printf(" Queries completed: %u queries\n", num_queries_completed
);
2029 printf(" Queries lost: %u queries\n", timed_out
);
2030 printf(" Queries delayed(?): %u queries\n", possibly_delayed
);
2034 printf(" RTT max: %3.6lf sec\n", rmax
);
2035 printf(" RTT min: %3.6lf sec\n", rmin
);
2036 printf(" RTT average: %3.6lf sec\n", rtt_average
);
2037 printf(" RTT std deviation: %3.6lf sec\n", rtt_stddev
);
2038 printf(" RTT out of range: %u queries\n", roverflows
);
2040 if (!intermediate
) /* XXX should we print this case also? */
2041 print_histogram(num_queries_completed
);
2048 for (i
= 0; i
< 16; i
++) {
2049 if (rcodecounts
[i
] == 0)
2051 printf(" Returned %8s: %u queries\n",
2052 rcode_strings
[i
], rcodecounts
[i
]);
2057 printf(" Percentage completed: %6.2lf%%\n", per_completed
);
2058 if (possibly_delayed
> 0)
2059 printf(" (w/ delayed qrys): %6.2lf%%\n", per_completed2
);
2060 printf(" Percentage lost: %6.2lf%%\n", per_lost
);
2061 if (possibly_delayed
> 0)
2062 printf(" (w/o delayed qrys): %6.2lf%%\n", per_lost2
);
2066 printf(" Started at: %s",
2067 ctime((const time_t *)&start_time
.tv_sec
));
2068 printf(" Finished at: %s",
2069 ctime((const time_t *)&end_perf
->tv_sec
));
2070 printf(" Ran for: %.6lf seconds\n", run_time
);
2074 printf(" Queries per second: %.6lf qps\n", queries_per_sec
);
2075 if (possibly_delayed
> 0) {
2076 printf(" (w/ delayed qrys): %.6lf qps\n",
2079 if (target_qps
> 0) {
2080 printf(" Total QPS/target: %.6lf/%d qps\n",
2081 queries_per_sec_total
, target_qps
);
2088 print_interval_statistics() {
2089 struct timeval time_now
;
2091 if (use_timelimit
== FALSE
)
2094 if (setup_phase
== TRUE
)
2097 if (print_interval
== 0)
2100 if (timelimit_reached() == TRUE
)
2103 set_timenow(&time_now
);
2104 if (difftv(time_now
, time_of_first_query_interval
)
2105 <= (double)print_interval
)
2108 /* Don't count currently outstanding queries */
2109 num_queries_sent_interval
-= queries_outstanding();
2110 print_statistics(TRUE
, num_queries_sent_interval
,
2111 num_queries_timed_out_interval
,
2112 num_queries_possiblydelayed_interval
,
2113 &time_of_first_query_interval
,
2114 &time_of_first_query_interval
, &time_now
, &time_now
,
2115 rtt_counted_interval
, rtt_max_interval
,
2116 rtt_min_interval
, rtt_total_interval
,
2117 rtt_overflows_interval
, rttarray_interval
);
2119 /* Reset intermediate counters */
2120 num_queries_sent_interval
= 0;
2121 num_queries_timed_out_interval
= 0;
2122 num_queries_possiblydelayed_interval
= 0;
2123 rtt_max_interval
= -1;
2124 rtt_min_interval
= -1;
2125 rtt_total_interval
= 0.0;
2126 rtt_counted_interval
= 0.0;
2127 rtt_overflows_interval
= 0;
2128 if (rttarray_interval
!= NULL
) {
2129 memset(rttarray_interval
, 0,
2130 sizeof(rttarray_interval
[0]) * rttarray_size
);
2135 * queryperf Program Mainline
2138 main(int argc
, char **argv
) {
2140 int sending
= FALSE
;
2141 int got_eof
= FALSE
;
2142 int input_length
= MAX_INPUT_LEN
;
2143 char input_line
[MAX_INPUT_LEN
+ 1];
2145 set_timenow(&time_of_program_start
);
2146 time_of_first_query
.tv_sec
= 0;
2147 time_of_first_query
.tv_usec
= 0;
2148 time_of_first_query_interval
.tv_sec
= 0;
2149 time_of_first_query_interval
.tv_usec
= 0;
2150 time_of_end_of_run
.tv_sec
= 0;
2151 time_of_end_of_run
.tv_usec
= 0;
2153 input_line
[0] = '\0';
2155 show_startup_info();
2157 if (setup(argc
, argv
) == -1)
2160 /* XXX: move this to setup: */
2161 timeout_queries
= malloc(sizeof(struct query_mininfo
) * 65536);
2162 if (timeout_queries
== NULL
) {
2164 "failed to allocate memory for timeout queries\n");
2168 for (i
= 0; i
< 65536; i
++)
2169 timeout_queries
[i
].qtype
= -1;
2172 printf("[Status] Processing input data\n");
2174 while ((sending
= keep_sending(&got_eof
)) == TRUE
||
2175 queries_outstanding() > 0)
2177 if (num_queries_sent_interval
> 0){
2179 * After statistics are printed, send_query()
2180 * needs to be called at least once so that
2181 * time_of_first_query_interval is reset
2183 print_interval_statistics();
2185 adjust_rate
= FALSE
;
2187 while ((sending
= keep_sending(&got_eof
)) == TRUE
&&
2188 queries_outstanding() < max_queries_outstanding
)
2190 int len
= next_input_line(input_line
, input_length
);
2194 /* Zap the trailing newline */
2195 if (input_line
[len
- 1] == '\n')
2196 input_line
[len
- 1] = '\0';
2199 * TODO: Should test if we got a whole line
2200 * and flush to the next \n in input if not
2201 * here... Add this later. Only do the next
2202 * few lines if we got a whole line, else
2203 * print a warning. Alternative: Make the
2204 * max line size really big. BAD! :)
2207 if (input_line
[0] == CONFIG_CHAR
)
2208 update_config(input_line
);
2210 send_query(input_line
);
2211 if (target_qps
> 0 &&
2213 max_queries_outstanding
) == 0) {
2220 process_responses(adjust_rate
);
2221 retire_old_queries(sending
);
2224 set_timenow(&time_of_end_of_run
);
2226 printf("[Status] Testing complete\n");
2231 print_statistics(FALSE
, num_queries_sent
, num_queries_timed_out
,
2232 num_queries_possiblydelayed
,
2233 &time_of_first_query
, &time_of_program_start
,
2234 &time_of_end_of_run
, &time_of_stop_sending
,
2235 rtt_counted
, rtt_max
, rtt_min
, rtt_total
,
2236 rtt_overflows
, rttarray
);