Add very numerous bug fixes and features, from Norse Corp.ahc_fix_select
@@ -34,57 +34,92 @@ | |||
#include "config.h" | |||
#include <assert.h> | |||
#include <fcntl.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <sys/mman.h> | |||
#include <sys/stat.h> | |||
#include <generic_types.h> | |||
#include <object.h> | |||
#include <timer.h> | |||
#include <httperf.h> | |||
#include <call.h> | |||
#include <conn.h> | |||
#include <core.h> | |||
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 | |||
} | |||
@@ -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); | |||
@@ -30,6 +30,10 @@ | |||
#include "config.h" | |||
#ifdef __FreeBSD__ | |||
#define HAVE_KEVENT | |||
#endif | |||
#include <assert.h> | |||
#include <ctype.h> | |||
#include <errno.h> | |||
@@ -47,7 +51,21 @@ | |||
#ifdef HAVE_SYS_SELECT_H | |||
#include <sys/select.h> | |||
#endif | |||
#ifdef HAVE_KEVENT | |||
#include <sys/event.h> | |||
/* | |||
* 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 <ifaddrs.h> | |||
#endif | |||
#include <netinet/in.h> | |||
#include <netinet/tcp.h> | |||
#include <arpa/inet.h> | |||
@@ -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); | |||
@@ -36,7 +36,8 @@ | |||
#include <netinet/in.h> | |||
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); | |||
@@ -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 | |||
@@ -42,12 +42,17 @@ | |||
#include "config.h" | |||
#include <sys/types.h> | |||
#include <sys/mman.h> | |||
#include <sys/stat.h> | |||
#include <assert.h> | |||
#include <ctype.h> | |||
#include <errno.h> | |||
#include <fcntl.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <generic_types.h> | |||
#include <object.h> | |||
@@ -55,10 +60,10 @@ | |||
#include <call.h> | |||
#include <localevent.h> | |||
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); | |||
@@ -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) { | |||
@@ -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; | |||
@@ -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_int num_lifetimes; | |||
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_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 | |||
@@ -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; | |||