xref: /netbsd-src/external/bsd/unbound/dist/testcode/perf.c (revision 91f7d55fb697b5e0475da4718fa34c3a3ebeac85)
13b6c3722Schristos /*
23b6c3722Schristos  * testcode/perf.c - debug program to estimate name server performance.
33b6c3722Schristos  *
43b6c3722Schristos  * Copyright (c) 2008, NLnet Labs. All rights reserved.
53b6c3722Schristos  *
63b6c3722Schristos  * This software is open source.
73b6c3722Schristos  *
83b6c3722Schristos  * Redistribution and use in source and binary forms, with or without
93b6c3722Schristos  * modification, are permitted provided that the following conditions
103b6c3722Schristos  * are met:
113b6c3722Schristos  *
123b6c3722Schristos  * Redistributions of source code must retain the above copyright notice,
133b6c3722Schristos  * this list of conditions and the following disclaimer.
143b6c3722Schristos  *
153b6c3722Schristos  * Redistributions in binary form must reproduce the above copyright notice,
163b6c3722Schristos  * this list of conditions and the following disclaimer in the documentation
173b6c3722Schristos  * and/or other materials provided with the distribution.
183b6c3722Schristos  *
193b6c3722Schristos  * Neither the name of the NLNET LABS nor the names of its contributors may
203b6c3722Schristos  * be used to endorse or promote products derived from this software without
213b6c3722Schristos  * specific prior written permission.
223b6c3722Schristos  *
233b6c3722Schristos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243b6c3722Schristos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
253b6c3722Schristos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
263b6c3722Schristos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
273b6c3722Schristos  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283b6c3722Schristos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
293b6c3722Schristos  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
303b6c3722Schristos  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
313b6c3722Schristos  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
323b6c3722Schristos  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
333b6c3722Schristos  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
343b6c3722Schristos  */
353b6c3722Schristos 
363b6c3722Schristos /**
373b6c3722Schristos  * \file
383b6c3722Schristos  *
393b6c3722Schristos  * This program estimates DNS name server performance.
403b6c3722Schristos  */
413b6c3722Schristos 
423b6c3722Schristos #include "config.h"
433b6c3722Schristos #ifdef HAVE_GETOPT_H
443b6c3722Schristos #include <getopt.h>
453b6c3722Schristos #endif
463b6c3722Schristos #include <signal.h>
473b6c3722Schristos #include "util/log.h"
483b6c3722Schristos #include "util/locks.h"
493b6c3722Schristos #include "util/net_help.h"
503b6c3722Schristos #include "util/data/msgencode.h"
513b6c3722Schristos #include "util/data/msgreply.h"
523b6c3722Schristos #include "util/data/msgparse.h"
533b6c3722Schristos #include "sldns/sbuffer.h"
543b6c3722Schristos #include "sldns/wire2str.h"
553b6c3722Schristos #include "sldns/str2wire.h"
563b6c3722Schristos #include <sys/time.h>
573b6c3722Schristos 
583b6c3722Schristos /** usage information for perf */
usage(char * nm)593b6c3722Schristos static void usage(char* nm)
603b6c3722Schristos {
613b6c3722Schristos 	printf("usage: %s [options] server\n", nm);
623b6c3722Schristos 	printf("server: ip address of server, IP4 or IP6.\n");
633b6c3722Schristos 	printf("	If not on port %d add @port.\n", UNBOUND_DNS_PORT);
643b6c3722Schristos 	printf("-d sec	duration of test in whole seconds (0: wait for ^C)\n");
653b6c3722Schristos 	printf("-a str	query to ask, interpreted as a line from qfile\n");
663b6c3722Schristos 	printf("-f fnm	query list to read from file\n");
673b6c3722Schristos 	printf("	every line has format: qname qclass qtype [+-]{E}\n");
683b6c3722Schristos 	printf("	where + means RD set, E means EDNS enabled\n");
693b6c3722Schristos 	printf("-q 	quiet mode, print only final qps\n");
703b6c3722Schristos 	exit(1);
713b6c3722Schristos }
723b6c3722Schristos 
733b6c3722Schristos struct perfinfo;
743b6c3722Schristos struct perfio;
753b6c3722Schristos 
763b6c3722Schristos /** Global info for perf */
773b6c3722Schristos struct perfinfo {
783b6c3722Schristos 	/** need to exit */
793b6c3722Schristos 	volatile int exit;
803b6c3722Schristos 	/** all purpose buffer (for UDP send and receive) */
813b6c3722Schristos 	sldns_buffer* buf;
823b6c3722Schristos 
833b6c3722Schristos 	/** destination */
843b6c3722Schristos 	struct sockaddr_storage dest;
853b6c3722Schristos 	/** length of dest socket addr */
863b6c3722Schristos 	socklen_t destlen;
873b6c3722Schristos 
883b6c3722Schristos 	/** when did this time slice start */
893b6c3722Schristos 	struct timeval since;
903b6c3722Schristos 	/** number of queries received in that time */
913b6c3722Schristos 	size_t numrecv;
923b6c3722Schristos 	/** number of queries sent out in that time */
933b6c3722Schristos 	size_t numsent;
943b6c3722Schristos 
953b6c3722Schristos 	/** duration of test in seconds */
963b6c3722Schristos 	int duration;
973b6c3722Schristos 	/** quiet mode? */
983b6c3722Schristos 	int quiet;
993b6c3722Schristos 
1003b6c3722Schristos 	/** when did the total test start */
1013b6c3722Schristos 	struct timeval start;
1023b6c3722Schristos 	/** total number recvd */
1033b6c3722Schristos 	size_t total_recv;
1043b6c3722Schristos 	/** total number sent */
1053b6c3722Schristos 	size_t total_sent;
1063b6c3722Schristos 	/** numbers by rcode */
1073b6c3722Schristos 	size_t by_rcode[32];
1083b6c3722Schristos 
1093b6c3722Schristos 	/** number of I/O ports */
1103b6c3722Schristos 	size_t io_num;
1113b6c3722Schristos 	/** I/O ports array */
1123b6c3722Schristos 	struct perfio* io;
1133b6c3722Schristos 	/** max fd value in io ports */
1143b6c3722Schristos 	int maxfd;
1153b6c3722Schristos 	/** readset */
1163b6c3722Schristos 	fd_set rset;
1173b6c3722Schristos 
1183b6c3722Schristos 	/** size of querylist */
1193b6c3722Schristos 	size_t qlist_size;
1203b6c3722Schristos 	/** allocated size of qlist array */
1213b6c3722Schristos 	size_t qlist_capacity;
1223b6c3722Schristos 	/** list of query packets (data) */
1233b6c3722Schristos 	uint8_t** qlist_data;
1243b6c3722Schristos 	/** list of query packets (length of a packet) */
1253b6c3722Schristos 	size_t* qlist_len;
1263b6c3722Schristos 	/** index into querylist, for walking the list */
1273b6c3722Schristos 	size_t qlist_idx;
1283b6c3722Schristos };
1293b6c3722Schristos 
1303b6c3722Schristos /** I/O port for perf */
1313b6c3722Schristos struct perfio {
1323b6c3722Schristos 	/** id number */
1333b6c3722Schristos 	size_t id;
1343b6c3722Schristos 	/** file descriptor of socket */
1353b6c3722Schristos 	int fd;
1363b6c3722Schristos 	/** timeout value */
1373b6c3722Schristos 	struct timeval timeout;
1383b6c3722Schristos 	/** ptr back to perfinfo */
1393b6c3722Schristos 	struct perfinfo* info;
1403b6c3722Schristos };
1413b6c3722Schristos 
1423b6c3722Schristos /** number of msec between starting io ports */
1433b6c3722Schristos #define START_IO_INTERVAL 10
1443b6c3722Schristos /** number of msec timeout on io ports */
1453b6c3722Schristos #define IO_TIMEOUT 10
1463b6c3722Schristos 
1473b6c3722Schristos /** signal handler global info */
1483b6c3722Schristos static struct perfinfo* sig_info;
1493b6c3722Schristos 
1503b6c3722Schristos /** signal handler for user quit */
perf_sigh(int sig)1513b6c3722Schristos static RETSIGTYPE perf_sigh(int sig)
1523b6c3722Schristos {
1533b6c3722Schristos 	log_assert(sig_info);
1543b6c3722Schristos 	if(!sig_info->quiet)
1553b6c3722Schristos 		printf("exit on signal %d\n", sig);
1563b6c3722Schristos 	sig_info->exit = 1;
1573b6c3722Schristos }
1583b6c3722Schristos 
1593b6c3722Schristos /** timeval compare, t1 < t2 */
1603b6c3722Schristos static int
perf_tv_smaller(struct timeval * t1,struct timeval * t2)1613b6c3722Schristos perf_tv_smaller(struct timeval* t1, struct timeval* t2)
1623b6c3722Schristos {
1633b6c3722Schristos #ifndef S_SPLINT_S
1643b6c3722Schristos 	if(t1->tv_sec < t2->tv_sec)
1653b6c3722Schristos 		return 1;
1663b6c3722Schristos 	if(t1->tv_sec == t2->tv_sec &&
1673b6c3722Schristos 		t1->tv_usec < t2->tv_usec)
1683b6c3722Schristos 		return 1;
1693b6c3722Schristos #endif
1703b6c3722Schristos 	return 0;
1713b6c3722Schristos }
1723b6c3722Schristos 
1733b6c3722Schristos /** timeval add, t1 += t2 */
1743b6c3722Schristos static void
perf_tv_add(struct timeval * t1,struct timeval * t2)1753b6c3722Schristos perf_tv_add(struct timeval* t1, struct timeval* t2)
1763b6c3722Schristos {
1773b6c3722Schristos #ifndef S_SPLINT_S
1783b6c3722Schristos 	t1->tv_sec += t2->tv_sec;
1793b6c3722Schristos 	t1->tv_usec += t2->tv_usec;
18001049ae6Schristos 	while(t1->tv_usec >= 1000000) {
1813b6c3722Schristos 		t1->tv_usec -= 1000000;
1823b6c3722Schristos 		t1->tv_sec++;
1833b6c3722Schristos 	}
1843b6c3722Schristos #endif
1853b6c3722Schristos }
1863b6c3722Schristos 
1873b6c3722Schristos /** timeval subtract, t1 -= t2 */
1883b6c3722Schristos static void
perf_tv_subtract(struct timeval * t1,struct timeval * t2)1893b6c3722Schristos perf_tv_subtract(struct timeval* t1, struct timeval* t2)
1903b6c3722Schristos {
1913b6c3722Schristos #ifndef S_SPLINT_S
1923b6c3722Schristos 	t1->tv_sec -= t2->tv_sec;
1933b6c3722Schristos 	if(t1->tv_usec >= t2->tv_usec) {
1943b6c3722Schristos 		t1->tv_usec -= t2->tv_usec;
1953b6c3722Schristos 	} else {
1963b6c3722Schristos 		t1->tv_sec--;
1973b6c3722Schristos 		t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec);
1983b6c3722Schristos 	}
1993b6c3722Schristos #endif
2003b6c3722Schristos }
2013b6c3722Schristos 
2023b6c3722Schristos 
2033b6c3722Schristos /** setup perf test environment */
2043b6c3722Schristos static void
perfsetup(struct perfinfo * info)2053b6c3722Schristos perfsetup(struct perfinfo* info)
2063b6c3722Schristos {
2073b6c3722Schristos 	size_t i;
2083b6c3722Schristos 	if(gettimeofday(&info->start, NULL) < 0)
2093b6c3722Schristos 		fatal_exit("gettimeofday: %s", strerror(errno));
2103b6c3722Schristos 	sig_info = info;
2113b6c3722Schristos 	if( signal(SIGINT, perf_sigh) == SIG_ERR ||
2123b6c3722Schristos #ifdef SIGQUIT
2133b6c3722Schristos 		signal(SIGQUIT, perf_sigh) == SIG_ERR ||
2143b6c3722Schristos #endif
2153b6c3722Schristos #ifdef SIGHUP
2163b6c3722Schristos 		signal(SIGHUP, perf_sigh) == SIG_ERR ||
2173b6c3722Schristos #endif
2183b6c3722Schristos #ifdef SIGBREAK
2193b6c3722Schristos 		signal(SIGBREAK, perf_sigh) == SIG_ERR ||
2203b6c3722Schristos #endif
2213b6c3722Schristos 		signal(SIGTERM, perf_sigh) == SIG_ERR)
2223b6c3722Schristos 		fatal_exit("could not bind to signal");
2233b6c3722Schristos 	info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num);
2243b6c3722Schristos 	if(!info->io) fatal_exit("out of memory");
2253b6c3722Schristos #ifndef S_SPLINT_S
2263b6c3722Schristos 	FD_ZERO(&info->rset);
2273b6c3722Schristos #endif
2283b6c3722Schristos 	info->since = info->start;
2293b6c3722Schristos 	for(i=0; i<info->io_num; i++) {
2303b6c3722Schristos 		info->io[i].id = i;
2313b6c3722Schristos 		info->io[i].info = info;
2323b6c3722Schristos 		info->io[i].fd = socket(
2333b6c3722Schristos 			addr_is_ip6(&info->dest, info->destlen)?
2343b6c3722Schristos 			AF_INET6:AF_INET, SOCK_DGRAM, 0);
2353b6c3722Schristos 		if(info->io[i].fd == -1) {
236d0eba39bSchristos 			fatal_exit("socket: %s", sock_strerror(errno));
2373b6c3722Schristos 		}
2383b6c3722Schristos 		if(info->io[i].fd > info->maxfd)
2393b6c3722Schristos 			info->maxfd = info->io[i].fd;
2403b6c3722Schristos #ifndef S_SPLINT_S
2413b6c3722Schristos 		FD_SET(FD_SET_T info->io[i].fd, &info->rset);
2423b6c3722Schristos 		info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000)
2433b6c3722Schristos 						*1000;
2443b6c3722Schristos 		info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000;
2453b6c3722Schristos 		perf_tv_add(&info->io[i].timeout, &info->since);
2463b6c3722Schristos #endif
2473b6c3722Schristos 	}
2483b6c3722Schristos }
2493b6c3722Schristos 
2503b6c3722Schristos /** cleanup perf test environment */
2513b6c3722Schristos static void
perffree(struct perfinfo * info)2523b6c3722Schristos perffree(struct perfinfo* info)
2533b6c3722Schristos {
2543b6c3722Schristos 	size_t i;
2553b6c3722Schristos 	if(!info) return;
2563b6c3722Schristos 	if(info->io) {
2573b6c3722Schristos 		for(i=0; i<info->io_num; i++) {
258d0eba39bSchristos 			sock_close(info->io[i].fd);
2593b6c3722Schristos 		}
2603b6c3722Schristos 		free(info->io);
2613b6c3722Schristos 	}
2623b6c3722Schristos 	for(i=0; i<info->qlist_size; i++)
2633b6c3722Schristos 		free(info->qlist_data[i]);
2643b6c3722Schristos 	free(info->qlist_data);
2653b6c3722Schristos 	free(info->qlist_len);
2663b6c3722Schristos }
2673b6c3722Schristos 
2683b6c3722Schristos /** send new query for io */
2693b6c3722Schristos static void
perfsend(struct perfinfo * info,size_t n,struct timeval * now)2703b6c3722Schristos perfsend(struct perfinfo* info, size_t n, struct timeval* now)
2713b6c3722Schristos {
2723b6c3722Schristos 	ssize_t r;
2733b6c3722Schristos 	r = sendto(info->io[n].fd, (void*)info->qlist_data[info->qlist_idx],
2743b6c3722Schristos 		info->qlist_len[info->qlist_idx], 0,
2753b6c3722Schristos 		(struct sockaddr*)&info->dest, info->destlen);
2763b6c3722Schristos 	/*log_hex("send", info->qlist_data[info->qlist_idx],
2773b6c3722Schristos 		info->qlist_len[info->qlist_idx]);*/
2783b6c3722Schristos 	if(r == -1) {
279d0eba39bSchristos 		log_err("sendto: %s", sock_strerror(errno));
2803b6c3722Schristos 	} else if(r != (ssize_t)info->qlist_len[info->qlist_idx]) {
2813b6c3722Schristos 		log_err("partial sendto");
2823b6c3722Schristos 	}
2833b6c3722Schristos 	info->qlist_idx = (info->qlist_idx+1) % info->qlist_size;
2843b6c3722Schristos 	info->numsent++;
2853b6c3722Schristos 
2863b6c3722Schristos 	info->io[n].timeout.tv_sec = IO_TIMEOUT/1000;
2873b6c3722Schristos 	info->io[n].timeout.tv_usec = (IO_TIMEOUT%1000)*1000;
2883b6c3722Schristos 	perf_tv_add(&info->io[n].timeout, now);
2893b6c3722Schristos }
2903b6c3722Schristos 
2913b6c3722Schristos /** got reply for io */
2923b6c3722Schristos static void
perfreply(struct perfinfo * info,size_t n,struct timeval * now)2933b6c3722Schristos perfreply(struct perfinfo* info, size_t n, struct timeval* now)
2943b6c3722Schristos {
2953b6c3722Schristos 	ssize_t r;
2963b6c3722Schristos 	r = recv(info->io[n].fd, (void*)sldns_buffer_begin(info->buf),
2973b6c3722Schristos 		sldns_buffer_capacity(info->buf), 0);
2983b6c3722Schristos 	if(r == -1) {
299d0eba39bSchristos 		log_err("recv: %s", sock_strerror(errno));
3003b6c3722Schristos 	} else {
3013b6c3722Schristos 		info->by_rcode[LDNS_RCODE_WIRE(sldns_buffer_begin(
3023b6c3722Schristos 			info->buf))]++;
3033b6c3722Schristos 		info->numrecv++;
3043b6c3722Schristos 	}
3053b6c3722Schristos 	/*sldns_buffer_set_limit(info->buf, r);
3063b6c3722Schristos 	log_buf(0, "reply", info->buf);*/
3073b6c3722Schristos 	perfsend(info, n, now);
3083b6c3722Schristos }
3093b6c3722Schristos 
3103b6c3722Schristos /** got timeout for io */
3113b6c3722Schristos static void
perftimeout(struct perfinfo * info,size_t n,struct timeval * now)3123b6c3722Schristos perftimeout(struct perfinfo* info, size_t n, struct timeval* now)
3133b6c3722Schristos {
3143b6c3722Schristos 	/* may not be a dropped packet, this is also used to start
3153b6c3722Schristos 	 * up the sending IOs */
3163b6c3722Schristos 	perfsend(info, n, now);
3173b6c3722Schristos }
3183b6c3722Schristos 
3193b6c3722Schristos /** print nice stats about qps */
3203b6c3722Schristos static void
stat_printout(struct perfinfo * info,struct timeval * now,struct timeval * elapsed)3213b6c3722Schristos stat_printout(struct perfinfo* info, struct timeval* now,
3223b6c3722Schristos 	struct timeval* elapsed)
3233b6c3722Schristos {
3243b6c3722Schristos 	/* calculate qps */
3253b6c3722Schristos 	double dt, qps = 0;
3263b6c3722Schristos #ifndef S_SPLINT_S
3273b6c3722Schristos 	dt = (double)(elapsed->tv_sec*1000000 + elapsed->tv_usec) / 1000000;
3283b6c3722Schristos #endif
3293b6c3722Schristos 	if(dt > 0.001)
3303b6c3722Schristos 		qps = (double)(info->numrecv) / dt;
3313b6c3722Schristos 	if(!info->quiet)
3323b6c3722Schristos 		printf("qps: %g\n", qps);
3333b6c3722Schristos 	/* setup next slice */
3343b6c3722Schristos 	info->since = *now;
3353b6c3722Schristos 	info->total_sent += info->numsent;
3363b6c3722Schristos 	info->total_recv += info->numrecv;
3373b6c3722Schristos 	info->numrecv = 0;
3383b6c3722Schristos 	info->numsent = 0;
3393b6c3722Schristos }
3403b6c3722Schristos 
3413b6c3722Schristos /** wait for new events for performance test */
3423b6c3722Schristos static void
perfselect(struct perfinfo * info)3433b6c3722Schristos perfselect(struct perfinfo* info)
3443b6c3722Schristos {
3453b6c3722Schristos 	fd_set rset = info->rset;
3463b6c3722Schristos 	struct timeval timeout, now;
3473b6c3722Schristos 	int num;
3483b6c3722Schristos 	size_t i;
3493b6c3722Schristos 	if(gettimeofday(&now, NULL) < 0)
3503b6c3722Schristos 		fatal_exit("gettimeofday: %s", strerror(errno));
3513b6c3722Schristos 	/* time to exit? */
3523b6c3722Schristos 	if(info->duration > 0) {
3533b6c3722Schristos 		timeout = now;
3543b6c3722Schristos 		perf_tv_subtract(&timeout, &info->start);
3553b6c3722Schristos 		if((int)timeout.tv_sec >= info->duration) {
3563b6c3722Schristos 			info->exit = 1;
3573b6c3722Schristos 			return;
3583b6c3722Schristos 		}
3593b6c3722Schristos 	}
3603b6c3722Schristos 	/* time for stats printout? */
3613b6c3722Schristos 	timeout = now;
3623b6c3722Schristos 	perf_tv_subtract(&timeout, &info->since);
3633b6c3722Schristos 	if(timeout.tv_sec > 0) {
3643b6c3722Schristos 		stat_printout(info, &now, &timeout);
3653b6c3722Schristos 	}
3663b6c3722Schristos 	/* see what is closest port to timeout; or if there is a timeout */
3673b6c3722Schristos 	timeout = info->io[0].timeout;
3683b6c3722Schristos 	for(i=0; i<info->io_num; i++) {
3693b6c3722Schristos 		if(perf_tv_smaller(&info->io[i].timeout, &now)) {
3703b6c3722Schristos 			perftimeout(info, i, &now);
3713b6c3722Schristos 			return;
3723b6c3722Schristos 		}
3733b6c3722Schristos 		if(perf_tv_smaller(&info->io[i].timeout, &timeout)) {
3743b6c3722Schristos 			timeout = info->io[i].timeout;
3753b6c3722Schristos 		}
3763b6c3722Schristos 	}
3773b6c3722Schristos 	perf_tv_subtract(&timeout, &now);
3783b6c3722Schristos 
3793b6c3722Schristos 	num = select(info->maxfd+1, &rset, NULL, NULL, &timeout);
3803b6c3722Schristos 	if(num == -1) {
3813b6c3722Schristos 		if(errno == EAGAIN || errno == EINTR)
3823b6c3722Schristos 			return;
3833b6c3722Schristos 		log_err("select: %s", strerror(errno));
3843b6c3722Schristos 	}
3853b6c3722Schristos 
3863b6c3722Schristos 	/* handle new events */
3873b6c3722Schristos 	for(i=0; num && i<info->io_num; i++) {
3883b6c3722Schristos 		if(FD_ISSET(info->io[i].fd, &rset)) {
3893b6c3722Schristos 			perfreply(info, i, &now);
3903b6c3722Schristos 			num--;
3913b6c3722Schristos 		}
3923b6c3722Schristos 	}
3933b6c3722Schristos }
3943b6c3722Schristos 
3953b6c3722Schristos /** show end stats */
3963b6c3722Schristos static void
perfendstats(struct perfinfo * info)3973b6c3722Schristos perfendstats(struct perfinfo* info)
3983b6c3722Schristos {
3993b6c3722Schristos 	double dt, qps;
4003b6c3722Schristos 	struct timeval timeout, now;
4013b6c3722Schristos 	int i, lost;
4023b6c3722Schristos 	if(gettimeofday(&now, NULL) < 0)
4033b6c3722Schristos 		fatal_exit("gettimeofday: %s", strerror(errno));
4043b6c3722Schristos 	timeout = now;
4053b6c3722Schristos 	perf_tv_subtract(&timeout, &info->since);
4063b6c3722Schristos 	stat_printout(info, &now, &timeout);
4073b6c3722Schristos 
4083b6c3722Schristos 	timeout = now;
4093b6c3722Schristos 	perf_tv_subtract(&timeout, &info->start);
4103b6c3722Schristos 	dt = (double)(timeout.tv_sec*1000000 + timeout.tv_usec) / 1000000.0;
4113b6c3722Schristos 	qps = (double)(info->total_recv) / dt;
4123b6c3722Schristos 	lost = (int)(info->total_sent - info->total_recv) - (int)info->io_num;
4133b6c3722Schristos 	if(!info->quiet) {
4143b6c3722Schristos 		printf("overall time: 	%g sec\n",
4153b6c3722Schristos 			(double)timeout.tv_sec +
4163b6c3722Schristos 			(double)timeout.tv_usec/1000000.);
4173b6c3722Schristos 		if(lost > 0)
4183b6c3722Schristos 			printf("Packets lost: 	%d\n", (int)lost);
4193b6c3722Schristos 
4203b6c3722Schristos 		for(i=0; i<(int)(sizeof(info->by_rcode)/sizeof(size_t)); i++)
4213b6c3722Schristos 		{
4223b6c3722Schristos 			if(info->by_rcode[i] > 0) {
4233b6c3722Schristos 				char rc[16];
4243b6c3722Schristos 				sldns_wire2str_rcode_buf(i, rc, sizeof(rc));
4253b6c3722Schristos 				printf("%d(%5s): 	%u replies\n",
4263b6c3722Schristos 					i, rc, (unsigned)info->by_rcode[i]);
4273b6c3722Schristos 			}
4283b6c3722Schristos 		}
4293b6c3722Schristos 	}
4303b6c3722Schristos 	printf("average qps: 	%g\n", qps);
4313b6c3722Schristos }
4323b6c3722Schristos 
4333b6c3722Schristos /** perform the performance test */
4343b6c3722Schristos static void
perfmain(struct perfinfo * info)4353b6c3722Schristos perfmain(struct perfinfo* info)
4363b6c3722Schristos {
4373b6c3722Schristos 	perfsetup(info);
4383b6c3722Schristos 	while(!info->exit) {
4393b6c3722Schristos 		perfselect(info);
4403b6c3722Schristos 	}
4413b6c3722Schristos 	perfendstats(info);
4423b6c3722Schristos 	perffree(info);
4433b6c3722Schristos }
4443b6c3722Schristos 
4453b6c3722Schristos /** parse a query line to a packet into buffer */
4463b6c3722Schristos static int
qlist_parse_line(sldns_buffer * buf,char * p)4473b6c3722Schristos qlist_parse_line(sldns_buffer* buf, char* p)
4483b6c3722Schristos {
4493b6c3722Schristos 	char nm[1024], cl[1024], tp[1024], fl[1024];
4503b6c3722Schristos 	int r;
4513b6c3722Schristos 	int rec = 1, edns = 0;
4523b6c3722Schristos 	struct query_info qinfo;
4533b6c3722Schristos 	nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0;
4543b6c3722Schristos 	r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl);
4553b6c3722Schristos 	if(r != 3 && r != 4)
4563b6c3722Schristos 		return 0;
4573b6c3722Schristos 	/*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/
4583b6c3722Schristos 	if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) {
4593b6c3722Schristos 		qinfo.qtype = sldns_get_rr_type_by_name(cl);
4603b6c3722Schristos 		qinfo.qclass = sldns_get_rr_class_by_name(tp);
461*91f7d55fSchristos 		if((qinfo.qtype == 0 && strcmp(cl, "TYPE0") != 0) ||
462*91f7d55fSchristos 			(qinfo.qclass == 0 && strcmp(tp, "CLASS0") != 0)) {
463*91f7d55fSchristos 			return 0;
464*91f7d55fSchristos 		}
4653b6c3722Schristos 	} else {
4663b6c3722Schristos 		qinfo.qtype = sldns_get_rr_type_by_name(tp);
4673b6c3722Schristos 		qinfo.qclass = sldns_get_rr_class_by_name(cl);
468*91f7d55fSchristos 		if((qinfo.qtype == 0 && strcmp(tp, "TYPE0") != 0) ||
469*91f7d55fSchristos 			(qinfo.qclass == 0 && strcmp(cl, "CLASS0") != 0)) {
470*91f7d55fSchristos 			return 0;
471*91f7d55fSchristos 		}
4723b6c3722Schristos 	}
4733b6c3722Schristos 	if(fl[0] == '+') rec = 1;
4743b6c3722Schristos 	else if(fl[0] == '-') rec = 0;
4753b6c3722Schristos 	else if(fl[0] == 'E') edns = 1;
4763b6c3722Schristos 	if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E')
4773b6c3722Schristos 		edns = 1;
4783b6c3722Schristos 	qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len);
4793b6c3722Schristos 	if(!qinfo.qname)
4803b6c3722Schristos 		return 0;
4810cd9f4ecSchristos 	qinfo.local_alias = NULL;
4823b6c3722Schristos 	qinfo_query_encode(buf, &qinfo);
4833b6c3722Schristos 	sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */
4843b6c3722Schristos 	if(rec) LDNS_RD_SET(sldns_buffer_begin(buf));
4853b6c3722Schristos 	if(edns) {
4863b6c3722Schristos 		struct edns_data ed;
4873b6c3722Schristos 		memset(&ed, 0, sizeof(ed));
4883b6c3722Schristos 		ed.edns_present = 1;
4893b6c3722Schristos 		ed.udp_size = EDNS_ADVERTISED_SIZE;
4903b6c3722Schristos 		/* Set DO bit in all EDNS datagrams ... */
4913b6c3722Schristos 		ed.bits = EDNS_DO;
4923b6c3722Schristos 		attach_edns_record(buf, &ed);
4933b6c3722Schristos 	}
4943b6c3722Schristos 	free(qinfo.qname);
4953b6c3722Schristos 	return 1;
4963b6c3722Schristos }
4973b6c3722Schristos 
4983b6c3722Schristos /** grow query list capacity */
4993b6c3722Schristos static void
qlist_grow_capacity(struct perfinfo * info)5003b6c3722Schristos qlist_grow_capacity(struct perfinfo* info)
5013b6c3722Schristos {
5023b6c3722Schristos 	size_t newcap = (size_t)((info->qlist_capacity==0)?16:
5033b6c3722Schristos 		info->qlist_capacity*2);
5043b6c3722Schristos 	uint8_t** d = (uint8_t**)calloc(sizeof(uint8_t*), newcap);
5053b6c3722Schristos 	size_t* l = (size_t*)calloc(sizeof(size_t), newcap);
5063b6c3722Schristos 	if(!d || !l) fatal_exit("out of memory");
507f42d8de7Schristos 	if(info->qlist_data && info->qlist_capacity)
5083b6c3722Schristos 		memcpy(d, info->qlist_data, sizeof(uint8_t*)*
5093b6c3722Schristos 			info->qlist_capacity);
510f42d8de7Schristos 	if(info->qlist_len && info->qlist_capacity)
5113b6c3722Schristos 		memcpy(l, info->qlist_len, sizeof(size_t)*
5123b6c3722Schristos 			info->qlist_capacity);
5133b6c3722Schristos 	free(info->qlist_data);
5143b6c3722Schristos 	free(info->qlist_len);
5153b6c3722Schristos 	info->qlist_data = d;
5163b6c3722Schristos 	info->qlist_len = l;
5173b6c3722Schristos 	info->qlist_capacity = newcap;
5183b6c3722Schristos }
5193b6c3722Schristos 
5203b6c3722Schristos /** setup query list in info */
5213b6c3722Schristos static void
qlist_add_line(struct perfinfo * info,char * line,int no)5223b6c3722Schristos qlist_add_line(struct perfinfo* info, char* line, int no)
5233b6c3722Schristos {
5243b6c3722Schristos 	if(!qlist_parse_line(info->buf, line)) {
5253b6c3722Schristos 		printf("error parsing query %d: %s\n", no, line);
5263b6c3722Schristos 		exit(1);
5273b6c3722Schristos 	}
5283b6c3722Schristos 	sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size);
5293b6c3722Schristos 	if(info->qlist_size + 1 > info->qlist_capacity) {
5303b6c3722Schristos 		qlist_grow_capacity(info);
5313b6c3722Schristos 	}
5323b6c3722Schristos 	info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf);
5333b6c3722Schristos 	info->qlist_data[info->qlist_size] = memdup(
5343b6c3722Schristos 		sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf));
5353b6c3722Schristos 	if(!info->qlist_data[info->qlist_size])
5363b6c3722Schristos 		fatal_exit("out of memory");
5373b6c3722Schristos 	info->qlist_size ++;
5383b6c3722Schristos }
5393b6c3722Schristos 
5403b6c3722Schristos /** setup query list in info */
5413b6c3722Schristos static void
qlist_read_file(struct perfinfo * info,char * fname)5423b6c3722Schristos qlist_read_file(struct perfinfo* info, char* fname)
5433b6c3722Schristos {
5443b6c3722Schristos 	char buf[1024];
5453b6c3722Schristos 	char *p;
5463b6c3722Schristos 	FILE* in = fopen(fname, "r");
5473b6c3722Schristos 	int lineno = 0;
5483b6c3722Schristos 	if(!in) {
5493b6c3722Schristos 		perror(fname);
5503b6c3722Schristos 		exit(1);
5513b6c3722Schristos 	}
5523b6c3722Schristos 	while(fgets(buf, (int)sizeof(buf), in)) {
5533b6c3722Schristos 		lineno++;
5543b6c3722Schristos 		buf[sizeof(buf)-1] = 0;
5553b6c3722Schristos 		p = buf;
5563b6c3722Schristos 		while(*p == ' ' || *p == '\t')
5573b6c3722Schristos 			p++;
5583b6c3722Schristos 		if(p[0] == 0 || p[0] == '\n' || p[0] == ';' || p[0] == '#')
5593b6c3722Schristos 			continue;
5603b6c3722Schristos 		qlist_add_line(info, p, lineno);
5613b6c3722Schristos 	}
5623b6c3722Schristos 	printf("Read %s, got %u queries\n", fname, (unsigned)info->qlist_size);
5633b6c3722Schristos 	fclose(in);
5643b6c3722Schristos }
5653b6c3722Schristos 
5663b6c3722Schristos /** getopt global, in case header files fail to declare it. */
5673b6c3722Schristos extern int optind;
5683b6c3722Schristos /** getopt global, in case header files fail to declare it. */
5693b6c3722Schristos extern char* optarg;
5703b6c3722Schristos 
5713b6c3722Schristos /** main program for perf */
main(int argc,char * argv[])5723b6c3722Schristos int main(int argc, char* argv[])
5733b6c3722Schristos {
5743b6c3722Schristos 	char* nm = argv[0];
5753b6c3722Schristos 	int c;
5763b6c3722Schristos 	struct perfinfo info;
5773b6c3722Schristos #ifdef USE_WINSOCK
5783b6c3722Schristos 	int r;
5793b6c3722Schristos 	WSADATA wsa_data;
5803b6c3722Schristos #endif
5813b6c3722Schristos 
5823b6c3722Schristos 	/* defaults */
5833b6c3722Schristos 	memset(&info, 0, sizeof(info));
5843b6c3722Schristos 	info.io_num = 16;
5853b6c3722Schristos 
5867a540f2bSchristos 	checklock_start();
5873b6c3722Schristos 	log_init(NULL, 0, NULL);
5883b6c3722Schristos 	log_ident_set("perf");
5893b6c3722Schristos #ifdef USE_WINSOCK
5903b6c3722Schristos 	if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
5913b6c3722Schristos 		fatal_exit("WSAStartup failed: %s", wsa_strerror(r));
5923b6c3722Schristos #endif
5933b6c3722Schristos 
5943b6c3722Schristos 	info.buf = sldns_buffer_new(65553);
5953b6c3722Schristos 	if(!info.buf) fatal_exit("out of memory");
5963b6c3722Schristos 
5973b6c3722Schristos 	/* parse the options */
5983b6c3722Schristos 	while( (c=getopt(argc, argv, "d:ha:f:q")) != -1) {
5993b6c3722Schristos 		switch(c) {
6003b6c3722Schristos 		case 'q':
6013b6c3722Schristos 			info.quiet = 1;
6023b6c3722Schristos 			break;
6033b6c3722Schristos 		case 'd':
6043b6c3722Schristos 			if(atoi(optarg)==0 && strcmp(optarg, "0")!=0) {
6053b6c3722Schristos 				printf("-d not a number %s", optarg);
606f42d8de7Schristos 				exit(1);
6073b6c3722Schristos 			}
6083b6c3722Schristos 			info.duration = atoi(optarg);
6093b6c3722Schristos 			break;
6103b6c3722Schristos 		case 'a':
6113b6c3722Schristos 			qlist_add_line(&info, optarg, 0);
6123b6c3722Schristos 			break;
6133b6c3722Schristos 		case 'f':
6143b6c3722Schristos 			qlist_read_file(&info, optarg);
6153b6c3722Schristos 			break;
6163b6c3722Schristos 		case '?':
6173b6c3722Schristos 		case 'h':
6183b6c3722Schristos 		default:
6193b6c3722Schristos 			usage(nm);
6203b6c3722Schristos 		}
6213b6c3722Schristos 	}
6223b6c3722Schristos 	argc -= optind;
6233b6c3722Schristos 	argv += optind;
6243b6c3722Schristos 
6253b6c3722Schristos 	if(argc != 1) {
6263b6c3722Schristos 		printf("error: pass server IP address on commandline.\n");
6273b6c3722Schristos 		usage(nm);
6283b6c3722Schristos 	}
629*91f7d55fSchristos 	if(!extstrtoaddr(argv[0], &info.dest, &info.destlen, UNBOUND_DNS_PORT)) {
6303b6c3722Schristos 		printf("Could not parse ip: %s\n", argv[0]);
631f42d8de7Schristos 		exit(1);
6323b6c3722Schristos 	}
6333b6c3722Schristos 	if(info.qlist_size == 0) {
6343b6c3722Schristos 		printf("No queries to make, use -f or -a.\n");
635f42d8de7Schristos 		exit(1);
6363b6c3722Schristos 	}
6373b6c3722Schristos 
6383b6c3722Schristos 	/* do the performance test */
6393b6c3722Schristos 	perfmain(&info);
6403b6c3722Schristos 
6413b6c3722Schristos 	sldns_buffer_free(info.buf);
6423b6c3722Schristos #ifdef USE_WINSOCK
6433b6c3722Schristos 	WSACleanup();
6443b6c3722Schristos #endif
6453b6c3722Schristos 	checklock_stop();
6463b6c3722Schristos 	return 0;
6473b6c3722Schristos }
648