diff options
Diffstat (limited to '')
-rw-r--r-- | external/unbound/testcode/perf.c | 654 |
1 files changed, 0 insertions, 654 deletions
diff --git a/external/unbound/testcode/perf.c b/external/unbound/testcode/perf.c deleted file mode 100644 index d11357c4a..000000000 --- a/external/unbound/testcode/perf.c +++ /dev/null @@ -1,654 +0,0 @@ -/* - * testcode/perf.c - debug program to estimate name server performance. - * - * Copyright (c) 2008, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This program estimates DNS name server performance. - */ - -#include "config.h" -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#endif -#include <signal.h> -#include "util/log.h" -#include "util/locks.h" -#include "util/net_help.h" -#include "util/data/msgencode.h" -#include "util/data/msgreply.h" -#include "util/data/msgparse.h" -#include "sldns/sbuffer.h" -#include "sldns/wire2str.h" -#include "sldns/str2wire.h" -#include <sys/time.h> - -/** usage information for perf */ -static void usage(char* nm) -{ - printf("usage: %s [options] server\n", nm); - printf("server: ip address of server, IP4 or IP6.\n"); - printf(" If not on port %d add @port.\n", UNBOUND_DNS_PORT); - printf("-d sec duration of test in whole seconds (0: wait for ^C)\n"); - printf("-a str query to ask, interpreted as a line from qfile\n"); - printf("-f fnm query list to read from file\n"); - printf(" every line has format: qname qclass qtype [+-]{E}\n"); - printf(" where + means RD set, E means EDNS enabled\n"); - printf("-q quiet mode, print only final qps\n"); - exit(1); -} - -struct perfinfo; -struct perfio; - -/** Global info for perf */ -struct perfinfo { - /** need to exit */ - volatile int exit; - /** all purpose buffer (for UDP send and receive) */ - sldns_buffer* buf; - - /** destination */ - struct sockaddr_storage dest; - /** length of dest socket addr */ - socklen_t destlen; - - /** when did this time slice start */ - struct timeval since; - /** number of queries received in that time */ - size_t numrecv; - /** number of queries sent out in that time */ - size_t numsent; - - /** duration of test in seconds */ - int duration; - /** quiet mode? */ - int quiet; - - /** when did the total test start */ - struct timeval start; - /** total number recvd */ - size_t total_recv; - /** total number sent */ - size_t total_sent; - /** numbers by rcode */ - size_t by_rcode[32]; - - /** number of I/O ports */ - size_t io_num; - /** I/O ports array */ - struct perfio* io; - /** max fd value in io ports */ - int maxfd; - /** readset */ - fd_set rset; - - /** size of querylist */ - size_t qlist_size; - /** allocated size of qlist array */ - size_t qlist_capacity; - /** list of query packets (data) */ - uint8_t** qlist_data; - /** list of query packets (length of a packet) */ - size_t* qlist_len; - /** index into querylist, for walking the list */ - size_t qlist_idx; -}; - -/** I/O port for perf */ -struct perfio { - /** id number */ - size_t id; - /** file descriptor of socket */ - int fd; - /** timeout value */ - struct timeval timeout; - /** ptr back to perfinfo */ - struct perfinfo* info; -}; - -/** number of msec between starting io ports */ -#define START_IO_INTERVAL 10 -/** number of msec timeout on io ports */ -#define IO_TIMEOUT 10 - -/** signal handler global info */ -static struct perfinfo* sig_info; - -/** signal handler for user quit */ -static RETSIGTYPE perf_sigh(int sig) -{ - log_assert(sig_info); - if(!sig_info->quiet) - printf("exit on signal %d\n", sig); - sig_info->exit = 1; -} - -/** timeval compare, t1 < t2 */ -static int -perf_tv_smaller(struct timeval* t1, struct timeval* t2) -{ -#ifndef S_SPLINT_S - if(t1->tv_sec < t2->tv_sec) - return 1; - if(t1->tv_sec == t2->tv_sec && - t1->tv_usec < t2->tv_usec) - return 1; -#endif - return 0; -} - -/** timeval add, t1 += t2 */ -static void -perf_tv_add(struct timeval* t1, struct timeval* t2) -{ -#ifndef S_SPLINT_S - t1->tv_sec += t2->tv_sec; - t1->tv_usec += t2->tv_usec; - while(t1->tv_usec > 1000000) { - t1->tv_usec -= 1000000; - t1->tv_sec++; - } -#endif -} - -/** timeval subtract, t1 -= t2 */ -static void -perf_tv_subtract(struct timeval* t1, struct timeval* t2) -{ -#ifndef S_SPLINT_S - t1->tv_sec -= t2->tv_sec; - if(t1->tv_usec >= t2->tv_usec) { - t1->tv_usec -= t2->tv_usec; - } else { - t1->tv_sec--; - t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec); - } -#endif -} - - -/** setup perf test environment */ -static void -perfsetup(struct perfinfo* info) -{ - size_t i; - if(gettimeofday(&info->start, NULL) < 0) - fatal_exit("gettimeofday: %s", strerror(errno)); - sig_info = info; - if( signal(SIGINT, perf_sigh) == SIG_ERR || -#ifdef SIGQUIT - signal(SIGQUIT, perf_sigh) == SIG_ERR || -#endif -#ifdef SIGHUP - signal(SIGHUP, perf_sigh) == SIG_ERR || -#endif -#ifdef SIGBREAK - signal(SIGBREAK, perf_sigh) == SIG_ERR || -#endif - signal(SIGTERM, perf_sigh) == SIG_ERR) - fatal_exit("could not bind to signal"); - info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num); - if(!info->io) fatal_exit("out of memory"); -#ifndef S_SPLINT_S - FD_ZERO(&info->rset); -#endif - info->since = info->start; - for(i=0; i<info->io_num; i++) { - info->io[i].id = i; - info->io[i].info = info; - info->io[i].fd = socket( - addr_is_ip6(&info->dest, info->destlen)? - AF_INET6:AF_INET, SOCK_DGRAM, 0); - if(info->io[i].fd == -1) { -#ifndef USE_WINSOCK - fatal_exit("socket: %s", strerror(errno)); -#else - fatal_exit("socket: %s", - wsa_strerror(WSAGetLastError())); -#endif - } - if(info->io[i].fd > info->maxfd) - info->maxfd = info->io[i].fd; -#ifndef S_SPLINT_S - FD_SET(FD_SET_T info->io[i].fd, &info->rset); - info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000) - *1000; - info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000; - perf_tv_add(&info->io[i].timeout, &info->since); -#endif - } -} - -/** cleanup perf test environment */ -static void -perffree(struct perfinfo* info) -{ - size_t i; - if(!info) return; - if(info->io) { - for(i=0; i<info->io_num; i++) { -#ifndef USE_WINSOCK - close(info->io[i].fd); -#else - closesocket(info->io[i].fd); -#endif - } - free(info->io); - } - for(i=0; i<info->qlist_size; i++) - free(info->qlist_data[i]); - free(info->qlist_data); - free(info->qlist_len); -} - -/** send new query for io */ -static void -perfsend(struct perfinfo* info, size_t n, struct timeval* now) -{ - ssize_t r; - r = sendto(info->io[n].fd, (void*)info->qlist_data[info->qlist_idx], - info->qlist_len[info->qlist_idx], 0, - (struct sockaddr*)&info->dest, info->destlen); - /*log_hex("send", info->qlist_data[info->qlist_idx], - info->qlist_len[info->qlist_idx]);*/ - if(r == -1) { -#ifndef USE_WINSOCK - log_err("sendto: %s", strerror(errno)); -#else - log_err("sendto: %s", wsa_strerror(WSAGetLastError())); -#endif - } else if(r != (ssize_t)info->qlist_len[info->qlist_idx]) { - log_err("partial sendto"); - } - info->qlist_idx = (info->qlist_idx+1) % info->qlist_size; - info->numsent++; - - info->io[n].timeout.tv_sec = IO_TIMEOUT/1000; - info->io[n].timeout.tv_usec = (IO_TIMEOUT%1000)*1000; - perf_tv_add(&info->io[n].timeout, now); -} - -/** got reply for io */ -static void -perfreply(struct perfinfo* info, size_t n, struct timeval* now) -{ - ssize_t r; - r = recv(info->io[n].fd, (void*)sldns_buffer_begin(info->buf), - sldns_buffer_capacity(info->buf), 0); - if(r == -1) { -#ifndef USE_WINSOCK - log_err("recv: %s", strerror(errno)); -#else - log_err("recv: %s", wsa_strerror(WSAGetLastError())); -#endif - } else { - info->by_rcode[LDNS_RCODE_WIRE(sldns_buffer_begin( - info->buf))]++; - info->numrecv++; - } - /*sldns_buffer_set_limit(info->buf, r); - log_buf(0, "reply", info->buf);*/ - perfsend(info, n, now); -} - -/** got timeout for io */ -static void -perftimeout(struct perfinfo* info, size_t n, struct timeval* now) -{ - /* may not be a dropped packet, this is also used to start - * up the sending IOs */ - perfsend(info, n, now); -} - -/** print nice stats about qps */ -static void -stat_printout(struct perfinfo* info, struct timeval* now, - struct timeval* elapsed) -{ - /* calculate qps */ - double dt, qps = 0; -#ifndef S_SPLINT_S - dt = (double)(elapsed->tv_sec*1000000 + elapsed->tv_usec) / 1000000; -#endif - if(dt > 0.001) - qps = (double)(info->numrecv) / dt; - if(!info->quiet) - printf("qps: %g\n", qps); - /* setup next slice */ - info->since = *now; - info->total_sent += info->numsent; - info->total_recv += info->numrecv; - info->numrecv = 0; - info->numsent = 0; -} - -/** wait for new events for performance test */ -static void -perfselect(struct perfinfo* info) -{ - fd_set rset = info->rset; - struct timeval timeout, now; - int num; - size_t i; - if(gettimeofday(&now, NULL) < 0) - fatal_exit("gettimeofday: %s", strerror(errno)); - /* time to exit? */ - if(info->duration > 0) { - timeout = now; - perf_tv_subtract(&timeout, &info->start); - if((int)timeout.tv_sec >= info->duration) { - info->exit = 1; - return; - } - } - /* time for stats printout? */ - timeout = now; - perf_tv_subtract(&timeout, &info->since); - if(timeout.tv_sec > 0) { - stat_printout(info, &now, &timeout); - } - /* see what is closest port to timeout; or if there is a timeout */ - timeout = info->io[0].timeout; - for(i=0; i<info->io_num; i++) { - if(perf_tv_smaller(&info->io[i].timeout, &now)) { - perftimeout(info, i, &now); - return; - } - if(perf_tv_smaller(&info->io[i].timeout, &timeout)) { - timeout = info->io[i].timeout; - } - } - perf_tv_subtract(&timeout, &now); - - num = select(info->maxfd+1, &rset, NULL, NULL, &timeout); - if(num == -1) { - if(errno == EAGAIN || errno == EINTR) - return; - log_err("select: %s", strerror(errno)); - } - - /* handle new events */ - for(i=0; num && i<info->io_num; i++) { - if(FD_ISSET(info->io[i].fd, &rset)) { - perfreply(info, i, &now); - num--; - } - } -} - -/** show end stats */ -static void -perfendstats(struct perfinfo* info) -{ - double dt, qps; - struct timeval timeout, now; - int i, lost; - if(gettimeofday(&now, NULL) < 0) - fatal_exit("gettimeofday: %s", strerror(errno)); - timeout = now; - perf_tv_subtract(&timeout, &info->since); - stat_printout(info, &now, &timeout); - - timeout = now; - perf_tv_subtract(&timeout, &info->start); - dt = (double)(timeout.tv_sec*1000000 + timeout.tv_usec) / 1000000.0; - qps = (double)(info->total_recv) / dt; - lost = (int)(info->total_sent - info->total_recv) - (int)info->io_num; - if(!info->quiet) { - printf("overall time: %g sec\n", - (double)timeout.tv_sec + - (double)timeout.tv_usec/1000000.); - if(lost > 0) - printf("Packets lost: %d\n", (int)lost); - - for(i=0; i<(int)(sizeof(info->by_rcode)/sizeof(size_t)); i++) - { - if(info->by_rcode[i] > 0) { - char rc[16]; - sldns_wire2str_rcode_buf(i, rc, sizeof(rc)); - printf("%d(%5s): %u replies\n", - i, rc, (unsigned)info->by_rcode[i]); - } - } - } - printf("average qps: %g\n", qps); -} - -/** perform the performance test */ -static void -perfmain(struct perfinfo* info) -{ - perfsetup(info); - while(!info->exit) { - perfselect(info); - } - perfendstats(info); - perffree(info); -} - -/** parse a query line to a packet into buffer */ -static int -qlist_parse_line(sldns_buffer* buf, char* p) -{ - char nm[1024], cl[1024], tp[1024], fl[1024]; - int r; - int rec = 1, edns = 0; - struct query_info qinfo; - nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0; - r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl); - if(r != 3 && r != 4) - return 0; - /*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/ - if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) { - qinfo.qtype = sldns_get_rr_type_by_name(cl); - qinfo.qclass = sldns_get_rr_class_by_name(tp); - } else { - qinfo.qtype = sldns_get_rr_type_by_name(tp); - qinfo.qclass = sldns_get_rr_class_by_name(cl); - } - if(fl[0] == '+') rec = 1; - else if(fl[0] == '-') rec = 0; - else if(fl[0] == 'E') edns = 1; - if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E') - edns = 1; - qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len); - if(!qinfo.qname) - return 0; - qinfo.local_alias = NULL; - qinfo_query_encode(buf, &qinfo); - sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */ - if(rec) LDNS_RD_SET(sldns_buffer_begin(buf)); - if(edns) { - struct edns_data ed; - memset(&ed, 0, sizeof(ed)); - ed.edns_present = 1; - ed.udp_size = EDNS_ADVERTISED_SIZE; - /* Set DO bit in all EDNS datagrams ... */ - ed.bits = EDNS_DO; - attach_edns_record(buf, &ed); - } - free(qinfo.qname); - return 1; -} - -/** grow query list capacity */ -static void -qlist_grow_capacity(struct perfinfo* info) -{ - size_t newcap = (size_t)((info->qlist_capacity==0)?16: - info->qlist_capacity*2); - uint8_t** d = (uint8_t**)calloc(sizeof(uint8_t*), newcap); - size_t* l = (size_t*)calloc(sizeof(size_t), newcap); - if(!d || !l) fatal_exit("out of memory"); - memcpy(d, info->qlist_data, sizeof(uint8_t*)* - info->qlist_capacity); - memcpy(l, info->qlist_len, sizeof(size_t)* - info->qlist_capacity); - free(info->qlist_data); - free(info->qlist_len); - info->qlist_data = d; - info->qlist_len = l; - info->qlist_capacity = newcap; -} - -/** setup query list in info */ -static void -qlist_add_line(struct perfinfo* info, char* line, int no) -{ - if(!qlist_parse_line(info->buf, line)) { - printf("error parsing query %d: %s\n", no, line); - exit(1); - } - sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size); - if(info->qlist_size + 1 > info->qlist_capacity) { - qlist_grow_capacity(info); - } - info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf); - info->qlist_data[info->qlist_size] = memdup( - sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf)); - if(!info->qlist_data[info->qlist_size]) - fatal_exit("out of memory"); - info->qlist_size ++; -} - -/** setup query list in info */ -static void -qlist_read_file(struct perfinfo* info, char* fname) -{ - char buf[1024]; - char *p; - FILE* in = fopen(fname, "r"); - int lineno = 0; - if(!in) { - perror(fname); - exit(1); - } - while(fgets(buf, (int)sizeof(buf), in)) { - lineno++; - buf[sizeof(buf)-1] = 0; - p = buf; - while(*p == ' ' || *p == '\t') - p++; - if(p[0] == 0 || p[0] == '\n' || p[0] == ';' || p[0] == '#') - continue; - qlist_add_line(info, p, lineno); - } - printf("Read %s, got %u queries\n", fname, (unsigned)info->qlist_size); - fclose(in); -} - -/** getopt global, in case header files fail to declare it. */ -extern int optind; -/** getopt global, in case header files fail to declare it. */ -extern char* optarg; - -/** main program for perf */ -int main(int argc, char* argv[]) -{ - char* nm = argv[0]; - int c; - struct perfinfo info; -#ifdef USE_WINSOCK - int r; - WSADATA wsa_data; -#endif - - /* defaults */ - memset(&info, 0, sizeof(info)); - info.io_num = 16; - - log_init(NULL, 0, NULL); - log_ident_set("perf"); - checklock_start(); -#ifdef USE_WINSOCK - if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) - fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); -#endif - - info.buf = sldns_buffer_new(65553); - if(!info.buf) fatal_exit("out of memory"); - - /* parse the options */ - while( (c=getopt(argc, argv, "d:ha:f:q")) != -1) { - switch(c) { - case 'q': - info.quiet = 1; - break; - case 'd': - if(atoi(optarg)==0 && strcmp(optarg, "0")!=0) { - printf("-d not a number %s", optarg); - return 1; - } - info.duration = atoi(optarg); - break; - case 'a': - qlist_add_line(&info, optarg, 0); - break; - case 'f': - qlist_read_file(&info, optarg); - break; - case '?': - case 'h': - default: - usage(nm); - } - } - argc -= optind; - argv += optind; - - if(argc != 1) { - printf("error: pass server IP address on commandline.\n"); - usage(nm); - } - if(!extstrtoaddr(argv[0], &info.dest, &info.destlen)) { - printf("Could not parse ip: %s\n", argv[0]); - return 1; - } - if(info.qlist_size == 0) { - printf("No queries to make, use -f or -a.\n"); - return 1; - } - - /* do the performance test */ - perfmain(&info); - - sldns_buffer_free(info.buf); -#ifdef USE_WINSOCK - WSACleanup(); -#endif - checklock_stop(); - return 0; -} |