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