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>
29 #include <sys/types.h>
30 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/nameser.h>
46 #ifndef HAVE_GETADDRINFO
47 #include "missing/addrinfo.h"
52 * Configuration defaults
55 #define DEF_MAX_QUERIES_OUTSTANDING 20
56 #define DEF_QUERY_TIMEOUT 5 /* in seconds */
57 #define DEF_SERVER_TO_QUERY "127.0.0.1"
58 #define DEF_SERVER_PORT "53"
59 #define DEF_BUFFER_SIZE 32 /* in k */
61 #define DEF_RTTARRAY_SIZE 50000
62 #define DEF_RTTARRAY_UNIT 100 /* in usec */
65 * Other constants / definitions
68 #define COMMENT_CHAR ';'
69 #define CONFIG_CHAR '#'
70 #define MAX_PORT 65535
71 #define MAX_INPUT_LEN 512
72 #define MAX_DOMAIN_LEN 255
73 #define MAX_BUFFER_LEN 8192 /* in bytes */
74 #define HARD_TIMEOUT_EXTRA 5 /* in seconds */
75 #define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */
81 #define WHITESPACE " \t\n"
83 enum directives_enum
{ V_SERVER
, V_PORT
, V_MAXQUERIES
, V_MAXWAIT
};
84 #define DIRECTIVES { "server", "port", "maxqueries", "maxwait" }
85 #define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }
87 #define QTYPE_STRINGS { \
88 "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", \
89 "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", \
90 "AAAA", "SRV", "NAPTR", "A6", "AXFR", "MAILB", "MAILA", "*", "ANY" \
93 #define QTYPE_CODES { \
94 1, 2, 3, 4, 5, 6, 7, 8, \
95 9, 10, 11, 12, 13, 14, 15, 16, \
96 28, 33, 35, 38, 252, 253, 254, 255, 255 \
99 #define RCODE_STRINGS { \
100 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \
101 "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \
102 "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \
103 "rcode12", "rcode13", "rcode14", "rcode15" \
107 * Data type definitions
110 #define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */
111 #define VALID_QUERY_STATUS(q) ((q) != NULL && \
112 (q)->magic == QUERY_STATUS_MAGIC)
114 struct query_status
{
117 unsigned short int id
;
118 struct timeval sent_timestamp
;
123 * Configuration options (global)
126 unsigned int max_queries_outstanding
; /* init 0 */
127 unsigned int query_timeout
= DEF_QUERY_TIMEOUT
;
128 int ignore_config_changes
= FALSE
;
129 unsigned int socket_bufsize
= DEF_BUFFER_SIZE
;
131 int family
= AF_UNSPEC
;
132 int use_stdin
= TRUE
;
133 char *datafile_name
; /* init NULL */
135 char *server_to_query
; /* init NULL */
136 char *server_port
; /* init NULL */
137 struct addrinfo
*server_ai
; /* init NULL */
139 int run_only_once
= FALSE
;
140 int use_timelimit
= FALSE
;
141 unsigned int run_timelimit
; /* init 0 */
142 unsigned int print_interval
; /* init 0 */
144 unsigned int target_qps
; /* init 0 */
146 int serverset
= FALSE
, portset
= FALSE
;
147 int queriesset
= FALSE
, timeoutset
= FALSE
;
148 int edns
= FALSE
, dnssec
= FALSE
;
149 int countrcodes
= FALSE
;
150 int rcodecounts
[16] = {0};
158 int setup_phase
= TRUE
;
160 FILE *datafile_ptr
; /* init NULL */
161 unsigned int runs_through_file
; /* init 0 */
163 unsigned int num_queries_sent
; /* init 0 */
164 unsigned int num_queries_sent_interval
;
165 unsigned int num_queries_outstanding
; /* init 0 */
166 unsigned int num_queries_timed_out
; /* init 0 */
167 unsigned int num_queries_possiblydelayed
; /* init 0 */
168 unsigned int num_queries_timed_out_interval
;
169 unsigned int num_queries_possiblydelayed_interval
;
171 struct timeval time_of_program_start
;
172 struct timeval time_of_first_query
;
173 double time_of_first_query_sec
;
174 struct timeval time_of_first_query_interval
;
175 struct timeval time_of_end_of_run
;
176 struct timeval time_of_stop_sending
;
178 struct timeval time_of_queryset_start
;
179 double query_interval
;
180 struct timeval time_of_next_queryset
;
183 double rtt_max_interval
= -1;
185 double rtt_min_interval
= -1;
187 double rtt_total_interval
;
188 int rttarray_size
= DEF_RTTARRAY_SIZE
;
189 int rttarray_unit
= DEF_RTTARRAY_UNIT
;
190 unsigned int *rttarray
= NULL
;
191 unsigned int *rttarray_interval
= NULL
;
192 unsigned int rtt_overflows
;
193 unsigned int rtt_overflows_interval
;
194 char *rtt_histogram_file
= NULL
;
196 struct query_status
*status
; /* init NULL */
197 unsigned int query_status_allocated
; /* init 0 */
199 int query_socket
= -1;
200 int socket4
= -1, socket6
= -1;
202 static char *rcode_strings
[] = RCODE_STRINGS
;
206 * Get an unsigned short integer from a buffer (in network order)
208 static unsigned short
209 get_uint16(unsigned char *buf
) {
212 ret
= buf
[0] * 256 + buf
[1];
222 show_startup_info(void) {
224 "DNS Query Performance Testing Tool\n"
225 "Version: Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp\n"
231 * Print out usage/syntax information
237 "Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n"
238 " [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n"
239 " [-i interval] [-r arraysize] [-u unit] [-H histfile]\n"
240 " [-T qps] [-e] [-D] [-c] [-v] [-h]\n"
241 " -d specifies the input data file (default: stdin)\n"
242 " -s sets the server to query (default: %s)\n"
243 " -p sets the port on which to query the server (default: %s)\n"
244 " -q specifies the maximum number of queries outstanding (default: %d)\n"
245 " -t specifies the timeout for query completion in seconds (default: %d)\n"
246 " -n causes configuration changes to be ignored\n"
247 " -l specifies how a limit for how long to run tests in seconds (no default)\n"
248 " -1 run through input only once (default: multiple iff limit given)\n"
249 " -b set input/output buffer size in kilobytes (default: %d k)\n"
250 " -i specifies interval of intermediate outputs in seconds (default: 0=none)\n"
251 " -f specify address family of DNS transport, inet or inet6 (default: any)\n"
252 " -r set RTT statistics array size (default: %d)\n"
253 " -u set RTT statistics time unit in usec (default: %d)\n"
254 " -H specifies RTT histogram data file (default: none)\n"
255 " -T specify the target qps (default: 0=unspecified)\n"
256 " -e enable EDNS 0\n"
257 " -D set the DNSSEC OK bit (implies EDNS)\n"
258 " -c print the number of packets with each rcode\n"
259 " -v verbose: report the RCODE of each response on stdout\n"
260 " -h print this usage\n"
262 DEF_SERVER_TO_QUERY
, DEF_SERVER_PORT
,
263 DEF_MAX_QUERIES_OUTSTANDING
, DEF_QUERY_TIMEOUT
,
264 DEF_BUFFER_SIZE
, DEF_RTTARRAY_SIZE
, DEF_RTTARRAY_UNIT
);
269 * Set the datafile to read
271 * Return -1 on failure
272 * Return a non-negative integer otherwise
275 set_datafile(char *new_file
) {
278 if ((new_file
== NULL
) || (new_file
[0] == '\0')) {
279 fprintf(stderr
, "Error: null datafile name\n");
283 if ((dfname_tmp
= malloc(strlen(new_file
) + 1)) == NULL
) {
284 fprintf(stderr
, "Error allocating memory for datafile name: "
290 datafile_name
= dfname_tmp
;
292 strcpy(datafile_name
, new_file
);
300 * Set the input to be stdin (instead of a datafile)
303 set_input_stdin(void) {
306 datafile_name
= NULL
;
311 * Set the server to be queried
313 * Return -1 on failure
314 * Return a non-negative integer otherwise
317 set_server(char *new_name
) {
318 static struct hostent
*server_he
;
320 /* If no change in server name, don't do anything... */
321 if ((server_to_query
!= NULL
) && (new_name
!= NULL
))
322 if (strcmp(new_name
, server_to_query
) == 0)
325 if ((new_name
== NULL
) || (new_name
[0] == '\0')) {
326 fprintf(stderr
, "Error: null server name\n");
330 free(server_to_query
);
331 server_to_query
= NULL
;
333 if ((server_to_query
= malloc(strlen(new_name
) + 1)) == NULL
) {
334 fprintf(stderr
, "Error allocating memory for server name: "
339 strcpy(server_to_query
, new_name
);
346 * Set the port on which to contact the server
348 * Return -1 if port is invalid
349 * Return a non-negative integer otherwise
352 set_server_port(char *new_port
) {
353 unsigned int uint_val
;
355 if ((is_uint(new_port
, &uint_val
)) != TRUE
)
358 if (uint_val
&& uint_val
> MAX_PORT
)
361 if (server_port
!= NULL
&& new_port
!= NULL
&&
362 strcmp(server_port
, new_port
) == 0)
368 if ((server_port
= malloc(strlen(new_port
) + 1)) == NULL
) {
370 "Error allocating memory for server port: "
375 strcpy(server_port
, new_port
);
382 set_server_sa(void) {
383 struct addrinfo hints
, *res
;
384 static struct protoent
*proto
;
387 if (proto
== NULL
&& (proto
= getprotobyname("udp")) == NULL
) {
388 fprintf(stderr
, "Error: getprotobyname call failed");
392 memset(&hints
, 0, sizeof(hints
));
393 hints
.ai_family
= family
;
394 hints
.ai_socktype
= SOCK_DGRAM
;
395 hints
.ai_protocol
= proto
->p_proto
;
396 if ((error
= getaddrinfo(server_to_query
, server_port
,
397 &hints
, &res
)) != 0) {
398 fprintf(stderr
, "Error: getaddrinfo(%s, %s) failed\n",
399 server_to_query
, server_port
);
403 /* replace the server's addrinfo */
404 if (server_ai
!= NULL
)
405 freeaddrinfo(server_ai
);
412 * Tests if a character is a digit
414 * Return TRUE if it is
415 * Return FALSE if it is not
419 if (d
< '0' || d
> '9')
427 * Tests if a string, test_int, is a valid unsigned integer
429 * Sets *result to be the unsigned integer if it is valid
431 * Return TRUE if it is
432 * Return FALSE if it is not
435 is_uint(char *test_int
, unsigned int *result
) {
436 unsigned long int value
;
439 if (test_int
== NULL
)
442 if (is_digit(test_int
[0]) == FALSE
)
445 value
= strtoul(test_int
, &end
, 10);
447 if ((errno
== ERANGE
) || (*end
!= '\0') || (value
> UINT_MAX
))
450 *result
= (unsigned int)value
;
456 * Set the maximum number of outstanding queries
458 * Returns -1 on failure
459 * Returns a non-negative integer otherwise
462 set_max_queries(unsigned int new_max
) {
463 static unsigned int size_qs
= sizeof(struct query_status
);
464 struct query_status
*temp_stat
;
468 fprintf(stderr
, "Unable to change max outstanding queries: "
469 "must be positive and non-zero: %u\n", new_max
);
473 if (new_max
> query_status_allocated
) {
474 temp_stat
= realloc(status
, new_max
* size_qs
);
476 if (temp_stat
== NULL
) {
477 fprintf(stderr
, "Error resizing query_status\n");
483 * Be careful to only initialise between above
484 * the previously allocated space. Note that the
485 * allocation may be larger than the current
486 * max_queries_outstanding. We don't want to
487 * "forget" any outstanding queries! We might
488 * still have some above the bounds of the max.
490 count
= query_status_allocated
;
491 for (; count
< new_max
; count
++) {
492 status
[count
].in_use
= FALSE
;
493 status
[count
].magic
= QUERY_STATUS_MAGIC
;
494 status
[count
].desc
= NULL
;
497 query_status_allocated
= new_max
;
501 max_queries_outstanding
= new_max
;
508 * Parse program arguments and set configuration options
510 * Return -1 on failure
511 * Return a non-negative integer otherwise
514 parse_args(int argc
, char **argv
) {
516 unsigned int uint_arg_val
;
518 while ((c
= getopt(argc
, argv
,
519 "f:q:t:i:nd:s:p:1l:b:eDcvr:T::u:H:h")) != -1) {
522 if (strcmp(optarg
, "inet") == 0)
525 else if (strcmp(optarg
, "inet6") == 0)
528 else if (strcmp(optarg
, "any") == 0)
531 fprintf(stderr
, "Invalid address family: %s\n",
537 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
538 set_max_queries(uint_arg_val
);
541 fprintf(stderr
, "Option requires a positive "
542 "integer value: -%c %s\n",
549 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
550 query_timeout
= uint_arg_val
;
553 fprintf(stderr
, "Option requires a positive "
554 "integer value: -%c %s\n",
561 ignore_config_changes
= TRUE
;
565 if (set_datafile(optarg
) == -1) {
566 fprintf(stderr
, "Error setting datafile "
567 "name: %s\n", optarg
);
573 if (set_server(optarg
) == -1) {
574 fprintf(stderr
, "Error setting server "
575 "name: %s\n", optarg
);
582 if (is_uint(optarg
, &uint_arg_val
) == TRUE
&&
583 uint_arg_val
< MAX_PORT
)
585 set_server_port(optarg
);
588 fprintf(stderr
, "Option requires a positive "
589 "integer between 0 and %d: -%c %s\n",
590 MAX_PORT
- 1, c
, optarg
);
596 run_only_once
= TRUE
;
600 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
601 use_timelimit
= TRUE
;
602 run_timelimit
= uint_arg_val
;
604 fprintf(stderr
, "Option requires a positive "
612 if (is_uint(optarg
, &uint_arg_val
) == TRUE
) {
613 socket_bufsize
= uint_arg_val
;
615 fprintf(stderr
, "Option requires a positive "
635 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
636 print_interval
= uint_arg_val
;
638 fprintf(stderr
, "Invalid interval: %s\n",
644 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
645 rttarray_size
= uint_arg_val
;
647 fprintf(stderr
, "Invalid RTT array size: %s\n",
653 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
654 rttarray_unit
= uint_arg_val
;
656 fprintf(stderr
, "Invalid RTT unit: %s\n",
662 rtt_histogram_file
= optarg
;
665 if (is_uint(optarg
, &uint_arg_val
) == TRUE
)
666 target_qps
= uint_arg_val
;
668 fprintf(stderr
, "Invalid target qps: %s\n",
676 fprintf(stderr
, "Invalid option: -%c\n", optopt
);
681 if (run_only_once
== FALSE
&& use_timelimit
== FALSE
)
682 run_only_once
= TRUE
;
689 * Open the data file ready for reading
691 * Return -1 on failure
692 * Return non-negative integer on success
695 open_datafile(void) {
696 if (use_stdin
== TRUE
) {
697 datafile_ptr
= stdin
;
700 if ((datafile_ptr
= fopen(datafile_name
, "r")) == NULL
) {
701 fprintf(stderr
, "Error: unable to open datafile: %s\n",
711 * Close the data file if any is open
713 * Return -1 on failure
714 * Return non-negative integer on success, including if not needed
717 close_datafile(void) {
718 if ((use_stdin
== FALSE
) && (datafile_ptr
!= NULL
)) {
719 if (fclose(datafile_ptr
) != 0) {
720 fprintf(stderr
, "Error: unable to close datafile\n");
730 * Open a socket for the queries. When we have an active socket already,
731 * close it and open a new one.
733 * Return -1 on failure
734 * Return the socket identifier
741 struct addrinfo hints
, *res
;
743 memset(&hints
, 0, sizeof(hints
));
744 hints
.ai_family
= server_ai
->ai_family
;
745 hints
.ai_socktype
= server_ai
->ai_socktype
;
746 hints
.ai_protocol
= server_ai
->ai_protocol
;
747 hints
.ai_flags
= AI_PASSIVE
;
749 if ((ret
= getaddrinfo(NULL
, "0", &hints
, &res
)) != 0) {
751 "Error: getaddrinfo for bind socket failed: %s\n",
756 if ((sock
= socket(res
->ai_family
, SOCK_DGRAM
,
757 res
->ai_protocol
)) == -1) {
758 fprintf(stderr
, "Error: socket call failed");
762 #if defined(AF_INET6) && defined(IPV6_V6ONLY)
763 if (res
->ai_family
== AF_INET6
) {
766 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
767 &on
, sizeof(on
)) == -1) {
769 "Warning: setsockopt(IPV6_V6ONLY) failed\n");
774 if (bind(sock
, res
->ai_addr
, res
->ai_addrlen
) == -1)
775 fprintf(stderr
, "Error: bind call failed");
779 bufsize
= 1024 * socket_bufsize
;
781 ret
= setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
,
782 (char *) &bufsize
, sizeof(bufsize
));
784 fprintf(stderr
, "Warning: setsockbuf(SO_RCVBUF) failed\n");
786 ret
= setsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
,
787 (char *) &bufsize
, sizeof(bufsize
));
789 fprintf(stderr
, "Warning: setsockbuf(SO_SNDBUF) failed\n");
801 * Close the query socket(s)
803 * Return -1 on failure
804 * Return a non-negative integer otherwise
809 if (close(socket4
) != 0) {
811 "Error: unable to close IPv4 socket\n");
817 if (close(socket6
) != 0) {
819 "Error: unable to close IPv6 socket\n");
831 * Choose an appropriate socket according to the address family of the
832 * current server. Open a new socket if necessary.
834 * Return -1 on failure
835 * Return the socket identifier
838 change_socket(void) {
841 switch (server_ai
->ai_family
) {
851 fprintf(stderr
, "unexpected address family: %d\n",
852 server_ai
->ai_family
);
857 if ((s
= open_socket()) == -1)
867 * (re)allocate RTT array and zero-clear the whole buffer.
868 * if array is being used, it is freed.
869 * Returns -1 on failure
870 * Returns a non-negative integer otherwise
873 reset_rttarray(int size
) {
874 if (rttarray
!= NULL
)
876 if (rttarray_interval
!= NULL
)
877 free(rttarray_interval
);
880 rttarray_interval
= NULL
;
885 rttarray
= malloc(size
* sizeof(rttarray
[0]));
886 if (rttarray
== NULL
) {
888 "Error: allocating memory for RTT array\n");
891 memset(rttarray
, 0, size
* sizeof(rttarray
[0]));
893 rttarray_interval
= malloc(size
*
894 sizeof(rttarray_interval
[0]));
895 if (rttarray_interval
== NULL
) {
897 "Error: allocating memory for RTT array\n");
901 memset(rttarray_interval
, 0,
902 size
* sizeof(rttarray_interval
[0]));
909 * set_query_interval:
910 * set the interval of consecutive queries if the target qps are specified.
911 * Returns -1 on failure
912 * Returns a non-negative integer otherwise
915 set_query_interval(unsigned int qps
) {
919 query_interval
= (1.0 / (double)qps
);
926 * Set configuration options from command line arguments
927 * Open datafile ready for reading
929 * Return -1 on failure
930 * Return non-negative integer on success
933 setup(int argc
, char **argv
) {
936 if (set_max_queries(DEF_MAX_QUERIES_OUTSTANDING
) == -1) {
937 fprintf(stderr
, "%s: Unable to set default max outstanding "
938 "queries\n", argv
[0]);
942 if (set_server(DEF_SERVER_TO_QUERY
) == -1) {
943 fprintf(stderr
, "%s: Error setting default server name\n",
948 if (set_server_port(DEF_SERVER_PORT
) == -1) {
949 fprintf(stderr
, "%s: Error setting default server port\n",
954 if (parse_args(argc
, argv
) == -1) {
959 if (open_datafile() == -1)
962 if (set_server_sa() == -1)
965 if ((query_socket
= change_socket()) == -1)
968 if (reset_rttarray(rttarray_size
) == -1)
971 if (set_query_interval(target_qps
) == -1)
979 * Set a timeval struct to indicate the current time
982 set_timenow(struct timeval
*tv
) {
983 if (gettimeofday(tv
, NULL
) == -1) {
984 fprintf(stderr
, "Error in gettimeofday(). Using inaccurate "
986 tv
->tv_sec
= time(NULL
);
993 * add tv1 and tv2, store the result in tv_result.
996 addtv(struct timeval
*tv1
, struct timeval
*tv2
, struct timeval
*tv_result
) {
997 tv_result
->tv_sec
= tv1
->tv_sec
+ tv2
->tv_sec
;
998 tv_result
->tv_usec
= tv1
->tv_usec
+ tv2
->tv_usec
;
999 if (tv_result
->tv_usec
> 1000000) {
1000 tv_result
->tv_sec
++;
1001 tv_result
->tv_usec
-= 1000000;
1007 * Find the difference in seconds between two timeval structs.
1009 * Return the difference between tv1 and tv2 in seconds in a double.
1012 difftv(struct timeval tv1
, struct timeval tv2
) {
1013 long diff_sec
, diff_usec
;
1016 diff_sec
= tv1
.tv_sec
- tv2
.tv_sec
;
1017 diff_usec
= tv1
.tv_usec
- tv2
.tv_usec
;
1019 diff
= (double)diff_sec
+ ((double)diff_usec
/ 1000000.0);
1025 * timelimit_reached:
1026 * Have we reached the time limit (if any)?
1028 * Returns FALSE if there is no time limit or if we have not reached it
1029 * Returns TRUE otherwise
1032 timelimit_reached(void) {
1033 struct timeval time_now
;
1035 set_timenow(&time_now
);
1037 if (use_timelimit
== FALSE
)
1040 if (setup_phase
== TRUE
) {
1041 if (difftv(time_now
, time_of_program_start
)
1042 < (double)(run_timelimit
+ HARD_TIMEOUT_EXTRA
))
1047 if (difftv(time_now
, time_of_first_query
)
1048 < (double)run_timelimit
)
1057 * Should we keep sending queries or stop here?
1059 * Return TRUE if we should keep on sending queries
1060 * Return FALSE if we should stop
1063 * Rewinds the input and clears reached_end_input if we have reached the
1064 * end of the input, but we are meant to run through it multiple times
1065 * and have not hit the time limit yet (if any is set).
1068 keep_sending(int *reached_end_input
) {
1069 static int stop
= FALSE
;
1074 if ((*reached_end_input
== FALSE
) && (timelimit_reached() == FALSE
))
1076 else if ((*reached_end_input
== TRUE
) && (run_only_once
== FALSE
)
1077 && (timelimit_reached() == FALSE
)) {
1078 rewind(datafile_ptr
);
1079 *reached_end_input
= FALSE
;
1080 runs_through_file
++;
1083 if (*reached_end_input
== TRUE
)
1084 runs_through_file
++;
1085 set_timenow(&time_of_stop_sending
);
1092 * queries_outstanding:
1093 * How many queries are outstanding?
1095 * Returns the number of outstanding queries
1098 queries_outstanding(void) {
1099 return (num_queries_outstanding
);
1104 * Get the next non-comment line from the input file
1106 * Put text in line, up to max of n chars. Skip comment lines.
1109 * Return line length on success
1110 * Return 0 if cannot read a non-comment line (EOF or error)
1113 next_input_line(char *line
, int n
) {
1117 result
= fgets(line
, n
, datafile_ptr
);
1118 } while ((result
!= NULL
) &&
1119 ((line
[0] == COMMENT_CHAR
) || (line
[0] == '\n')));
1124 return (strlen(line
));
1128 * identify_directive:
1129 * Gives us a numerical value equivelant for a directive string
1131 * Returns the value for the directive
1132 * Returns -1 if not a valid directive
1135 identify_directive(char *dir
) {
1136 static char *directives
[] = DIRECTIVES
;
1137 static int dir_values
[] = DIR_VALUES
;
1138 unsigned int index
, num_directives
;
1140 num_directives
= sizeof(directives
) / sizeof(directives
[0]);
1142 if (num_directives
> (sizeof(dir_values
) / sizeof(int)))
1143 num_directives
= sizeof(dir_values
) / sizeof(int);
1145 for (index
= 0; index
< num_directives
; index
++) {
1146 if (strcmp(dir
, directives
[index
]) == 0)
1147 return (dir_values
[index
]);
1155 * Update configuration options from a line from the input file
1158 update_config(char *config_change_desc
) {
1159 char *directive
, *config_value
, *trailing_garbage
;
1160 char conf_copy
[MAX_INPUT_LEN
+ 1];
1161 unsigned int uint_val
;
1162 int directive_number
;
1166 if (ignore_config_changes
== TRUE
) {
1167 fprintf(stderr
, "Ignoring configuration change: %s",
1168 config_change_desc
);
1172 strcpy(conf_copy
, config_change_desc
);
1174 ++config_change_desc
;
1176 if (*config_change_desc
== '\0') {
1177 fprintf(stderr
, "Invalid config: No directive present: %s\n",
1182 if (index(WHITESPACE
, *config_change_desc
) != NULL
) {
1183 fprintf(stderr
, "Invalid config: Space before directive or "
1184 "no directive present: %s\n", conf_copy
);
1188 directive
= strtok(config_change_desc
, WHITESPACE
);
1189 config_value
= strtok(NULL
, WHITESPACE
);
1190 trailing_garbage
= strtok(NULL
, WHITESPACE
);
1192 if ((directive_number
= identify_directive(directive
)) == -1) {
1193 fprintf(stderr
, "Invalid config: Bad directive: %s\n",
1198 if (config_value
== NULL
) {
1199 fprintf(stderr
, "Invalid config: No value present: %s\n",
1204 if (trailing_garbage
!= NULL
) {
1205 fprintf(stderr
, "Config warning: "
1206 "trailing garbage: %s\n", conf_copy
);
1209 switch(directive_number
) {
1212 if (serverset
&& (setup_phase
== TRUE
)) {
1213 fprintf(stderr
, "Config change overridden by command "
1214 "line: %s\n", directive
);
1218 if (set_server(config_value
) == -1) {
1219 fprintf(stderr
, "Set server error: unable to change "
1220 "the server name to '%s'\n", config_value
);
1224 old_af
= server_ai
->ai_family
;
1225 if (set_server_sa() == -1) {
1226 fprintf(stderr
, "Set server error: unable to resolve "
1227 "a new server '%s'\n",
1231 if (old_af
!= server_ai
->ai_family
) {
1232 if ((query_socket
= change_socket()) == -1) {
1233 /* XXX: this is fatal */
1234 fprintf(stderr
, "Set server error: "
1235 "unable to open a new socket "
1236 "for '%s'\n", config_value
);
1244 if (portset
&& (setup_phase
== TRUE
)) {
1245 fprintf(stderr
, "Config change overridden by command "
1246 "line: %s\n", directive
);
1250 check
= is_uint(config_value
, &uint_val
);
1252 if ((check
== TRUE
) && (uint_val
> 0)) {
1253 if (set_server_port(config_value
) == -1) {
1254 fprintf(stderr
, "Invalid config: Bad value for"
1255 " %s: %s\n", directive
, config_value
);
1257 if (set_server_sa() == -1) {
1259 "Failed to set a new port\n");
1264 fprintf(stderr
, "Invalid config: Bad value for "
1265 "%s: %s\n", directive
, config_value
);
1269 if (queriesset
&& (setup_phase
== TRUE
)) {
1270 fprintf(stderr
, "Config change overridden by command "
1271 "line: %s\n", directive
);
1275 check
= is_uint(config_value
, &uint_val
);
1277 if ((check
== TRUE
) && (uint_val
> 0)) {
1278 set_max_queries(uint_val
);
1280 fprintf(stderr
, "Invalid config: Bad value for "
1281 "%s: %s\n", directive
, config_value
);
1285 if (timeoutset
&& (setup_phase
== TRUE
)) {
1286 fprintf(stderr
, "Config change overridden by command "
1287 "line: %s\n", directive
);
1291 check
= is_uint(config_value
, &uint_val
);
1293 if ((check
== TRUE
) && (uint_val
> 0)) {
1294 query_timeout
= uint_val
;
1296 fprintf(stderr
, "Invalid config: Bad value for "
1297 "%s: %s\n", directive
, config_value
);
1301 fprintf(stderr
, "Invalid config: Bad directive: %s\n",
1309 * Parse a query line from the input file
1311 * Set qname to be the domain to query (up to a max of qnlen chars)
1312 * Set qtype to be the type of the query
1314 * Return -1 on failure
1315 * Return a non-negative integer otherwise
1318 parse_query(char *input
, char *qname
, int qnlen
, int *qtype
) {
1319 static char *qtype_strings
[] = QTYPE_STRINGS
;
1320 static int qtype_codes
[] = QTYPE_CODES
;
1321 int num_types
, index
;
1323 char incopy
[MAX_INPUT_LEN
+ 1];
1324 char *domain_str
, *type_str
;
1326 num_types
= sizeof(qtype_strings
) / sizeof(qtype_strings
[0]);
1327 if (num_types
> (sizeof(qtype_codes
) / sizeof(int)))
1328 num_types
= sizeof(qtype_codes
) / sizeof(int);
1330 strcpy(incopy
, input
);
1332 domain_str
= strtok(incopy
, WHITESPACE
);
1333 type_str
= strtok(NULL
, WHITESPACE
);
1335 if ((domain_str
== NULL
) || (type_str
== NULL
)) {
1336 fprintf(stderr
, "Invalid query input format: %s\n", input
);
1340 if (strlen(domain_str
) > qnlen
) {
1341 fprintf(stderr
, "Query domain too long: %s\n", domain_str
);
1345 for (index
= 0; (index
< num_types
) && (found
== FALSE
); index
++) {
1346 if (strcasecmp(type_str
, qtype_strings
[index
]) == 0) {
1347 *qtype
= qtype_codes
[index
];
1352 if (found
== FALSE
) {
1353 fprintf(stderr
, "Query type not understood: %s\n", type_str
);
1357 strcpy(qname
, domain_str
);
1364 * Send the query packet for the entry
1366 * Return -1 on failure
1367 * Return a non-negative integer otherwise
1370 dispatch_query(unsigned short int id
, char *dom
, int qt
) {
1371 static u_char packet_buffer
[PACKETSZ
+ 1];
1372 static socklen_t sockaddrlen
= sizeof(struct sockaddr
);
1373 int buffer_len
= PACKETSZ
;
1375 unsigned short int net_id
= htons(id
);
1376 char *id_ptr
= (char *)&net_id
;
1378 buffer_len
= res_mkquery(QUERY
, dom
, C_IN
, qt
, NULL
, 0,
1379 NULL
, packet_buffer
, PACKETSZ
);
1380 if (buffer_len
== -1) {
1381 fprintf(stderr
, "Failed to create query packet: %s %d\n",
1387 if (buffer_len
+ EDNSLEN
>= PACKETSZ
) {
1388 fprintf(stderr
, "Failed to add OPT to query packet\n");
1391 packet_buffer
[11] = 1;
1392 p
= &packet_buffer
[buffer_len
];
1393 *p
++ = 0; /* root name */
1395 *p
++ = 41; /* OPT */
1397 *p
++ = 0; /* UDP payload size (4K) */
1398 *p
++ = 0; /* extended rcode */
1399 *p
++ = 0; /* version */
1401 *p
++ = 0x80; /* upper flag bits - DO set */
1403 *p
++ = 0; /* upper flag bits */
1404 *p
++ = 0; /* lower flag bit */
1406 *p
++ = 0; /* rdlen == 0 */
1407 buffer_len
+= EDNSLEN
;
1410 packet_buffer
[0] = id_ptr
[0];
1411 packet_buffer
[1] = id_ptr
[1];
1413 bytes_sent
= sendto(query_socket
, packet_buffer
, buffer_len
, 0,
1414 server_ai
->ai_addr
, server_ai
->ai_addrlen
);
1415 if (bytes_sent
== -1) {
1416 fprintf(stderr
, "Failed to send query packet: %s %d\n",
1421 if (bytes_sent
!= buffer_len
)
1422 fprintf(stderr
, "Warning: incomplete packet sent: %s %d\n",
1430 * Send a query based on a line of input
1433 send_query(char *query_desc
) {
1434 static unsigned short int use_query_id
= 0;
1435 static int qname_len
= MAX_DOMAIN_LEN
;
1436 static char domain
[MAX_DOMAIN_LEN
+ 1];
1437 char serveraddr
[NI_MAXHOST
];
1443 if (parse_query(query_desc
, domain
, qname_len
, &query_type
) == -1) {
1444 fprintf(stderr
, "Error parsing query: %s\n", query_desc
);
1448 if (dispatch_query(use_query_id
, domain
, query_type
) == -1) {
1451 if (getnameinfo(server_ai
->ai_addr
, server_ai
->ai_addrlen
,
1452 serveraddr
, sizeof(serveraddr
), NULL
, 0,
1453 NI_NUMERICHOST
) == 0) {
1454 addrstr
= serveraddr
;
1456 addrstr
= "???"; /* XXX: this should not happen */
1457 fprintf(stderr
, "Error sending query to %s: %s\n",
1458 addrstr
, query_desc
);
1462 if (setup_phase
== TRUE
) {
1463 set_timenow(&time_of_first_query
);
1464 time_of_first_query_sec
= (double)time_of_first_query
.tv_sec
+
1465 ((double)time_of_first_query
.tv_usec
/ 1000000.0);
1466 setup_phase
= FALSE
;
1467 if (getnameinfo(server_ai
->ai_addr
, server_ai
->ai_addrlen
,
1468 serveraddr
, sizeof(serveraddr
), NULL
, 0,
1469 NI_NUMERICHOST
) != 0) {
1470 fprintf(stderr
, "Error printing server address\n");
1473 printf("[Status] Sending queries (beginning with %s)\n",
1477 /* Find the first slot in status[] that is not in use */
1478 for (count
= 0; (status
[count
].in_use
== TRUE
)
1479 && (count
< max_queries_outstanding
); count
++);
1481 if (status
[count
].in_use
== TRUE
) {
1482 fprintf(stderr
, "Unexpected error: We have run out of "
1483 "status[] space!\n");
1487 /* Register the query in status[] */
1488 status
[count
].in_use
= TRUE
;
1489 status
[count
].id
= use_query_id
;
1491 status
[count
].desc
= strdup(query_desc
);
1492 set_timenow(&status
[count
].sent_timestamp
);
1494 if (num_queries_sent_interval
== 0)
1495 set_timenow(&time_of_first_query_interval
);
1498 num_queries_sent_interval
++;
1499 num_queries_outstanding
++;
1503 register_rtt(struct timeval
*timestamp
) {
1505 int oldquery
= FALSE
;
1510 rtt
= difftv(now
, *timestamp
);
1512 if (difftv(*timestamp
, time_of_first_query_interval
) < 0)
1515 if (rtt_max
< 0 || rtt_max
< rtt
)
1518 if (rtt_min
< 0 || rtt_min
> rtt
)
1524 if (rtt_max_interval
< 0 || rtt_max_interval
< rtt
)
1525 rtt_max_interval
= rtt
;
1527 if (rtt_min_interval
< 0 || rtt_min_interval
> rtt
)
1528 rtt_min_interval
= rtt
;
1530 rtt_total_interval
+= rtt
;
1533 if (rttarray
== NULL
)
1536 i
= (int)(rtt
* (1000000.0 / rttarray_unit
));
1537 if (i
< rttarray_size
) {
1540 rttarray_interval
[i
]++;
1542 fprintf(stderr
, "Warning: RTT is out of range: %.6lf\n",
1546 rtt_overflows_interval
++;
1551 * register_response:
1552 * Register receipt of a query
1554 * Removes (sets in_use = FALSE) the record for the given query id in
1555 * status[] if any exists.
1558 register_response(unsigned short int id
, unsigned int rcode
) {
1559 unsigned int ct
= 0;
1564 for (; (ct
< query_status_allocated
) && (found
== FALSE
); ct
++) {
1565 if ((status
[ct
].in_use
== TRUE
) && (status
[ct
].id
== id
)) {
1566 status
[ct
].in_use
= FALSE
;
1567 num_queries_outstanding
--;
1570 register_rtt(&status
[ct
].sent_timestamp
);
1572 if (status
[ct
].desc
) {
1573 printf("> %s %s\n", rcode_strings
[rcode
],
1575 free(status
[ct
].desc
);
1578 rcodecounts
[rcode
]++;
1582 if (found
== FALSE
) {
1583 if (target_qps
> 0) {
1584 num_queries_possiblydelayed
++;
1585 num_queries_possiblydelayed_interval
++;
1587 fprintf(stderr
, "Warning: Received a response with an "
1588 "unexpected (maybe timed out) id: %u\n", id
);
1594 * process_single_response:
1595 * Receive from the given socket & process an invididual response packet.
1596 * Remove it from the list of open queries (status[]) and decrement the
1597 * number of outstanding queries if it matches an open query.
1600 process_single_response(int sockfd
) {
1601 struct sockaddr_storage from_addr_ss
;
1602 struct sockaddr
*from_addr
;
1603 static unsigned char in_buf
[MAX_BUFFER_LEN
];
1604 int numbytes
, addr_len
, resp_id
;
1607 memset(&from_addr_ss
, 0, sizeof(from_addr_ss
));
1608 from_addr
= (struct sockaddr
*)&from_addr_ss
;
1609 addr_len
= sizeof(from_addr_ss
);
1611 if ((numbytes
= recvfrom(sockfd
, in_buf
, MAX_BUFFER_LEN
,
1612 0, from_addr
, &addr_len
)) == -1) {
1613 fprintf(stderr
, "Error receiving datagram\n");
1617 resp_id
= get_uint16(in_buf
);
1618 flags
= get_uint16(in_buf
+ 2);
1620 register_response(resp_id
, flags
& 0xF);
1625 * Is there data available on the given file descriptor?
1627 * Return TRUE if there is
1628 * Return FALSE otherwise
1631 data_available(double wait
) {
1635 int available
= FALSE
;
1638 /* Set list of file descriptors */
1640 if (socket4
!= -1) {
1641 FD_SET(socket4
, &read_fds
);
1644 if (socket6
!= -1) {
1645 FD_SET(socket6
, &read_fds
);
1646 if (maxfd
== -1 || maxfd
< socket6
)
1650 if ((wait
> 0.0) && (wait
< (double)LONG_MAX
)) {
1651 tv
.tv_sec
= (long)floor(wait
);
1652 tv
.tv_usec
= (long)(1000000.0 * (wait
- floor(wait
)));
1658 retval
= select(maxfd
+ 1, &read_fds
, NULL
, NULL
, &tv
);
1660 if (socket4
!= -1 && FD_ISSET(socket4
, &read_fds
)) {
1662 process_single_response(socket4
);
1664 if (socket6
!= -1 && FD_ISSET(socket6
, &read_fds
)) {
1666 process_single_response(socket6
);
1673 * process_responses:
1674 * Go through any/all received responses and remove them from the list of
1675 * open queries (set in_use = FALSE for their entry in status[]), also
1676 * decrementing the number of outstanding queries.
1679 process_responses(int adjust_rate
) {
1681 struct timeval now
, waituntil
;
1682 double first_packet_wait
= RESPONSE_BLOCKING_WAIT_TIME
;
1683 unsigned int outstanding
= queries_outstanding();
1685 if (adjust_rate
== TRUE
) {
1688 u
= time_of_first_query_sec
+
1689 query_interval
* num_queries_sent
;
1690 waituntil
.tv_sec
= (long)floor(u
);
1691 waituntil
.tv_usec
= (long)(1000000.0 * (u
- waituntil
.tv_sec
));
1694 * Wait until a response arrives or the specified limit is
1699 wait
= difftv(waituntil
, now
);
1702 if (data_available(wait
) != TRUE
)
1706 * We have reached the limit. Read as many responses
1707 * as possible without waiting, and exit.
1710 while (data_available(0.0) == TRUE
)
1717 * Don't block waiting for packets at all if we aren't
1718 * looking for any responses or if we are now able to send new
1721 if ((outstanding
== 0) ||
1722 (outstanding
< max_queries_outstanding
)) {
1723 first_packet_wait
= 0.0;
1726 if (data_available(first_packet_wait
) == TRUE
) {
1727 while (data_available(0.0) == TRUE
)
1734 * retire_old_queries:
1735 * Go through the list of open queries (status[]) and remove any queries
1736 * (i.e. set in_use = FALSE) which are older than the timeout, decrementing
1737 * the number of queries outstanding for each one removed.
1740 retire_old_queries(int sending
) {
1741 unsigned int count
= 0;
1742 struct timeval curr_time
;
1743 double timeout
= query_timeout
;
1744 int timeout_reduced
= FALSE
;
1747 * If we have target qps and would not be able to send any packets
1748 * due to buffer full, check whether we are behind the schedule.
1749 * If we are, purge some queries more aggressively.
1751 if (target_qps
> 0 && sending
== TRUE
&& count
== 0 &&
1752 queries_outstanding() == max_queries_outstanding
) {
1753 struct timeval next
, now
;
1756 n
= time_of_first_query_sec
+
1757 query_interval
* num_queries_sent
;
1758 next
.tv_sec
= (long)floor(n
);
1759 next
.tv_usec
= (long)(1000000.0 * (n
- next
.tv_sec
));
1762 if (difftv(next
, now
) <= 0) {
1763 timeout_reduced
= TRUE
;
1764 timeout
= 0.001; /* XXX: ad-hoc value */
1768 set_timenow(&curr_time
);
1770 for (; count
< query_status_allocated
; count
++) {
1772 if ((status
[count
].in_use
== TRUE
)
1773 && (difftv(curr_time
, status
[count
].sent_timestamp
)
1774 >= (double)timeout
)) {
1776 status
[count
].in_use
= FALSE
;
1777 num_queries_outstanding
--;
1778 num_queries_timed_out
++;
1779 num_queries_timed_out_interval
++;
1781 if (timeout_reduced
== FALSE
) {
1782 if (status
[count
].desc
) {
1783 printf("> T %s\n", status
[count
].desc
);
1784 free(status
[count
].desc
);
1786 printf("[Timeout] Query timed out: "
1797 * Print RTT histogram to the specified file in the gnuplot format
1800 print_histogram(unsigned int total
) {
1805 if (rtt_histogram_file
== NULL
|| rttarray
== NULL
)
1808 fp
= fopen((const char *)rtt_histogram_file
, "w+");
1810 fprintf(stderr
, "Error opening RTT histogram file: %s\n",
1811 rtt_histogram_file
);
1815 for (i
= 0; i
< rttarray_size
; i
++) {
1816 ratio
= ((double)rttarray
[i
] / (double)total
) * 100;
1817 fprintf(fp
, "%.6lf %.3lf\n",
1818 (double)(i
* rttarray_unit
) +
1819 (double)rttarray_unit
/ 2,
1828 * Print out statistics based on the results of the test
1831 print_statistics(int intermediate
, unsigned int sent
, unsigned int timed_out
,
1832 unsigned int possibly_delayed
,
1833 struct timeval
*first_query
,
1834 struct timeval
*program_start
,
1835 struct timeval
*end_perf
, struct timeval
*end_query
,
1836 double rmax
, double rmin
, double rtotal
,
1837 unsigned int roverflows
, unsigned int *rarray
)
1839 unsigned int num_queries_completed
;
1840 double per_lost
, per_completed
, per_lost2
, per_completed2
;
1841 double run_time
, queries_per_sec
, queries_per_sec2
;
1842 double queries_per_sec_total
;
1843 double rtt_average
, rtt_stddev
;
1844 struct timeval start_time
;
1846 num_queries_completed
= sent
- timed_out
;
1848 if (num_queries_completed
== 0) {
1850 per_completed
= 0.0;
1853 per_completed2
= 0.0;
1855 per_lost
= (100.0 * (double)timed_out
) / (double)sent
;
1856 per_completed
= 100.0 - per_lost
;
1858 per_lost2
= (100.0 * (double)(timed_out
- possibly_delayed
))
1860 per_completed2
= 100 - per_lost2
;
1864 start_time
.tv_sec
= program_start
->tv_sec
;
1865 start_time
.tv_usec
= program_start
->tv_usec
;
1867 queries_per_sec
= 0.0;
1868 queries_per_sec2
= 0.0;
1869 queries_per_sec_total
= 0.0;
1871 start_time
.tv_sec
= first_query
->tv_sec
;
1872 start_time
.tv_usec
= first_query
->tv_usec
;
1873 run_time
= difftv(*end_perf
, *first_query
);
1874 queries_per_sec
= (double)num_queries_completed
/ run_time
;
1875 queries_per_sec2
= (double)(num_queries_completed
+
1876 possibly_delayed
) / run_time
;
1878 queries_per_sec_total
= (double)sent
/
1879 difftv(*end_query
, *first_query
);
1882 if (num_queries_completed
> 0) {
1886 rtt_average
= rtt_total
/ (double)num_queries_completed
;
1887 for (i
= 0; i
< rttarray_size
; i
++) {
1888 if (rarray
[i
] != 0) {
1891 mean
= (double)(i
* rttarray_unit
) +
1892 (double)rttarray_unit
/ 2;
1893 diff
= rtt_average
- (mean
/ 1000000.0);
1894 sum
+= (diff
* diff
) * rarray
[i
];
1897 rtt_stddev
= sqrt(sum
/ (double)num_queries_completed
);
1905 printf("%sStatistics:\n", intermediate
? "Intermediate " : "");
1909 if (!intermediate
) {
1910 printf(" Parse input file: %s\n",
1911 ((run_only_once
== TRUE
) ? "once" : "multiple times"));
1913 printf(" Run time limit: %u seconds\n",
1915 if (run_only_once
== FALSE
)
1916 printf(" Ran through file: %u times\n",
1919 printf(" Ended due to: reaching %s\n",
1920 ((runs_through_file
== 0) ? "time limit"
1926 printf(" Queries sent: %u queries\n", sent
);
1927 printf(" Queries completed: %u queries\n", num_queries_completed
);
1928 printf(" Queries lost: %u queries\n", timed_out
);
1929 printf(" Queries delayed(?): %u queries\n", possibly_delayed
);
1933 printf(" RTT max: %3.6lf sec\n", rmax
);
1934 printf(" RTT min: %3.6lf sec\n", rmin
);
1935 printf(" RTT average: %3.6lf sec\n", rtt_average
);
1936 printf(" RTT std deviation: %3.6lf sec\n", rtt_stddev
);
1937 printf(" RTT out of range: %u queries\n", roverflows
);
1939 if (!intermediate
) /* XXX should we print this case also? */
1940 print_histogram(num_queries_completed
);
1947 for (i
= 0; i
< 16; i
++) {
1948 if (rcodecounts
[i
] == 0)
1950 printf(" Returned %8s: %u queries\n",
1951 rcode_strings
[i
], rcodecounts
[i
]);
1956 printf(" Percentage completed: %6.2lf%%\n", per_completed
);
1957 if (possibly_delayed
> 0)
1958 printf(" (w/ delayed qrys): %6.2lf%%\n", per_completed2
);
1959 printf(" Percentage lost: %6.2lf%%\n", per_lost
);
1960 if (possibly_delayed
> 0)
1961 printf(" (w/o delayed qrys): %6.2lf%%\n", per_lost2
);
1965 printf(" Started at: %s",
1966 ctime((const time_t *)&start_time
.tv_sec
));
1967 printf(" Finished at: %s",
1968 ctime((const time_t *)&end_perf
->tv_sec
));
1969 printf(" Ran for: %.6lf seconds\n", run_time
);
1973 printf(" Queries per second: %.6lf qps\n", queries_per_sec
);
1974 if (possibly_delayed
> 0) {
1975 printf(" (w/ delayed qrys): %.6lf qps\n",
1978 if (target_qps
> 0) {
1979 printf(" Total QPS/target: %.6lf/%d qps\n",
1980 queries_per_sec_total
, target_qps
);
1987 print_interval_statistics() {
1988 struct timeval time_now
;
1990 if (use_timelimit
== FALSE
)
1993 if (setup_phase
== TRUE
)
1996 if (print_interval
== 0)
1999 if (timelimit_reached() == TRUE
)
2002 set_timenow(&time_now
);
2003 if (difftv(time_now
, time_of_first_query_interval
)
2004 <= (double)print_interval
)
2007 /* Don't count currently outstanding queries */
2008 num_queries_sent_interval
-= queries_outstanding();
2009 print_statistics(TRUE
, num_queries_sent_interval
,
2010 num_queries_timed_out_interval
,
2011 num_queries_possiblydelayed_interval
,
2012 &time_of_first_query_interval
,
2013 &time_of_first_query_interval
, &time_now
, &time_now
,
2014 rtt_max_interval
, rtt_min_interval
,
2015 rtt_total_interval
, rtt_overflows_interval
,
2018 /* Reset intermediate counters */
2019 num_queries_sent_interval
= 0;
2020 num_queries_timed_out_interval
= 0;
2021 num_queries_possiblydelayed_interval
= 0;
2022 rtt_max_interval
= -1;
2023 rtt_min_interval
= -1;
2024 rtt_total_interval
= 0.0;
2025 rtt_overflows_interval
= 0;
2026 if (rttarray_interval
!= NULL
) {
2027 memset(rttarray_interval
, 0,
2028 sizeof(rttarray_interval
[0]) * rttarray_size
);
2033 * queryperf Program Mainline
2036 main(int argc
, char **argv
) {
2038 int sending
= FALSE
;
2039 int got_eof
= FALSE
;
2040 int input_length
= MAX_INPUT_LEN
;
2041 char input_line
[MAX_INPUT_LEN
+ 1];
2043 set_timenow(&time_of_program_start
);
2044 time_of_first_query
.tv_sec
= 0;
2045 time_of_first_query
.tv_usec
= 0;
2046 time_of_first_query_interval
.tv_sec
= 0;
2047 time_of_first_query_interval
.tv_usec
= 0;
2048 time_of_end_of_run
.tv_sec
= 0;
2049 time_of_end_of_run
.tv_usec
= 0;
2051 input_line
[0] = '\0';
2053 show_startup_info();
2055 if (setup(argc
, argv
) == -1)
2058 printf("[Status] Processing input data\n");
2060 while ((sending
= keep_sending(&got_eof
)) == TRUE
||
2061 queries_outstanding() > 0) {
2062 print_interval_statistics();
2063 adjust_rate
= FALSE
;
2065 while ((sending
= keep_sending(&got_eof
)) == TRUE
&&
2066 queries_outstanding() < max_queries_outstanding
) {
2067 int len
= next_input_line(input_line
, input_length
);
2071 /* Zap the trailing newline */
2072 if (input_line
[len
- 1] == '\n')
2073 input_line
[len
- 1] = '\0';
2076 * TODO: Should test if we got a whole line
2077 * and flush to the next \n in input if not
2078 * here... Add this later. Only do the next
2079 * few lines if we got a whole line, else
2080 * print a warning. Alternative: Make the
2081 * max line size really big. BAD! :)
2084 if (input_line
[0] == CONFIG_CHAR
)
2085 update_config(input_line
);
2087 send_query(input_line
);
2088 if (target_qps
> 0 &&
2090 max_queries_outstanding
) == 0) {
2097 process_responses(adjust_rate
);
2098 retire_old_queries(sending
);
2101 set_timenow(&time_of_end_of_run
);
2103 printf("[Status] Testing complete\n");
2108 print_statistics(FALSE
, num_queries_sent
, num_queries_timed_out
,
2109 num_queries_possiblydelayed
,
2110 &time_of_first_query
, &time_of_program_start
,
2111 &time_of_end_of_run
, &time_of_stop_sending
,
2112 rtt_max
, rtt_min
, rtt_total
, rtt_overflows
, rttarray
);