2 * The software known as "DragonFly" or "DragonFly BSD" is distributed under
5 * Copyright (c) 2003, 2004, 2005 The DragonFly Project. All rights reserved.
7 * This code is derived from software contributed to The DragonFly Project
8 * by Matthew Dillon <dillon@backplane.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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
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
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
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>
70 #include <netinet/in.h>
71 #include <netinet/tcp.h>
72 #include <arpa/inet.h>
82 typedef struct urlnode
{
83 struct urlnode
*url_next
;
84 struct sockaddr_in url_sin
;
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
);
96 int limit_opt
= 1000000;
97 int report_interval
= 100;
100 urlnode_t
*url_nextp
= &url_base
;
104 main(int ac
, char **av
)
112 while ((ch
= getopt(ac
, av
, "c:f:l:n:r")) != -1) {
113 printf("CH %c\n", ch
);
116 loop_count
= strtol(optarg
, NULL
, 0);
117 if (report_interval
> loop_count
)
118 report_interval
= loop_count
;
121 fork_opt
= strtol(optarg
, NULL
, 0);
127 read_url_file(optarg
);
130 limit_opt
= strtol(optarg
, NULL
, 0) * 1000;
140 for (i
= 0; i
< ac
; ++i
)
142 if (url_base
== NULL
)
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
) {
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
),
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
);
173 } else if (pid
== (pid_t
)-1) {
174 printf("unable to fork child %d\n", i
);
178 while (wait(NULL
) >= 0 || errno
== EINTR
)
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"
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",
207 read_url_file(const char *path
)
213 if ((fi
= fopen(path
, "r")) != NULL
) {
214 while (fgets(buf
, sizeof(buf
), fi
) != NULL
) {
218 if (len
&& buf
[len
-1] == '\n')
224 fprintf(stderr
, "Unable to open %s\n", path
);
231 add_url(const char *url
)
240 node
= malloc(sizeof(*node
));
241 bzero(node
, sizeof(*node
));
244 if (strncmp(url
, "http://", 7) == 0)
246 if ((ptr
= strchr(base
, '/')) == NULL
) {
247 fprintf(stderr
, "malformed URL: %s\n", base
);
251 hostname
= malloc(ptr
- base
+ 1);
252 bcopy(base
, hostname
, ptr
- base
);
253 hostname
[ptr
- base
] = 0;
255 if ((ptr
= strrchr(hostname
, ':')) != NULL
) {
256 *strrchr(hostname
, ':') = 0;
258 node
->url_sin
.sin_port
= htons(strtol(ptr
, NULL
, 0));
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
);
266 fprintf(stderr
, "unable to parse host/ip: %s (%s)\n",
267 hostname
, strerror(errno
));
272 if ((hen
= gethostbyname(hostname
)) == NULL
) {
273 fprintf(stderr
, "unable to resolve host: %s (%s)\n",
274 hostname
, hstrerror(h_errno
));
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
);
284 url_nextp
= &node
->url_next
;
290 run_test(urlnode_t
*array
, int count
)
308 * Make sure children's random number generators are NOT synchronized.
313 for (loops
= 0; loop_count
== 0 || loops
< loop_count
; ++loops
) {
318 for (i
= count
* 4; i
; --i
) {
319 int ex1
= random() % count
;
320 int ex2
= random() % count
;
322 array
[ex1
] = array
[ex2
];
328 * Run through the array
330 for (i
= 0; i
< count
; ++i
) {
333 if ((fd
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0) {
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
),
350 if ((fp
= fdopen(fd
, "r+")) == NULL
) {
354 fprintf(fp
, "GET /%s HTTP/1.1\r\n"
359 shutdown(fileno(fp
), SHUT_WR
);
360 while (fgets(buf
, sizeof(buf
), fp
) != NULL
)
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
),
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 /
382 * don't let the loops variable wrap if we are running
383 * forever, it will cause weird times to be reported.