]>
git.saurik.com Git - apple/network_cmds.git/blob - unbound/testcode/perf.c
c51eee4b161d4533528b4638491ce0301f9495a8
2 * testcode/perf.c - debug program to estimate name server performance.
4 * Copyright (c) 2008, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 * This program estimates DNS name server performance.
48 #include "util/locks.h"
49 #include "util/net_help.h"
50 #include "util/data/msgencode.h"
51 #include "util/data/msgreply.h"
52 #include "util/data/msgparse.h"
53 #include "ldns/sbuffer.h"
54 #include "ldns/wire2str.h"
55 #include "ldns/str2wire.h"
58 /** usage information for perf */
59 static void usage(char* nm
)
61 printf("usage: %s [options] server\n", nm
);
62 printf("server: ip address of server, IP4 or IP6.\n");
63 printf(" If not on port %d add @port.\n", UNBOUND_DNS_PORT
);
64 printf("-d sec duration of test in whole seconds (0: wait for ^C)\n");
65 printf("-a str query to ask, interpreted as a line from qfile\n");
66 printf("-f fnm query list to read from file\n");
67 printf(" every line has format: qname qclass qtype [+-]{E}\n");
68 printf(" where + means RD set, E means EDNS enabled\n");
69 printf("-q quiet mode, print only final qps\n");
76 /** Global info for perf */
80 /** all purpose buffer (for UDP send and receive) */
84 struct sockaddr_storage dest
;
85 /** length of dest socket addr */
88 /** when did this time slice start */
90 /** number of queries received in that time */
92 /** number of queries sent out in that time */
95 /** duration of test in seconds */
100 /** when did the total test start */
101 struct timeval start
;
102 /** total number recvd */
104 /** total number sent */
106 /** numbers by rcode */
109 /** number of I/O ports */
111 /** I/O ports array */
113 /** max fd value in io ports */
118 /** size of querylist */
120 /** allocated size of qlist array */
121 size_t qlist_capacity
;
122 /** list of query packets (data) */
123 uint8_t** qlist_data
;
124 /** list of query packets (length of a packet) */
126 /** index into querylist, for walking the list */
130 /** I/O port for perf */
134 /** file descriptor of socket */
137 struct timeval timeout
;
138 /** ptr back to perfinfo */
139 struct perfinfo
* info
;
142 /** number of msec between starting io ports */
143 #define START_IO_INTERVAL 10
144 /** number of msec timeout on io ports */
145 #define IO_TIMEOUT 10
147 /** signal handler global info */
148 static struct perfinfo
* sig_info
;
150 /** signal handler for user quit */
151 static RETSIGTYPE
perf_sigh(int sig
)
153 log_assert(sig_info
);
155 printf("exit on signal %d\n", sig
);
159 /** timeval compare, t1 < t2 */
161 perf_tv_smaller(struct timeval
* t1
, struct timeval
* t2
)
164 if(t1
->tv_sec
< t2
->tv_sec
)
166 if(t1
->tv_sec
== t2
->tv_sec
&&
167 t1
->tv_usec
< t2
->tv_usec
)
173 /** timeval add, t1 += t2 */
175 perf_tv_add(struct timeval
* t1
, struct timeval
* t2
)
178 t1
->tv_sec
+= t2
->tv_sec
;
179 t1
->tv_usec
+= t2
->tv_usec
;
180 while(t1
->tv_usec
> 1000000) {
181 t1
->tv_usec
-= 1000000;
187 /** timeval subtract, t1 -= t2 */
189 perf_tv_subtract(struct timeval
* t1
, struct timeval
* t2
)
192 t1
->tv_sec
-= t2
->tv_sec
;
193 if(t1
->tv_usec
>= t2
->tv_usec
) {
194 t1
->tv_usec
-= t2
->tv_usec
;
197 t1
->tv_usec
= 1000000-(t2
->tv_usec
-t1
->tv_usec
);
203 /** setup perf test environment */
205 perfsetup(struct perfinfo
* info
)
208 if(gettimeofday(&info
->start
, NULL
) < 0)
209 fatal_exit("gettimeofday: %s", strerror(errno
));
211 if( signal(SIGINT
, perf_sigh
) == SIG_ERR
||
213 signal(SIGQUIT
, perf_sigh
) == SIG_ERR
||
216 signal(SIGHUP
, perf_sigh
) == SIG_ERR
||
219 signal(SIGBREAK
, perf_sigh
) == SIG_ERR
||
221 signal(SIGTERM
, perf_sigh
) == SIG_ERR
)
222 fatal_exit("could not bind to signal");
223 info
->io
= (struct perfio
*)calloc(sizeof(struct perfio
), info
->io_num
);
224 if(!info
->io
) fatal_exit("out of memory");
226 FD_ZERO(&info
->rset
);
228 info
->since
= info
->start
;
229 for(i
=0; i
<info
->io_num
; i
++) {
231 info
->io
[i
].info
= info
;
232 info
->io
[i
].fd
= socket(
233 addr_is_ip6(&info
->dest
, info
->destlen
)?
234 AF_INET6
:AF_INET
, SOCK_DGRAM
, 0);
235 if(info
->io
[i
].fd
== -1) {
237 fatal_exit("socket: %s", strerror(errno
));
239 fatal_exit("socket: %s",
240 wsa_strerror(WSAGetLastError()));
243 if(info
->io
[i
].fd
> info
->maxfd
)
244 info
->maxfd
= info
->io
[i
].fd
;
246 FD_SET(FD_SET_T info
->io
[i
].fd
, &info
->rset
);
247 info
->io
[i
].timeout
.tv_usec
= ((START_IO_INTERVAL
*i
)%1000
)
249 info
->io
[i
].timeout
.tv_sec
= (START_IO_INTERVAL
*i
)/1000;
250 perf_tv_add(&info
->io
[i
].timeout
, &info
->since
);
255 /** cleanup perf test environment */
257 perffree(struct perfinfo
* info
)
262 for(i
=0; i
<info
->io_num
; i
++) {
264 close(info
->io
[i
].fd
);
266 closesocket(info
->io
[i
].fd
);
271 for(i
=0; i
<info
->qlist_size
; i
++)
272 free(info
->qlist_data
[i
]);
273 free(info
->qlist_data
);
274 free(info
->qlist_len
);
277 /** send new query for io */
279 perfsend(struct perfinfo
* info
, size_t n
, struct timeval
* now
)
282 r
= sendto(info
->io
[n
].fd
, (void*)info
->qlist_data
[info
->qlist_idx
],
283 info
->qlist_len
[info
->qlist_idx
], 0,
284 (struct sockaddr
*)&info
->dest
, info
->destlen
);
285 /*log_hex("send", info->qlist_data[info->qlist_idx],
286 info->qlist_len[info->qlist_idx]);*/
289 log_err("sendto: %s", strerror(errno
));
291 log_err("sendto: %s", wsa_strerror(WSAGetLastError()));
293 } else if(r
!= (ssize_t
)info
->qlist_len
[info
->qlist_idx
]) {
294 log_err("partial sendto");
296 info
->qlist_idx
= (info
->qlist_idx
+1) % info
->qlist_size
;
299 info
->io
[n
].timeout
.tv_sec
= IO_TIMEOUT
/1000;
300 info
->io
[n
].timeout
.tv_usec
= (IO_TIMEOUT%1000
)*1000;
301 perf_tv_add(&info
->io
[n
].timeout
, now
);
304 /** got reply for io */
306 perfreply(struct perfinfo
* info
, size_t n
, struct timeval
* now
)
309 r
= recv(info
->io
[n
].fd
, (void*)sldns_buffer_begin(info
->buf
),
310 sldns_buffer_capacity(info
->buf
), 0);
313 log_err("recv: %s", strerror(errno
));
315 log_err("recv: %s", wsa_strerror(WSAGetLastError()));
318 info
->by_rcode
[LDNS_RCODE_WIRE(sldns_buffer_begin(
322 /*sldns_buffer_set_limit(info->buf, r);
323 log_buf(0, "reply", info->buf);*/
324 perfsend(info
, n
, now
);
327 /** got timeout for io */
329 perftimeout(struct perfinfo
* info
, size_t n
, struct timeval
* now
)
331 /* may not be a dropped packet, this is also used to start
332 * up the sending IOs */
333 perfsend(info
, n
, now
);
336 /** print nice stats about qps */
338 stat_printout(struct perfinfo
* info
, struct timeval
* now
,
339 struct timeval
* elapsed
)
344 dt
= (double)(elapsed
->tv_sec
*1000000 + elapsed
->tv_usec
) / 1000000;
347 qps
= (double)(info
->numrecv
) / dt
;
349 printf("qps: %g\n", qps
);
350 /* setup next slice */
352 info
->total_sent
+= info
->numsent
;
353 info
->total_recv
+= info
->numrecv
;
358 /** wait for new events for performance test */
360 perfselect(struct perfinfo
* info
)
362 fd_set rset
= info
->rset
;
363 struct timeval timeout
, now
;
366 if(gettimeofday(&now
, NULL
) < 0)
367 fatal_exit("gettimeofday: %s", strerror(errno
));
369 if(info
->duration
> 0) {
371 perf_tv_subtract(&timeout
, &info
->start
);
372 if((int)timeout
.tv_sec
>= info
->duration
) {
377 /* time for stats printout? */
379 perf_tv_subtract(&timeout
, &info
->since
);
380 if(timeout
.tv_sec
> 0) {
381 stat_printout(info
, &now
, &timeout
);
383 /* see what is closest port to timeout; or if there is a timeout */
384 timeout
= info
->io
[0].timeout
;
385 for(i
=0; i
<info
->io_num
; i
++) {
386 if(perf_tv_smaller(&info
->io
[i
].timeout
, &now
)) {
387 perftimeout(info
, i
, &now
);
390 if(perf_tv_smaller(&info
->io
[i
].timeout
, &timeout
)) {
391 timeout
= info
->io
[i
].timeout
;
394 perf_tv_subtract(&timeout
, &now
);
396 num
= select(info
->maxfd
+1, &rset
, NULL
, NULL
, &timeout
);
398 if(errno
== EAGAIN
|| errno
== EINTR
)
400 log_err("select: %s", strerror(errno
));
403 /* handle new events */
404 for(i
=0; num
&& i
<info
->io_num
; i
++) {
405 if(FD_ISSET(info
->io
[i
].fd
, &rset
)) {
406 perfreply(info
, i
, &now
);
412 /** show end stats */
414 perfendstats(struct perfinfo
* info
)
417 struct timeval timeout
, now
;
419 if(gettimeofday(&now
, NULL
) < 0)
420 fatal_exit("gettimeofday: %s", strerror(errno
));
422 perf_tv_subtract(&timeout
, &info
->since
);
423 stat_printout(info
, &now
, &timeout
);
426 perf_tv_subtract(&timeout
, &info
->start
);
427 dt
= (double)(timeout
.tv_sec
*1000000 + timeout
.tv_usec
) / 1000000.0;
428 qps
= (double)(info
->total_recv
) / dt
;
429 lost
= (int)(info
->total_sent
- info
->total_recv
) - (int)info
->io_num
;
431 printf("overall time: %g sec\n",
432 (double)timeout
.tv_sec
+
433 (double)timeout
.tv_usec
/1000000.);
435 printf("Packets lost: %d\n", (int)lost
);
437 for(i
=0; i
<(int)(sizeof(info
->by_rcode
)/sizeof(size_t)); i
++)
439 if(info
->by_rcode
[i
] > 0) {
441 sldns_wire2str_rcode_buf(i
, rc
, sizeof(rc
));
442 printf("%d(%5s): %u replies\n",
443 i
, rc
, (unsigned)info
->by_rcode
[i
]);
447 printf("average qps: %g\n", qps
);
450 /** perform the performance test */
452 perfmain(struct perfinfo
* info
)
462 /** parse a query line to a packet into buffer */
464 qlist_parse_line(sldns_buffer
* buf
, char* p
)
466 char nm
[1024], cl
[1024], tp
[1024], fl
[1024];
468 int rec
= 1, edns
= 0;
469 struct query_info qinfo
;
470 nm
[0] = 0; cl
[0] = 0; tp
[0] = 0; fl
[0] = 0;
471 r
= sscanf(p
, " %1023s %1023s %1023s %1023s", nm
, cl
, tp
, fl
);
474 /*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/
475 if(strcmp(tp
, "IN") == 0 || strcmp(tp
, "CH") == 0) {
476 qinfo
.qtype
= sldns_get_rr_type_by_name(cl
);
477 qinfo
.qclass
= sldns_get_rr_class_by_name(tp
);
479 qinfo
.qtype
= sldns_get_rr_type_by_name(tp
);
480 qinfo
.qclass
= sldns_get_rr_class_by_name(cl
);
482 if(fl
[0] == '+') rec
= 1;
483 else if(fl
[0] == '-') rec
= 0;
484 else if(fl
[0] == 'E') edns
= 1;
485 if((fl
[0] == '+' || fl
[0] == '-') && fl
[1] == 'E')
487 qinfo
.qname
= sldns_str2wire_dname(nm
, &qinfo
.qname_len
);
490 qinfo_query_encode(buf
, &qinfo
);
491 sldns_buffer_write_u16_at(buf
, 0, 0); /* zero ID */
492 if(rec
) LDNS_RD_SET(sldns_buffer_begin(buf
));
495 memset(&ed
, 0, sizeof(ed
));
497 ed
.udp_size
= EDNS_ADVERTISED_SIZE
;
498 /* Set DO bit in all EDNS datagrams ... */
500 attach_edns_record(buf
, &ed
);
506 /** grow query list capacity */
508 qlist_grow_capacity(struct perfinfo
* info
)
510 size_t newcap
= (size_t)((info
->qlist_capacity
==0)?16:
511 info
->qlist_capacity
*2);
512 uint8_t** d
= (uint8_t**)calloc(sizeof(uint8_t*), newcap
);
513 size_t* l
= (size_t*)calloc(sizeof(size_t), newcap
);
514 if(!d
|| !l
) fatal_exit("out of memory");
515 memcpy(d
, info
->qlist_data
, sizeof(uint8_t*)*
516 info
->qlist_capacity
);
517 memcpy(l
, info
->qlist_len
, sizeof(size_t)*
518 info
->qlist_capacity
);
519 free(info
->qlist_data
);
520 free(info
->qlist_len
);
521 info
->qlist_data
= d
;
523 info
->qlist_capacity
= newcap
;
526 /** setup query list in info */
528 qlist_add_line(struct perfinfo
* info
, char* line
, int no
)
530 if(!qlist_parse_line(info
->buf
, line
)) {
531 printf("error parsing query %d: %s\n", no
, line
);
534 sldns_buffer_write_u16_at(info
->buf
, 0, (uint16_t)info
->qlist_size
);
535 if(info
->qlist_size
+ 1 > info
->qlist_capacity
) {
536 qlist_grow_capacity(info
);
538 info
->qlist_len
[info
->qlist_size
] = sldns_buffer_limit(info
->buf
);
539 info
->qlist_data
[info
->qlist_size
] = memdup(
540 sldns_buffer_begin(info
->buf
), sldns_buffer_limit(info
->buf
));
541 if(!info
->qlist_data
[info
->qlist_size
])
542 fatal_exit("out of memory");
546 /** setup query list in info */
548 qlist_read_file(struct perfinfo
* info
, char* fname
)
552 FILE* in
= fopen(fname
, "r");
558 while(fgets(buf
, (int)sizeof(buf
), in
)) {
560 buf
[sizeof(buf
)-1] = 0;
562 while(*p
== ' ' || *p
== '\t')
564 if(p
[0] == 0 || p
[0] == '\n' || p
[0] == ';' || p
[0] == '#')
566 qlist_add_line(info
, p
, lineno
);
568 printf("Read %s, got %u queries\n", fname
, (unsigned)info
->qlist_size
);
572 /** getopt global, in case header files fail to declare it. */
574 /** getopt global, in case header files fail to declare it. */
577 /** main program for perf */
578 int main(int argc
, char* argv
[])
582 struct perfinfo info
;
589 memset(&info
, 0, sizeof(info
));
592 log_init(NULL
, 0, NULL
);
593 log_ident_set("perf");
596 if((r
= WSAStartup(MAKEWORD(2,2), &wsa_data
)) != 0)
597 fatal_exit("WSAStartup failed: %s", wsa_strerror(r
));
600 info
.buf
= sldns_buffer_new(65553);
601 if(!info
.buf
) fatal_exit("out of memory");
603 /* parse the options */
604 while( (c
=getopt(argc
, argv
, "d:ha:f:q")) != -1) {
610 if(atoi(optarg
)==0 && strcmp(optarg
, "0")!=0) {
611 printf("-d not a number %s", optarg
);
614 info
.duration
= atoi(optarg
);
617 qlist_add_line(&info
, optarg
, 0);
620 qlist_read_file(&info
, optarg
);
632 printf("error: pass server IP address on commandline.\n");
635 if(!extstrtoaddr(argv
[0], &info
.dest
, &info
.destlen
)) {
636 printf("Could not parse ip: %s\n", argv
[0]);
639 if(info
.qlist_size
== 0) {
640 printf("No queries to make, use -f or -a.\n");
644 /* do the performance test */
647 sldns_buffer_free(info
.buf
);