queryperf.c revision a18fc12ba3d48b66bea298c80f3e3f09f3c91527
/*
* Copyright (C) 2000, 2001 Nominum, Inc.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/***
*** DNS Query Performance Testing Tool (queryperf.c)
***
*** Version $Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp $
***
*** Stephen Jacob <sj@nominum.com>
***/
#define BIND_8_COMPAT /* Pull in <arpa/nameser_compat.h> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
#include <netdb.h>
#include <resolv.h>
#include <math.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#ifndef HAVE_GETADDRINFO
#include "missing/addrinfo.h"
#endif
#endif
/*
* Configuration defaults
*/
#define DEF_MAX_QUERIES_OUTSTANDING 20
#define DEF_SERVER_TO_QUERY "127.0.0.1"
#define DEF_SERVER_PORT "53"
#define DEF_RTTARRAY_SIZE 50000
/*
* Other constants / definitions
*/
#define COMMENT_CHAR ';'
#define CONFIG_CHAR '#'
#define MAX_PORT 65535
#define MAX_INPUT_LEN 512
#define MAX_DOMAIN_LEN 255
#define EDNSLEN 11
#define DNS_HEADERLEN 12
#define FALSE 0
#define TRUE 1
#define WHITESPACE " \t\n"
#define QTYPE_STRINGS { \
"A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR", \
"NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", "RP", \
"AFSDB", "X25", "ISDN", "RT", "NSAP", "NSAP-PTR", "SIG", \
"KEY", "PX", "GPOS", "AAAA", "LOC", "NXT", "EID", "NIMLOC", \
"SRV", "ATMA", "NAPTR", "KX", "CERT", "A6", "DNAME", "SINK", \
"OPT", "APL", "DS", "SSHFP", "IPSECKEY", "RRSIG", "NSEC", \
"DNSKEY", "DHCID", "NSEC3", "NSEC3PARAM", "TLSA", "HIP", \
"NINFO", "RKEY", "TALINK", "CDS", "SPF", "UINFO", "UID", \
"GID", "UNSPEC", "NID", "L32", "L64", "LP", "TKEY", "TSIG", \
"IXFR", "AXFR", "MAILB", "MAILA", "URI", "CAA", "*", "ANY", \
"TA", "DLV" \
}
#define QTYPE_CODES { \
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, \
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, \
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, \
49, 50, 51, 52, 55, 56, 57, 58, 59, 99, 100, 101, 102, 103, \
104, 105, 106, 107, 249, 250, 251, 252, 253, 254, 255, 255, \
256, 257, 32768, 32769 \
}
#define RCODE_STRINGS { \
"NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \
"NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \
"NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \
"rcode12", "rcode13", "rcode14", "rcode15" \
}
/*
* Data type definitions
*/
#define VALID_QUERY_STATUS(q) ((q) != NULL && \
(q)->magic == QUERY_STATUS_MAGIC)
struct query_status {
unsigned int magic;
int in_use;
unsigned short int id;
struct timeval sent_timestamp;
char *desc;
int qtype;
};
struct query_mininfo { /* minimum info for timeout queries */
int qtype; /* use -1 if N/A */
struct timeval sent_timestamp;
};
/*
* Forward declarations.
*/
/*
* Configuration options (global)
*/
unsigned int max_queries_outstanding; /* init 0 */
unsigned int query_timeout = DEF_QUERY_TIMEOUT;
int ignore_config_changes = FALSE;
unsigned int socket_bufsize = DEF_BUFFER_SIZE;
char *datafile_name; /* init NULL */
char *server_to_query; /* init NULL */
char *server_port; /* init NULL */
int run_only_once = FALSE;
int use_timelimit = FALSE;
unsigned int run_timelimit; /* init 0 */
unsigned int print_interval; /* init 0 */
unsigned int target_qps; /* init 0 */
int countrcodes = FALSE;
int rcodecounts[16] = {0};
int recurse = 1;
/*
* Other global stuff
*/
int setup_phase = TRUE;
unsigned int runs_through_file; /* init 0 */
unsigned int num_queries_sent; /* init 0 */
unsigned int num_queries_sent_interval;
unsigned int num_queries_outstanding; /* init 0 */
unsigned int num_queries_timed_out; /* init 0 */
unsigned int num_queries_possiblydelayed; /* init 0 */
unsigned int num_queries_timed_out_interval;
unsigned int num_queries_possiblydelayed_interval;
struct timeval time_of_program_start;
struct timeval time_of_first_query;
double time_of_first_query_sec;
struct timeval time_of_first_query_interval;
struct timeval time_of_end_of_run;
struct timeval time_of_stop_sending;
struct timeval time_of_queryset_start;
double query_interval;
struct timeval time_of_next_queryset;
double rtt_max = -1;
double rtt_max_interval = -1;
double rtt_min = -1;
double rtt_min_interval = -1;
double rtt_total;
double rtt_total_interval;
int rttarray_size = DEF_RTTARRAY_SIZE;
int rttarray_unit = DEF_RTTARRAY_UNIT;
unsigned int *rttarray_interval = NULL;
unsigned int rtt_overflows;
unsigned int rtt_overflows_interval;
unsigned int rtt_counted;
unsigned int rtt_counted_interval;
char *rtt_histogram_file = NULL;
unsigned int query_status_allocated; /* init 0 */
int query_socket = -1;
static char *rcode_strings[] = RCODE_STRINGS;
static struct query_mininfo *timeout_queries;
/*
* get_uint16:
* Get an unsigned short integer from a buffer (in network order)
*/
static unsigned short
get_uint16(unsigned char *buf) {
unsigned short ret;
return (ret);
}
/*
* show_startup_info:
*/
void
show_startup_info(void) {
printf("\n"
"DNS Query Performance Testing Tool\n"
"Version: $Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp $\n"
"\n");
}
/*
* show_usage:
*/
void
show_usage(void) {
"\n"
"Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n"
" [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n"
" [-i interval] [-r arraysize] [-u unit] [-H histfile]\n"
" [-T qps] [-e] [-D] [-R] [-c] [-v] [-h]\n"
" -d specifies the input data file (default: stdin)\n"
" -s sets the server to query (default: %s)\n"
" -p sets the port on which to query the server (default: %s)\n"
" -q specifies the maximum number of queries outstanding (default: %d)\n"
" -t specifies the timeout for query completion in seconds (default: %d)\n"
" -n causes configuration changes to be ignored\n"
" -l specifies how a limit for how long to run tests in seconds (no default)\n"
" -1 run through input only once (default: multiple iff limit given)\n"
" -i specifies interval of intermediate outputs in seconds (default: 0=none)\n"
" -f specify address family of DNS transport, inet or inet6 (default: any)\n"
" -r set RTT statistics array size (default: %d)\n"
" -u set RTT statistics time unit in usec (default: %d)\n"
" -H specifies RTT histogram data file (default: none)\n"
" -T specify the target qps (default: 0=unspecified)\n"
" -e enable EDNS 0\n"
" -D set the DNSSEC OK bit (implies EDNS)\n"
" -R disable recursion\n"
" -c print the number of packets with each rcode\n"
" -v verbose: report the RCODE of each response on stdout\n"
" -h print this usage\n"
"\n",
}
/*
* set_datafile:
* Set the datafile to read
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
set_datafile(char *new_file) {
char *dfname_tmp;
return (-1);
}
"%s\n", new_file);
return (-1);
}
return (0);
}
/*
* set_input_stdin:
* Set the input to be stdin (instead of a datafile)
*/
void
set_input_stdin(void) {
}
/*
* set_server:
* Set the server to be queried
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
set_server(char *new_name) {
/* If no change in server name, don't do anything... */
return (0);
return (-1);
}
"%s\n", new_name);
return (-1);
}
return (0);
}
/*
* set_server_port:
* Set the port on which to contact the server
*
* Return -1 if port is invalid
* Return a non-negative integer otherwise
*/
int
set_server_port(char *new_port) {
unsigned int uint_val;
return (-1);
return (-1);
else {
return (0);
server_port = NULL;
"Error allocating memory for server port: "
"%s\n", new_port);
return (-1);
}
return (0);
}
}
int
set_server_sa(void) {
int error;
return (-1);
}
return (-1);
}
/* replace the server's addrinfo */
return (0);
}
/*
* is_digit:
* Tests if a character is a digit
*
* Return TRUE if it is
* Return FALSE if it is not
*/
int
is_digit(char d) {
if (d < '0' || d > '9')
return (FALSE);
else
return (TRUE);
}
/*
* is_uint:
* Tests if a string, test_int, is a valid unsigned integer
*
* Sets *result to be the unsigned integer if it is valid
*
* Return TRUE if it is
* Return FALSE if it is not
*/
int
unsigned long int value;
char *end;
return (FALSE);
return (FALSE);
return (FALSE);
return (TRUE);
}
/*
* set_max_queries:
* Set the maximum number of outstanding queries
*
* Returns -1 on failure
* Returns a non-negative integer otherwise
*/
int
set_max_queries(unsigned int new_max) {
static unsigned int size_qs = sizeof(struct query_status);
struct query_status *temp_stat;
unsigned int count;
if (new_max > query_status_allocated) {
return (-1);
} else {
/*
* Be careful to only initialise between above
* the previously allocated space. Note that the
* allocation may be larger than the current
* max_queries_outstanding. We don't want to
* "forget" any outstanding queries! We might
* still have some above the bounds of the max.
*/
}
}
}
return (0);
}
/*
* parse_args:
* Parse program arguments and set configuration options
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
int c;
unsigned int uint_arg_val;
"f:q:t:i:nd:s:p:1l:b:eDcvr:RT:u:H:h")) != -1) {
switch (c) {
case 'f':
#ifdef AF_INET6
#endif
else {
optarg);
return (-1);
}
break;
case 'q':
queriesset = TRUE;
} else {
"integer value: -%c %s\n",
c, optarg);
return (-1);
}
break;
case 't':
timeoutset = TRUE;
} else {
"integer value: -%c %s\n",
c, optarg);
return (-1);
}
break;
case 'n':
break;
case 'd':
"name: %s\n", optarg);
return (-1);
}
break;
case 's':
"name: %s\n", optarg);
return (-1);
}
break;
case 'p':
{
} else {
"integer between 0 and %d: -%c %s\n",
return (-1);
}
break;
case '1':
break;
case 'l':
} else {
"integer: -%c %s\n",
c, optarg);
return (-1);
}
break;
case 'b':
} else {
"integer: -%c %s\n",
c, optarg);
return (-1);
}
break;
case 'e':
break;
case 'D':
break;
case 'c':
countrcodes = TRUE;
break;
case 'v':
verbose = 1;
break;
case 'i':
else {
optarg);
return (-1);
}
break;
case 'R':
recurse = 0;
break;
case 'r':
else {
optarg);
return (-1);
}
break;
case 'u':
else {
optarg);
return (-1);
}
break;
case 'H':
break;
case 'T':
else {
optarg);
return (-1);
}
break;
case 'h':
return (-1);
default:
return (-1);
}
}
return (0);
}
/*
* open_datafile:
* Open the data file ready for reading
*
* Return -1 on failure
* Return non-negative integer on success
*/
int
open_datafile(void) {
return (0);
} else {
return (-1);
} else
return (0);
}
}
/*
* close_datafile:
* Close the data file if any is open
*
* Return -1 on failure
* Return non-negative integer on success, including if not needed
*/
int
close_datafile(void) {
if (fclose(datafile_ptr) != 0) {
return (-1);
}
}
return (0);
}
/*
* open_socket:
* Open a socket for the queries. When we have an active socket already,
* close it and open a new one.
*
* Return -1 on failure
* Return the socket identifier
*/
int
open_socket(void) {
int sock;
int ret;
int bufsize;
"Error: getaddrinfo for bind socket failed: %s\n",
gai_strerror(ret));
return (-1);
}
goto fail;
}
#if defined(AF_INET6) && defined(IPV6_V6ONLY)
int on = 1;
"Warning: setsockopt(IPV6_V6ONLY) failed\n");
}
}
#endif
if (ret < 0)
if (ret < 0)
return (sock);
fail:
if (res)
return (-1);
}
/*
* close_socket:
* Close the query socket(s)
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
close_socket(void) {
if (socket4 != -1) {
"Error: unable to close IPv4 socket\n");
return (-1);
}
}
if (socket6 != -1) {
"Error: unable to close IPv6 socket\n");
return (-1);
}
}
query_socket = -1;
return (0);
}
/*
* change_socket:
* Choose an appropriate socket according to the address family of the
* current server. Open a new socket if necessary.
*
* Return -1 on failure
* Return the socket identifier
*/
int
change_socket(void) {
int s, *sockp;
case AF_INET:
break;
#ifdef AF_INET6
case AF_INET6:
break;
#endif
default:
exit(1);
}
if (*sockp == -1) {
if ((s = open_socket()) == -1)
return (-1);
*sockp = s;
}
return (*sockp);
}
/*
* reset_rttarray:
* (re)allocate RTT array and zero-clear the whole buffer.
* if array is being used, it is freed.
* Returns -1 on failure
* Returns a non-negative integer otherwise
*/
int
reset_rttarray(int size) {
if (rttarray_interval != NULL)
rtt_max = -1;
rtt_min = -1;
if (size > 0) {
"Error: allocating memory for RTT array\n");
return (-1);
}
sizeof(rttarray_interval[0]));
if (rttarray_interval == NULL) {
"Error: allocating memory for RTT array\n");
return (-1);
}
size * sizeof(rttarray_interval[0]));
}
return (0);
}
/*
* set_query_interval:
* set the interval of consecutive queries if the target qps are specified.
* Returns -1 on failure
* Returns a non-negative integer otherwise
*/
int
set_query_interval(unsigned int qps) {
if (qps == 0)
return (0);
return (0);
}
/*
* setup:
* Set configuration options from command line arguments
* Open datafile ready for reading
*
* Return -1 on failure
* Return non-negative integer on success
*/
int
"queries\n", argv[0]);
return (-1);
}
argv[0]);
return (-1);
}
argv[0]);
return (-1);
}
show_usage();
return (-1);
}
if (open_datafile() == -1)
return (-1);
if (set_server_sa() == -1)
return (-1);
return (-1);
return (-1);
return (-1);
return (0);
}
/*
* set_timenow:
* Set a timeval struct to indicate the current time
*/
void
"time() instead\n");
}
}
/*
* addtv:
* add tv1 and tv2, store the result in tv_result.
*/
void
}
}
/*
* difftv:
* Find the difference in seconds between two timeval structs.
*
* Return the difference between tv1 and tv2 in seconds in a double.
*/
double
double diff;
return (diff);
}
/*
* timelimit_reached:
* Have we reached the time limit (if any)?
*
* Returns FALSE if there is no time limit or if we have not reached it
* Returns TRUE otherwise
*/
int
timelimit_reached(void) {
if (use_timelimit == FALSE)
return (FALSE);
if (setup_phase == TRUE) {
< (double)(run_timelimit + HARD_TIMEOUT_EXTRA))
return (FALSE);
else
return (TRUE);
} else {
< (double)run_timelimit)
return (FALSE);
else
return (TRUE);
}
}
/*
* keep_sending:
* Should we keep sending queries or stop here?
*
* Return TRUE if we should keep on sending queries
* Return FALSE if we should stop
*
* Side effects:
* Rewinds the input and clears reached_end_input if we have reached the
* end of the input, but we are meant to run through it multiple times
* and have not hit the time limit yet (if any is set).
*/
int
keep_sending(int *reached_end_input) {
return (FALSE);
return (TRUE);
&& (timelimit_reached() == FALSE)) {
return (TRUE);
} else {
if (*reached_end_input == TRUE)
return (FALSE);
}
}
/*
* queries_outstanding:
* How many queries are outstanding?
*
* Returns the number of outstanding queries
*/
unsigned int
queries_outstanding(void) {
return (num_queries_outstanding);
}
/*
* next_input_line:
* Get the next non-comment line from the input file
*
* Put text in line, up to max of n chars. Skip comment lines.
* Skip empty lines.
*
* Return line length on success
* Return 0 if cannot read a non-comment line (EOF or error)
*/
int
next_input_line(char *line, int n) {
char *result;
do {
return (0);
else
}
/*
* identify_directive:
* Gives us a numerical value equivelant for a directive string
*
* Returns the value for the directive
* Returns -1 if not a valid directive
*/
int
identify_directive(char *dir) {
static char *directives[] = DIRECTIVES;
static int dir_values[] = DIR_VALUES;
unsigned int index, num_directives;
if (num_directives > (sizeof(dir_values) / sizeof(int)))
num_directives = sizeof(dir_values) / sizeof(int);
return (dir_values[index]);
}
return (-1);
}
/*
* update_config:
* Update configuration options from a line from the input file
*/
void
update_config(char *config_change_desc) {
unsigned int uint_val;
int directive_number;
int check;
int old_af;
if (ignore_config_changes == TRUE) {
return;
}
if (*config_change_desc == '\0') {
return;
}
"no directive present: %s\n", conf_copy);
return;
}
return;
}
if (config_value == NULL) {
return;
}
if (trailing_garbage != NULL) {
"trailing garbage: %s\n", conf_copy);
}
switch(directive_number) {
case V_SERVER:
"line: %s\n", directive);
return;
}
"the server name to '%s'\n", config_value);
return;
}
if (set_server_sa() == -1) {
"a new server '%s'\n",
return;
}
/* XXX: this is fatal */
"unable to open a new socket "
"for '%s'\n", config_value);
exit(1);
}
}
break;
case V_PORT:
"line: %s\n", directive);
return;
}
} else {
if (set_server_sa() == -1) {
"Failed to set a new port\n");
return;
}
}
} else
break;
case V_MAXQUERIES:
"line: %s\n", directive);
return;
}
} else
break;
case V_MAXWAIT:
"line: %s\n", directive);
return;
}
} else
break;
default:
break;
}
}
/*
* parse_query:
* Parse a query line from the input file
*
* Set qname to be the domain to query (up to a max of qnlen chars)
* Set qtype to be the type of the query
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
static char *qtype_strings[] = QTYPE_STRINGS;
static int qtype_codes[] = QTYPE_CODES;
char *domain_str, *type_str;
if (num_types > (sizeof(qtype_codes) / sizeof(int)))
num_types = sizeof(qtype_codes) / sizeof(int);
return (-1);
}
return (-1);
}
}
}
return (-1);
}
return (0);
}
/*
* dispatch_query:
* Send the query packet for the entry
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
int *pktlenp)
{
int buffer_len = PACKETSZ;
int bytes_sent;
if (buffer_len == -1) {
return (-1);
}
if (edns) {
unsigned char *p;
return (-1);
}
p = &packet_buffer[buffer_len];
*p++ = 0; /* root name */
*p++ = 0;
*p++ = 41; /* OPT */
*p++ = 16;
*p++ = 0; /* UDP payload size (4K) */
*p++ = 0; /* extended rcode */
*p++ = 0; /* version */
if (dnssec)
*p++ = 0x80; /* upper flag bits - DO set */
else
*p++ = 0; /* upper flag bits */
*p++ = 0; /* lower flag bit */
*p++ = 0;
*p++ = 0; /* rdlen == 0 */
buffer_len += EDNSLEN;
}
packet_buffer[0] = id_ptr[0];
if (bytes_sent == -1) {
return (-1);
}
if (bytes_sent != buffer_len)
*pktp = packet_buffer;
*pktlenp = buffer_len;
return (0);
}
/*
* send_query:
* Send a query based on a line of input
*/
void
send_query(char *query_desc) {
static unsigned short int use_query_id = 0;
static int qname_len = MAX_DOMAIN_LEN;
char serveraddr[NI_MAXHOST];
int query_type, qpkt_len;
unsigned int count;
use_query_id++;
return;
}
char *addrstr;
NI_NUMERICHOST) == 0) {
} else
return;
}
if (setup_phase == TRUE) {
setup_phase = FALSE;
NI_NUMERICHOST) != 0) {
return;
}
printf("[Status] Sending queries (beginning with %s)\n",
}
/* Find the first slot in status[] that is not in use */
"status[] space!\n");
return;
}
/* Register the query in status[] */
if (verbose)
"query message doesn't have qname?\n");
return;
}
if (num_queries_sent_interval == 0)
}
void
unsigned int rcode)
{
int i;
double rtt;
set_timenow(&now);
rtt_counted++;
if (!oldquery) {
}
return;
if (i < rttarray_size) {
rttarray[i]++;
if (!oldquery)
rttarray_interval[i]++;
} else {
if (!oldquery)
}
}
/*
* register_response:
* Register receipt of a query
*
* Removes (sets in_use = FALSE) the record for the given query id in
* status[] if any exists.
*/
void
int qtype)
{
unsigned int ct = 0;
double rtt;
if (timeout_queries != NULL) {
}
}
rcode);
}
}
}
rcodecounts[rcode]++;
if (target_qps > 0) {
} else {
"Warning: Received a response with an "
"unexpected (maybe timed out) id: %u\n", id);
}
}
}
/*
* process_single_response:
* Receive from the given socket & process an invididual response packet.
* Remove it from the list of open queries (status[]) and decrement the
* number of outstanding queries if it matches an open query.
*/
void
process_single_response(int sockfd) {
struct sockaddr_storage from_addr_ss;
static unsigned char in_buf[MAX_BUFFER_LEN];
addr_len = sizeof(from_addr_ss);
return;
}
if (numbytes < DNS_HEADERLEN) {
if (verbose)
return;
}
if (qnamelen == -1) {
if (verbose)
"Failed to retrieve qname from response\n");
return;
}
if (verbose)
return;
}
}
/*
* data_available:
* Is there data available on the given file descriptor?
*
* Return TRUE if there is
* Return FALSE otherwise
*/
int
data_available(double wait) {
int retval;
int maxfd = -1;
/* Set list of file descriptors */
if (socket4 != -1) {
}
if (socket6 != -1) {
}
} else {
}
}
}
return (available);
}
/*
* process_responses:
* open queries (set in_use = FALSE for their entry in status[]), also
* decrementing the number of outstanding queries.
*/
void
process_responses(int adjust_rate) {
double wait;
double first_packet_wait = RESPONSE_BLOCKING_WAIT_TIME;
unsigned int outstanding = queries_outstanding();
if (adjust_rate == TRUE) {
double u;
u = time_of_first_query_sec +
/*
* Wait until a response arrives or the specified limit is
* reached.
*/
while (1) {
set_timenow(&now);
if (wait <= 0)
wait = 0.0;
break;
/*
* We have reached the limit. Read as many responses
* as possible without waiting, and exit.
*/
if (wait == 0) {
;
break;
}
}
} else {
/*
* Don't block waiting for packets at all if we aren't
* looking for any responses or if we are now able to send new
* queries.
*/
if ((outstanding == 0) ||
first_packet_wait = 0.0;
}
;
}
}
}
/*
* retire_old_queries:
* Go through the list of open queries (status[]) and remove any queries
* (i.e. set in_use = FALSE) which are older than the timeout, decrementing
* the number of queries outstanding for each one removed.
*/
void
retire_old_queries(int sending) {
unsigned int count = 0;
double timeout = query_timeout;
int timeout_reduced = FALSE;
/*
* If we have target qps and would not be able to send any packets
* due to buffer full, check whether we are behind the schedule.
* If we are, purge some queries more aggressively.
*/
double n;
n = time_of_first_query_sec +
set_timenow(&now);
}
}
{
if (timeout_queries != NULL) {
struct query_mininfo *qi;
/* now really retire this query */
}
qi->sent_timestamp =
} else {
}
if (timeout_reduced == FALSE) {
} else {
printf("[Timeout] Query timed out: "
"msg id %u\n",
}
}
}
}
}
/*
* print_histogram
* Print RTT histogram to the specified file in the gnuplot format
*/
void
print_histogram(unsigned int total) {
int i;
double ratio;
return;
return;
}
for (i = 0; i < rttarray_size; i++) {
(double)(i * rttarray_unit) +
(double)rttarray_unit / 2,
ratio);
}
}
/*
* print_statistics:
* Print out statistics based on the results of the test
*/
void
unsigned int possibly_delayed,
struct timeval *first_query,
struct timeval *program_start,
unsigned int roverflows, unsigned int *rarray)
{
unsigned int num_queries_completed;
double queries_per_sec_total;
double rtt_average, rtt_stddev;
struct timeval start_time;
if (num_queries_completed == 0) {
per_lost = 0.0;
per_completed = 0.0;
per_lost2 = 0.0;
per_completed2 = 0.0;
} else {
/ (double)sent;
}
if (sent == 0) {
run_time = 0.0;
queries_per_sec = 0.0;
queries_per_sec2 = 0.0;
queries_per_sec_total = 0.0;
} else {
queries_per_sec2 = (double)(num_queries_completed +
queries_per_sec_total = (double)sent /
}
if (rcounted > 0) {
int i;
double sum = 0;
for (i = 0; i < rttarray_size; i++) {
if (rarray[i] != 0) {
mean = (double)(i * rttarray_unit) +
(double)rttarray_unit / 2;
}
}
} else {
rtt_average = 0.0;
rtt_stddev = 0.0;
}
printf("\n");
printf("\n");
if (!intermediate) {
printf(" Parse input file: %s\n",
if (use_timelimit)
printf(" Run time limit: %u seconds\n",
if (run_only_once == FALSE)
printf(" Ran through file: %u times\n",
else
printf(" Ended due to: reaching %s\n",
((runs_through_file == 0) ? "time limit"
: "end of file"));
printf("\n");
}
printf("\n");
if (!intermediate) /* XXX should we print this case also? */
printf("\n");
if (countrcodes) {
unsigned int i;
for (i = 0; i < 16; i++) {
if (rcodecounts[i] == 0)
continue;
printf(" Returned %8s: %u queries\n",
rcode_strings[i], rcodecounts[i]);
}
printf("\n");
}
if (possibly_delayed > 0)
if (possibly_delayed > 0)
printf("\n");
printf(" Started at: %s",
printf(" Finished at: %s",
printf("\n");
if (possibly_delayed > 0) {
printf(" (w/ delayed qrys): %.6lf qps\n",
}
if (target_qps > 0) {
}
printf("\n");
}
void
if (use_timelimit == FALSE)
return;
if (setup_phase == TRUE)
return;
if (print_interval == 0)
return;
if (timelimit_reached() == TRUE)
return;
<= (double)print_interval)
return;
/* Don't count currently outstanding queries */
/* Reset intermediate counters */
rtt_max_interval = -1;
rtt_min_interval = -1;
rtt_total_interval = 0.0;
rtt_counted_interval = 0.0;
if (rttarray_interval != NULL) {
sizeof(rttarray_interval[0]) * rttarray_size);
}
}
/*
* queryperf Program Mainline
*/
int
int adjust_rate;
int input_length = MAX_INPUT_LEN;
time_of_end_of_run.tv_sec = 0;
input_line[0] = '\0';
return (-1);
/* XXX: move this to setup: */
if (timeout_queries == NULL) {
"failed to allocate memory for timeout queries\n");
return (-1);
} else {
int i;
for (i = 0; i < 65536; i++)
}
printf("[Status] Processing input data\n");
queries_outstanding() > 0)
{
if (num_queries_sent_interval > 0){
/*
* After statistics are printed, send_query()
* needs to be called at least once so that
* time_of_first_query_interval is reset
*/
}
adjust_rate = FALSE;
{
if (len == 0) {
} else {
/* Zap the trailing newline */
/*
* TODO: Should test if we got a whole line
* and flush to the next \n in input if not
* here... Add this later. Only do the next
* few lines if we got a whole line, else
* print a warning. Alternative: Make the
* max line size really big. BAD! :)
*/
if (input_line[0] == CONFIG_CHAR)
else {
if (target_qps > 0 &&
max_queries_outstanding) == 0) {
adjust_rate = TRUE;
}
}
}
}
}
printf("[Status] Testing complete\n");
close_socket();
return (0);
}