📄 core.c
字号:
/* httperf -- a tool for measuring web server performance Copyright (C) 2000 Hewlett-Packard Company Contributed by David Mosberger-Tang <davidm@hpl.hp.com> This file is part of httperf, a web server performance measurment tool. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include "config.h"#include <assert.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <netdb.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/types.h>#include <sys/resource.h> /* grrr, must come after sys/types.h for BSD */#include <netinet/in.h>#include <netinet/tcp.h>#include <arpa/inet.h>#include <httperf.h>#include <call.h>#include <core.h>#include <event.h>#include <http.h>#include <conn.h>#define HASH_TABLE_SIZE 1024 /* can't have more than this many servers */#define MIN_IP_PORT IPPORT_RESERVED#define MAX_IP_PORT 65535#define BITSPERLONG (8*sizeof (u_long))static int running = 1;static int iteration;static u_long max_burst_len;static fd_set rdfds, wrfds;static int min_sd = 0x7fffffff, max_sd = 0, alloced_sd_to_conn = 0;static struct timeval select_timeout;static struct sockaddr_in myaddr;Conn **sd_to_conn;static u_long port_free_map[((MAX_IP_PORT - MIN_IP_PORT + BITSPERLONG) / BITSPERLONG)];static char http10req[] = " HTTP/1.0\r\nUser-Agent: httperf/"VERSION"\r\nHost: ";static char http11req[] = " HTTP/1.1\r\nUser-Agent: httperf/"VERSION"\r\nHost: ";static char http10req_nohost[] = " HTTP/1.0\r\nUser-Agent: httperf/"VERSION"\r\n";static char http11req_nohost[] = " HTTP/1.1\r\nUser-Agent: httperf/"VERSION"\r\n";#ifndef SOL_TCP# define SOL_TCP 6 /* probably ought to do getprotlbyname () */#endif#ifdef TIME_SYSCALLS# define SYSCALL(n,s) \ { \ Time start, stop; \ do \ { \ errno = 0; \ start = timer_now_forced (); \ s; /* execute the syscall */ \ stop = timer_now_forced (); \ syscall_time[SC_##n] += stop - start; \ ++syscall_count[SC_##n]; \ } \ while (errno == EINTR); \ } enum Syscalls { SC_BIND, SC_CONNECT, SC_READ, SC_SELECT, SC_SOCKET, SC_WRITEV, SC_SSL_READ, SC_SSL_WRITEV, SC_NUM_SYSCALLS }; static const char * const syscall_name[SC_NUM_SYSCALLS] = { "bind", "connct", "read", "select", "socket", "writev", "ssl_read", "ssl_writev" }; static Time syscall_time[SC_NUM_SYSCALLS]; static u_int syscall_count[SC_NUM_SYSCALLS];#else# define SYSCALL(n,s) \ { \ do \ { \ errno = 0; \ s; \ } \ while (errno == EINTR); \ }#endifstruct hash_entry { const char *hostname; int port; struct sockaddr_in sin; }hash_table[HASH_TABLE_SIZE];static inthash_code (const char *server, size_t server_len, int port){ u_char *cp = (u_char *) server; u_long h = port; u_long g; int ch; /* Basically the ELF hash algorithm: */ while ((ch = *cp++) != '\0') { h = (h << 4) + ch; if ((g = (h & 0xf0000000)) != 0) { h ^= g >> 24; h &= ~g; } } return h;}static struct hash_entry*hash_enter (const char *server, size_t server_len, int port, struct sockaddr_in *sin){ struct hash_entry *he; int index = hash_code (server, server_len, port) % HASH_TABLE_SIZE; while (hash_table[index].hostname) { ++index; if (index >= HASH_TABLE_SIZE) index = 0; } he = hash_table + index; he->hostname = server; he->port = port; he->sin = *sin; return he;}static struct sockaddr_in*hash_lookup (const char *server, size_t server_len, int port){ int index, start_index; index = start_index = hash_code (server, server_len, port) % HASH_TABLE_SIZE; while (hash_table[index].hostname) { if (hash_table[index].port == port && strcmp (hash_table[index].hostname, server) == 0) return &hash_table[index].sin; ++index; if (index >= HASH_TABLE_SIZE) index = 0; if (index == start_index) break; } return 0;}static intlffs (long w){ int r; if (sizeof (w) == sizeof (int)) r = ffs (w); else { r = ffs (w);#if SIZEOF_LONG > 4 if (r == 0) { r = ffs (w >> (8*sizeof (int))); if (r > 0) r += 8*sizeof (int); }#endif } return r;}static voidport_put (int port){ int i, bit; port -= MIN_IP_PORT; i = port / BITSPERLONG; bit = port % BITSPERLONG; port_free_map[i] |= (1UL << bit);}static intport_get (void){ static u_long mask = ~0UL; static int previous = 0; int port, bit, i; i = previous; if ((port_free_map[i] & mask) == 0) { do { ++i; if (i >= NELEMS (port_free_map)) i = 0; if (i == previous) { if (DBG > 0) fprintf (stderr, "%s.port_get: Yikes! I'm out of port numbers!\n", prog_name); return -1; } } while (port_free_map[i] == 0); mask = ~0UL; } previous = i; bit = lffs (port_free_map[i] & mask) - 1; if (bit >= BITSPERLONG - 1) mask = 0; else mask = ~((1UL << (bit + 1)) - 1); port_free_map[i] &= ~(1UL << bit); port = bit + i*BITSPERLONG + MIN_IP_PORT; return port;}static voidconn_failure (Conn *s, int err){ Any_Type arg; arg.l = err; event_signal (EV_CONN_FAILED, (Object *) s, arg); core_close (s);}static voidconn_timeout (Timer *t, Any_Type arg){ Conn *s = arg.vp; Time now; Call *c; assert (object_is_conn (s)); s->watchdog = 0; if (DBG > 0) { c = 0; if (s->sd >= 0) { now = timer_now (); if (FD_ISSET (s->sd, &rdfds) && s->recvq && now >= s->recvq->timeout) c = s->recvq; else if (FD_ISSET (s->sd, &wrfds) && s->sendq && now >= s->sendq->timeout) c = s->sendq; } if (DBG > 0) { fprintf (stderr, "connection_timeout"); if (c) fprintf (stderr, ".%lu", c->id); fprintf (stderr, ": t=%p, connection=%p\n", t, s); } } arg.l = 0; event_signal (EV_CONN_TIMEOUT, (Object *) s, arg); core_close (s);}static voidset_active (Conn *s, fd_set *fdset){ int sd = s->sd; Any_Type arg; Time timeout; FD_SET (sd, fdset); if (sd < min_sd) min_sd = sd; if (sd >= max_sd) max_sd = sd; if (s->watchdog) return; timeout = 0.0; if (s->sendq) timeout = s->sendq->timeout; if (s->recvq && (timeout == 0.0 || timeout > s->recvq->timeout)) timeout = s->recvq->timeout; if (timeout > 0.0) { arg.vp = s; s->watchdog = timer_schedule (conn_timeout, arg, timeout - timer_now ()); }}static voiddo_send (Conn *conn){ int async_errno, len; struct iovec *iovp; int sd = conn->sd; ssize_t nsent = 0; Any_Type arg; Call *call; while (1) { call = conn->sendq; assert (call); arg.l = 0; event_signal (EV_CALL_SEND_RAW_DATA, (Object *) call, arg);#ifdef HAVE_SSL if (param.use_ssl) { extern ssize_t SSL_writev (SSL *, const struct iovec *, int); SYSCALL (SSL_WRITEV, nsent = SSL_writev(conn->ssl, call->req.iov + call->req.iov_index, (NELEMS (call->req.iov) - call->req.iov_index))); } else#endif { SYSCALL (WRITEV, nsent = writev (sd, call->req.iov + call->req.iov_index, (NELEMS (call->req.iov) - call->req.iov_index))); } if (DBG > 0) fprintf (stderr, "do_send.%lu: wrote %ld bytes on %p\n", call->id, (long) nsent, conn); if (nsent < 0) { if (errno == EAGAIN) return; len = sizeof (async_errno); if (getsockopt (sd, SOL_SOCKET, SO_ERROR, &async_errno, &len) == 0 && async_errno != 0) errno = async_errno; if (DBG > 0) fprintf (stderr, "%s.do_send: writev() failed: %s\n", prog_name, strerror (errno)); conn_failure (conn, errno); return; } call->req.size += nsent; iovp = call->req.iov + call->req.iov_index; while (iovp < call->req.iov + NELEMS (call->req.iov)) { if (nsent < iovp->iov_len) { iovp->iov_len -= nsent; iovp->iov_base = (caddr_t) ((char *) iovp->iov_base + nsent); break; } else { /* we're done with this fragment: */ nsent -= iovp->iov_len; *iovp = call->req.iov_saved; ++iovp; call->req.iov_saved = *iovp; } } call->req.iov_index = iovp - call->req.iov; if (call->req.iov_index < NELEMS (call->req.iov)) { /* there are more header bytes to write */ call->timeout = param.timeout ? timer_now () + param.timeout : 0.0; set_active (conn, &wrfds); return; } /* we're done with sending this request */ conn->sendq = call->sendq_next; if (!conn->sendq) { conn->sendq_tail = 0; FD_CLR (sd, &wrfds); } arg.l = 0; event_signal (EV_CALL_SEND_STOP, (Object *) call, arg); if (conn->state >= S_CLOSING) { call_dec_ref (call); return; } /* get ready to receive matching reply (note that we implicitly pass on the reference to the call from the sendq to the recvq): */ call->recvq_next = 0; if (!conn->recvq) conn->recvq = conn->recvq_tail = call; else { conn->recvq_tail->recvq_next = call; conn->recvq_tail = call; } call->timeout = param.timeout + param.think_timeout; if (call->timeout > 0.0) call->timeout += timer_now (); set_active (conn, &rdfds); if (conn->state < S_REPLY_STATUS) conn->state = S_REPLY_STATUS; /* expecting reply status */ if (!conn->sendq) return; arg.l = 0; event_signal (EV_CALL_SEND_START, (Object *) conn->sendq, arg); if (conn->state >= S_CLOSING) return; }}static voidrecv_done (Call *call){ Conn *conn = call->conn; Any_Type arg; conn->recvq = call->recvq_next; if (!conn->recvq) { FD_CLR (conn->sd, &rdfds); conn->recvq_tail = 0; } /* we're done with receiving this request */ arg.l = 0; event_signal (EV_CALL_RECV_STOP, (Object *) call, arg); call_dec_ref (call);}static voiddo_recv (Conn *s){ char *cp, buf[8193]; Call *c = s->recvq; int i, saved_errno; ssize_t nread = 0; size_t buf_len; assert (c);#ifdef HAVE_SSL if (param.use_ssl) { SYSCALL (SSL_READ, nread = SSL_read (s->ssl, buf, sizeof (buf) - 1)); } else#endif { SYSCALL (READ, nread = read (s->sd, buf, sizeof (buf) - 1)); } saved_errno = errno; if (nread <= 0) { if (DBG > 0) { fprintf (stderr, "do_recv.%lu: received %lu reply bytes on %p\n", c->id, (u_long) (c->reply.header_bytes + c->reply.content_bytes), s); if (nread < 0) fprintf (stderr, "%s.do_recv: read() failed: %s\n", prog_name, strerror (saved_errno)); } if (nread < 0) { if (saved_errno != EAGAIN) conn_failure (s, saved_errno); } else if (s->state != S_REPLY_DATA) conn_failure (s, ECONNRESET); else { if (s->state < S_CLOSING) s->state = S_REPLY_DONE; recv_done (c); } return; } buf[nread] = '\0'; /* ensure buffer is '\0' terminated */ if (DBG > 3) { /* dump received data in hex & ascii: */ fprintf (stderr, "do_recv.%lu: received reply data:\n", c->id); for (cp = buf; cp < buf + nread; ) { fprintf (stderr, " %04x:", (int) (c->reply.header_bytes + c->reply.content_bytes + (cp - buf))); for (i = 0; i < 16 && i < buf + nread - cp; ++i) fprintf (stderr, " %02x", cp[i] & 0xff); i *= 3; while (i++ < 50) fputc (' ', stderr); for (i = 0; i < 16 && cp < buf + nread; ++i, ++cp) fprintf (stderr, "%c", isprint (*cp) ? *cp : '.'); fprintf (stderr, "\n"); } } /* process the replies in this buffer: */ buf_len = nread; cp = buf; do { c = s->recvq; assert (c); http_process_reply_bytes (c, &cp, &buf_len); if (s->state == S_REPLY_DONE) { recv_done (c); if (s->state >= S_CLOSING) return; s->state = S_REPLY_STATUS; } } while (buf_len > 0); if (s->recvq) set_active (c->conn, &rdfds);}struct sockaddr_in*core_addr_intern (const char *server, size_t server_len, int port){ struct sockaddr_in sin; struct hash_entry *h; struct hostent *he; Any_Type arg; memset (&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_port = htons (port); arg.cvp = server; event_signal (EV_HOSTNAME_LOOKUP_START, 0, arg); he = gethostbyname (server); event_signal (EV_HOSTNAME_LOOKUP_STOP, 0, arg); if (he) { if (he->h_addrtype != AF_INET || he->h_length != sizeof (sin.sin_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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -