kmalloc: Avoid code duplication.
[dragonfly.git] / test / stress / webstress / webstress.c
blobe4ece1f1abcb2be8e4836145b4252f6a083b32bc
1 /*
2 * The software known as "DragonFly" or "DragonFly BSD" is distributed under
3 * the following terms:
4 *
5 * Copyright (c) 2003, 2004, 2005 The DragonFly Project. All rights reserved.
6 *
7 * This code is derived from software contributed to The DragonFly Project
8 * by Matthew Dillon <dillon@backplane.com>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of The DragonFly Project nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific, prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
37 * $DragonFly: src/test/stress/webstress/webstress.c,v 1.1 2005/04/05 00:13:20 dillon Exp $
40 * webstress [-n num] [-r] [-f url_file] [-l limit_ms] [-c count] url url
41 * url...
43 * Fork N processes (default 8). Each process makes a series of connections
44 * to retrieve the specified URLs. Any transaction that takes longer the
45 * limit_ms (default 1000) to perform is reported.
47 * If the -r option is specified the list of URLs is randomized.
49 * If the -f option is specified URLs are read from a file. Multiple -f
50 * options may be specified instead of or in addition to specifying additional
51 * URLs on the command line.
53 * All URLs should begin with http:// but this is optional. Only http is
54 * supported.
56 * By default the program runs until you ^C it. The -c option may be
57 * used to limit the number of loops.
59 * WARNING! This can easily blow out available sockets on the client or
60 * server, or blow out available ports on the client, due to sockets left
61 * in TIME_WAIT. It is recommended that net.inet.tcp.msl be lowered
62 * considerably to run this test. The test will abort if the system runs
63 * out of sockets or ports.
66 #include <sys/types.h>
67 #include <sys/socket.h>
68 #include <sys/wait.h>
69 #include <sys/time.h>
70 #include <netinet/in.h>
71 #include <netinet/tcp.h>
72 #include <arpa/inet.h>
74 #include <stdio.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <stdarg.h>
78 #include <unistd.h>
79 #include <errno.h>
80 #include <netdb.h>
82 typedef struct urlnode {
83 struct urlnode *url_next;
84 struct sockaddr_in url_sin;
85 char *url_host;
86 char *url_path;
87 } *urlnode_t;
89 static void usage(void);
90 static void read_url_file(const char *path);
91 static void run_test(urlnode_t *array, int count);
92 static void add_url(const char *url);
94 int fork_opt = 8;
95 int random_opt;
96 int limit_opt = 1000000;
97 int report_interval = 100;
98 int loop_count = 0;
99 urlnode_t url_base;
100 urlnode_t *url_nextp = &url_base;
101 int url_count;
104 main(int ac, char **av)
106 int ch;
107 int i;
108 urlnode_t node;
109 urlnode_t *array;
110 pid_t pid;
112 while ((ch = getopt(ac, av, "c:f:l:n:r")) != -1) {
113 printf("CH %c\n", ch);
114 switch(ch) {
115 case 'c':
116 loop_count = strtol(optarg, NULL, 0);
117 if (report_interval > loop_count)
118 report_interval = loop_count;
119 break;
120 case 'n':
121 fork_opt = strtol(optarg, NULL, 0);
122 break;
123 case 'r':
124 random_opt = 1;
125 break;
126 case 'f':
127 read_url_file(optarg);
128 break;
129 case 'l':
130 limit_opt = strtol(optarg, NULL, 0) * 1000;
131 break;
132 default:
133 usage();
134 /* not reached */
135 break;
138 ac -= optind;
139 av += optind;
140 for (i = 0; i < ac; ++i)
141 add_url(av[i]);
142 if (url_base == NULL)
143 usage();
146 * Convert the list to an array
148 array = malloc(sizeof(urlnode_t) * url_count);
149 for (i = 0, node = url_base; i < url_count; ++i, node = node->url_next) {
150 array[i] = node;
154 * Dump the list
156 printf("URL LIST:\n");
157 for (node = url_base; node; node = node->url_next) {
158 printf(" http://%s:%d/%s\n",
159 inet_ntoa(node->url_sin.sin_addr),
160 ntohs(node->url_sin.sin_port),
161 node->url_path
164 printf("Running...\n");
167 * Fork children and start the test
169 for (i = 0; i < fork_opt; ++i) {
170 if ((pid = fork()) == 0) {
171 run_test(array, url_count);
172 exit(0);
173 } else if (pid == (pid_t)-1) {
174 printf("unable to fork child %d\n", i);
175 exit(1);
178 while (wait(NULL) >= 0 || errno == EINTR)
180 return(0);
183 static
184 void
185 usage(void)
187 fprintf(stderr,
188 "%s [-n num] [-r] [-f url_file] [-l limit_ms] [-c loops] url url...\n"
189 " -n num number of forks (8)\n"
190 " -r randomize list (off)\n"
191 " -f url_file read URLs from file\n"
192 " -l limit_ms report if transaction latency >limit (1000)\n"
193 " -c loops test loops (0 == infinite)\n"
194 "\n"
195 "WARNING! This can easily blow out available sockets on the client or\n"
196 "server, or blow out available ports on the client, due to sockets left\n"
197 "in TIME_WAIT. It is recommended that net.inet.tcp.msl be lowered\n"
198 "considerably to run this test. The test will abort if the system runs\n"
199 "out of sockets or ports.\n",
200 getprogname()
202 exit(1);
205 static
206 void
207 read_url_file(const char *path)
209 char buf[1024];
210 FILE *fi;
211 int len;
213 if ((fi = fopen(path, "r")) != NULL) {
214 while (fgets(buf, sizeof(buf), fi) != NULL) {
215 if (buf[0] == '#')
216 continue;
217 len = strlen(buf);
218 if (len && buf[len-1] == '\n')
219 buf[len-1] = 0;
220 add_url(buf);
222 fclose(fi);
223 } else {
224 fprintf(stderr, "Unable to open %s\n", path);
225 exit(1);
229 static
230 void
231 add_url(const char *url)
233 struct hostent *hen;
234 const char *base;
235 const char *ptr;
236 char *hostname;
237 urlnode_t node;
238 int error;
240 node = malloc(sizeof(*node));
241 bzero(node, sizeof(*node));
243 base = url;
244 if (strncmp(url, "http://", 7) == 0)
245 base += 7;
246 if ((ptr = strchr(base, '/')) == NULL) {
247 fprintf(stderr, "malformed URL: %s\n", base);
248 free(node);
249 return;
251 hostname = malloc(ptr - base + 1);
252 bcopy(base, hostname, ptr - base);
253 hostname[ptr - base] = 0;
254 base = ptr + 1;
255 if ((ptr = strrchr(hostname, ':')) != NULL) {
256 *strrchr(hostname, ':') = 0;
257 ++ptr;
258 node->url_sin.sin_port = htons(strtol(ptr, NULL, 0));
259 } else {
260 node->url_sin.sin_port = htons(80);
262 node->url_sin.sin_len = sizeof(node->url_sin);
263 node->url_sin.sin_family = AF_INET;
264 error = inet_aton(hostname, &node->url_sin.sin_addr);
265 if (error < 0) {
266 fprintf(stderr, "unable to parse host/ip: %s (%s)\n",
267 hostname, strerror(errno));
268 free(node);
269 return;
271 if (error == 0) {
272 if ((hen = gethostbyname(hostname)) == NULL) {
273 fprintf(stderr, "unable to resolve host: %s (%s)\n",
274 hostname, hstrerror(h_errno));
275 free(node);
276 return;
278 bcopy(hen->h_addr, &node->url_sin.sin_addr, hen->h_length);
279 node->url_sin.sin_family = hen->h_addrtype;
281 node->url_host = strdup(hostname);
282 node->url_path = strdup(base);
283 *url_nextp = node;
284 url_nextp = &node->url_next;
285 ++url_count;
288 static
289 void
290 run_test(urlnode_t *array, int count)
292 struct timeval tv1;
293 struct timeval tv2;
294 char buf[1024];
295 urlnode_t node;
296 FILE *fp;
297 int loops;
298 int one;
299 int fd;
300 int us;
301 int i;
302 double total_time;
304 total_time = 0.0;
305 one = 1;
308 * Make sure children's random number generators are NOT synchronized.
310 if (random_opt)
311 srandomdev();
313 for (loops = 0; loop_count == 0 || loops < loop_count; ++loops) {
315 * Random requests
317 if (random_opt) {
318 for (i = count * 4; i; --i) {
319 int ex1 = random() % count;
320 int ex2 = random() % count;
321 node = array[ex1];
322 array[ex1] = array[ex2];
323 array[ex2] = node;
328 * Run through the array
330 for (i = 0; i < count; ++i) {
331 node = array[i];
333 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
334 perror("socket");
335 exit(1);
337 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
338 gettimeofday(&tv1, NULL);
339 if (connect(fd, (void *)&node->url_sin, node->url_sin.sin_len) < 0) {
340 gettimeofday(&tv2, NULL);
341 us = (tv2.tv_sec - tv1.tv_sec) * 1000000;
342 us += (int)(tv2.tv_usec - tv1.tv_usec) / 1000000;
343 printf("connect_failure %6.2fms: http://%s:%d/%s\n",
344 (double)us / 1000.0, node->url_host,
345 ntohs(node->url_sin.sin_port),
346 node->url_path);
347 close(fd);
348 continue;
350 if ((fp = fdopen(fd, "r+")) == NULL) {
351 perror("fdopen");
352 exit(1);
354 fprintf(fp, "GET /%s HTTP/1.1\r\n"
355 "Host: %s\r\n\r\n",
356 node->url_path,
357 node->url_host);
358 fflush(fp);
359 shutdown(fileno(fp), SHUT_WR);
360 while (fgets(buf, sizeof(buf), fp) != NULL)
362 fclose(fp);
363 gettimeofday(&tv2, NULL);
364 us = (tv2.tv_sec - tv1.tv_sec) * 1000000;
365 us += (int)(tv2.tv_usec - tv1.tv_usec);
366 if (us > limit_opt) {
367 printf("access_time %6.2fms: http://%s:%d/%s\n",
368 (double)us / 1000.0, node->url_host,
369 ntohs(node->url_sin.sin_port),
370 node->url_path);
372 total_time += (double)us / 1000000.0;
374 if (report_interval && (loops + 1) % report_interval == 0) {
375 printf("loop_time: %6.3fmS avg/url %6.3fmS\n",
376 total_time / (double)report_interval * 1000.0,
377 total_time / (double)report_interval * 1000.0 /
378 (double)count);
379 total_time = 0.0;
382 * don't let the loops variable wrap if we are running
383 * forever, it will cause weird times to be reported.
385 if (loop_count == 0)
386 loops = 0;