* Fix some corner cases with greedy mode, where ports would be tracked inside httperf rather than the OS. There were handfuls of corner cases where it'd either loop over an already-allocated port by the OS and chew 100% of cpu with no progress. Also, hit an already allocated port by the OS, skip/stop, but then the next lookup would hit that port again - it would never find a new port to open. * Add kqueue support for FreeBSD. (No, not using libevent. Yet.) * Add a periodic stats option, to print out 200/302 counts once every second. This is to aid in automatic scripts looking to graph the progress being made. * Add the ability to define multiple local client addresses, so requests can come from a much bigger pool of IP addresses. This allows for much more concurrency per process.) * Add an explicit timeout on how long httperf can run - again, to aid in automated testing. * Add an option to allow header contents to be loaded in from a file. * Other fixes, mostly type related fixes, thanks to clang/llvm on FreeBSD-10. We're using this in production at Norse to stress test out things, upwards of 25,000 requests per second per httperf process.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; | |||