1150990Srwatson/*- 2155892Srwatson * Copyright (c) 2005-2006 Robert N. M. Watson 3150990Srwatson * All rights reserved. 4150990Srwatson * 5150990Srwatson * Redistribution and use in source and binary forms, with or without 6150990Srwatson * modification, are permitted provided that the following conditions 7150990Srwatson * are met: 8150990Srwatson * 1. Redistributions of source code must retain the above copyright 9150990Srwatson * notice, this list of conditions and the following disclaimer. 10150990Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11150990Srwatson * notice, this list of conditions and the following disclaimer in the 12150990Srwatson * documentation and/or other materials provided with the distribution. 13150990Srwatson * 14150990Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15150990Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16150990Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17150990Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18150990Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19150990Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20150990Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21150990Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22150990Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23150990Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24150990Srwatson * SUCH DAMAGE. 25150990Srwatson * 26150990Srwatson * $FreeBSD$ 27150990Srwatson */ 28150990Srwatson 29150990Srwatson#include <sys/types.h> 30155892Srwatson#include <sys/mman.h> 31150990Srwatson#include <sys/socket.h> 32155892Srwatson#include <sys/wait.h> 33150990Srwatson 34150990Srwatson#include <netinet/in.h> 35150990Srwatson 36150990Srwatson#include <arpa/inet.h> 37150990Srwatson 38150990Srwatson#include <err.h> 39155892Srwatson#include <errno.h> 40150990Srwatson#include <pthread.h> 41155892Srwatson#include <signal.h> 42151705Spjd#include <stdint.h> 43150990Srwatson#include <stdio.h> 44150990Srwatson#include <stdlib.h> 45150990Srwatson#include <string.h> 46155892Srwatson#include <sysexits.h> 47150990Srwatson#include <unistd.h> 48150990Srwatson 49155892Srwatsonstatic int threaded; /* 1 for threaded, 0 for forked. */ 50155892Srwatsonstatic int numthreads; /* Number of threads/procs. */ 51155892Srwatsonstatic int numseconds; /* Length of test. */ 52155892Srwatson 53150990Srwatson/* 54150990Srwatson * Simple, multi-threaded HTTP benchmark. Fetches a single URL using the 55150990Srwatson * specified parameters, and after a period of execution, reports on how it 56150990Srwatson * worked out. 57150990Srwatson */ 58155892Srwatson#define MAXTHREADS 128 59155892Srwatson#define DEFAULTTHREADS 32 60155892Srwatson#define DEFAULTSECONDS 20 61150990Srwatson#define BUFFER (48*1024) 62150990Srwatson#define QUIET 1 63150990Srwatson 64150990Srwatsonstruct http_worker_description { 65150990Srwatson pthread_t hwd_thread; 66155892Srwatson pid_t hwd_pid; 67151705Spjd uintmax_t hwd_count; 68151705Spjd uintmax_t hwd_errorcount; 69155892Srwatson int hwd_start_signal_barrier; 70150990Srwatson}; 71150990Srwatson 72155892Srwatsonstatic struct state { 73155892Srwatson struct sockaddr_in sin; 74155892Srwatson char *path; 75155892Srwatson struct http_worker_description hwd[MAXTHREADS]; 76155892Srwatson int run_done; 77155892Srwatson pthread_barrier_t start_barrier; 78155892Srwatson} *statep; 79150990Srwatson 80155892Srwatsonint curthread; 81155892Srwatson 82150990Srwatson/* 83155892Srwatson * Borrowed from sys/param.h> 84155892Srwatson */ 85155892Srwatson#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ 86155892Srwatson 87155892Srwatson/* 88150990Srwatson * Given a partially processed URL, fetch it from the specified host. 89150990Srwatson */ 90150990Srwatsonstatic int 91150990Srwatsonhttp_fetch(struct sockaddr_in *sin, char *path, int quiet) 92150990Srwatson{ 93150990Srwatson u_char buffer[BUFFER]; 94150990Srwatson ssize_t len; 95150990Srwatson size_t sofar; 96150990Srwatson int sock; 97150990Srwatson 98150990Srwatson sock = socket(PF_INET, SOCK_STREAM, 0); 99150990Srwatson if (sock < 0) { 100150990Srwatson if (!quiet) 101150990Srwatson warn("socket(PF_INET, SOCK_STREAM)"); 102150990Srwatson return (-1); 103150990Srwatson } 104150990Srwatson 105155892Srwatson /* XXX: Mark non-blocking. */ 106155892Srwatson 107150990Srwatson if (connect(sock, (struct sockaddr *)sin, sizeof(*sin)) < 0) { 108150990Srwatson if (!quiet) 109150990Srwatson warn("connect"); 110150990Srwatson close(sock); 111150990Srwatson return (-1); 112150990Srwatson } 113150990Srwatson 114155892Srwatson /* XXX: select for connection. */ 115155892Srwatson 116150990Srwatson /* Send a request. */ 117150990Srwatson snprintf(buffer, BUFFER, "GET %s HTTP/1.0\n\n", path); 118150990Srwatson sofar = 0; 119150990Srwatson while (sofar < strlen(buffer)) { 120150990Srwatson len = send(sock, buffer, strlen(buffer), 0); 121150990Srwatson if (len < 0) { 122150990Srwatson if (!quiet) 123150990Srwatson warn("send"); 124150990Srwatson close(sock); 125150990Srwatson return (-1); 126150990Srwatson } 127150990Srwatson if (len == 0) { 128150990Srwatson if (!quiet) 129150990Srwatson warnx("send: len == 0"); 130150990Srwatson } 131150990Srwatson sofar += len; 132150990Srwatson } 133150990Srwatson 134150990Srwatson /* Read until done. Not very smart. */ 135150990Srwatson while (1) { 136150990Srwatson len = recv(sock, buffer, BUFFER, 0); 137150990Srwatson if (len < 0) { 138150990Srwatson if (!quiet) 139150990Srwatson warn("recv"); 140150990Srwatson close(sock); 141150990Srwatson return (-1); 142150990Srwatson } 143150990Srwatson if (len == 0) 144150990Srwatson break; 145150990Srwatson } 146150990Srwatson 147150990Srwatson close(sock); 148150990Srwatson return (0); 149150990Srwatson} 150150990Srwatson 151155892Srwatsonstatic void 152155892Srwatsonkillall(void) 153155892Srwatson{ 154155892Srwatson int i; 155155892Srwatson 156155892Srwatson for (i = 0; i < numthreads; i++) { 157155892Srwatson if (statep->hwd[i].hwd_pid != 0) 158155892Srwatson kill(statep->hwd[i].hwd_pid, SIGTERM); 159155892Srwatson } 160155892Srwatson} 161155892Srwatson 162155892Srwatsonstatic void 163155892Srwatsonsignal_handler(int signum) 164155892Srwatson{ 165155892Srwatson 166155892Srwatson statep->hwd[curthread].hwd_start_signal_barrier = 1; 167155892Srwatson} 168155892Srwatson 169155892Srwatsonstatic void 170155892Srwatsonsignal_barrier_wait(void) 171155892Srwatson{ 172155892Srwatson 173155892Srwatson /* Wait for EINTR. */ 174155892Srwatson if (signal(SIGHUP, signal_handler) == SIG_ERR) 175155892Srwatson err(-1, "signal"); 176155892Srwatson while (1) { 177155892Srwatson sleep(100); 178155892Srwatson if (statep->hwd[curthread].hwd_start_signal_barrier) 179155892Srwatson break; 180155892Srwatson } 181155892Srwatson} 182155892Srwatson 183155892Srwatsonstatic void 184155892Srwatsonsignal_barrier_wakeup(void) 185155892Srwatson{ 186155892Srwatson int i; 187155892Srwatson 188155892Srwatson for (i = 0; i < numthreads; i++) { 189155892Srwatson if (statep->hwd[i].hwd_pid != 0) 190155892Srwatson kill(statep->hwd[i].hwd_pid, SIGHUP); 191155892Srwatson } 192155892Srwatson} 193155892Srwatson 194150990Srwatsonstatic void * 195150990Srwatsonhttp_worker(void *arg) 196150990Srwatson{ 197150990Srwatson struct http_worker_description *hwdp; 198150990Srwatson int ret; 199150990Srwatson 200155892Srwatson if (threaded) { 201155892Srwatson ret = pthread_barrier_wait(&statep->start_barrier); 202155892Srwatson if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) 203155892Srwatson err(-1, "pthread_barrier_wait"); 204155892Srwatson } else { 205155892Srwatson signal_barrier_wait(); 206155892Srwatson } 207150990Srwatson 208150990Srwatson hwdp = arg; 209155892Srwatson while (!statep->run_done) { 210155892Srwatson if (http_fetch(&statep->sin, statep->path, QUIET) < 0) { 211150990Srwatson hwdp->hwd_errorcount++; 212150990Srwatson continue; 213150990Srwatson } 214150990Srwatson /* Don't count transfers that didn't finish in time. */ 215155892Srwatson if (!statep->run_done) 216150990Srwatson hwdp->hwd_count++; 217150990Srwatson } 218150990Srwatson 219155892Srwatson if (threaded) 220155892Srwatson return (NULL); 221155892Srwatson else 222155892Srwatson exit(0); 223150990Srwatson} 224150990Srwatson 225155892Srwatsonstatic void 226155892Srwatsonusage(void) 227155892Srwatson{ 228155892Srwatson 229155892Srwatson fprintf(stderr, 230155892Srwatson "http [-n numthreads] [-s seconds] [-t] ip port path\n"); 231155892Srwatson exit(EX_USAGE); 232155892Srwatson} 233155892Srwatson 234155892Srwatsonstatic void 235155892Srwatsonmain_sighup(int signum) 236155892Srwatson{ 237155892Srwatson 238155892Srwatson killall(); 239155892Srwatson} 240155892Srwatson 241150990Srwatsonint 242150990Srwatsonmain(int argc, char *argv[]) 243150990Srwatson{ 244155892Srwatson int ch, error, i; 245155892Srwatson char *pagebuffer; 246151705Spjd uintmax_t total; 247155892Srwatson size_t len; 248155892Srwatson pid_t pid; 249150990Srwatson 250155892Srwatson numthreads = DEFAULTTHREADS; 251155892Srwatson numseconds = DEFAULTSECONDS; 252155892Srwatson while ((ch = getopt(argc, argv, "n:s:t")) != -1) { 253155892Srwatson switch (ch) { 254155892Srwatson case 'n': 255155892Srwatson numthreads = atoi(optarg); 256155892Srwatson break; 257150990Srwatson 258155892Srwatson case 's': 259155892Srwatson numseconds = atoi(optarg); 260155892Srwatson break; 261150990Srwatson 262155892Srwatson case 't': 263155892Srwatson threaded = 1; 264155892Srwatson break; 265155892Srwatson 266155892Srwatson default: 267155892Srwatson usage(); 268155892Srwatson } 269155892Srwatson } 270155892Srwatson argc -= optind; 271155892Srwatson argv += optind; 272155892Srwatson 273155892Srwatson if (argc != 3) 274155892Srwatson usage(); 275155892Srwatson 276161861Srwatson if (numthreads > MAXTHREADS) 277161861Srwatson errx(-1, "%d exceeds max threads %d", numthreads, 278161861Srwatson MAXTHREADS); 279161861Srwatson 280155892Srwatson len = roundup(sizeof(struct state), getpagesize()); 281155892Srwatson pagebuffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); 282155892Srwatson if (pagebuffer == MAP_FAILED) 283155892Srwatson err(-1, "mmap"); 284155892Srwatson if (minherit(pagebuffer, len, INHERIT_SHARE) < 0) 285155892Srwatson err(-1, "minherit"); 286155892Srwatson statep = (struct state *)pagebuffer; 287155892Srwatson 288155892Srwatson bzero(&statep->sin, sizeof(statep->sin)); 289155892Srwatson statep->sin.sin_len = sizeof(statep->sin); 290155892Srwatson statep->sin.sin_family = AF_INET; 291155892Srwatson statep->sin.sin_addr.s_addr = inet_addr(argv[0]); 292155892Srwatson statep->sin.sin_port = htons(atoi(argv[1])); 293155892Srwatson statep->path = argv[2]; 294155892Srwatson 295150990Srwatson /* 296150990Srwatson * Do one test retrieve so we can report the error from it, if any. 297150990Srwatson */ 298155892Srwatson if (http_fetch(&statep->sin, statep->path, 0) < 0) 299150990Srwatson exit(-1); 300150990Srwatson 301155892Srwatson if (threaded) { 302155892Srwatson if (pthread_barrier_init(&statep->start_barrier, NULL, 303203800Sru numthreads) != 0) 304203800Sru err(-1, "pthread_barrier_init"); 305155892Srwatson } 306150990Srwatson 307155892Srwatson for (i = 0; i < numthreads; i++) { 308155892Srwatson statep->hwd[i].hwd_count = 0; 309155892Srwatson if (threaded) { 310155892Srwatson if (pthread_create(&statep->hwd[i].hwd_thread, NULL, 311203800Sru http_worker, &statep->hwd[i]) != 0) 312155892Srwatson err(-1, "pthread_create"); 313155892Srwatson } else { 314155892Srwatson curthread = i; 315155892Srwatson pid = fork(); 316155892Srwatson if (pid < 0) { 317155892Srwatson error = errno; 318155892Srwatson killall(); 319155892Srwatson errno = error; 320155892Srwatson err(-1, "fork"); 321155892Srwatson } 322155892Srwatson if (pid == 0) { 323155892Srwatson http_worker(&statep->hwd[i]); 324155892Srwatson printf("Doh\n"); 325155892Srwatson exit(0); 326155892Srwatson } 327155892Srwatson statep->hwd[i].hwd_pid = pid; 328155892Srwatson } 329150990Srwatson } 330155892Srwatson if (!threaded) { 331155892Srwatson signal(SIGHUP, main_sighup); 332155892Srwatson sleep(2); 333155892Srwatson signal_barrier_wakeup(); 334150990Srwatson } 335155892Srwatson sleep(numseconds); 336155892Srwatson statep->run_done = 1; 337155892Srwatson if (!threaded) 338155892Srwatson sleep(2); 339155892Srwatson for (i = 0; i < numthreads; i++) { 340155892Srwatson if (threaded) { 341155892Srwatson if (pthread_join(statep->hwd[i].hwd_thread, NULL) 342203800Sru != 0) 343155892Srwatson err(-1, "pthread_join"); 344155892Srwatson } else { 345155892Srwatson pid = waitpid(statep->hwd[i].hwd_pid, NULL, 0); 346155892Srwatson if (pid == statep->hwd[i].hwd_pid) 347155892Srwatson statep->hwd[i].hwd_pid = 0; 348155892Srwatson } 349155892Srwatson } 350155892Srwatson if (!threaded) 351155892Srwatson killall(); 352150990Srwatson total = 0; 353155892Srwatson for (i = 0; i < numthreads; i++) 354155892Srwatson total += statep->hwd[i].hwd_count; 355155892Srwatson printf("%ju transfers/second\n", total / numseconds); 356150990Srwatson total = 0; 357155892Srwatson for (i = 0; i < numthreads; i++) 358155892Srwatson total += statep->hwd[i].hwd_errorcount; 359155892Srwatson printf("%ju errors/second\n", total / numseconds); 360150990Srwatson return (0); 361150990Srwatson} 362