]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/tools/netrate/http/http.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / tools / tools / netrate / http / http.c
1 /*-
2  * Copyright (c) 2005-2006 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/mman.h>
31 #include <sys/socket.h>
32 #include <sys/wait.h>
33
34 #include <netinet/in.h>
35
36 #include <arpa/inet.h>
37
38 #include <err.h>
39 #include <errno.h>
40 #include <pthread.h>
41 #include <signal.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48
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. */
52
53 /*
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
56  * worked out.
57  */
58 #define MAXTHREADS      128
59 #define DEFAULTTHREADS  32
60 #define DEFAULTSECONDS  20
61 #define BUFFER  (48*1024)
62 #define QUIET   1
63
64 struct http_worker_description {
65         pthread_t       hwd_thread;
66         pid_t           hwd_pid;
67         uintmax_t       hwd_count;
68         uintmax_t       hwd_errorcount;
69         int             hwd_start_signal_barrier;
70 };
71
72 static struct state {
73         struct sockaddr_in               sin;
74         char                            *path;
75         struct http_worker_description   hwd[MAXTHREADS];
76         int                              run_done;
77         pthread_barrier_t                start_barrier;
78 } *statep;
79
80 int curthread;
81
82 /*
83  * Borrowed from sys/param.h>
84  */
85 #define roundup(x, y)   ((((x)+((y)-1))/(y))*(y))       /* to any y */
86
87 /*
88  * Given a partially processed URL, fetch it from the specified host.
89  */
90 static int
91 http_fetch(struct sockaddr_in *sin, char *path, int quiet)
92 {
93         u_char buffer[BUFFER];
94         ssize_t len;
95         size_t sofar;
96         int sock;
97
98         sock = socket(PF_INET, SOCK_STREAM, 0);
99         if (sock < 0) {
100                 if (!quiet)
101                         warn("socket(PF_INET, SOCK_STREAM)");
102                 return (-1);
103         }
104
105         /* XXX: Mark non-blocking. */
106
107         if (connect(sock, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
108                 if (!quiet)
109                         warn("connect");
110                 close(sock);
111                 return (-1);
112         }
113
114         /* XXX: select for connection. */
115
116         /* Send a request. */
117         snprintf(buffer, BUFFER, "GET %s HTTP/1.0\n\n", path);
118         sofar = 0;
119         while (sofar < strlen(buffer)) {
120                 len = send(sock, buffer, strlen(buffer), 0);
121                 if (len < 0) {
122                         if (!quiet)
123                                 warn("send");
124                         close(sock);
125                         return (-1);
126                 }
127                 if (len == 0) {
128                         if (!quiet)
129                                 warnx("send: len == 0");
130                 }
131                 sofar += len;
132         }
133
134         /* Read until done.  Not very smart. */
135         while (1) {
136                 len = recv(sock, buffer, BUFFER, 0);
137                 if (len < 0) {
138                         if (!quiet)
139                                 warn("recv");
140                         close(sock);
141                         return (-1);
142                 }
143                 if (len == 0)
144                         break;
145         }
146
147         close(sock);
148         return (0);
149 }
150
151 static void
152 killall(void)
153 {
154         int i;
155
156         for (i = 0; i < numthreads; i++) {
157                 if (statep->hwd[i].hwd_pid != 0)
158                         kill(statep->hwd[i].hwd_pid, SIGTERM);
159         }
160 }
161
162 static void
163 signal_handler(int signum)
164 {
165
166         statep->hwd[curthread].hwd_start_signal_barrier = 1;
167 }
168
169 static void
170 signal_barrier_wait(void)
171 {
172
173         /* Wait for EINTR. */
174         if (signal(SIGHUP, signal_handler) == SIG_ERR)
175                 err(-1, "signal");
176         while (1) {
177                 sleep(100);
178                 if (statep->hwd[curthread].hwd_start_signal_barrier)
179                         break;
180         }
181 }
182
183 static void
184 signal_barrier_wakeup(void)
185 {
186         int i;
187
188         for (i = 0; i < numthreads; i++) {
189                 if (statep->hwd[i].hwd_pid != 0)
190                         kill(statep->hwd[i].hwd_pid, SIGHUP);
191         }
192 }
193
194 static void *
195 http_worker(void *arg)
196 {
197         struct http_worker_description *hwdp;
198         int ret;
199
200         if (threaded) {
201                 ret = pthread_barrier_wait(&statep->start_barrier);
202                 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
203                         err(-1, "pthread_barrier_wait");
204         } else {
205                 signal_barrier_wait();
206         }
207
208         hwdp = arg;
209         while (!statep->run_done) {
210                 if (http_fetch(&statep->sin, statep->path, QUIET) < 0) {
211                         hwdp->hwd_errorcount++;
212                         continue;
213                 }
214                 /* Don't count transfers that didn't finish in time. */
215                 if (!statep->run_done)
216                         hwdp->hwd_count++;
217         }
218
219         if (threaded)
220                 return (NULL);
221         else
222                 exit(0);
223 }
224
225 static void
226 usage(void)
227 {
228
229         fprintf(stderr,
230             "http [-n numthreads] [-s seconds] [-t] ip port path\n");
231         exit(EX_USAGE);
232 }
233
234 static void
235 main_sighup(int signum)
236 {
237
238         killall();
239 }
240
241 int
242 main(int argc, char *argv[])
243 {
244         int ch, error, i;
245         char *pagebuffer;
246         uintmax_t total;
247         size_t len;
248         pid_t pid;
249
250         numthreads = DEFAULTTHREADS;
251         numseconds = DEFAULTSECONDS;
252         while ((ch = getopt(argc, argv, "n:s:t")) != -1) {
253                 switch (ch) {
254                 case 'n':
255                         numthreads = atoi(optarg);
256                         break;
257
258                 case 's':
259                         numseconds = atoi(optarg);
260                         break;
261
262                 case 't':
263                         threaded = 1;
264                         break;
265
266                 default:
267                         usage();
268                 }
269         }
270         argc -= optind;
271         argv += optind;
272
273         if (argc != 3)
274                 usage();
275
276         if (numthreads > MAXTHREADS)
277                 errx(-1, "%d exceeds max threads %d", numthreads,
278                     MAXTHREADS);
279
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)
283                 err(-1, "mmap");
284         if (minherit(pagebuffer, len, INHERIT_SHARE) < 0)
285                 err(-1, "minherit");
286         statep = (struct state *)pagebuffer;
287
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];
294
295         /*
296          * Do one test retrieve so we can report the error from it, if any.
297          */
298         if (http_fetch(&statep->sin, statep->path, 0) < 0)
299                 exit(-1);
300
301         if (threaded) {
302                 if (pthread_barrier_init(&statep->start_barrier, NULL,
303                     numthreads) != 0)
304                         err(-1, "pthread_barrier_init");
305         }
306
307         for (i = 0; i < numthreads; i++) {
308                 statep->hwd[i].hwd_count = 0;
309                 if (threaded) {
310                         if (pthread_create(&statep->hwd[i].hwd_thread, NULL,
311                             http_worker, &statep->hwd[i]) != 0)
312                                 err(-1, "pthread_create");
313                 } else {
314                         curthread = i;
315                         pid = fork();
316                         if (pid < 0) {
317                                 error = errno;
318                                 killall();
319                                 errno = error;
320                                 err(-1, "fork");
321                         }
322                         if (pid == 0) {
323                                 http_worker(&statep->hwd[i]);
324                                 printf("Doh\n");
325                                 exit(0);
326                         }
327                         statep->hwd[i].hwd_pid = pid;
328                 }
329         }
330         if (!threaded) {
331                 signal(SIGHUP, main_sighup);
332                 sleep(2);
333                 signal_barrier_wakeup();
334         }
335         sleep(numseconds);
336         statep->run_done = 1;
337         if (!threaded)
338                 sleep(2);
339         for (i = 0; i < numthreads; i++) {
340                 if (threaded) {
341                         if (pthread_join(statep->hwd[i].hwd_thread, NULL)
342                             != 0)
343                                 err(-1, "pthread_join");
344                 } else {
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;
348                 }
349         }
350         if (!threaded)
351                 killall();
352         total = 0;
353         for (i = 0; i < numthreads; i++)
354                 total += statep->hwd[i].hwd_count;
355         printf("%ju transfers/second\n", total / numseconds);
356         total = 0;
357         for (i = 0; i < numthreads; i++)
358                 total += statep->hwd[i].hwd_errorcount;
359         printf("%ju errors/second\n", total / numseconds);
360         return (0);
361 }