1712b2f30Ssthen /*
2712b2f30Ssthen * testcode/perf.c - debug program to estimate name server performance.
3712b2f30Ssthen *
4712b2f30Ssthen * Copyright (c) 2008, NLnet Labs. All rights reserved.
5712b2f30Ssthen *
6712b2f30Ssthen * This software is open source.
7712b2f30Ssthen *
8712b2f30Ssthen * Redistribution and use in source and binary forms, with or without
9712b2f30Ssthen * modification, are permitted provided that the following conditions
10712b2f30Ssthen * are met:
11712b2f30Ssthen *
12712b2f30Ssthen * Redistributions of source code must retain the above copyright notice,
13712b2f30Ssthen * this list of conditions and the following disclaimer.
14712b2f30Ssthen *
15712b2f30Ssthen * Redistributions in binary form must reproduce the above copyright notice,
16712b2f30Ssthen * this list of conditions and the following disclaimer in the documentation
17712b2f30Ssthen * and/or other materials provided with the distribution.
18712b2f30Ssthen *
19712b2f30Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may
20712b2f30Ssthen * be used to endorse or promote products derived from this software without
21712b2f30Ssthen * specific prior written permission.
22712b2f30Ssthen *
23712b2f30Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24712b2f30Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25712b2f30Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26712b2f30Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27712b2f30Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28712b2f30Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29712b2f30Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30712b2f30Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31712b2f30Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32712b2f30Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33712b2f30Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34712b2f30Ssthen */
35712b2f30Ssthen
36712b2f30Ssthen /**
37712b2f30Ssthen * \file
38712b2f30Ssthen *
39712b2f30Ssthen * This program estimates DNS name server performance.
40712b2f30Ssthen */
41712b2f30Ssthen
42712b2f30Ssthen #include "config.h"
43712b2f30Ssthen #ifdef HAVE_GETOPT_H
44712b2f30Ssthen #include <getopt.h>
45712b2f30Ssthen #endif
46712b2f30Ssthen #include <signal.h>
47712b2f30Ssthen #include "util/log.h"
48712b2f30Ssthen #include "util/locks.h"
49712b2f30Ssthen #include "util/net_help.h"
50712b2f30Ssthen #include "util/data/msgencode.h"
51712b2f30Ssthen #include "util/data/msgreply.h"
52712b2f30Ssthen #include "util/data/msgparse.h"
53712b2f30Ssthen #include "sldns/sbuffer.h"
54712b2f30Ssthen #include "sldns/wire2str.h"
55712b2f30Ssthen #include "sldns/str2wire.h"
56712b2f30Ssthen #include <sys/time.h>
57712b2f30Ssthen
58712b2f30Ssthen /** usage information for perf */
usage(char * nm)59712b2f30Ssthen static void usage(char* nm)
60712b2f30Ssthen {
61712b2f30Ssthen printf("usage: %s [options] server\n", nm);
62712b2f30Ssthen printf("server: ip address of server, IP4 or IP6.\n");
63712b2f30Ssthen printf(" If not on port %d add @port.\n", UNBOUND_DNS_PORT);
64712b2f30Ssthen printf("-d sec duration of test in whole seconds (0: wait for ^C)\n");
65712b2f30Ssthen printf("-a str query to ask, interpreted as a line from qfile\n");
66712b2f30Ssthen printf("-f fnm query list to read from file\n");
67712b2f30Ssthen printf(" every line has format: qname qclass qtype [+-]{E}\n");
68712b2f30Ssthen printf(" where + means RD set, E means EDNS enabled\n");
69712b2f30Ssthen printf("-q quiet mode, print only final qps\n");
70712b2f30Ssthen exit(1);
71712b2f30Ssthen }
72712b2f30Ssthen
73712b2f30Ssthen struct perfinfo;
74712b2f30Ssthen struct perfio;
75712b2f30Ssthen
76712b2f30Ssthen /** Global info for perf */
77712b2f30Ssthen struct perfinfo {
78712b2f30Ssthen /** need to exit */
79712b2f30Ssthen volatile int exit;
80712b2f30Ssthen /** all purpose buffer (for UDP send and receive) */
81712b2f30Ssthen sldns_buffer* buf;
82712b2f30Ssthen
83712b2f30Ssthen /** destination */
84712b2f30Ssthen struct sockaddr_storage dest;
85712b2f30Ssthen /** length of dest socket addr */
86712b2f30Ssthen socklen_t destlen;
87712b2f30Ssthen
88712b2f30Ssthen /** when did this time slice start */
89712b2f30Ssthen struct timeval since;
90712b2f30Ssthen /** number of queries received in that time */
91712b2f30Ssthen size_t numrecv;
92712b2f30Ssthen /** number of queries sent out in that time */
93712b2f30Ssthen size_t numsent;
94712b2f30Ssthen
95712b2f30Ssthen /** duration of test in seconds */
96712b2f30Ssthen int duration;
97712b2f30Ssthen /** quiet mode? */
98712b2f30Ssthen int quiet;
99712b2f30Ssthen
100712b2f30Ssthen /** when did the total test start */
101712b2f30Ssthen struct timeval start;
102712b2f30Ssthen /** total number recvd */
103712b2f30Ssthen size_t total_recv;
104712b2f30Ssthen /** total number sent */
105712b2f30Ssthen size_t total_sent;
106712b2f30Ssthen /** numbers by rcode */
107712b2f30Ssthen size_t by_rcode[32];
108712b2f30Ssthen
109712b2f30Ssthen /** number of I/O ports */
110712b2f30Ssthen size_t io_num;
111712b2f30Ssthen /** I/O ports array */
112712b2f30Ssthen struct perfio* io;
113712b2f30Ssthen /** max fd value in io ports */
114712b2f30Ssthen int maxfd;
115712b2f30Ssthen /** readset */
116712b2f30Ssthen fd_set rset;
117712b2f30Ssthen
118712b2f30Ssthen /** size of querylist */
119712b2f30Ssthen size_t qlist_size;
120712b2f30Ssthen /** allocated size of qlist array */
121712b2f30Ssthen size_t qlist_capacity;
122712b2f30Ssthen /** list of query packets (data) */
123712b2f30Ssthen uint8_t** qlist_data;
124712b2f30Ssthen /** list of query packets (length of a packet) */
125712b2f30Ssthen size_t* qlist_len;
126712b2f30Ssthen /** index into querylist, for walking the list */
127712b2f30Ssthen size_t qlist_idx;
128712b2f30Ssthen };
129712b2f30Ssthen
130712b2f30Ssthen /** I/O port for perf */
131712b2f30Ssthen struct perfio {
132712b2f30Ssthen /** id number */
133712b2f30Ssthen size_t id;
134712b2f30Ssthen /** file descriptor of socket */
135712b2f30Ssthen int fd;
136712b2f30Ssthen /** timeout value */
137712b2f30Ssthen struct timeval timeout;
138712b2f30Ssthen /** ptr back to perfinfo */
139712b2f30Ssthen struct perfinfo* info;
140712b2f30Ssthen };
141712b2f30Ssthen
142712b2f30Ssthen /** number of msec between starting io ports */
143712b2f30Ssthen #define START_IO_INTERVAL 10
144712b2f30Ssthen /** number of msec timeout on io ports */
145712b2f30Ssthen #define IO_TIMEOUT 10
146712b2f30Ssthen
147712b2f30Ssthen /** signal handler global info */
148712b2f30Ssthen static struct perfinfo* sig_info;
149712b2f30Ssthen
150712b2f30Ssthen /** signal handler for user quit */
perf_sigh(int sig)151712b2f30Ssthen static RETSIGTYPE perf_sigh(int sig)
152712b2f30Ssthen {
153712b2f30Ssthen log_assert(sig_info);
154712b2f30Ssthen if(!sig_info->quiet)
155712b2f30Ssthen printf("exit on signal %d\n", sig);
156712b2f30Ssthen sig_info->exit = 1;
157712b2f30Ssthen }
158712b2f30Ssthen
159712b2f30Ssthen /** timeval compare, t1 < t2 */
160712b2f30Ssthen static int
perf_tv_smaller(struct timeval * t1,struct timeval * t2)161712b2f30Ssthen perf_tv_smaller(struct timeval* t1, struct timeval* t2)
162712b2f30Ssthen {
163712b2f30Ssthen #ifndef S_SPLINT_S
164712b2f30Ssthen if(t1->tv_sec < t2->tv_sec)
165712b2f30Ssthen return 1;
166712b2f30Ssthen if(t1->tv_sec == t2->tv_sec &&
167712b2f30Ssthen t1->tv_usec < t2->tv_usec)
168712b2f30Ssthen return 1;
169712b2f30Ssthen #endif
170712b2f30Ssthen return 0;
171712b2f30Ssthen }
172712b2f30Ssthen
173712b2f30Ssthen /** timeval add, t1 += t2 */
174712b2f30Ssthen static void
perf_tv_add(struct timeval * t1,struct timeval * t2)175712b2f30Ssthen perf_tv_add(struct timeval* t1, struct timeval* t2)
176712b2f30Ssthen {
177712b2f30Ssthen #ifndef S_SPLINT_S
178712b2f30Ssthen t1->tv_sec += t2->tv_sec;
179712b2f30Ssthen t1->tv_usec += t2->tv_usec;
180c981f76fSsthen while(t1->tv_usec >= 1000000) {
181712b2f30Ssthen t1->tv_usec -= 1000000;
182712b2f30Ssthen t1->tv_sec++;
183712b2f30Ssthen }
184712b2f30Ssthen #endif
185712b2f30Ssthen }
186712b2f30Ssthen
187712b2f30Ssthen /** timeval subtract, t1 -= t2 */
188712b2f30Ssthen static void
perf_tv_subtract(struct timeval * t1,struct timeval * t2)189712b2f30Ssthen perf_tv_subtract(struct timeval* t1, struct timeval* t2)
190712b2f30Ssthen {
191712b2f30Ssthen #ifndef S_SPLINT_S
192712b2f30Ssthen t1->tv_sec -= t2->tv_sec;
193712b2f30Ssthen if(t1->tv_usec >= t2->tv_usec) {
194712b2f30Ssthen t1->tv_usec -= t2->tv_usec;
195712b2f30Ssthen } else {
196712b2f30Ssthen t1->tv_sec--;
197712b2f30Ssthen t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec);
198712b2f30Ssthen }
199712b2f30Ssthen #endif
200712b2f30Ssthen }
201712b2f30Ssthen
202712b2f30Ssthen
203712b2f30Ssthen /** setup perf test environment */
204712b2f30Ssthen static void
perfsetup(struct perfinfo * info)205712b2f30Ssthen perfsetup(struct perfinfo* info)
206712b2f30Ssthen {
207712b2f30Ssthen size_t i;
208712b2f30Ssthen if(gettimeofday(&info->start, NULL) < 0)
209712b2f30Ssthen fatal_exit("gettimeofday: %s", strerror(errno));
210712b2f30Ssthen sig_info = info;
211712b2f30Ssthen if( signal(SIGINT, perf_sigh) == SIG_ERR ||
212712b2f30Ssthen #ifdef SIGQUIT
213712b2f30Ssthen signal(SIGQUIT, perf_sigh) == SIG_ERR ||
214712b2f30Ssthen #endif
215712b2f30Ssthen #ifdef SIGHUP
216712b2f30Ssthen signal(SIGHUP, perf_sigh) == SIG_ERR ||
217712b2f30Ssthen #endif
218712b2f30Ssthen #ifdef SIGBREAK
219712b2f30Ssthen signal(SIGBREAK, perf_sigh) == SIG_ERR ||
220712b2f30Ssthen #endif
221712b2f30Ssthen signal(SIGTERM, perf_sigh) == SIG_ERR)
222712b2f30Ssthen fatal_exit("could not bind to signal");
223712b2f30Ssthen info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num);
224712b2f30Ssthen if(!info->io) fatal_exit("out of memory");
225712b2f30Ssthen #ifndef S_SPLINT_S
226712b2f30Ssthen FD_ZERO(&info->rset);
227712b2f30Ssthen #endif
228712b2f30Ssthen info->since = info->start;
229712b2f30Ssthen for(i=0; i<info->io_num; i++) {
230712b2f30Ssthen info->io[i].id = i;
231712b2f30Ssthen info->io[i].info = info;
232712b2f30Ssthen info->io[i].fd = socket(
233712b2f30Ssthen addr_is_ip6(&info->dest, info->destlen)?
234712b2f30Ssthen AF_INET6:AF_INET, SOCK_DGRAM, 0);
235712b2f30Ssthen if(info->io[i].fd == -1) {
236e2a0f313Ssthen fatal_exit("socket: %s", sock_strerror(errno));
237712b2f30Ssthen }
238712b2f30Ssthen if(info->io[i].fd > info->maxfd)
239712b2f30Ssthen info->maxfd = info->io[i].fd;
240712b2f30Ssthen #ifndef S_SPLINT_S
241712b2f30Ssthen FD_SET(FD_SET_T info->io[i].fd, &info->rset);
242712b2f30Ssthen info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000)
243712b2f30Ssthen *1000;
244712b2f30Ssthen info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000;
245712b2f30Ssthen perf_tv_add(&info->io[i].timeout, &info->since);
246712b2f30Ssthen #endif
247712b2f30Ssthen }
248712b2f30Ssthen }
249712b2f30Ssthen
250712b2f30Ssthen /** cleanup perf test environment */
251712b2f30Ssthen static void
perffree(struct perfinfo * info)252712b2f30Ssthen perffree(struct perfinfo* info)
253712b2f30Ssthen {
254712b2f30Ssthen size_t i;
255712b2f30Ssthen if(!info) return;
256712b2f30Ssthen if(info->io) {
257712b2f30Ssthen for(i=0; i<info->io_num; i++) {
258e2a0f313Ssthen sock_close(info->io[i].fd);
259712b2f30Ssthen }
260712b2f30Ssthen free(info->io);
261712b2f30Ssthen }
262712b2f30Ssthen for(i=0; i<info->qlist_size; i++)
263712b2f30Ssthen free(info->qlist_data[i]);
264712b2f30Ssthen free(info->qlist_data);
265712b2f30Ssthen free(info->qlist_len);
266712b2f30Ssthen }
267712b2f30Ssthen
268712b2f30Ssthen /** send new query for io */
269712b2f30Ssthen static void
perfsend(struct perfinfo * info,size_t n,struct timeval * now)270712b2f30Ssthen perfsend(struct perfinfo* info, size_t n, struct timeval* now)
271712b2f30Ssthen {
272712b2f30Ssthen ssize_t r;
273712b2f30Ssthen r = sendto(info->io[n].fd, (void*)info->qlist_data[info->qlist_idx],
274712b2f30Ssthen info->qlist_len[info->qlist_idx], 0,
275712b2f30Ssthen (struct sockaddr*)&info->dest, info->destlen);
276712b2f30Ssthen /*log_hex("send", info->qlist_data[info->qlist_idx],
277712b2f30Ssthen info->qlist_len[info->qlist_idx]);*/
278712b2f30Ssthen if(r == -1) {
279e2a0f313Ssthen log_err("sendto: %s", sock_strerror(errno));
280712b2f30Ssthen } else if(r != (ssize_t)info->qlist_len[info->qlist_idx]) {
281712b2f30Ssthen log_err("partial sendto");
282712b2f30Ssthen }
283712b2f30Ssthen info->qlist_idx = (info->qlist_idx+1) % info->qlist_size;
284712b2f30Ssthen info->numsent++;
285712b2f30Ssthen
286712b2f30Ssthen info->io[n].timeout.tv_sec = IO_TIMEOUT/1000;
287712b2f30Ssthen info->io[n].timeout.tv_usec = (IO_TIMEOUT%1000)*1000;
288712b2f30Ssthen perf_tv_add(&info->io[n].timeout, now);
289712b2f30Ssthen }
290712b2f30Ssthen
291712b2f30Ssthen /** got reply for io */
292712b2f30Ssthen static void
perfreply(struct perfinfo * info,size_t n,struct timeval * now)293712b2f30Ssthen perfreply(struct perfinfo* info, size_t n, struct timeval* now)
294712b2f30Ssthen {
295712b2f30Ssthen ssize_t r;
296712b2f30Ssthen r = recv(info->io[n].fd, (void*)sldns_buffer_begin(info->buf),
297712b2f30Ssthen sldns_buffer_capacity(info->buf), 0);
298712b2f30Ssthen if(r == -1) {
299e2a0f313Ssthen log_err("recv: %s", sock_strerror(errno));
300712b2f30Ssthen } else {
301712b2f30Ssthen info->by_rcode[LDNS_RCODE_WIRE(sldns_buffer_begin(
302712b2f30Ssthen info->buf))]++;
303712b2f30Ssthen info->numrecv++;
304712b2f30Ssthen }
305712b2f30Ssthen /*sldns_buffer_set_limit(info->buf, r);
306712b2f30Ssthen log_buf(0, "reply", info->buf);*/
307712b2f30Ssthen perfsend(info, n, now);
308712b2f30Ssthen }
309712b2f30Ssthen
310712b2f30Ssthen /** got timeout for io */
311712b2f30Ssthen static void
perftimeout(struct perfinfo * info,size_t n,struct timeval * now)312712b2f30Ssthen perftimeout(struct perfinfo* info, size_t n, struct timeval* now)
313712b2f30Ssthen {
314712b2f30Ssthen /* may not be a dropped packet, this is also used to start
315712b2f30Ssthen * up the sending IOs */
316712b2f30Ssthen perfsend(info, n, now);
317712b2f30Ssthen }
318712b2f30Ssthen
319712b2f30Ssthen /** print nice stats about qps */
320712b2f30Ssthen static void
stat_printout(struct perfinfo * info,struct timeval * now,struct timeval * elapsed)321712b2f30Ssthen stat_printout(struct perfinfo* info, struct timeval* now,
322712b2f30Ssthen struct timeval* elapsed)
323712b2f30Ssthen {
324712b2f30Ssthen /* calculate qps */
325712b2f30Ssthen double dt, qps = 0;
326712b2f30Ssthen #ifndef S_SPLINT_S
327712b2f30Ssthen dt = (double)(elapsed->tv_sec*1000000 + elapsed->tv_usec) / 1000000;
328712b2f30Ssthen #endif
329712b2f30Ssthen if(dt > 0.001)
330712b2f30Ssthen qps = (double)(info->numrecv) / dt;
331712b2f30Ssthen if(!info->quiet)
332712b2f30Ssthen printf("qps: %g\n", qps);
333712b2f30Ssthen /* setup next slice */
334712b2f30Ssthen info->since = *now;
335712b2f30Ssthen info->total_sent += info->numsent;
336712b2f30Ssthen info->total_recv += info->numrecv;
337712b2f30Ssthen info->numrecv = 0;
338712b2f30Ssthen info->numsent = 0;
339712b2f30Ssthen }
340712b2f30Ssthen
341712b2f30Ssthen /** wait for new events for performance test */
342712b2f30Ssthen static void
perfselect(struct perfinfo * info)343712b2f30Ssthen perfselect(struct perfinfo* info)
344712b2f30Ssthen {
345712b2f30Ssthen fd_set rset = info->rset;
346712b2f30Ssthen struct timeval timeout, now;
347712b2f30Ssthen int num;
348712b2f30Ssthen size_t i;
349712b2f30Ssthen if(gettimeofday(&now, NULL) < 0)
350712b2f30Ssthen fatal_exit("gettimeofday: %s", strerror(errno));
351712b2f30Ssthen /* time to exit? */
352712b2f30Ssthen if(info->duration > 0) {
353712b2f30Ssthen timeout = now;
354712b2f30Ssthen perf_tv_subtract(&timeout, &info->start);
355712b2f30Ssthen if((int)timeout.tv_sec >= info->duration) {
356712b2f30Ssthen info->exit = 1;
357712b2f30Ssthen return;
358712b2f30Ssthen }
359712b2f30Ssthen }
360712b2f30Ssthen /* time for stats printout? */
361712b2f30Ssthen timeout = now;
362712b2f30Ssthen perf_tv_subtract(&timeout, &info->since);
363712b2f30Ssthen if(timeout.tv_sec > 0) {
364712b2f30Ssthen stat_printout(info, &now, &timeout);
365712b2f30Ssthen }
366712b2f30Ssthen /* see what is closest port to timeout; or if there is a timeout */
367712b2f30Ssthen timeout = info->io[0].timeout;
368712b2f30Ssthen for(i=0; i<info->io_num; i++) {
369712b2f30Ssthen if(perf_tv_smaller(&info->io[i].timeout, &now)) {
370712b2f30Ssthen perftimeout(info, i, &now);
371712b2f30Ssthen return;
372712b2f30Ssthen }
373712b2f30Ssthen if(perf_tv_smaller(&info->io[i].timeout, &timeout)) {
374712b2f30Ssthen timeout = info->io[i].timeout;
375712b2f30Ssthen }
376712b2f30Ssthen }
377712b2f30Ssthen perf_tv_subtract(&timeout, &now);
378712b2f30Ssthen
379712b2f30Ssthen num = select(info->maxfd+1, &rset, NULL, NULL, &timeout);
380712b2f30Ssthen if(num == -1) {
381712b2f30Ssthen if(errno == EAGAIN || errno == EINTR)
382712b2f30Ssthen return;
383712b2f30Ssthen log_err("select: %s", strerror(errno));
384712b2f30Ssthen }
385712b2f30Ssthen
386712b2f30Ssthen /* handle new events */
387712b2f30Ssthen for(i=0; num && i<info->io_num; i++) {
388712b2f30Ssthen if(FD_ISSET(info->io[i].fd, &rset)) {
389712b2f30Ssthen perfreply(info, i, &now);
390712b2f30Ssthen num--;
391712b2f30Ssthen }
392712b2f30Ssthen }
393712b2f30Ssthen }
394712b2f30Ssthen
395712b2f30Ssthen /** show end stats */
396712b2f30Ssthen static void
perfendstats(struct perfinfo * info)397712b2f30Ssthen perfendstats(struct perfinfo* info)
398712b2f30Ssthen {
399712b2f30Ssthen double dt, qps;
400712b2f30Ssthen struct timeval timeout, now;
401712b2f30Ssthen int i, lost;
402712b2f30Ssthen if(gettimeofday(&now, NULL) < 0)
403712b2f30Ssthen fatal_exit("gettimeofday: %s", strerror(errno));
404712b2f30Ssthen timeout = now;
405712b2f30Ssthen perf_tv_subtract(&timeout, &info->since);
406712b2f30Ssthen stat_printout(info, &now, &timeout);
407712b2f30Ssthen
408712b2f30Ssthen timeout = now;
409712b2f30Ssthen perf_tv_subtract(&timeout, &info->start);
410712b2f30Ssthen dt = (double)(timeout.tv_sec*1000000 + timeout.tv_usec) / 1000000.0;
411712b2f30Ssthen qps = (double)(info->total_recv) / dt;
412712b2f30Ssthen lost = (int)(info->total_sent - info->total_recv) - (int)info->io_num;
413712b2f30Ssthen if(!info->quiet) {
414712b2f30Ssthen printf("overall time: %g sec\n",
415712b2f30Ssthen (double)timeout.tv_sec +
416712b2f30Ssthen (double)timeout.tv_usec/1000000.);
417712b2f30Ssthen if(lost > 0)
418712b2f30Ssthen printf("Packets lost: %d\n", (int)lost);
419712b2f30Ssthen
420712b2f30Ssthen for(i=0; i<(int)(sizeof(info->by_rcode)/sizeof(size_t)); i++)
421712b2f30Ssthen {
422712b2f30Ssthen if(info->by_rcode[i] > 0) {
423712b2f30Ssthen char rc[16];
424712b2f30Ssthen sldns_wire2str_rcode_buf(i, rc, sizeof(rc));
425712b2f30Ssthen printf("%d(%5s): %u replies\n",
426712b2f30Ssthen i, rc, (unsigned)info->by_rcode[i]);
427712b2f30Ssthen }
428712b2f30Ssthen }
429712b2f30Ssthen }
430712b2f30Ssthen printf("average qps: %g\n", qps);
431712b2f30Ssthen }
432712b2f30Ssthen
433712b2f30Ssthen /** perform the performance test */
434712b2f30Ssthen static void
perfmain(struct perfinfo * info)435712b2f30Ssthen perfmain(struct perfinfo* info)
436712b2f30Ssthen {
437712b2f30Ssthen perfsetup(info);
438712b2f30Ssthen while(!info->exit) {
439712b2f30Ssthen perfselect(info);
440712b2f30Ssthen }
441712b2f30Ssthen perfendstats(info);
442712b2f30Ssthen perffree(info);
443712b2f30Ssthen }
444712b2f30Ssthen
445712b2f30Ssthen /** parse a query line to a packet into buffer */
446712b2f30Ssthen static int
qlist_parse_line(sldns_buffer * buf,char * p)447712b2f30Ssthen qlist_parse_line(sldns_buffer* buf, char* p)
448712b2f30Ssthen {
449712b2f30Ssthen char nm[1024], cl[1024], tp[1024], fl[1024];
450712b2f30Ssthen int r;
451712b2f30Ssthen int rec = 1, edns = 0;
452712b2f30Ssthen struct query_info qinfo;
453712b2f30Ssthen nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0;
454712b2f30Ssthen r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl);
455712b2f30Ssthen if(r != 3 && r != 4)
456712b2f30Ssthen return 0;
457712b2f30Ssthen /*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/
458712b2f30Ssthen if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) {
459712b2f30Ssthen qinfo.qtype = sldns_get_rr_type_by_name(cl);
460712b2f30Ssthen qinfo.qclass = sldns_get_rr_class_by_name(tp);
461*437d2860Ssthen if((qinfo.qtype == 0 && strcmp(cl, "TYPE0") != 0) ||
462*437d2860Ssthen (qinfo.qclass == 0 && strcmp(tp, "CLASS0") != 0)) {
463*437d2860Ssthen return 0;
464*437d2860Ssthen }
465712b2f30Ssthen } else {
466712b2f30Ssthen qinfo.qtype = sldns_get_rr_type_by_name(tp);
467712b2f30Ssthen qinfo.qclass = sldns_get_rr_class_by_name(cl);
468*437d2860Ssthen if((qinfo.qtype == 0 && strcmp(tp, "TYPE0") != 0) ||
469*437d2860Ssthen (qinfo.qclass == 0 && strcmp(cl, "CLASS0") != 0)) {
470*437d2860Ssthen return 0;
471*437d2860Ssthen }
472712b2f30Ssthen }
473712b2f30Ssthen if(fl[0] == '+') rec = 1;
474712b2f30Ssthen else if(fl[0] == '-') rec = 0;
475712b2f30Ssthen else if(fl[0] == 'E') edns = 1;
476712b2f30Ssthen if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E')
477712b2f30Ssthen edns = 1;
478712b2f30Ssthen qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len);
479712b2f30Ssthen if(!qinfo.qname)
480712b2f30Ssthen return 0;
481712b2f30Ssthen qinfo.local_alias = NULL;
482712b2f30Ssthen qinfo_query_encode(buf, &qinfo);
483712b2f30Ssthen sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */
484712b2f30Ssthen if(rec) LDNS_RD_SET(sldns_buffer_begin(buf));
485712b2f30Ssthen if(edns) {
486712b2f30Ssthen struct edns_data ed;
487712b2f30Ssthen memset(&ed, 0, sizeof(ed));
488712b2f30Ssthen ed.edns_present = 1;
489712b2f30Ssthen ed.udp_size = EDNS_ADVERTISED_SIZE;
490712b2f30Ssthen /* Set DO bit in all EDNS datagrams ... */
491712b2f30Ssthen ed.bits = EDNS_DO;
492712b2f30Ssthen attach_edns_record(buf, &ed);
493712b2f30Ssthen }
494712b2f30Ssthen free(qinfo.qname);
495712b2f30Ssthen return 1;
496712b2f30Ssthen }
497712b2f30Ssthen
498712b2f30Ssthen /** grow query list capacity */
499712b2f30Ssthen static void
qlist_grow_capacity(struct perfinfo * info)500712b2f30Ssthen qlist_grow_capacity(struct perfinfo* info)
501712b2f30Ssthen {
502712b2f30Ssthen size_t newcap = (size_t)((info->qlist_capacity==0)?16:
503712b2f30Ssthen info->qlist_capacity*2);
504712b2f30Ssthen uint8_t** d = (uint8_t**)calloc(sizeof(uint8_t*), newcap);
505712b2f30Ssthen size_t* l = (size_t*)calloc(sizeof(size_t), newcap);
506712b2f30Ssthen if(!d || !l) fatal_exit("out of memory");
50766a34dc2Ssthen if(info->qlist_data && info->qlist_capacity)
508712b2f30Ssthen memcpy(d, info->qlist_data, sizeof(uint8_t*)*
509712b2f30Ssthen info->qlist_capacity);
51066a34dc2Ssthen if(info->qlist_len && info->qlist_capacity)
511712b2f30Ssthen memcpy(l, info->qlist_len, sizeof(size_t)*
512712b2f30Ssthen info->qlist_capacity);
513712b2f30Ssthen free(info->qlist_data);
514712b2f30Ssthen free(info->qlist_len);
515712b2f30Ssthen info->qlist_data = d;
516712b2f30Ssthen info->qlist_len = l;
517712b2f30Ssthen info->qlist_capacity = newcap;
518712b2f30Ssthen }
519712b2f30Ssthen
520712b2f30Ssthen /** setup query list in info */
521712b2f30Ssthen static void
qlist_add_line(struct perfinfo * info,char * line,int no)522712b2f30Ssthen qlist_add_line(struct perfinfo* info, char* line, int no)
523712b2f30Ssthen {
524712b2f30Ssthen if(!qlist_parse_line(info->buf, line)) {
525712b2f30Ssthen printf("error parsing query %d: %s\n", no, line);
526712b2f30Ssthen exit(1);
527712b2f30Ssthen }
528712b2f30Ssthen sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size);
529712b2f30Ssthen if(info->qlist_size + 1 > info->qlist_capacity) {
530712b2f30Ssthen qlist_grow_capacity(info);
531712b2f30Ssthen }
532712b2f30Ssthen info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf);
533712b2f30Ssthen info->qlist_data[info->qlist_size] = memdup(
534712b2f30Ssthen sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf));
535712b2f30Ssthen if(!info->qlist_data[info->qlist_size])
536712b2f30Ssthen fatal_exit("out of memory");
537712b2f30Ssthen info->qlist_size ++;
538712b2f30Ssthen }
539712b2f30Ssthen
540712b2f30Ssthen /** setup query list in info */
541712b2f30Ssthen static void
qlist_read_file(struct perfinfo * info,char * fname)542712b2f30Ssthen qlist_read_file(struct perfinfo* info, char* fname)
543712b2f30Ssthen {
544712b2f30Ssthen char buf[1024];
545712b2f30Ssthen char *p;
546712b2f30Ssthen FILE* in = fopen(fname, "r");
547712b2f30Ssthen int lineno = 0;
548712b2f30Ssthen if(!in) {
549712b2f30Ssthen perror(fname);
550712b2f30Ssthen exit(1);
551712b2f30Ssthen }
552712b2f30Ssthen while(fgets(buf, (int)sizeof(buf), in)) {
553712b2f30Ssthen lineno++;
554712b2f30Ssthen buf[sizeof(buf)-1] = 0;
555712b2f30Ssthen p = buf;
556712b2f30Ssthen while(*p == ' ' || *p == '\t')
557712b2f30Ssthen p++;
558712b2f30Ssthen if(p[0] == 0 || p[0] == '\n' || p[0] == ';' || p[0] == '#')
559712b2f30Ssthen continue;
560712b2f30Ssthen qlist_add_line(info, p, lineno);
561712b2f30Ssthen }
562712b2f30Ssthen printf("Read %s, got %u queries\n", fname, (unsigned)info->qlist_size);
563712b2f30Ssthen fclose(in);
564712b2f30Ssthen }
565712b2f30Ssthen
566712b2f30Ssthen /** getopt global, in case header files fail to declare it. */
567712b2f30Ssthen extern int optind;
568712b2f30Ssthen /** getopt global, in case header files fail to declare it. */
569712b2f30Ssthen extern char* optarg;
570712b2f30Ssthen
571712b2f30Ssthen /** main program for perf */
main(int argc,char * argv[])572712b2f30Ssthen int main(int argc, char* argv[])
573712b2f30Ssthen {
574712b2f30Ssthen char* nm = argv[0];
575712b2f30Ssthen int c;
576712b2f30Ssthen struct perfinfo info;
577712b2f30Ssthen #ifdef USE_WINSOCK
578712b2f30Ssthen int r;
579712b2f30Ssthen WSADATA wsa_data;
580712b2f30Ssthen #endif
581712b2f30Ssthen
582712b2f30Ssthen /* defaults */
583712b2f30Ssthen memset(&info, 0, sizeof(info));
584712b2f30Ssthen info.io_num = 16;
585712b2f30Ssthen
58683152a15Ssthen checklock_start();
587712b2f30Ssthen log_init(NULL, 0, NULL);
588712b2f30Ssthen log_ident_set("perf");
589712b2f30Ssthen #ifdef USE_WINSOCK
590712b2f30Ssthen if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
591712b2f30Ssthen fatal_exit("WSAStartup failed: %s", wsa_strerror(r));
592712b2f30Ssthen #endif
593712b2f30Ssthen
594712b2f30Ssthen info.buf = sldns_buffer_new(65553);
595712b2f30Ssthen if(!info.buf) fatal_exit("out of memory");
596712b2f30Ssthen
597712b2f30Ssthen /* parse the options */
598712b2f30Ssthen while( (c=getopt(argc, argv, "d:ha:f:q")) != -1) {
599712b2f30Ssthen switch(c) {
600712b2f30Ssthen case 'q':
601712b2f30Ssthen info.quiet = 1;
602712b2f30Ssthen break;
603712b2f30Ssthen case 'd':
604712b2f30Ssthen if(atoi(optarg)==0 && strcmp(optarg, "0")!=0) {
605712b2f30Ssthen printf("-d not a number %s", optarg);
6068771e50fSsthen exit(1);
607712b2f30Ssthen }
608712b2f30Ssthen info.duration = atoi(optarg);
609712b2f30Ssthen break;
610712b2f30Ssthen case 'a':
611712b2f30Ssthen qlist_add_line(&info, optarg, 0);
612712b2f30Ssthen break;
613712b2f30Ssthen case 'f':
614712b2f30Ssthen qlist_read_file(&info, optarg);
615712b2f30Ssthen break;
616712b2f30Ssthen case '?':
617712b2f30Ssthen case 'h':
618712b2f30Ssthen default:
619712b2f30Ssthen usage(nm);
620712b2f30Ssthen }
621712b2f30Ssthen }
622712b2f30Ssthen argc -= optind;
623712b2f30Ssthen argv += optind;
624712b2f30Ssthen
625712b2f30Ssthen if(argc != 1) {
626712b2f30Ssthen printf("error: pass server IP address on commandline.\n");
627712b2f30Ssthen usage(nm);
628712b2f30Ssthen }
6290e9b6f9fSsthen if(!extstrtoaddr(argv[0], &info.dest, &info.destlen, UNBOUND_DNS_PORT)) {
630712b2f30Ssthen printf("Could not parse ip: %s\n", argv[0]);
6318771e50fSsthen exit(1);
632712b2f30Ssthen }
633712b2f30Ssthen if(info.qlist_size == 0) {
634712b2f30Ssthen printf("No queries to make, use -f or -a.\n");
6358771e50fSsthen exit(1);
636712b2f30Ssthen }
637712b2f30Ssthen
638712b2f30Ssthen /* do the performance test */
639712b2f30Ssthen perfmain(&info);
640712b2f30Ssthen
641712b2f30Ssthen sldns_buffer_free(info.buf);
642712b2f30Ssthen #ifdef USE_WINSOCK
643712b2f30Ssthen WSACleanup();
644712b2f30Ssthen #endif
645712b2f30Ssthen checklock_stop();
646712b2f30Ssthen return 0;
647712b2f30Ssthen }
648