perf.c revision 1.1 1 1.1 christos /*
2 1.1 christos * testcode/perf.c - debug program to estimate name server performance.
3 1.1 christos *
4 1.1 christos * Copyright (c) 2008, NLnet Labs. All rights reserved.
5 1.1 christos *
6 1.1 christos * This software is open source.
7 1.1 christos *
8 1.1 christos * Redistribution and use in source and binary forms, with or without
9 1.1 christos * modification, are permitted provided that the following conditions
10 1.1 christos * are met:
11 1.1 christos *
12 1.1 christos * Redistributions of source code must retain the above copyright notice,
13 1.1 christos * this list of conditions and the following disclaimer.
14 1.1 christos *
15 1.1 christos * Redistributions in binary form must reproduce the above copyright notice,
16 1.1 christos * this list of conditions and the following disclaimer in the documentation
17 1.1 christos * and/or other materials provided with the distribution.
18 1.1 christos *
19 1.1 christos * Neither the name of the NLNET LABS nor the names of its contributors may
20 1.1 christos * be used to endorse or promote products derived from this software without
21 1.1 christos * specific prior written permission.
22 1.1 christos *
23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 1.1 christos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 1.1 christos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 1.1 christos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 1.1 christos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 1.1 christos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 1.1 christos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 1.1 christos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 1.1 christos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 1.1 christos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 1.1 christos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 1.1 christos */
35 1.1 christos
36 1.1 christos /**
37 1.1 christos * \file
38 1.1 christos *
39 1.1 christos * This program estimates DNS name server performance.
40 1.1 christos */
41 1.1 christos
42 1.1 christos #include "config.h"
43 1.1 christos #ifdef HAVE_GETOPT_H
44 1.1 christos #include <getopt.h>
45 1.1 christos #endif
46 1.1 christos #include <signal.h>
47 1.1 christos #include "util/log.h"
48 1.1 christos #include "util/locks.h"
49 1.1 christos #include "util/net_help.h"
50 1.1 christos #include "util/data/msgencode.h"
51 1.1 christos #include "util/data/msgreply.h"
52 1.1 christos #include "util/data/msgparse.h"
53 1.1 christos #include "sldns/sbuffer.h"
54 1.1 christos #include "sldns/wire2str.h"
55 1.1 christos #include "sldns/str2wire.h"
56 1.1 christos #include <sys/time.h>
57 1.1 christos
58 1.1 christos /** usage information for perf */
59 1.1 christos static void usage(char* nm)
60 1.1 christos {
61 1.1 christos printf("usage: %s [options] server\n", nm);
62 1.1 christos printf("server: ip address of server, IP4 or IP6.\n");
63 1.1 christos printf(" If not on port %d add @port.\n", UNBOUND_DNS_PORT);
64 1.1 christos printf("-d sec duration of test in whole seconds (0: wait for ^C)\n");
65 1.1 christos printf("-a str query to ask, interpreted as a line from qfile\n");
66 1.1 christos printf("-f fnm query list to read from file\n");
67 1.1 christos printf(" every line has format: qname qclass qtype [+-]{E}\n");
68 1.1 christos printf(" where + means RD set, E means EDNS enabled\n");
69 1.1 christos printf("-q quiet mode, print only final qps\n");
70 1.1 christos exit(1);
71 1.1 christos }
72 1.1 christos
73 1.1 christos struct perfinfo;
74 1.1 christos struct perfio;
75 1.1 christos
76 1.1 christos /** Global info for perf */
77 1.1 christos struct perfinfo {
78 1.1 christos /** need to exit */
79 1.1 christos volatile int exit;
80 1.1 christos /** all purpose buffer (for UDP send and receive) */
81 1.1 christos sldns_buffer* buf;
82 1.1 christos
83 1.1 christos /** destination */
84 1.1 christos struct sockaddr_storage dest;
85 1.1 christos /** length of dest socket addr */
86 1.1 christos socklen_t destlen;
87 1.1 christos
88 1.1 christos /** when did this time slice start */
89 1.1 christos struct timeval since;
90 1.1 christos /** number of queries received in that time */
91 1.1 christos size_t numrecv;
92 1.1 christos /** number of queries sent out in that time */
93 1.1 christos size_t numsent;
94 1.1 christos
95 1.1 christos /** duration of test in seconds */
96 1.1 christos int duration;
97 1.1 christos /** quiet mode? */
98 1.1 christos int quiet;
99 1.1 christos
100 1.1 christos /** when did the total test start */
101 1.1 christos struct timeval start;
102 1.1 christos /** total number recvd */
103 1.1 christos size_t total_recv;
104 1.1 christos /** total number sent */
105 1.1 christos size_t total_sent;
106 1.1 christos /** numbers by rcode */
107 1.1 christos size_t by_rcode[32];
108 1.1 christos
109 1.1 christos /** number of I/O ports */
110 1.1 christos size_t io_num;
111 1.1 christos /** I/O ports array */
112 1.1 christos struct perfio* io;
113 1.1 christos /** max fd value in io ports */
114 1.1 christos int maxfd;
115 1.1 christos /** readset */
116 1.1 christos fd_set rset;
117 1.1 christos
118 1.1 christos /** size of querylist */
119 1.1 christos size_t qlist_size;
120 1.1 christos /** allocated size of qlist array */
121 1.1 christos size_t qlist_capacity;
122 1.1 christos /** list of query packets (data) */
123 1.1 christos uint8_t** qlist_data;
124 1.1 christos /** list of query packets (length of a packet) */
125 1.1 christos size_t* qlist_len;
126 1.1 christos /** index into querylist, for walking the list */
127 1.1 christos size_t qlist_idx;
128 1.1 christos };
129 1.1 christos
130 1.1 christos /** I/O port for perf */
131 1.1 christos struct perfio {
132 1.1 christos /** id number */
133 1.1 christos size_t id;
134 1.1 christos /** file descriptor of socket */
135 1.1 christos int fd;
136 1.1 christos /** timeout value */
137 1.1 christos struct timeval timeout;
138 1.1 christos /** ptr back to perfinfo */
139 1.1 christos struct perfinfo* info;
140 1.1 christos };
141 1.1 christos
142 1.1 christos /** number of msec between starting io ports */
143 1.1 christos #define START_IO_INTERVAL 10
144 1.1 christos /** number of msec timeout on io ports */
145 1.1 christos #define IO_TIMEOUT 10
146 1.1 christos
147 1.1 christos /** signal handler global info */
148 1.1 christos static struct perfinfo* sig_info;
149 1.1 christos
150 1.1 christos /** signal handler for user quit */
151 1.1 christos static RETSIGTYPE perf_sigh(int sig)
152 1.1 christos {
153 1.1 christos log_assert(sig_info);
154 1.1 christos if(!sig_info->quiet)
155 1.1 christos printf("exit on signal %d\n", sig);
156 1.1 christos sig_info->exit = 1;
157 1.1 christos }
158 1.1 christos
159 1.1 christos /** timeval compare, t1 < t2 */
160 1.1 christos static int
161 1.1 christos perf_tv_smaller(struct timeval* t1, struct timeval* t2)
162 1.1 christos {
163 1.1 christos #ifndef S_SPLINT_S
164 1.1 christos if(t1->tv_sec < t2->tv_sec)
165 1.1 christos return 1;
166 1.1 christos if(t1->tv_sec == t2->tv_sec &&
167 1.1 christos t1->tv_usec < t2->tv_usec)
168 1.1 christos return 1;
169 1.1 christos #endif
170 1.1 christos return 0;
171 1.1 christos }
172 1.1 christos
173 1.1 christos /** timeval add, t1 += t2 */
174 1.1 christos static void
175 1.1 christos perf_tv_add(struct timeval* t1, struct timeval* t2)
176 1.1 christos {
177 1.1 christos #ifndef S_SPLINT_S
178 1.1 christos t1->tv_sec += t2->tv_sec;
179 1.1 christos t1->tv_usec += t2->tv_usec;
180 1.1 christos while(t1->tv_usec > 1000000) {
181 1.1 christos t1->tv_usec -= 1000000;
182 1.1 christos t1->tv_sec++;
183 1.1 christos }
184 1.1 christos #endif
185 1.1 christos }
186 1.1 christos
187 1.1 christos /** timeval subtract, t1 -= t2 */
188 1.1 christos static void
189 1.1 christos perf_tv_subtract(struct timeval* t1, struct timeval* t2)
190 1.1 christos {
191 1.1 christos #ifndef S_SPLINT_S
192 1.1 christos t1->tv_sec -= t2->tv_sec;
193 1.1 christos if(t1->tv_usec >= t2->tv_usec) {
194 1.1 christos t1->tv_usec -= t2->tv_usec;
195 1.1 christos } else {
196 1.1 christos t1->tv_sec--;
197 1.1 christos t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec);
198 1.1 christos }
199 1.1 christos #endif
200 1.1 christos }
201 1.1 christos
202 1.1 christos
203 1.1 christos /** setup perf test environment */
204 1.1 christos static void
205 1.1 christos perfsetup(struct perfinfo* info)
206 1.1 christos {
207 1.1 christos size_t i;
208 1.1 christos if(gettimeofday(&info->start, NULL) < 0)
209 1.1 christos fatal_exit("gettimeofday: %s", strerror(errno));
210 1.1 christos sig_info = info;
211 1.1 christos if( signal(SIGINT, perf_sigh) == SIG_ERR ||
212 1.1 christos #ifdef SIGQUIT
213 1.1 christos signal(SIGQUIT, perf_sigh) == SIG_ERR ||
214 1.1 christos #endif
215 1.1 christos #ifdef SIGHUP
216 1.1 christos signal(SIGHUP, perf_sigh) == SIG_ERR ||
217 1.1 christos #endif
218 1.1 christos #ifdef SIGBREAK
219 1.1 christos signal(SIGBREAK, perf_sigh) == SIG_ERR ||
220 1.1 christos #endif
221 1.1 christos signal(SIGTERM, perf_sigh) == SIG_ERR)
222 1.1 christos fatal_exit("could not bind to signal");
223 1.1 christos info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num);
224 1.1 christos if(!info->io) fatal_exit("out of memory");
225 1.1 christos #ifndef S_SPLINT_S
226 1.1 christos FD_ZERO(&info->rset);
227 1.1 christos #endif
228 1.1 christos info->since = info->start;
229 1.1 christos for(i=0; i<info->io_num; i++) {
230 1.1 christos info->io[i].id = i;
231 1.1 christos info->io[i].info = info;
232 1.1 christos info->io[i].fd = socket(
233 1.1 christos addr_is_ip6(&info->dest, info->destlen)?
234 1.1 christos AF_INET6:AF_INET, SOCK_DGRAM, 0);
235 1.1 christos if(info->io[i].fd == -1) {
236 1.1 christos #ifndef USE_WINSOCK
237 1.1 christos fatal_exit("socket: %s", strerror(errno));
238 1.1 christos #else
239 1.1 christos fatal_exit("socket: %s",
240 1.1 christos wsa_strerror(WSAGetLastError()));
241 1.1 christos #endif
242 1.1 christos }
243 1.1 christos if(info->io[i].fd > info->maxfd)
244 1.1 christos info->maxfd = info->io[i].fd;
245 1.1 christos #ifndef S_SPLINT_S
246 1.1 christos FD_SET(FD_SET_T info->io[i].fd, &info->rset);
247 1.1 christos info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000)
248 1.1 christos *1000;
249 1.1 christos info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000;
250 1.1 christos perf_tv_add(&info->io[i].timeout, &info->since);
251 1.1 christos #endif
252 1.1 christos }
253 1.1 christos }
254 1.1 christos
255 1.1 christos /** cleanup perf test environment */
256 1.1 christos static void
257 1.1 christos perffree(struct perfinfo* info)
258 1.1 christos {
259 1.1 christos size_t i;
260 1.1 christos if(!info) return;
261 1.1 christos if(info->io) {
262 1.1 christos for(i=0; i<info->io_num; i++) {
263 1.1 christos #ifndef USE_WINSOCK
264 1.1 christos close(info->io[i].fd);
265 1.1 christos #else
266 1.1 christos closesocket(info->io[i].fd);
267 1.1 christos #endif
268 1.1 christos }
269 1.1 christos free(info->io);
270 1.1 christos }
271 1.1 christos for(i=0; i<info->qlist_size; i++)
272 1.1 christos free(info->qlist_data[i]);
273 1.1 christos free(info->qlist_data);
274 1.1 christos free(info->qlist_len);
275 1.1 christos }
276 1.1 christos
277 1.1 christos /** send new query for io */
278 1.1 christos static void
279 1.1 christos perfsend(struct perfinfo* info, size_t n, struct timeval* now)
280 1.1 christos {
281 1.1 christos ssize_t r;
282 1.1 christos r = sendto(info->io[n].fd, (void*)info->qlist_data[info->qlist_idx],
283 1.1 christos info->qlist_len[info->qlist_idx], 0,
284 1.1 christos (struct sockaddr*)&info->dest, info->destlen);
285 1.1 christos /*log_hex("send", info->qlist_data[info->qlist_idx],
286 1.1 christos info->qlist_len[info->qlist_idx]);*/
287 1.1 christos if(r == -1) {
288 1.1 christos #ifndef USE_WINSOCK
289 1.1 christos log_err("sendto: %s", strerror(errno));
290 1.1 christos #else
291 1.1 christos log_err("sendto: %s", wsa_strerror(WSAGetLastError()));
292 1.1 christos #endif
293 1.1 christos } else if(r != (ssize_t)info->qlist_len[info->qlist_idx]) {
294 1.1 christos log_err("partial sendto");
295 1.1 christos }
296 1.1 christos info->qlist_idx = (info->qlist_idx+1) % info->qlist_size;
297 1.1 christos info->numsent++;
298 1.1 christos
299 1.1 christos info->io[n].timeout.tv_sec = IO_TIMEOUT/1000;
300 1.1 christos info->io[n].timeout.tv_usec = (IO_TIMEOUT%1000)*1000;
301 1.1 christos perf_tv_add(&info->io[n].timeout, now);
302 1.1 christos }
303 1.1 christos
304 1.1 christos /** got reply for io */
305 1.1 christos static void
306 1.1 christos perfreply(struct perfinfo* info, size_t n, struct timeval* now)
307 1.1 christos {
308 1.1 christos ssize_t r;
309 1.1 christos r = recv(info->io[n].fd, (void*)sldns_buffer_begin(info->buf),
310 1.1 christos sldns_buffer_capacity(info->buf), 0);
311 1.1 christos if(r == -1) {
312 1.1 christos #ifndef USE_WINSOCK
313 1.1 christos log_err("recv: %s", strerror(errno));
314 1.1 christos #else
315 1.1 christos log_err("recv: %s", wsa_strerror(WSAGetLastError()));
316 1.1 christos #endif
317 1.1 christos } else {
318 1.1 christos info->by_rcode[LDNS_RCODE_WIRE(sldns_buffer_begin(
319 1.1 christos info->buf))]++;
320 1.1 christos info->numrecv++;
321 1.1 christos }
322 1.1 christos /*sldns_buffer_set_limit(info->buf, r);
323 1.1 christos log_buf(0, "reply", info->buf);*/
324 1.1 christos perfsend(info, n, now);
325 1.1 christos }
326 1.1 christos
327 1.1 christos /** got timeout for io */
328 1.1 christos static void
329 1.1 christos perftimeout(struct perfinfo* info, size_t n, struct timeval* now)
330 1.1 christos {
331 1.1 christos /* may not be a dropped packet, this is also used to start
332 1.1 christos * up the sending IOs */
333 1.1 christos perfsend(info, n, now);
334 1.1 christos }
335 1.1 christos
336 1.1 christos /** print nice stats about qps */
337 1.1 christos static void
338 1.1 christos stat_printout(struct perfinfo* info, struct timeval* now,
339 1.1 christos struct timeval* elapsed)
340 1.1 christos {
341 1.1 christos /* calculate qps */
342 1.1 christos double dt, qps = 0;
343 1.1 christos #ifndef S_SPLINT_S
344 1.1 christos dt = (double)(elapsed->tv_sec*1000000 + elapsed->tv_usec) / 1000000;
345 1.1 christos #endif
346 1.1 christos if(dt > 0.001)
347 1.1 christos qps = (double)(info->numrecv) / dt;
348 1.1 christos if(!info->quiet)
349 1.1 christos printf("qps: %g\n", qps);
350 1.1 christos /* setup next slice */
351 1.1 christos info->since = *now;
352 1.1 christos info->total_sent += info->numsent;
353 1.1 christos info->total_recv += info->numrecv;
354 1.1 christos info->numrecv = 0;
355 1.1 christos info->numsent = 0;
356 1.1 christos }
357 1.1 christos
358 1.1 christos /** wait for new events for performance test */
359 1.1 christos static void
360 1.1 christos perfselect(struct perfinfo* info)
361 1.1 christos {
362 1.1 christos fd_set rset = info->rset;
363 1.1 christos struct timeval timeout, now;
364 1.1 christos int num;
365 1.1 christos size_t i;
366 1.1 christos if(gettimeofday(&now, NULL) < 0)
367 1.1 christos fatal_exit("gettimeofday: %s", strerror(errno));
368 1.1 christos /* time to exit? */
369 1.1 christos if(info->duration > 0) {
370 1.1 christos timeout = now;
371 1.1 christos perf_tv_subtract(&timeout, &info->start);
372 1.1 christos if((int)timeout.tv_sec >= info->duration) {
373 1.1 christos info->exit = 1;
374 1.1 christos return;
375 1.1 christos }
376 1.1 christos }
377 1.1 christos /* time for stats printout? */
378 1.1 christos timeout = now;
379 1.1 christos perf_tv_subtract(&timeout, &info->since);
380 1.1 christos if(timeout.tv_sec > 0) {
381 1.1 christos stat_printout(info, &now, &timeout);
382 1.1 christos }
383 1.1 christos /* see what is closest port to timeout; or if there is a timeout */
384 1.1 christos timeout = info->io[0].timeout;
385 1.1 christos for(i=0; i<info->io_num; i++) {
386 1.1 christos if(perf_tv_smaller(&info->io[i].timeout, &now)) {
387 1.1 christos perftimeout(info, i, &now);
388 1.1 christos return;
389 1.1 christos }
390 1.1 christos if(perf_tv_smaller(&info->io[i].timeout, &timeout)) {
391 1.1 christos timeout = info->io[i].timeout;
392 1.1 christos }
393 1.1 christos }
394 1.1 christos perf_tv_subtract(&timeout, &now);
395 1.1 christos
396 1.1 christos num = select(info->maxfd+1, &rset, NULL, NULL, &timeout);
397 1.1 christos if(num == -1) {
398 1.1 christos if(errno == EAGAIN || errno == EINTR)
399 1.1 christos return;
400 1.1 christos log_err("select: %s", strerror(errno));
401 1.1 christos }
402 1.1 christos
403 1.1 christos /* handle new events */
404 1.1 christos for(i=0; num && i<info->io_num; i++) {
405 1.1 christos if(FD_ISSET(info->io[i].fd, &rset)) {
406 1.1 christos perfreply(info, i, &now);
407 1.1 christos num--;
408 1.1 christos }
409 1.1 christos }
410 1.1 christos }
411 1.1 christos
412 1.1 christos /** show end stats */
413 1.1 christos static void
414 1.1 christos perfendstats(struct perfinfo* info)
415 1.1 christos {
416 1.1 christos double dt, qps;
417 1.1 christos struct timeval timeout, now;
418 1.1 christos int i, lost;
419 1.1 christos if(gettimeofday(&now, NULL) < 0)
420 1.1 christos fatal_exit("gettimeofday: %s", strerror(errno));
421 1.1 christos timeout = now;
422 1.1 christos perf_tv_subtract(&timeout, &info->since);
423 1.1 christos stat_printout(info, &now, &timeout);
424 1.1 christos
425 1.1 christos timeout = now;
426 1.1 christos perf_tv_subtract(&timeout, &info->start);
427 1.1 christos dt = (double)(timeout.tv_sec*1000000 + timeout.tv_usec) / 1000000.0;
428 1.1 christos qps = (double)(info->total_recv) / dt;
429 1.1 christos lost = (int)(info->total_sent - info->total_recv) - (int)info->io_num;
430 1.1 christos if(!info->quiet) {
431 1.1 christos printf("overall time: %g sec\n",
432 1.1 christos (double)timeout.tv_sec +
433 1.1 christos (double)timeout.tv_usec/1000000.);
434 1.1 christos if(lost > 0)
435 1.1 christos printf("Packets lost: %d\n", (int)lost);
436 1.1 christos
437 1.1 christos for(i=0; i<(int)(sizeof(info->by_rcode)/sizeof(size_t)); i++)
438 1.1 christos {
439 1.1 christos if(info->by_rcode[i] > 0) {
440 1.1 christos char rc[16];
441 1.1 christos sldns_wire2str_rcode_buf(i, rc, sizeof(rc));
442 1.1 christos printf("%d(%5s): %u replies\n",
443 1.1 christos i, rc, (unsigned)info->by_rcode[i]);
444 1.1 christos }
445 1.1 christos }
446 1.1 christos }
447 1.1 christos printf("average qps: %g\n", qps);
448 1.1 christos }
449 1.1 christos
450 1.1 christos /** perform the performance test */
451 1.1 christos static void
452 1.1 christos perfmain(struct perfinfo* info)
453 1.1 christos {
454 1.1 christos perfsetup(info);
455 1.1 christos while(!info->exit) {
456 1.1 christos perfselect(info);
457 1.1 christos }
458 1.1 christos perfendstats(info);
459 1.1 christos perffree(info);
460 1.1 christos }
461 1.1 christos
462 1.1 christos /** parse a query line to a packet into buffer */
463 1.1 christos static int
464 1.1 christos qlist_parse_line(sldns_buffer* buf, char* p)
465 1.1 christos {
466 1.1 christos char nm[1024], cl[1024], tp[1024], fl[1024];
467 1.1 christos int r;
468 1.1 christos int rec = 1, edns = 0;
469 1.1 christos struct query_info qinfo;
470 1.1 christos nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0;
471 1.1 christos r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl);
472 1.1 christos if(r != 3 && r != 4)
473 1.1 christos return 0;
474 1.1 christos /*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/
475 1.1 christos if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) {
476 1.1 christos qinfo.qtype = sldns_get_rr_type_by_name(cl);
477 1.1 christos qinfo.qclass = sldns_get_rr_class_by_name(tp);
478 1.1 christos } else {
479 1.1 christos qinfo.qtype = sldns_get_rr_type_by_name(tp);
480 1.1 christos qinfo.qclass = sldns_get_rr_class_by_name(cl);
481 1.1 christos }
482 1.1 christos if(fl[0] == '+') rec = 1;
483 1.1 christos else if(fl[0] == '-') rec = 0;
484 1.1 christos else if(fl[0] == 'E') edns = 1;
485 1.1 christos if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E')
486 1.1 christos edns = 1;
487 1.1 christos qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len);
488 1.1 christos if(!qinfo.qname)
489 1.1 christos return 0;
490 1.1 christos qinfo_query_encode(buf, &qinfo);
491 1.1 christos sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */
492 1.1 christos if(rec) LDNS_RD_SET(sldns_buffer_begin(buf));
493 1.1 christos if(edns) {
494 1.1 christos struct edns_data ed;
495 1.1 christos memset(&ed, 0, sizeof(ed));
496 1.1 christos ed.edns_present = 1;
497 1.1 christos ed.udp_size = EDNS_ADVERTISED_SIZE;
498 1.1 christos /* Set DO bit in all EDNS datagrams ... */
499 1.1 christos ed.bits = EDNS_DO;
500 1.1 christos attach_edns_record(buf, &ed);
501 1.1 christos }
502 1.1 christos free(qinfo.qname);
503 1.1 christos return 1;
504 1.1 christos }
505 1.1 christos
506 1.1 christos /** grow query list capacity */
507 1.1 christos static void
508 1.1 christos qlist_grow_capacity(struct perfinfo* info)
509 1.1 christos {
510 1.1 christos size_t newcap = (size_t)((info->qlist_capacity==0)?16:
511 1.1 christos info->qlist_capacity*2);
512 1.1 christos uint8_t** d = (uint8_t**)calloc(sizeof(uint8_t*), newcap);
513 1.1 christos size_t* l = (size_t*)calloc(sizeof(size_t), newcap);
514 1.1 christos if(!d || !l) fatal_exit("out of memory");
515 1.1 christos memcpy(d, info->qlist_data, sizeof(uint8_t*)*
516 1.1 christos info->qlist_capacity);
517 1.1 christos memcpy(l, info->qlist_len, sizeof(size_t)*
518 1.1 christos info->qlist_capacity);
519 1.1 christos free(info->qlist_data);
520 1.1 christos free(info->qlist_len);
521 1.1 christos info->qlist_data = d;
522 1.1 christos info->qlist_len = l;
523 1.1 christos info->qlist_capacity = newcap;
524 1.1 christos }
525 1.1 christos
526 1.1 christos /** setup query list in info */
527 1.1 christos static void
528 1.1 christos qlist_add_line(struct perfinfo* info, char* line, int no)
529 1.1 christos {
530 1.1 christos if(!qlist_parse_line(info->buf, line)) {
531 1.1 christos printf("error parsing query %d: %s\n", no, line);
532 1.1 christos exit(1);
533 1.1 christos }
534 1.1 christos sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size);
535 1.1 christos if(info->qlist_size + 1 > info->qlist_capacity) {
536 1.1 christos qlist_grow_capacity(info);
537 1.1 christos }
538 1.1 christos info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf);
539 1.1 christos info->qlist_data[info->qlist_size] = memdup(
540 1.1 christos sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf));
541 1.1 christos if(!info->qlist_data[info->qlist_size])
542 1.1 christos fatal_exit("out of memory");
543 1.1 christos info->qlist_size ++;
544 1.1 christos }
545 1.1 christos
546 1.1 christos /** setup query list in info */
547 1.1 christos static void
548 1.1 christos qlist_read_file(struct perfinfo* info, char* fname)
549 1.1 christos {
550 1.1 christos char buf[1024];
551 1.1 christos char *p;
552 1.1 christos FILE* in = fopen(fname, "r");
553 1.1 christos int lineno = 0;
554 1.1 christos if(!in) {
555 1.1 christos perror(fname);
556 1.1 christos exit(1);
557 1.1 christos }
558 1.1 christos while(fgets(buf, (int)sizeof(buf), in)) {
559 1.1 christos lineno++;
560 1.1 christos buf[sizeof(buf)-1] = 0;
561 1.1 christos p = buf;
562 1.1 christos while(*p == ' ' || *p == '\t')
563 1.1 christos p++;
564 1.1 christos if(p[0] == 0 || p[0] == '\n' || p[0] == ';' || p[0] == '#')
565 1.1 christos continue;
566 1.1 christos qlist_add_line(info, p, lineno);
567 1.1 christos }
568 1.1 christos printf("Read %s, got %u queries\n", fname, (unsigned)info->qlist_size);
569 1.1 christos fclose(in);
570 1.1 christos }
571 1.1 christos
572 1.1 christos /** getopt global, in case header files fail to declare it. */
573 1.1 christos extern int optind;
574 1.1 christos /** getopt global, in case header files fail to declare it. */
575 1.1 christos extern char* optarg;
576 1.1 christos
577 1.1 christos /** main program for perf */
578 1.1 christos int main(int argc, char* argv[])
579 1.1 christos {
580 1.1 christos char* nm = argv[0];
581 1.1 christos int c;
582 1.1 christos struct perfinfo info;
583 1.1 christos #ifdef USE_WINSOCK
584 1.1 christos int r;
585 1.1 christos WSADATA wsa_data;
586 1.1 christos #endif
587 1.1 christos
588 1.1 christos /* defaults */
589 1.1 christos memset(&info, 0, sizeof(info));
590 1.1 christos info.io_num = 16;
591 1.1 christos
592 1.1 christos log_init(NULL, 0, NULL);
593 1.1 christos log_ident_set("perf");
594 1.1 christos checklock_start();
595 1.1 christos #ifdef USE_WINSOCK
596 1.1 christos if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
597 1.1 christos fatal_exit("WSAStartup failed: %s", wsa_strerror(r));
598 1.1 christos #endif
599 1.1 christos
600 1.1 christos info.buf = sldns_buffer_new(65553);
601 1.1 christos if(!info.buf) fatal_exit("out of memory");
602 1.1 christos
603 1.1 christos /* parse the options */
604 1.1 christos while( (c=getopt(argc, argv, "d:ha:f:q")) != -1) {
605 1.1 christos switch(c) {
606 1.1 christos case 'q':
607 1.1 christos info.quiet = 1;
608 1.1 christos break;
609 1.1 christos case 'd':
610 1.1 christos if(atoi(optarg)==0 && strcmp(optarg, "0")!=0) {
611 1.1 christos printf("-d not a number %s", optarg);
612 1.1 christos return 1;
613 1.1 christos }
614 1.1 christos info.duration = atoi(optarg);
615 1.1 christos break;
616 1.1 christos case 'a':
617 1.1 christos qlist_add_line(&info, optarg, 0);
618 1.1 christos break;
619 1.1 christos case 'f':
620 1.1 christos qlist_read_file(&info, optarg);
621 1.1 christos break;
622 1.1 christos case '?':
623 1.1 christos case 'h':
624 1.1 christos default:
625 1.1 christos usage(nm);
626 1.1 christos }
627 1.1 christos }
628 1.1 christos argc -= optind;
629 1.1 christos argv += optind;
630 1.1 christos
631 1.1 christos if(argc != 1) {
632 1.1 christos printf("error: pass server IP address on commandline.\n");
633 1.1 christos usage(nm);
634 1.1 christos }
635 1.1 christos if(!extstrtoaddr(argv[0], &info.dest, &info.destlen)) {
636 1.1 christos printf("Could not parse ip: %s\n", argv[0]);
637 1.1 christos return 1;
638 1.1 christos }
639 1.1 christos if(info.qlist_size == 0) {
640 1.1 christos printf("No queries to make, use -f or -a.\n");
641 1.1 christos return 1;
642 1.1 christos }
643 1.1 christos
644 1.1 christos /* do the performance test */
645 1.1 christos perfmain(&info);
646 1.1 christos
647 1.1 christos sldns_buffer_free(info.buf);
648 1.1 christos #ifdef USE_WINSOCK
649 1.1 christos WSACleanup();
650 1.1 christos #endif
651 1.1 christos checklock_stop();
652 1.1 christos return 0;
653 1.1 christos }
654