2 * Copyright (c) 2005-2006 Robert N. M. Watson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/types.h>
31 #include <sys/socket.h>
34 #include <netinet/in.h>
36 #include <arpa/inet.h>
49 static int threaded; /* 1 for threaded, 0 for forked. */
50 static int numthreads; /* Number of threads/procs. */
51 static int numseconds; /* Length of test. */
54 * Simple, multi-threaded HTTP benchmark. Fetches a single URL using the
55 * specified parameters, and after a period of execution, reports on how it
58 #define MAXTHREADS 128
59 #define DEFAULTTHREADS 32
60 #define DEFAULTSECONDS 20
61 #define BUFFER (48*1024)
64 struct http_worker_description {
68 uintmax_t hwd_errorcount;
69 int hwd_start_signal_barrier;
73 struct sockaddr_in sin;
75 struct http_worker_description hwd[MAXTHREADS];
77 pthread_barrier_t start_barrier;
83 * Borrowed from sys/param.h>
85 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */
88 * Given a partially processed URL, fetch it from the specified host.
91 http_fetch(struct sockaddr_in *sin, char *path, int quiet)
93 u_char buffer[BUFFER];
98 sock = socket(PF_INET, SOCK_STREAM, 0);
101 warn("socket(PF_INET, SOCK_STREAM)");
105 /* XXX: Mark non-blocking. */
107 if (connect(sock, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
114 /* XXX: select for connection. */
116 /* Send a request. */
117 snprintf(buffer, BUFFER, "GET %s HTTP/1.0\n\n", path);
119 while (sofar < strlen(buffer)) {
120 len = send(sock, buffer, strlen(buffer), 0);
129 warnx("send: len == 0");
134 /* Read until done. Not very smart. */
136 len = recv(sock, buffer, BUFFER, 0);
156 for (i = 0; i < numthreads; i++) {
157 if (statep->hwd[i].hwd_pid != 0)
158 kill(statep->hwd[i].hwd_pid, SIGTERM);
163 signal_handler(int signum)
166 statep->hwd[curthread].hwd_start_signal_barrier = 1;
170 signal_barrier_wait(void)
173 /* Wait for EINTR. */
174 if (signal(SIGHUP, signal_handler) == SIG_ERR)
178 if (statep->hwd[curthread].hwd_start_signal_barrier)
184 signal_barrier_wakeup(void)
188 for (i = 0; i < numthreads; i++) {
189 if (statep->hwd[i].hwd_pid != 0)
190 kill(statep->hwd[i].hwd_pid, SIGHUP);
195 http_worker(void *arg)
197 struct http_worker_description *hwdp;
201 ret = pthread_barrier_wait(&statep->start_barrier);
202 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
203 err(-1, "pthread_barrier_wait");
205 signal_barrier_wait();
209 while (!statep->run_done) {
210 if (http_fetch(&statep->sin, statep->path, QUIET) < 0) {
211 hwdp->hwd_errorcount++;
214 /* Don't count transfers that didn't finish in time. */
215 if (!statep->run_done)
230 "http [-n numthreads] [-s seconds] [-t] ip port path\n");
235 main_sighup(int signum)
242 main(int argc, char *argv[])
250 numthreads = DEFAULTTHREADS;
251 numseconds = DEFAULTSECONDS;
252 while ((ch = getopt(argc, argv, "n:s:t")) != -1) {
255 numthreads = atoi(optarg);
259 numseconds = atoi(optarg);
276 if (numthreads > MAXTHREADS)
277 errx(-1, "%d exceeds max threads %d", numthreads,
280 len = roundup(sizeof(struct state), getpagesize());
281 pagebuffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
282 if (pagebuffer == MAP_FAILED)
284 if (minherit(pagebuffer, len, INHERIT_SHARE) < 0)
286 statep = (struct state *)pagebuffer;
288 bzero(&statep->sin, sizeof(statep->sin));
289 statep->sin.sin_len = sizeof(statep->sin);
290 statep->sin.sin_family = AF_INET;
291 statep->sin.sin_addr.s_addr = inet_addr(argv[0]);
292 statep->sin.sin_port = htons(atoi(argv[1]));
293 statep->path = argv[2];
296 * Do one test retrieve so we can report the error from it, if any.
298 if (http_fetch(&statep->sin, statep->path, 0) < 0)
302 if (pthread_barrier_init(&statep->start_barrier, NULL,
304 err(-1, "pthread_barrier_init");
307 for (i = 0; i < numthreads; i++) {
308 statep->hwd[i].hwd_count = 0;
310 if (pthread_create(&statep->hwd[i].hwd_thread, NULL,
311 http_worker, &statep->hwd[i]) != 0)
312 err(-1, "pthread_create");
323 http_worker(&statep->hwd[i]);
327 statep->hwd[i].hwd_pid = pid;
331 signal(SIGHUP, main_sighup);
333 signal_barrier_wakeup();
336 statep->run_done = 1;
339 for (i = 0; i < numthreads; i++) {
341 if (pthread_join(statep->hwd[i].hwd_thread, NULL)
343 err(-1, "pthread_join");
345 pid = waitpid(statep->hwd[i].hwd_pid, NULL, 0);
346 if (pid == statep->hwd[i].hwd_pid)
347 statep->hwd[i].hwd_pid = 0;
353 for (i = 0; i < numthreads; i++)
354 total += statep->hwd[i].hwd_count;
355 printf("%ju transfers/second\n", total / numseconds);
357 for (i = 0; i < numthreads; i++)
358 total += statep->hwd[i].hwd_errorcount;
359 printf("%ju errors/second\n", total / numseconds);