Add very numerous bug fixes and features, from Norse Corp.

* 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.
This commit is contained in:
Adrian Chadd 2015-03-30 14:07:53 -07:00
vanhempi 5875eafd22
commit 544b67b374
10 muutettua tiedostoa jossa 642 lisäystä ja 142 poistoa

Näytä tiedosto

@ -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
}

Näytä tiedosto

@ -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);

Näytä tiedosto

@ -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);

Näytä tiedosto

@ -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);

Näytä tiedosto

@ -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

Näytä tiedosto

@ -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);

Näytä tiedosto

@ -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 *) &param.additional_header, 0},
{"burst-length", required_argument, &param.burst_len, 0},
{"add-header-file", required_argument, (int *) &param.additional_header_file, 0 },
{"burst-length", required_argument, (int *) &param.burst_len, 0},
{"client", required_argument, (int *) &param.client, 0},
{"close-with-reset", no_argument, &param.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, &param.hog, 1},
{"http-version", required_argument, (int *) &param.http_version, 0},
{"max-connections", required_argument, &param.max_conns, 0},
{"max-piped-calls", required_argument, &param.max_piped, 0},
{"max-connections", required_argument, (int *) &param.max_conns, 0},
{"max-piped-calls", required_argument, (int *) &param.max_piped, 0},
{"method", required_argument, (int *) &param.method, 0},
{"myaddr", required_argument, (int *) &param.myaddr, 0},
{"no-host-hdr", no_argument, &param.no_host_hdr, 1},
{"num-calls", required_argument, (int *) &param.num_calls, 0},
{"num-conns", required_argument, (int *) &param.num_conns, 0},
@ -131,9 +134,9 @@ static struct option longopts[] = {
{"rate", required_argument, (int *) &param.rate, 0},
{"recv-buffer", required_argument, (int *) &param.recv_buffer_size, 0},
{"retry-on-failure", no_argument, &param.retry_on_failure, 1},
{"runtime", required_argument, (int *) &param.runtime, 0},
{"send-buffer", required_argument, (int *) &param.send_buffer_size, 0},
{"server", required_argument, (int *) &param.server, 0},
{"server-name", required_argument, (int *) &param.server_name, 0},
{"uri", required_argument, (int *) &param.uri, 0},
{"session-cookies", no_argument, (int *) &param.session_cookies, 1},
#ifdef HAVE_SSL
@ -146,6 +149,7 @@ static struct option longopts[] = {
{"use-timer-cache", no_argument, &param.use_timer_cache, 1},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"periodic-stats", no_argument, 0, 'n'},
{"wlog", required_argument, (int *) &param.wlog, 0},
{"wsess", required_argument, (int *) &param.wsess, 0},
{"wsesslog", required_argument, (int *) &param.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 == &param.additional_header)
param.additional_header = optarg;
else if (flag == &param.additional_header_file)
param.additional_header_file = optarg;
else if (flag == &param.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 == &param.myaddr) {
core_add_addresses(optarg);
} else if (flag == &param.burst_len) {
errno = 0;
param.burst_len = strtoul(optarg, &end, 10);
@ -345,8 +354,7 @@ main(int argc, char **argv)
} else if (flag == &param.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 == &param.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 == &param.server)
param.server = optarg;
else if (flag == &param.server_name)
param.server_name = optarg;
#ifdef HAVE_SSL
else if (flag == &param.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 == &param.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 == &param.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) {

Näytä tiedosto

@ -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;

Näytä tiedosto

@ -59,44 +59,46 @@
#define NUM_BINS ((u_int) (MAX_LIFETIME / BIN_WIDTH))
static struct {
u_int num_conns_issued; /* total # of connections * issued */
u_int num_replies[6]; /* completion count per status class */
u_int num_client_timeouts; /* # of client timeouts */
u_int num_sock_fdunavail; /* # of times out of *
u_long num_conns_issued; /* total # of connections * issued */
u_long num_replies[6]; /* completion count per status class */
u_long num_200; /* total # of 200 responses */
u_long num_302; /* total # of 302 responses */
u_long num_client_timeouts; /* # of client timeouts */
u_long num_sock_fdunavail; /* # of times out of *
* filedescriptors */
u_int num_sock_ftabfull; /* # of times file table was full */
u_int num_sock_refused; /* # of ECONNREFUSED */
u_int num_sock_reset; /* # of ECONNRESET */
u_int num_sock_timeouts; /* # of ETIMEDOUT */
u_int num_sock_addrunavail; /* # of EADDRNOTAVAIL */
u_int num_other_errors; /* # of other errors */
u_int max_conns; /* max # of concurrent connections */
u_long num_sock_ftabfull; /* # of times file table was full */
u_long num_sock_refused; /* # of ECONNREFUSED */
u_long num_sock_reset; /* # of ECONNRESET */
u_long num_sock_timeouts; /* # of ETIMEDOUT */
u_long num_sock_addrunavail; /* # of EADDRNOTAVAIL */
u_long num_other_errors; /* # of other errors */
u_long max_conns; /* max # of concurrent connections */
u_int num_lifetimes;
u_long num_lifetimes;
Time conn_lifetime_sum; /* sum of connection lifetimes */
Time conn_lifetime_sum2; /* sum of connection lifetimes
* squared */
Time conn_lifetime_min; /* minimum connection lifetime */
Time conn_lifetime_max; /* maximum connection lifetime */
u_int num_reply_rates;
u_long num_reply_rates;
Time reply_rate_sum;
Time reply_rate_sum2;
Time reply_rate_min;
Time reply_rate_max;
u_int num_connects; /* # of completed connect()s */
u_long num_connects; /* # of completed connect()s */
Time conn_connect_sum; /* sum of connect times */
u_int num_responses;
u_long num_responses;
Time call_response_sum; /* sum of response times */
Time call_xfer_sum; /* sum of response times */
u_int num_sent; /* # of requests sent */
u_long num_sent; /* # of requests sent */
size_t req_bytes_sent;
u_int num_received; /* # of replies received */
u_long num_received; /* # of replies received */
u_wide hdr_bytes_received; /* sum of all header bytes */
u_wide reply_bytes_received; /* sum of all data bytes */
u_wide footer_bytes_received; /* sum of all footer bytes */
@ -105,8 +107,8 @@ static struct {
* connection lifetimes */
} basic;
static u_int num_active_conns;
static u_int num_replies; /* # of replies received in this interval */
static u_long num_active_conns;
static u_long num_replies; /* # of replies received in this interval */
static void
perf_sample(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
@ -278,6 +280,14 @@ recv_start(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
basic.call_response_sum += now - c->basic.time_send_start;
c->basic.time_recv_start = now;
++basic.num_responses;
if (periodic_stats) {
if (c->reply.status == 200)
++basic.num_200;
if (c->reply.status == 302)
++basic.num_302;
}
}
static void
@ -303,6 +313,23 @@ recv_stop(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
++c->conn->basic.num_calls_completed;
}
static void
one_second_timer(struct Timer *t, Any_Type arg)
{
static u_long prev200 = 0;
static u_long prev302 = 0;
printf("[%.6f s] 200=%lu, 302=%lu\n",
timer_now() - test_time_start,
basic.num_200 - prev200,
basic.num_302 - prev302);
prev200 = basic.num_200;
prev302 = basic.num_302;
timer_schedule(one_second_timer, arg, 1);
}
static void
init(void)
{
@ -323,6 +350,9 @@ init(void)
event_register_handler(EV_CALL_SEND_STOP, send_stop, arg);
event_register_handler(EV_CALL_RECV_START, recv_start, arg);
event_register_handler(EV_CALL_RECV_STOP, recv_stop, arg);
if (periodic_stats)
timer_schedule(one_second_timer, arg, 1);
}
static void
@ -335,7 +365,8 @@ dump(void)
Time lifetime_avg = 0.0, lifetime_stddev =
0.0, lifetime_median = 0.0;
double reply_rate_avg = 0.0, reply_rate_stddev = 0.0;
int i, total_replies = 0;
int i;
u_long total_replies = 0;
Time delta, user, sys;
u_wide total_size;
Time time;
@ -358,7 +389,7 @@ dump(void)
}
}
printf("\nTotal: connections %u requests %u replies %u "
printf("\nTotal: connections %lu requests %lu replies %lu "
"test-duration %.3f s\n",
basic.num_conns_issued, basic.num_sent, total_replies, delta);
@ -367,7 +398,7 @@ dump(void)
if (basic.num_conns_issued)
conn_period = delta / basic.num_conns_issued;
printf("Connection rate: %.1f conn/s (%.1f ms/conn, "
"<=%u concurrent connections)\n",
"<=%lu concurrent connections)\n",
basic.num_conns_issued / delta, 1e3 * conn_period,
basic.max_conns);
@ -420,7 +451,7 @@ dump(void)
}
printf
("Reply rate [replies/s]: min %.1f avg %.1f max %.1f stddev %.1f "
"(%u samples)\n",
"(%lu samples)\n",
basic.num_reply_rates > 0 ? basic.reply_rate_min : 0.0,
reply_rate_avg, basic.reply_rate_max, reply_rate_stddev,
basic.num_reply_rates);
@ -441,12 +472,17 @@ dump(void)
"(total %.1f)\n", hdr_size, reply_size, footer_size,
hdr_size + reply_size + footer_size);
printf("Reply status: 1xx=%u 2xx=%u 3xx=%u 4xx=%u 5xx=%u\n",
printf("Reply status: 1xx=%lu 2xx=%lu 3xx=%lu 4xx=%lu 5xx=%lu\n",
basic.num_replies[1], basic.num_replies[2],
basic.num_replies[3], basic.num_replies[4], basic.num_replies[5]);
putchar('\n');
if (periodic_stats) {
printf("Periodic stats: 200=%lu 302=%lu\n", basic.num_200, basic.num_302);
putchar('\n');
}
user = (TV_TO_SEC(test_rusage_stop.ru_utime)
- TV_TO_SEC(test_rusage_start.ru_utime));
sys = (TV_TO_SEC(test_rusage_stop.ru_stime)
@ -463,9 +499,9 @@ dump(void)
putchar('\n');
printf("Errors: total %u client-timo %u socket-timo %u "
"connrefused %u connreset %u\n"
"Errors: fd-unavail %u addrunavail %u ftab-full %u other %u\n",
printf("Errors: total %lu client-timo %lu socket-timo %lu "
"connrefused %lu connreset %lu\n"
"Errors: fd-unavail %lu addrunavail %lu ftab-full %lu other %lu\n",
(basic.num_client_timeouts + basic.num_sock_timeouts
+ basic.num_sock_fdunavail + basic.num_sock_ftabfull
+ basic.num_sock_refused + basic.num_sock_reset

Näytä tiedosto

@ -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;