Merge pull request #21 from erikarn/20150330_norse_fixes

Add very numerous bug fixes and features, from Norse Corp.
This commit is contained in:
Mark Nottingham 2015-04-01 11:38:22 +11:00
commit ab2b96b0f5
10 changed files with 642 additions and 142 deletions

View File

@ -34,54 +34,89 @@
#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);
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;
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;
}
#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)
{
if (param.ssl_cipher_list) {
/* set order of ciphers */
int ssl_err = SSL_set_cipher_list (conn->ssl, param.ssl_cipher_list);
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",
fprintf(stderr,
"core_ssl_connect: set_cipher_list returned %d\n",
ssl_err);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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