diff --git a/src/conn.c b/src/conn.c index a08f1ae..2f62167 100755 --- a/src/conn.c +++ b/src/conn.c @@ -34,57 +34,92 @@ #include "config.h" #include +#include #include #include #include +#include + +#include +#include #include #include #include #include +#include #include +#include + +static char *srvbase, *srvend, *srvcurrent; + +void +conn_add_servers (void) +{ + struct stat st; + int fd, len; + + fd = open(param.server, O_RDONLY, 0); + if (fd == -1) + panic("%s: can't open %s\n", prog_name, param.server); + + fstat(fd, &st); + if (st.st_size == 0) + panic("%s: file %s is empty\n", prog_name, param.server); + + srvbase = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (srvbase == (char *)-1) + panic("%s: can't mmap the file: %s\n", prog_name, strerror(errno)); + + close (fd); + + srvend = srvbase + st.st_size; + for (srvcurrent = srvbase; srvcurrent < srvend; srvcurrent += len + 1) { + len = strlen(srvcurrent); + core_addr_intern(srvcurrent, len, param.port); + } + srvcurrent = srvbase; +} void conn_init (Conn *conn) { - conn->hostname = param.server; - conn->hostname_len = strlen (param.server); - conn->port = param.port; - conn->sd = -1; - conn->myport = -1; - conn->line.iov_base = conn->line_buf; - if (param.server_name) - { - conn->fqdname = param.server_name; - conn->fqdname_len = strlen (param.server_name); - } - else - { - conn->fqdname = conn->hostname; - conn->fqdname_len = conn->hostname_len; - } + int len; + + len = strlen(srvcurrent); + conn->hostname = srvcurrent; + conn->hostname_len = len; + + srvcurrent += len + 1; + if (srvcurrent >= srvend) + srvcurrent = srvbase; + + conn->port = param.port; + conn->sd = -1; + conn->myport = -1; + conn->line.iov_base = conn->line_buf; + conn->fqdname = conn->hostname; + conn->fqdname_len = conn->hostname_len; #ifdef HAVE_SSL - if (param.use_ssl) - { - conn->ssl = SSL_new (ssl_ctx); - if (!conn->ssl) - { - ERR_print_errors_fp (stderr); - exit (-1); - } + if (param.use_ssl) { + conn->ssl = SSL_new(ssl_ctx); + if (!conn->ssl) { + ERR_print_errors_fp(stderr); + exit(-1); + } - if (param.ssl_cipher_list) - { - /* set order of ciphers */ - int ssl_err = SSL_set_cipher_list (conn->ssl, param.ssl_cipher_list); + if (param.ssl_cipher_list) { + /* set order of ciphers */ + int ssl_err = SSL_set_cipher_list(conn->ssl, param.ssl_cipher_list); - if (DBG > 2) - fprintf (stderr, "core_ssl_connect: set_cipher_list returned %d\n", - ssl_err); + if (DBG > 2) + fprintf(stderr, + "core_ssl_connect: set_cipher_list returned %d\n", + ssl_err); + } } - } #endif } diff --git a/src/conn.h b/src/conn.h index 425895d..a5d18ae 100755 --- a/src/conn.h +++ b/src/conn.h @@ -66,6 +66,8 @@ typedef enum Conn_State } Conn_State; +struct local_addr; + typedef struct Conn { Object obj; @@ -92,6 +94,7 @@ typedef struct Conn int port; /* server's port (or -1 for default) */ int sd; /* socket descriptor */ int myport; /* local port number or -1 */ + struct local_addr *myaddr; /* Since replies are read off the socket sequentially, much of the reply-processing related state can be kept here instead of in the reply structure: */ @@ -99,6 +102,8 @@ typedef struct Conn size_t content_length; /* content length (or INF if unknown) */ u_int has_body : 1; /* does reply have a body? */ u_int is_chunked : 1; /* is the reply chunked? */ + u_int reading : 1; + u_int writing : 1; char line_buf[MAX_HDR_LINE_LEN]; /* default line buffer */ #ifdef HAVE_SSL @@ -110,6 +115,9 @@ Conn; extern int max_num_conn; extern Conn *conn; +/* Store the servers to connect to in memory. */ +extern void conn_add_servers (void); + /* Initialize the new connection object C. */ extern void conn_init (Conn *c); diff --git a/src/core.c b/src/core.c index fdc7f92..0993bb9 100755 --- a/src/core.c +++ b/src/core.c @@ -30,6 +30,10 @@ #include "config.h" +#ifdef __FreeBSD__ +#define HAVE_KEVENT +#endif + #include #include #include @@ -47,7 +51,21 @@ #ifdef HAVE_SYS_SELECT_H #include #endif +#ifdef HAVE_KEVENT +#include +/* + * Older systems using kevent() always specify the time in + * milliseconds and do not have a flag to select a different scale. + */ +#ifndef NOTE_MSECONDS +#define NOTE_MSECONDS 0 +#endif +#endif + +#ifdef __FreeBSD__ +#include +#endif #include #include #include @@ -69,16 +87,35 @@ #define MAX_IP_PORT 65535 #define BITSPERLONG (8*sizeof (u_long)) -static int running = 1; +struct local_addr { + struct in_addr ip; + u_long port_free_map[((MAX_IP_PORT - MIN_IP_PORT + BITSPERLONG) + / BITSPERLONG)]; + u_long mask; + int previous; +}; + +struct address_pool { + struct local_addr *addresses; + int count; + int last; +}; + +static volatile int running = 1; static int iteration; static u_long max_burst_len; +#ifdef HAVE_KEVENT +static int kq, max_sd = 0; +#else static fd_set rdfds, wrfds; static int min_sd = 0x7fffffff, max_sd = 0, alloced_sd_to_conn = 0; static struct timeval select_timeout; +#endif static struct sockaddr_in myaddr; +static struct address_pool myaddrs; +#ifndef HAVE_KEVENT Conn **sd_to_conn; -static u_long port_free_map[((MAX_IP_PORT - MIN_IP_PORT + BITSPERLONG) - / BITSPERLONG)]; +#endif static char http10req[] = " HTTP/1.0\r\nUser-Agent: httperf/" VERSION "\r\nConnection: keep-alive\r\nHost: "; @@ -113,13 +150,13 @@ static char http11req_nohost[] = enum Syscalls { SC_BIND, SC_CONNECT, SC_READ, SC_SELECT, SC_SOCKET, SC_WRITEV, - SC_SSL_READ, SC_SSL_WRITEV, + SC_SSL_READ, SC_SSL_WRITEV, SC_KEVENT, SC_NUM_SYSCALLS }; static const char *const syscall_name[SC_NUM_SYSCALLS] = { "bind", "connct", "read", "select", "socket", "writev", - "ssl_read", "ssl_writev" + "ssl_read", "ssl_writev", "kevent" }; static Time syscall_time[SC_NUM_SYSCALLS]; static u_int syscall_count[SC_NUM_SYSCALLS]; @@ -208,6 +245,9 @@ hash_lookup(const char *server, size_t server_len, int port) static int lffs(long w) { +#ifdef __FreeBSD__ + return ffsl(w); +#else int r; if (sizeof(w) == sizeof(int)) @@ -223,33 +263,32 @@ lffs(long w) #endif } return r; +#endif } static void -port_put(int port) +port_put(struct local_addr *addr, int port) { int i, bit; port -= MIN_IP_PORT; i = port / BITSPERLONG; bit = port % BITSPERLONG; - port_free_map[i] |= (1UL << bit); + addr->port_free_map[i] |= (1UL << bit); } static int -port_get(void) +port_get(struct local_addr *addr) { - static u_long mask = ~0UL; - static int previous = 0; int port, bit, i; - i = previous; - if ((port_free_map[i] & mask) == 0) { + i = addr->previous; + if ((addr->port_free_map[i] & addr->mask) == 0) { do { ++i; - if (i >= NELEMS(port_free_map)) + if (i >= NELEMS(addr->port_free_map)) i = 0; - if (i == previous) { + if (i == addr->previous) { if (DBG > 0) fprintf(stderr, "%s.port_get: Yikes! I'm out of port numbers!\n", @@ -257,17 +296,17 @@ port_get(void) return -1; } } - while (port_free_map[i] == 0); - mask = ~0UL; + while (addr->port_free_map[i] == 0); + addr->mask = ~0UL; } - previous = i; + addr->previous = i; - bit = lffs(port_free_map[i] & mask) - 1; + bit = lffs(addr->port_free_map[i] & addr->mask) - 1; if (bit >= BITSPERLONG - 1) - mask = 0; + addr->mask = 0; else - mask = ~((1UL << (bit + 1)) - 1); - port_free_map[i] &= ~(1UL << bit); + addr->mask = ~((1UL << (bit + 1)) - 1); + addr->port_free_map[i] &= ~(1UL << bit); port = bit + i * BITSPERLONG + MIN_IP_PORT; return port; } @@ -297,10 +336,10 @@ conn_timeout(struct Timer *t, Any_Type arg) c = 0; if (s->sd >= 0) { now = timer_now(); - if (FD_ISSET(s->sd, &rdfds) + if (s->reading && s->recvq && now >= s->recvq->timeout) c = s->recvq; - else if (FD_ISSET(s->sd, &wrfds) + else if (s->writing && s->sendq && now >= s->sendq->timeout) c = s->sendq; } @@ -318,18 +357,70 @@ conn_timeout(struct Timer *t, Any_Type arg) core_close(s); } +enum IO_DIR { READ, WRITE }; + static void -set_active(Conn * s, fd_set * fdset) +clear_active(Conn * s, enum IO_DIR dir) +{ + int sd = s->sd; +#ifdef HAVE_KEVENT + struct kevent ev; + + EV_SET(&ev, sd, dir == WRITE ? EVFILT_WRITE : EVFILT_READ, EV_DELETE, + 0, 0, s); + if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) { + fprintf(stderr, "failed to add %s filter\n", write ? + "write" : "read"); + exit(1); + } +#else + fd_set * fdset; + + if (dir == WRITE) + fdset = &wrfds; + else + fdset = &rdfds; + FD_CLR(sd, fdset); +#endif + if (dir == WRITE) + s->writing = 0; + else + s->reading = 0; +} + +static void +set_active(Conn * s, enum IO_DIR dir) { int sd = s->sd; Any_Type arg; Time timeout; +#ifdef HAVE_KEVENT + struct kevent ev; + EV_SET(&ev, sd, dir == WRITE ? EVFILT_WRITE : EVFILT_READ, EV_ADD, + 0, 0, s); + if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) { + fprintf(stderr, "failed to add %s filter\n", write ? + "write" : "read"); + exit(1); + } +#else + fd_set * fdset; + + if (dir == WRITE) + fdset = &wrfds; + else + fdset = &rdfds; FD_SET(sd, fdset); if (sd < min_sd) min_sd = sd; +#endif if (sd >= max_sd) max_sd = sd; + if (dir == WRITE) + s->writing = 1; + else + s->reading = 1; if (s->watchdog) return; @@ -434,7 +525,7 @@ do_send(Conn * conn) */ call->timeout = param.timeout ? timer_now() + param.timeout : 0.0; - set_active(conn, &wrfds); + set_active(conn, WRITE); return; } @@ -444,7 +535,7 @@ do_send(Conn * conn) conn->sendq = call->sendq_next; if (!conn->sendq) { conn->sendq_tail = 0; - FD_CLR(sd, &wrfds); + clear_active(conn, WRITE); } arg.l = 0; event_signal(EV_CALL_SEND_STOP, (Object *) call, arg); @@ -468,7 +559,7 @@ do_send(Conn * conn) call->timeout = param.timeout + param.think_timeout; if (call->timeout > 0.0) call->timeout += timer_now(); - set_active(conn, &rdfds); + set_active(conn, READ); if (conn->state < S_REPLY_STATUS) conn->state = S_REPLY_STATUS; /* expecting reply * status */ @@ -491,7 +582,7 @@ recv_done(Call * call) conn->recvq = call->recvq_next; if (!conn->recvq) { - FD_CLR(conn->sd, &rdfds); + clear_active(conn, READ); conn->recvq_tail = 0; } /* @@ -602,7 +693,7 @@ do_recv(Conn * s) while (buf_len > 0); if (s->recvq) - set_active(c->conn, &rdfds); + set_active(c->conn, READ); } struct sockaddr_in * @@ -645,22 +736,192 @@ core_addr_intern(const char *server, size_t server_len, int port) return &h->sin; } +static void +core_add_address(struct in_addr ip) +{ + struct local_addr *addr; + + myaddrs.addresses = realloc(myaddrs.addresses, + sizeof(struct local_addr) * (myaddrs.count + 1)); + if (myaddrs.addresses == NULL) { + fprintf(stderr, + "%s: out of memory parsing address list\n", + prog_name); + exit(1); + } + addr = &myaddrs.addresses[myaddrs.count]; + addr->ip = ip; + memset(&addr->port_free_map, 0xff, sizeof(addr->port_free_map)); + addr->mask = ~0UL; + addr->previous = 0; + myaddrs.count++; +} + +/* + * Parses the value provided to --myaddr. A value can either be a + * hostname or IP, or an IP range. Multiple values can be specified + * in which case all matches are added to a pool which new connections + * use in a round-robin fashion. An interface name may also be + * specified in which case all IP addresses assigned to that interface + * are used. + */ +void +core_add_addresses(const char *spec) +{ + struct hostent *he; + struct in_addr ip; +#ifdef __FreeBSD__ + struct ifaddrs *iflist, *ifa; +#endif + char *cp; + + /* First try to resolve the argument as a hostname. */ + he = gethostbyname(spec); + if (he) { + if (he->h_addrtype != AF_INET || + he->h_length != sizeof(struct in_addr)) { + fprintf(stderr, + "%s: can't deal with addr family %d or size %d\n", + prog_name, he->h_addrtype, he->h_length); + exit(1); + } + core_add_address(*(struct in_addr *)he->h_addr_list[0]); + return; + } + + /* If there seems to be an IP range, try that next. */ + cp = strchr(spec, '-'); + if (cp != NULL) { + char *start_s; + struct in_addr end_ip; + + start_s = strndup(spec, cp - spec); + if (!inet_aton(start_s, &ip)) { + fprintf(stderr, "%s: invalid starting address %s\n", + prog_name, start_s); + exit(1); + } + if (!inet_aton(cp + 1, &end_ip)) { + fprintf(stderr, "%s: invalid ending address %s\n", + prog_name, cp + 1); + exit(1); + } + + while (ip.s_addr != end_ip.s_addr) { + core_add_address(ip); + ip.s_addr += htonl(1); + } + core_add_address(end_ip); + return; + } + + /* Check for a single IP. */ + if (inet_aton(spec, &ip)) { + core_add_address(ip); + return; + } + +#ifdef __FreeBSD__ + /* Check for an interface name. */ + if (getifaddrs(&iflist) == 0) { + int found; + + found = 0; + for (ifa = iflist; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, spec) != 0) + continue; + if (found == 0) + found = 1; + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + found = 2; + core_add_address( + ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr); + } + freeifaddrs(iflist); + if (found == 2) + return; + if (found == 1) { + fprintf(stderr, + "%s: no valid addresses found on interface %s\n", + prog_name, spec); + exit(1); + } + } +#endif + + fprintf(stderr, "%s: invalid address list %s\n", + prog_name, spec); + exit(1); +} + +static struct local_addr * +core_get_next_myaddr(void) +{ + struct local_addr *addr; + + assert(myaddrs.last >= 0 && myaddrs.last < myaddrs.count); + addr = &myaddrs.addresses[myaddrs.last]; + myaddrs.last++; + if (myaddrs.last == myaddrs.count) + myaddrs.last = 0; + return (addr); +} + +static void +core_runtime_timer(struct Timer *t, Any_Type arg) +{ + + core_exit(); +} + void core_init(void) { struct rlimit rlimit; + Any_Type arg; memset(&hash_table, 0, sizeof(hash_table)); +#ifndef HAVE_KEVENT memset(&rdfds, 0, sizeof(rdfds)); memset(&wrfds, 0, sizeof(wrfds)); +#endif memset(&myaddr, 0, sizeof(myaddr)); - memset(&port_free_map, 0xff, sizeof(port_free_map)); +#ifdef __FreeBSD__ + myaddr.sin_len = sizeof(myaddr); +#endif + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (myaddrs.count == 0) + core_add_address(myaddr.sin_addr); /* * Don't disturb just because a TCP connection closed on us... */ signal(SIGPIPE, SIG_IGN); +#ifdef HAVE_KEVENT + kq = kqueue(); + if (kq < 0) { + fprintf(stderr, + "%s: failed to create kqueue: %s", prog_name, + strerror(errno)); + exit(1); + } + + /* + * TIMER_INTERVAL doesn't exist anymore, so just take a wild + * guess. + */ + struct kevent ev; + EV_SET(&ev, 0, EVFILT_TIMER, EV_ADD, NOTE_MSECONDS, 1, NULL); + if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) { + fprintf(stderr, + "%s: failed to add timer event: %s", prog_name, + strerror(errno)); + } +#else #ifdef DONT_POLL /* * This causes select() to take several milliseconds on both Linux/x86 @@ -677,6 +938,7 @@ core_init(void) */ select_timeout.tv_sec = 0; select_timeout.tv_usec = 0; +#endif #endif /* @@ -702,8 +964,11 @@ core_init(void) prog_name, rlimit.rlim_max); if (param.server) - core_addr_intern(param.server, strlen(param.server), - param.port); + conn_add_servers(); + if (param.runtime) { + arg.l = 0; + timer_schedule(core_runtime_timer, arg, param.runtime); + } } #ifdef HAVE_SSL @@ -735,13 +1000,13 @@ core_ssl_connect(Conn * s) SSL_ERROR_WANT_READ) ? "read" : "write"); if (reason == SSL_ERROR_WANT_READ - && !FD_ISSET(s->sd, &rdfds)) { - FD_CLR(s->sd, &wrfds); - set_active(s, &rdfds); + && !s->reading) { + clear_active(s, WRITE); + set_active(s, READ); } else if (reason == SSL_ERROR_WANT_WRITE - && !FD_ISSET(s->sd, &wrfds)) { - FD_CLR(s->sd, &rdfds); - set_active(s, &wrfds); + && !s->writing) { + clear_active(s, READ); + set_active(s, WRITE); } return; } @@ -758,7 +1023,7 @@ core_ssl_connect(Conn * s) fprintf(stderr, "core_ssl_connect: SSL is connected!\n"); if (DBG > 1) { - SSL_CIPHER *ssl_cipher; + const SSL_CIPHER *ssl_cipher; ssl_cipher = SSL_get_current_cipher(s->ssl); if (!ssl_cipher) @@ -852,6 +1117,7 @@ core_connect(Conn * s) } s->sd = sd; +#ifndef HAVE_KEVENT if (sd >= alloced_sd_to_conn) { size_t size, old_size; @@ -873,6 +1139,7 @@ core_connect(Conn * s) } assert(!sd_to_conn[sd]); sd_to_conn[sd] = s; +#endif sin = hash_lookup(s->hostname, s->hostname_len, s->port); if (!sin) { @@ -888,9 +1155,11 @@ core_connect(Conn * s) if (s->state >= S_CLOSING) goto failure; + s->myaddr = core_get_next_myaddr(); + myaddr.sin_addr = s->myaddr->ip; if (param.hog) { while (1) { - myport = port_get(); + myport = port_get(s->myaddr); if (myport < 0) goto failure; @@ -910,6 +1179,12 @@ core_connect(Conn * s) } } s->myport = myport; + } else if (myaddr.sin_addr.s_addr != htonl(INADDR_ANY)) { + SYSCALL(BIND, + result = bind(sd, (struct sockaddr *) &myaddr, + sizeof(myaddr))); + if (result != 0) + goto failure; } SYSCALL(CONNECT, @@ -932,7 +1207,7 @@ core_connect(Conn * s) * connection establishment. */ s->state = S_CONNECTING; - set_active(s, &wrfds); + set_active(s, WRITE); if (param.timeout > 0.0) { arg.vp = s; assert(!s->watchdog); @@ -949,6 +1224,8 @@ core_connect(Conn * s) fprintf(stderr, "%s.core_connect.connect: %s (max_sd=%d)\n", prog_name, strerror(errno), max_sd); + if (s->myport > 0) + port_put(s->myaddr, s->myport); goto failure; } return 0; @@ -1033,7 +1310,7 @@ core_send(Conn * conn, Call * call) return -1; call->timeout = param.timeout ? timer_now() + param.timeout : 0.0; - set_active(conn, &wrfds); + set_active(conn, WRITE); } else { conn->sendq_tail->sendq_next = call; conn->sendq_tail = call; @@ -1089,12 +1366,16 @@ core_close(Conn * conn) if (sd >= 0) { close(sd); +#ifndef HAVE_KEVENT sd_to_conn[sd] = 0; FD_CLR(sd, &wrfds); FD_CLR(sd, &rdfds); +#endif + conn->reading = 0; + conn->writing = 0; } if (conn->myport > 0) - port_put(conn->myport); + port_put(conn->myaddr, conn->myport); /* * A connection that has been closed is not useful anymore, so we give @@ -1104,6 +1385,63 @@ core_close(Conn * conn) conn_dec_ref(conn); } +#ifdef HAVE_KEVENT +void +core_loop(void) +{ + struct kevent ev; + int n; + Any_Type arg; + Conn *conn; + + while (running) { + ++iteration; + + n = kevent(kq, NULL, 0, &ev, 1, NULL); + if (n < 0 && errno != EINTR) { + fprintf(stderr, "failed to fetch event: %s", + strerror(errno)); + exit(1); + } + + switch (ev.filter) { + case EVFILT_TIMER: + timer_tick(); + break; + case EVFILT_READ: + case EVFILT_WRITE: + conn = ev.udata; + conn_inc_ref(conn); + + if (conn->watchdog) { + timer_cancel(conn->watchdog); + conn->watchdog = 0; + } + if (conn->state == S_CONNECTING) { +#ifdef HAVE_SSL + if (param.use_ssl) + core_ssl_connect(conn); + else +#endif + if (ev.filter == EVFILT_WRITE) { + clear_active(conn, WRITE); + conn->state = S_CONNECTED; + arg.l = 0; + event_signal(EV_CONN_CONNECTED, (Object*)conn, arg); + } + } else { + if (ev.filter == EVFILT_WRITE && conn->sendq) + do_send(conn); + if (ev.filter == EVFILT_READ && conn->recvq) + do_recv(conn); + } + + conn_dec_ref(conn); + break; + } + } +} +#else void core_loop(void) { @@ -1175,7 +1513,7 @@ core_loop(void) else #endif if (is_writable) { - FD_CLR(sd, &wrfds); + clear_active(conn, WRITE); conn->state = S_CONNECTED; arg.l = 0; event_signal(EV_CONN_CONNECTED, (Object*)conn, arg); @@ -1199,11 +1537,13 @@ core_loop(void) } } } +#endif void core_exit(void) { running = 0; + param.num_conns = 0; printf("Maximum connect burst length: %lu\n", max_burst_len); diff --git a/src/core.h b/src/core.h index 93696dc..b6d4dad 100755 --- a/src/core.h +++ b/src/core.h @@ -36,7 +36,8 @@ #include extern void core_init (void); -extern struct sockaddr_in *core_intern_addr (const char *hostname, +extern void core_add_addresses (const char *spec); +extern struct sockaddr_in *core_addr_intern (const char *hostname, size_t hostname_len, int port); extern int core_connect (Conn *conn); extern int core_send (Conn *conn, Call *call); diff --git a/src/gen/conn_rate.c b/src/gen/conn_rate.c index e6c1cb4..48ba6bd 100755 --- a/src/gen/conn_rate.c +++ b/src/gen/conn_rate.c @@ -52,29 +52,46 @@ static int num_conns_generated; static int num_conns_destroyed; +static int num_conns_open; static Rate_Generator rg; +static bool paused; static int make_conn (Any_Type arg) { Conn *s; + if (paused) + return 0; + if (num_conns_generated++ >= param.num_conns) return -1; + if (param.max_conns != 0 && num_conns_open >= param.max_conns) + return 0; + s = conn_new (); if (!s) return -1; - core_connect (s); + num_conns_open++; + if (core_connect (s) == -1) { + num_conns_generated--; + num_conns_destroyed--; + paused = true; + } return 0; } static void destroyed (void) { + Any_Type arg; + if (++num_conns_destroyed >= param.num_conns) core_exit (); + num_conns_open--; + paused = false; } static void diff --git a/src/gen/misc.c b/src/gen/misc.c index 44601b3..5e1544d 100755 --- a/src/gen/misc.c +++ b/src/gen/misc.c @@ -42,12 +42,17 @@ #include "config.h" +#include +#include +#include #include #include #include +#include #include #include #include +#include #include #include @@ -55,10 +60,10 @@ #include #include -static const char *extra; +static const char *extra, *extra_file; static size_t extra_len; -static size_t method_len; +static size_t method_len, file_len; /* A simple module that collects cookies from the server responses and includes them in future calls to the server. */ @@ -125,6 +130,9 @@ call_created (Event_Type et, Object *obj, Any_Type reg_arg, Any_Type arg) if (extra_len > 0) call_append_request_header (c, extra, extra_len); + + if (file_len > 0) + call_append_request_header (c, extra_file, file_len); } @@ -132,10 +140,31 @@ static void init (void) { Any_Type arg; + struct stat st; + int fd; if (param.additional_header) extra = unescape (param.additional_header, &extra_len); + if (param.additional_header_file) { + fd = open (param.additional_header_file, O_RDONLY); + if (fd < 0) + fprintf (stderr, "%s: failed to open header file\n", prog_name); + else { + if (fstat(fd, &st) < 0) + fprintf (stderr, "%s: failed to stat header file\n", prog_name); + else { + extra_file = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (extra_file == (char *)MAP_FAILED) { + fprintf (stderr, "%s: failed to map header file\n", prog_name); + extra_file = NULL; + } else + file_len = st.st_size; + } + close(fd); + } + } + if (param.method) method_len = strlen (param.method); diff --git a/src/httperf.c b/src/httperf.c index 32ed384..18fded1 100755 --- a/src/httperf.c +++ b/src/httperf.c @@ -91,6 +91,7 @@ const char *prog_name; int verbose; +int periodic_stats; Cmdline_Params param; Time test_time_start; Time test_time_stop; @@ -110,7 +111,8 @@ static Time perf_sample_start; static struct option longopts[] = { {"add-header", required_argument, (int *) ¶m.additional_header, 0}, - {"burst-length", required_argument, ¶m.burst_len, 0}, + {"add-header-file", required_argument, (int *) ¶m.additional_header_file, 0 }, + {"burst-length", required_argument, (int *) ¶m.burst_len, 0}, {"client", required_argument, (int *) ¶m.client, 0}, {"close-with-reset", no_argument, ¶m.close_with_reset, 1}, {"debug", required_argument, 0, 'd'}, @@ -118,9 +120,10 @@ static struct option longopts[] = { {"help", no_argument, 0, 'h'}, {"hog", no_argument, ¶m.hog, 1}, {"http-version", required_argument, (int *) ¶m.http_version, 0}, - {"max-connections", required_argument, ¶m.max_conns, 0}, - {"max-piped-calls", required_argument, ¶m.max_piped, 0}, + {"max-connections", required_argument, (int *) ¶m.max_conns, 0}, + {"max-piped-calls", required_argument, (int *) ¶m.max_piped, 0}, {"method", required_argument, (int *) ¶m.method, 0}, + {"myaddr", required_argument, (int *) ¶m.myaddr, 0}, {"no-host-hdr", no_argument, ¶m.no_host_hdr, 1}, {"num-calls", required_argument, (int *) ¶m.num_calls, 0}, {"num-conns", required_argument, (int *) ¶m.num_conns, 0}, @@ -131,9 +134,9 @@ static struct option longopts[] = { {"rate", required_argument, (int *) ¶m.rate, 0}, {"recv-buffer", required_argument, (int *) ¶m.recv_buffer_size, 0}, {"retry-on-failure", no_argument, ¶m.retry_on_failure, 1}, + {"runtime", required_argument, (int *) ¶m.runtime, 0}, {"send-buffer", required_argument, (int *) ¶m.send_buffer_size, 0}, {"server", required_argument, (int *) ¶m.server, 0}, - {"server-name", required_argument, (int *) ¶m.server_name, 0}, {"uri", required_argument, (int *) ¶m.uri, 0}, {"session-cookies", no_argument, (int *) ¶m.session_cookies, 1}, #ifdef HAVE_SSL @@ -146,6 +149,7 @@ static struct option longopts[] = { {"use-timer-cache", no_argument, ¶m.use_timer_cache, 1}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, + {"periodic-stats", no_argument, 0, 'n'}, {"wlog", required_argument, (int *) ¶m.wlog, 0}, {"wsess", required_argument, (int *) ¶m.wsess, 0}, {"wsesslog", required_argument, (int *) ¶m.wsesslog, 0}, @@ -167,14 +171,16 @@ usage(void) "\t[--print-reply [header|body]] [--print-request [header|body]]\n" "\t[--rate X] [--recv-buffer N] [--retry-on-failure] " "[--send-buffer N]\n" - "\t[--server S] [--server-name S] [--port N] [--uri S] \n" + "\t<--server file> [--port N] [--uri S] [--myaddr S]\n" #ifdef HAVE_SSL "\t[--ssl] [--ssl-ciphers L] [--ssl-no-reuse]\n" #endif "\t[--think-timeout X] [--timeout X] [--verbose] [--version]\n" "\t[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]\n" "\t[--wset N,X]\n" - "\t[--use-timer-cache]\n", prog_name); + "\t[--runtime X]\n" + "\t[--use-timer-cache]\n" + "\t[--periodic-stats]\n", prog_name); } void @@ -254,7 +260,6 @@ main(int argc, char **argv) param.http_version = 0x10001; /* default to HTTP/1.1 */ param.client.id = 0; param.client.num_clients = 1; - param.server = "localhost"; param.port = -1; param.uri = "/"; param.num_calls = 1; @@ -284,7 +289,7 @@ main(int argc, char **argv) * process command line options: */ while ((ch = - getopt_long(argc, argv, "d:hvV", longopts, &longindex)) >= 0) { + getopt_long(argc, argv, "d:hvVn", longopts, &longindex)) >= 0) { switch (ch) { case 0: flag = longopts[longindex].flag; @@ -293,6 +298,8 @@ main(int argc, char **argv) param.method = optarg; else if (flag == ¶m.additional_header) param.additional_header = optarg; + else if (flag == ¶m.additional_header_file) + param.additional_header_file = optarg; else if (flag == ¶m.num_calls) { errno = 0; param.num_calls = strtoul(optarg, &end, 10); @@ -312,6 +319,8 @@ main(int argc, char **argv) } param.http_version = (major << 16) | (minor & 0xffff); + } else if (flag == ¶m.myaddr) { + core_add_addresses(optarg); } else if (flag == ¶m.burst_len) { errno = 0; param.burst_len = strtoul(optarg, &end, 10); @@ -345,8 +354,7 @@ main(int argc, char **argv) } else if (flag == ¶m.max_conns) { errno = 0; param.max_conns = strtoul(optarg, &end, 10); - if (errno == ERANGE || end == optarg || *end - || param.max_conns < 0) { + if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal max. # of connection %s\n", prog_name, optarg); @@ -355,8 +363,7 @@ main(int argc, char **argv) } else if (flag == ¶m.max_piped) { errno = 0; param.max_piped = strtoul(optarg, &end, 10); - if (errno == ERANGE || end == optarg || *end - || param.max_piped < 0) { + if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal max. # of piped calls %s\n", prog_name, optarg); @@ -619,8 +626,6 @@ main(int argc, char **argv) } } else if (flag == ¶m.server) param.server = optarg; - else if (flag == ¶m.server_name) - param.server_name = optarg; #ifdef HAVE_SSL else if (flag == ¶m.ssl_cipher_list) param.ssl_cipher_list = optarg; @@ -645,6 +650,16 @@ main(int argc, char **argv) prog_name, optarg); exit(1); } + } else if (flag == ¶m.runtime) { + errno = 0; + param.runtime = strtod(optarg, &end); + if (errno == ERANGE || end == optarg || *end || + param.runtime <= 0.0) { + fprintf(stderr, + "%s: illegal runtime value %s\n", + prog_name, optarg); + exit(1); + } } else if (flag == ¶m.wlog) { gen[1] = &uri_wlog; /* XXX fix * me---somehow */ @@ -880,6 +895,10 @@ main(int argc, char **argv) " TIME_SYSCALLS.\n", prog_name); exit(0); + case 'n': + ++periodic_stats; + break; + case 'h': usage(); exit(0); @@ -907,6 +926,13 @@ main(int argc, char **argv) } } + if (param.server == NULL) { + fprintf(stderr, + "%s: must specify --server\n", + prog_name); + exit(-1); + } + #ifdef HAVE_SSL if (param.use_ssl) { char buf[1024]; @@ -947,7 +973,8 @@ main(int argc, char **argv) gen[num_gen++] = &sess_cookie; } - if (param.additional_header || param.method) + if (param.additional_header || param.additional_header_file || + param.method) gen[num_gen++] = &misc; /* @@ -990,11 +1017,11 @@ main(int argc, char **argv) printf(" --think-timeout=%g", param.think_timeout); if (param.timeout > 0) printf(" --timeout=%g", param.timeout); + if (param.runtime > 0) + printf(" --runtime=%g", param.runtime); printf(" --client=%u/%u", param.client.id, param.client.num_clients); if (param.server) printf(" --server=%s", param.server); - if (param.server_name) - printf(" --server_name=%s", param.server_name); if (param.port) printf(" --port=%d", param.port); if (param.uri) @@ -1005,9 +1032,9 @@ main(int argc, char **argv) printf(" --http-version=%u.%u", param.http_version >> 16, param.http_version & 0xffff); if (param.max_conns) - printf(" --max-connections=%u", param.max_conns); + printf(" --max-connections=%lu", param.max_conns); if (param.max_piped) - printf(" --max-piped-calls=%u", param.max_piped); + printf(" --max-piped-calls=%lu", param.max_piped); if (param.rate.rate_param > 0.0) { switch (param.rate.dist) { case DETERMINISTIC: @@ -1045,10 +1072,10 @@ main(int argc, char **argv) break; } } - printf(" --send-buffer=%d", param.send_buffer_size); + printf(" --send-buffer=%lu", param.send_buffer_size); if (param.retry_on_failure) printf(" --retry-on-failure"); - printf(" --recv-buffer=%d", param.recv_buffer_size); + printf(" --recv-buffer=%lu", param.recv_buffer_size); if (param.session_cookies) printf(" --session-cookies"); #ifdef HAVE_SSL @@ -1061,6 +1088,8 @@ main(int argc, char **argv) #endif if (param.additional_header) printf(" --add-header='%s'", param.additional_header); + if (param.additional_header_file) + printf(" --add-header-file='%s'", param.additional_header_file); if (param.method) printf(" --method=%s", param.method); if (param.use_timer_cache) @@ -1081,17 +1110,19 @@ main(int argc, char **argv) param.wsess.num_calls, param.wsess.think_time); else { if (param.num_conns) - printf(" --num-conns=%d", param.num_conns); + printf(" --num-conns=%lu", param.num_conns); if (param.num_calls) - printf(" --num-calls=%d", param.num_calls); + printf(" --num-calls=%lu", param.num_calls); } if (param.burst_len != 1) - printf(" --burst-length=%d", param.burst_len); + printf(" --burst-length=%lu", param.burst_len); if (param.wset.num_files) printf(" --wset=%u,%.3f", param.wset.num_files, param.wset.target_miss_rate); } + if (periodic_stats) + printf(" --periodic-stats"); printf("\n"); if (timer_init() == false) { diff --git a/src/httperf.h b/src/httperf.h index e944bde..7e73a3d 100755 --- a/src/httperf.h +++ b/src/httperf.h @@ -91,21 +91,22 @@ Rate_Info; typedef struct Cmdline_Params { int http_version; /* (default) HTTP protocol version */ - const char *server; /* (default) hostname */ - const char *server_name; /* fully qualified server name */ + const char *server; int port; /* (default) server port */ const char *uri; /* (default) uri */ + const char *myaddr; Rate_Info rate; Time timeout; /* watchdog timeout */ Time think_timeout; /* timeout for server think time */ - int num_conns; /* # of connections to generate */ - int num_calls; /* # of calls to generate per connection */ - int burst_len; /* # of calls to burst back-to-back */ - int max_piped; /* max # of piped calls per connection */ - int max_conns; /* max # of connections per session */ + Time runtime; /* how long to run the test */ + u_long num_conns; /* # of connections to generate */ + u_long num_calls; /* # of calls to generate per connection */ + u_long burst_len; /* # of calls to burst back-to-back */ + u_long max_piped; /* max # of piped calls per connection */ + u_long max_conns; /* max # of connections per session */ int hog; /* client may hog as much resources as possible */ - int send_buffer_size; - int recv_buffer_size; + u_long send_buffer_size; + u_long recv_buffer_size; int failure_status; /* status code that should be considered failure */ int retry_on_failure; /* when a call fails, should we retry? */ int close_with_reset; /* close connections with TCP RESET? */ @@ -120,6 +121,7 @@ typedef struct Cmdline_Params #endif int use_timer_cache; const char *additional_header; /* additional request header(s) */ + const char *additional_header_file; const char *method; /* default call method */ struct { @@ -146,7 +148,7 @@ typedef struct Cmdline_Params u_int num_reqs; /* # of user requests per session */ Time think_time; /* user think time between requests */ } - wsesspage; + wsesspage; /* XXX Currently broken */ struct { u_int num_sessions; /* # of user-sessions */ @@ -165,6 +167,7 @@ Cmdline_Params; extern const char *prog_name; extern int verbose; +extern int periodic_stats; extern Cmdline_Params param; extern Time test_time_start; extern Time test_time_stop; diff --git a/src/stat/basic.c b/src/stat/basic.c index d24154a..286d389 100755 --- a/src/stat/basic.c +++ b/src/stat/basic.c @@ -59,44 +59,46 @@ #define NUM_BINS ((u_int) (MAX_LIFETIME / BIN_WIDTH)) static struct { - u_int num_conns_issued; /* total # of connections * issued */ - u_int num_replies[6]; /* completion count per status class */ - u_int num_client_timeouts; /* # of client timeouts */ - u_int num_sock_fdunavail; /* # of times out of * + u_long num_conns_issued; /* total # of connections * issued */ + u_long num_replies[6]; /* completion count per status class */ + u_long num_200; /* total # of 200 responses */ + u_long num_302; /* total # of 302 responses */ + u_long num_client_timeouts; /* # of client timeouts */ + u_long num_sock_fdunavail; /* # of times out of * * filedescriptors */ - u_int num_sock_ftabfull; /* # of times file table was full */ - u_int num_sock_refused; /* # of ECONNREFUSED */ - u_int num_sock_reset; /* # of ECONNRESET */ - u_int num_sock_timeouts; /* # of ETIMEDOUT */ - u_int num_sock_addrunavail; /* # of EADDRNOTAVAIL */ - u_int num_other_errors; /* # of other errors */ - u_int max_conns; /* max # of concurrent connections */ + u_long num_sock_ftabfull; /* # of times file table was full */ + u_long num_sock_refused; /* # of ECONNREFUSED */ + u_long num_sock_reset; /* # of ECONNRESET */ + u_long num_sock_timeouts; /* # of ETIMEDOUT */ + u_long num_sock_addrunavail; /* # of EADDRNOTAVAIL */ + u_long num_other_errors; /* # of other errors */ + u_long max_conns; /* max # of concurrent connections */ - u_int num_lifetimes; + u_long num_lifetimes; Time conn_lifetime_sum; /* sum of connection lifetimes */ Time conn_lifetime_sum2; /* sum of connection lifetimes * squared */ Time conn_lifetime_min; /* minimum connection lifetime */ Time conn_lifetime_max; /* maximum connection lifetime */ - u_int num_reply_rates; + u_long num_reply_rates; Time reply_rate_sum; Time reply_rate_sum2; Time reply_rate_min; Time reply_rate_max; - u_int num_connects; /* # of completed connect()s */ + u_long num_connects; /* # of completed connect()s */ Time conn_connect_sum; /* sum of connect times */ - u_int num_responses; + u_long num_responses; Time call_response_sum; /* sum of response times */ Time call_xfer_sum; /* sum of response times */ - u_int num_sent; /* # of requests sent */ + u_long num_sent; /* # of requests sent */ size_t req_bytes_sent; - u_int num_received; /* # of replies received */ + u_long num_received; /* # of replies received */ u_wide hdr_bytes_received; /* sum of all header bytes */ u_wide reply_bytes_received; /* sum of all data bytes */ u_wide footer_bytes_received; /* sum of all footer bytes */ @@ -105,8 +107,8 @@ static struct { * connection lifetimes */ } basic; -static u_int num_active_conns; -static u_int num_replies; /* # of replies received in this interval */ +static u_long num_active_conns; +static u_long num_replies; /* # of replies received in this interval */ static void perf_sample(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg) @@ -278,6 +280,14 @@ recv_start(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg) basic.call_response_sum += now - c->basic.time_send_start; c->basic.time_recv_start = now; ++basic.num_responses; + + if (periodic_stats) { + if (c->reply.status == 200) + ++basic.num_200; + + if (c->reply.status == 302) + ++basic.num_302; + } } static void @@ -303,6 +313,23 @@ recv_stop(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg) ++c->conn->basic.num_calls_completed; } +static void +one_second_timer(struct Timer *t, Any_Type arg) +{ + static u_long prev200 = 0; + static u_long prev302 = 0; + + printf("[%.6f s] 200=%lu, 302=%lu\n", + timer_now() - test_time_start, + basic.num_200 - prev200, + basic.num_302 - prev302); + + prev200 = basic.num_200; + prev302 = basic.num_302; + + timer_schedule(one_second_timer, arg, 1); +} + static void init(void) { @@ -323,6 +350,9 @@ init(void) event_register_handler(EV_CALL_SEND_STOP, send_stop, arg); event_register_handler(EV_CALL_RECV_START, recv_start, arg); event_register_handler(EV_CALL_RECV_STOP, recv_stop, arg); + + if (periodic_stats) + timer_schedule(one_second_timer, arg, 1); } static void @@ -335,7 +365,8 @@ dump(void) Time lifetime_avg = 0.0, lifetime_stddev = 0.0, lifetime_median = 0.0; double reply_rate_avg = 0.0, reply_rate_stddev = 0.0; - int i, total_replies = 0; + int i; + u_long total_replies = 0; Time delta, user, sys; u_wide total_size; Time time; @@ -358,7 +389,7 @@ dump(void) } } - printf("\nTotal: connections %u requests %u replies %u " + printf("\nTotal: connections %lu requests %lu replies %lu " "test-duration %.3f s\n", basic.num_conns_issued, basic.num_sent, total_replies, delta); @@ -367,7 +398,7 @@ dump(void) if (basic.num_conns_issued) conn_period = delta / basic.num_conns_issued; printf("Connection rate: %.1f conn/s (%.1f ms/conn, " - "<=%u concurrent connections)\n", + "<=%lu concurrent connections)\n", basic.num_conns_issued / delta, 1e3 * conn_period, basic.max_conns); @@ -420,7 +451,7 @@ dump(void) } printf ("Reply rate [replies/s]: min %.1f avg %.1f max %.1f stddev %.1f " - "(%u samples)\n", + "(%lu samples)\n", basic.num_reply_rates > 0 ? basic.reply_rate_min : 0.0, reply_rate_avg, basic.reply_rate_max, reply_rate_stddev, basic.num_reply_rates); @@ -441,12 +472,17 @@ dump(void) "(total %.1f)\n", hdr_size, reply_size, footer_size, hdr_size + reply_size + footer_size); - printf("Reply status: 1xx=%u 2xx=%u 3xx=%u 4xx=%u 5xx=%u\n", + printf("Reply status: 1xx=%lu 2xx=%lu 3xx=%lu 4xx=%lu 5xx=%lu\n", basic.num_replies[1], basic.num_replies[2], basic.num_replies[3], basic.num_replies[4], basic.num_replies[5]); putchar('\n'); + if (periodic_stats) { + printf("Periodic stats: 200=%lu 302=%lu\n", basic.num_200, basic.num_302); + putchar('\n'); + } + user = (TV_TO_SEC(test_rusage_stop.ru_utime) - TV_TO_SEC(test_rusage_start.ru_utime)); sys = (TV_TO_SEC(test_rusage_stop.ru_stime) @@ -463,9 +499,9 @@ dump(void) putchar('\n'); - printf("Errors: total %u client-timo %u socket-timo %u " - "connrefused %u connreset %u\n" - "Errors: fd-unavail %u addrunavail %u ftab-full %u other %u\n", + printf("Errors: total %lu client-timo %lu socket-timo %lu " + "connrefused %lu connreset %lu\n" + "Errors: fd-unavail %lu addrunavail %lu ftab-full %lu other %lu\n", (basic.num_client_timeouts + basic.num_sock_timeouts + basic.num_sock_fdunavail + basic.num_sock_ftabfull + basic.num_sock_refused + basic.num_sock_reset diff --git a/src/timer.c b/src/timer.c index 2ba3386..77a2367 100755 --- a/src/timer.c +++ b/src/timer.c @@ -240,7 +240,7 @@ timer_schedule(void (*timeout) (struct Timer * t, Any_Type arg), t->time_started = timer_now(); t->timeout_delay = delay; - if (delay > 0) + if (delay > 0 || true) { Any_Type temp; temp.vp = (void *)t;