"Fossies" - the Fresh Open Source Software Archive 
Member "httperf-0.9.0/src/httperf.c" (7 Apr 2007, 28474 Bytes) of package /linux/www/old/httperf-0.9.0.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "httperf.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 httperf -- a tool for measuring web server performance
3 Copyright 2000-2007 Hewlett-Packard Company and Contributors listed in
4 AUTHORS file. Originally contributed by David Mosberger-Tang
5
6 This file is part of httperf, a web server performance measurment
7 tool.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
13
14 In addition, as a special exception, the copyright holders give
15 permission to link the code of this work with the OpenSSL project's
16 "OpenSSL" library (or with modified versions of it that use the same
17 license as the "OpenSSL" library), and distribute linked combinations
18 including the two. You must obey the GNU General Public License in
19 all respects for all of the code used other than "OpenSSL". If you
20 modify this file, you may extend this exception to your version of the
21 file, but you are not obligated to do so. If you do not wish to do
22 so, delete this exception statement from your version.
23
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 02110-1301, USA
33 */
34
35 /*
36 Fundamentals:
37
38 There are three subsystems to httperf:
39
40 1) The load generator which determines what URI is fetched next.
41
42 2) The core engine that handles the mechanics of issuing a request.
43
44 3) The instrumentation infrastructure that measures various aspects
45 of the transaction(s).
46
47 Since there is considerable potential variation in all three, it
48 seems like an event-based approach might be ideal in tying the three
49 together. Ideally, it should be possible to write a new load
50 generator without modifications to the other subsystems. Similarly,
51 it should be possible to add instrumentation without requiring
52 changes to the load generator or http engine.
53
54 Axioms:
55 - The only point at which the client will fall back is if
56 the client itself is overloaded. There is no point trying
57 to fix up this case---simply declare defeat and abort the
58 test.
59 */
60 #include "config.h"
61
62 #include <ctype.h>
63 #include <errno.h>
64 #include <getopt.h>
65 #include <signal.h>
66 #include <stdarg.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <unistd.h>
71
72 #include <sys/time.h>
73 #include <sys/resource.h>
74
75 #include <core.h>
76 #include <event.h>
77 #include <httperf.h>
78 #include <conn.h>
79 #include <timer.h>
80
81 #ifdef HAVE_SSL
82 # include <openssl/rand.h>
83 #endif
84
85 #define RATE_INTERVAL 5.0
86
87 const char *prog_name;
88 int verbose;
89 Cmdline_Params param;
90 Time test_time_start;
91 Time test_time_stop;
92 struct rusage test_rusage_start;
93 struct rusage test_rusage_stop;
94 size_t object_type_size[OBJ_NUM_TYPES];
95
96 #ifdef HAVE_SSL
97 SSL_CTX *ssl_ctx;
98 #endif
99
100 #ifdef DEBUG
101 int debug_level;
102 #endif
103
104 static Time perf_sample_start;
105
106 static struct option longopts[] =
107 {
108 {"add-header", required_argument, (int *) ¶m.additional_header, 0},
109 {"burst-length", required_argument, ¶m.burst_len, 0},
110 {"client", required_argument, (int *) ¶m.client, 0},
111 {"close-with-reset", no_argument, ¶m.close_with_reset, 1},
112 {"debug", required_argument, 0, 'd'},
113 {"failure-status", required_argument, ¶m.failure_status, 0},
114 {"help", no_argument, 0, 'h'},
115 {"hog", no_argument, ¶m.hog, 1},
116 {"http-version", required_argument, (int *) ¶m.http_version, 0},
117 {"max-connections", required_argument, ¶m.max_conns, 0},
118 {"max-piped-calls", required_argument, ¶m.max_piped, 0},
119 {"method", required_argument, (int *) ¶m.method, 0},
120 {"no-host-hdr", no_argument, ¶m.no_host_hdr, 1},
121 {"num-calls", required_argument, (int *) ¶m.num_calls, 0},
122 {"num-conns", required_argument, (int *) ¶m.num_conns, 0},
123 {"period", required_argument, (int *) ¶m.rate.mean_iat, 0},
124 {"port", required_argument, (int *) ¶m.port, 0},
125 {"print-reply", optional_argument, ¶m.print_reply, 0},
126 {"print-request",optional_argument, ¶m.print_request, 0},
127 {"rate", required_argument, (int *) ¶m.rate, 0},
128 {"recv-buffer", required_argument, (int *) ¶m.recv_buffer_size, 0},
129 {"retry-on-failure", no_argument, ¶m.retry_on_failure, 1},
130 {"send-buffer", required_argument, (int *) ¶m.send_buffer_size, 0},
131 {"server", required_argument, (int *) ¶m.server, 0},
132 {"server-name", required_argument, (int *) ¶m.server_name, 0},
133 {"session-cookies", no_argument, (int *) ¶m.session_cookies, 1},
134 #ifdef HAVE_SSL
135 {"ssl", no_argument, ¶m.use_ssl, 1},
136 {"ssl-ciphers", required_argument, (int *) ¶m.ssl_cipher_list, 0},
137 {"ssl-no-reuse", no_argument, ¶m.ssl_reuse, 0},
138 #endif
139 {"think-timeout",required_argument, (int *) ¶m.think_timeout, 0},
140 {"timeout", required_argument, (int *) ¶m.timeout, 0},
141 {"uri", required_argument, (int *) ¶m.uri, 0},
142 {"verbose", no_argument, 0, 'v'},
143 {"version", no_argument, 0, 'V'},
144 {"wlog", required_argument, (int *) ¶m.wlog, 0},
145 {"wsess", required_argument, (int *) ¶m.wsess, 0},
146 {"wsesslog", required_argument, (int *) ¶m.wsesslog, 0},
147 {"wsesspage", required_argument, (int *) ¶m.wsesspage, 0},
148 {"wset", required_argument, (int *) ¶m.wset, 0},
149 {0, 0, 0, 0}
150 };
151
152 static void
153 usage (void)
154 {
155 printf ("Usage: %s "
156 "[-hdvV] [--add-header S] [--burst-length N] [--client N/N]\n"
157 "\t[--close-with-reset] [--debug N] [--failure-status N]\n"
158 "\t[--help] [--hog] [--http-version S] [--max-connections N]\n"
159 "\t[--max-piped-calls N] [--method S] [--no-host-hdr]\n"
160 "\t[--num-calls N] [--num-conns N] [--period [d|u|e]T1[,T2]]\n"
161 "\t[--port N] "
162 "[--print-reply [header|body]] [--print-request [header|body]]\n"
163 "\t[--rate X] [--recv-buffer N] [--retry-on-failure] "
164 "[--send-buffer N]\n"
165 "\t[--server S] [--server-name S] [--session-cookies]\n"
166 #ifdef HAVE_SSL
167 "\t[--ssl] [--ssl-ciphers L] [--ssl-no-reuse]\n"
168 #endif
169 "\t[--think-timeout X] [--timeout X] [--uri S] [--verbose] "
170 "[--version]\n"
171 "\t[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]\n"
172 "\t[--wset N,X]\n",
173 prog_name);
174 }
175
176 void
177 panic (const char *msg, ...)
178 {
179 va_list va;
180
181 va_start (va, msg);
182 vfprintf (stderr, msg, va);
183 va_end (va);
184 exit (1);
185 }
186
187 void
188 no_op (void)
189 {
190 }
191
192 static void
193 perf_sample (Timer *t, Any_Type regarg)
194 {
195 Any_Type callarg;
196
197 callarg.d = 1.0 / (timer_now () - perf_sample_start);
198 event_signal (EV_PERF_SAMPLE, 0, callarg);
199
200 /* prepare for next sample interval: */
201 perf_sample_start = timer_now ();
202 timer_schedule (perf_sample, regarg, RATE_INTERVAL);
203 }
204
205 int
206 main (int argc, char **argv)
207 {
208 extern Load_Generator uri_fixed, uri_wlog, uri_wset, conn_rate, call_seq;
209 extern Load_Generator wsess, wsesslog, wsesspage, sess_cookie, misc;
210 extern Stat_Collector stats_basic, session_stat;
211 extern Stat_Collector stats_print_reply;
212 extern char *optarg;
213 int session_workload = 0;
214 int num_gen = 3;
215 Load_Generator *gen[5] =
216 {
217 &call_seq,
218 &uri_fixed,
219 &conn_rate,
220 };
221 int num_stats = 1;
222 Stat_Collector *stat[3] =
223 {
224 &stats_basic
225 };
226 int i, ch, longindex;
227 u_int minor, major;
228 char *end, *name;
229 Any_Type arg;
230 void *flag;
231 Time t;
232
233 #ifdef __FreeBSD__
234 /* This works around a bug in earlier versions of FreeBSD that cause
235 non-finite IEEE arithmetic to cause SIGFPE instead of the
236 non-finite arithmetic as defined by IEEE. */
237 fpsetmask (0);
238 #endif
239
240 object_type_size[OBJ_CONN] = sizeof (Conn);
241 object_type_size[OBJ_CALL] = sizeof (Call);
242
243 param.http_version = 0x10001; /* default to HTTP/1.1 */
244 param.client.id = 0;
245 param.client.num_clients = 1;
246 param.server = "localhost";
247 param.port = -1;
248 param.uri = "/";
249 param.num_calls = 1;
250 param.burst_len = 1;
251 param.num_conns = 1;
252 /* These should be set to the minimum of 2*bandwidth*delay and the
253 maximum request/reply size for single-call connections. */
254 param.send_buffer_size = 4096;
255 param.recv_buffer_size = 16384;
256 param.rate.dist = DETERMINISTIC;
257 #ifdef HAVE_SSL
258 param.ssl_reuse = 1;
259 #endif
260
261 /* get program name: */
262 prog_name = strrchr (argv[0], '/');
263 if (prog_name)
264 ++prog_name;
265 else
266 prog_name = argv[0];
267
268 /* process command line options: */
269 while ((ch = getopt_long (argc, argv, "d:hvV", longopts, &longindex)) >= 0)
270 {
271 switch (ch)
272 {
273 case 0:
274 flag = longopts[longindex].flag;
275
276 if (flag == ¶m.method)
277 param.method = optarg;
278 else if (flag == ¶m.additional_header)
279 param.additional_header = optarg;
280 else if (flag == ¶m.num_calls)
281 {
282 errno = 0;
283 param.num_calls = strtoul (optarg, &end, 10);
284 if (errno == ERANGE || end == optarg || *end)
285 {
286 fprintf (stderr, "%s: illegal number of calls %s\n",
287 prog_name, optarg);
288 exit (1);
289 }
290 }
291 else if (flag == ¶m.http_version)
292 {
293 if (sscanf (optarg, "%u.%u", &major, &minor) != 2)
294 {
295 fprintf (stderr, "%s: illegal version number %s\n",
296 prog_name, optarg);
297 exit (1);
298 }
299 param.http_version = (major << 16) | (minor & 0xffff);
300 }
301 else if (flag == ¶m.burst_len)
302 {
303 errno = 0;
304 param.burst_len = strtoul (optarg, &end, 10);
305 if (errno == ERANGE || end == optarg || *end
306 || param.burst_len < 1)
307 {
308 fprintf (stderr, "%s: illegal burst-length %s\n",
309 prog_name, optarg);
310 exit (1);
311 }
312 }
313 else if (flag == ¶m.failure_status)
314 {
315 errno = 0;
316 param.failure_status = strtoul (optarg, &end, 10);
317 if (errno == ERANGE || end == optarg || *end
318 || param.failure_status <= 0)
319 {
320 fprintf (stderr, "%s: illegal failure status %s\n",
321 prog_name, optarg);
322 exit (1);
323 }
324 }
325 else if (flag == ¶m.num_conns)
326 {
327 errno = 0;
328 param.num_conns = strtoul (optarg, &end, 10);
329 if (errno == ERANGE || end == optarg || *end)
330 {
331 fprintf (stderr, "%s: illegal number of connections %s\n",
332 prog_name, optarg);
333 exit (1);
334 }
335 }
336 else if (flag == ¶m.max_conns)
337 {
338 errno = 0;
339 param.max_conns = strtoul (optarg, &end, 10);
340 if (errno == ERANGE || end == optarg || *end
341 || param.max_conns < 0)
342 {
343 fprintf (stderr, "%s: illegal max. # of connection %s\n",
344 prog_name, optarg);
345 exit (1);
346 }
347 }
348 else if (flag == ¶m.max_piped)
349 {
350 errno = 0;
351 param.max_piped = strtoul (optarg, &end, 10);
352 if (errno == ERANGE || end == optarg || *end
353 || param.max_piped < 0)
354 {
355 fprintf (stderr, "%s: illegal max. # of piped calls %s\n",
356 prog_name, optarg);
357 exit (1);
358 }
359 }
360 else if (flag == ¶m.port)
361 {
362 errno = 0;
363 param.port = strtoul (optarg, &end, 10);
364 if (errno == ERANGE || end == optarg || *end
365 || (unsigned) param.port > 0xffff)
366 {
367 fprintf (stderr, "%s: illegal port number %s\n",
368 prog_name, optarg);
369 exit (1);
370 }
371 }
372 else if (flag == ¶m.print_request || flag == ¶m.print_reply)
373 {
374 int val;
375
376 if (!optarg)
377 val = PRINT_HEADER | PRINT_BODY;
378 else
379 switch (tolower (optarg[0]))
380 {
381 case 'h': val = PRINT_HEADER; break;
382 case 'b': val = PRINT_BODY; break;
383 default: val = PRINT_HEADER | PRINT_BODY; break;
384 }
385 *(int *) flag = val;
386 }
387 else if (flag == ¶m.rate)
388 {
389 errno = 0;
390 param.rate.rate_param = strtod (optarg, &end);
391 if (errno == ERANGE || end == optarg || *end)
392 {
393 fprintf (stderr, "%s: illegal request rate %s\n",
394 prog_name, optarg);
395 exit (1);
396 }
397 if (param.rate.rate_param <= 0.0)
398 param.rate.mean_iat = 0.0;
399 else
400 param.rate.mean_iat = 1/param.rate.rate_param;
401 param.rate.dist = DETERMINISTIC;
402 }
403 else if (flag == ¶m.rate.mean_iat) /* --period */
404 {
405 param.rate.dist = DETERMINISTIC;
406 if (!isdigit (*optarg))
407 switch (tolower(*optarg++))
408 {
409 case 'd': param.rate.dist = DETERMINISTIC; break;
410 case 'u': param.rate.dist = UNIFORM; break;
411 case 'e': param.rate.dist = EXPONENTIAL; break;
412 default:
413 fprintf (stderr, "%s: illegal interarrival distribution "
414 "'%c' in %s\n",
415 prog_name, optarg[-1], optarg - 1);
416 exit (1);
417 }
418
419 /* remaining params depend on selected distribution: */
420 errno = 0;
421 switch (param.rate.dist)
422 {
423 case DETERMINISTIC:
424 case EXPONENTIAL:
425 param.rate.mean_iat = strtod (optarg, &end);
426 if (errno == ERANGE || end == optarg || *end
427 || param.rate.mean_iat < 0)
428 {
429 fprintf (stderr, "%s: illegal mean interarrival "
430 "time %s\n", prog_name, optarg);
431 exit (1);
432 }
433 break;
434
435 case UNIFORM:
436 param.rate.min_iat = strtod (optarg, &end);
437 if (errno == ERANGE || end == optarg
438 || param.rate.min_iat < 0)
439 {
440 fprintf (stderr, "%s: illegal minimum interarrival "
441 "time %s\n", prog_name, optarg);
442 exit (1);
443 }
444 if (*end != ',')
445 {
446 fprintf (stderr, "%s: minimum interarrival time not "
447 "followed by `,MAX_IAT' (rest: `%s')\n",
448 prog_name, end);
449 exit (1);
450 }
451 optarg = end + 1;
452 param.rate.max_iat = strtod (optarg, &end);
453 if (errno == ERANGE || end == optarg || *end
454 || param.rate.max_iat < 0)
455 {
456 fprintf (stderr, "%s: illegal request period %s\n",
457 prog_name, optarg);
458 exit (1);
459 }
460 param.rate.mean_iat = 0.5*(param.rate.min_iat
461 + param.rate.max_iat);
462 break;
463
464 default:
465 fprintf (stderr, "%s: internal error parsing %s\n",
466 prog_name, optarg);
467 exit (1);
468 break;
469 }
470 param.rate.rate_param = ((param.rate.mean_iat <= 0.0)
471 ? 0.0 : (1.0 / param.rate.mean_iat));
472 }
473 else if (flag == ¶m.recv_buffer_size)
474 {
475 errno = 0;
476 param.recv_buffer_size = strtoul (optarg, &end, 10);
477 if (errno == ERANGE || end == optarg || *end
478 || param.port > 0xffff)
479 {
480 fprintf (stderr, "%s: illegal receive buffer size %s\n",
481 prog_name, optarg);
482 exit (1);
483 }
484 }
485 else if (flag == ¶m.send_buffer_size)
486 {
487 errno = 0;
488 param.send_buffer_size = strtoul (optarg, &end, 10);
489 if (errno == ERANGE || end == optarg || *end
490 || param.port > 0xffff)
491 {
492 fprintf (stderr, "%s: illegal send buffer size %s\n",
493 prog_name, optarg);
494 exit (1);
495 }
496 }
497 else if (flag == ¶m.client)
498 {
499 errno = 0;
500 param.client.id = strtoul (optarg, &end, 0);
501 if (end == optarg || errno == ERANGE)
502 {
503 fprintf (stderr, "%s: bad client id (rest: `%s')\n",
504 prog_name, optarg);
505 exit (1);
506 }
507
508 if (*end != '/')
509 {
510 fprintf (stderr,
511 "%s: client id not followed by `/' (rest: `%s')\n",
512 prog_name, end);
513 exit (1);
514 }
515 optarg = end + 1;
516
517 param.client.num_clients = strtoul (optarg, &end, 0);
518 if (end == optarg || errno == ERANGE
519 || param.client.id >= param.client.num_clients)
520 {
521 fprintf (stderr, "%s: bad number of clients (rest: `%s')\n",
522 prog_name, optarg);
523 exit (1);
524 }
525 }
526 else if (flag == ¶m.server)
527 param.server = optarg;
528 else if (flag == ¶m.server_name)
529 param.server_name = optarg;
530 #ifdef HAVE_SSL
531 else if (flag == ¶m.ssl_cipher_list)
532 param.ssl_cipher_list = optarg;
533 #endif
534 else if (flag == ¶m.uri)
535 param.uri = optarg;
536 else if (flag == ¶m.think_timeout)
537 {
538 errno = 0;
539 param.think_timeout = strtod (optarg, &end);
540 if (errno == ERANGE || end == optarg || *end)
541 {
542 fprintf (stderr, "%s: illegal think timeout value %s\n",
543 prog_name, optarg);
544 exit (1);
545 }
546 }
547 else if (flag == ¶m.timeout)
548 {
549 errno = 0;
550 param.timeout = strtod (optarg, &end);
551 if (errno == ERANGE || end == optarg || *end)
552 {
553 fprintf (stderr, "%s: illegal connect timeout %s\n",
554 prog_name, optarg);
555 exit (1);
556 }
557 }
558 else if (flag == ¶m.wlog)
559 {
560 gen[1] = &uri_wlog; /* XXX fix me---somehow */
561
562 param.wlog.do_loop = (*optarg == 'y') || (*optarg == 'Y');
563 param.wlog.file = optarg + 2;
564 }
565 else if (flag == ¶m.wsess)
566 {
567 num_gen = 2; /* XXX fix me---somehow */
568 gen[0] = &wsess;
569
570 stat[num_stats++] = &session_stat;
571
572 errno = 0;
573 name = "bad number of sessions (1st param)";
574 param.wsess.num_sessions = strtoul (optarg, &end, 0);
575 if (end == optarg || errno == ERANGE)
576 goto bad_wsess_param;
577 optarg = end + 1;
578
579 name = "bad number of calls per session (2nd param)";
580 if (*end != ',')
581 goto bad_wsess_param;
582 optarg = end + 1;
583
584 param.wsess.num_calls = strtoul (optarg, &end, 0);
585 if (end == optarg || errno == ERANGE)
586 goto bad_wsess_param;
587
588 name = "bad user think time (3rd param)";
589 if (*end != ',')
590 goto bad_wsess_param;
591 optarg = end + 1;
592
593 param.wsess.think_time = strtod (optarg, &end);
594 if (end == optarg || errno == ERANGE
595 || param.wsess.think_time < 0.0)
596 goto bad_wsess_param;
597
598 name = "extraneous parameter";
599 if (*end)
600 {
601 bad_wsess_param:
602 fprintf (stderr, "%s: %s in --wsess arg (rest: `%s')",
603 prog_name, name, end);
604 if (errno)
605 fprintf (stderr, ": %s", strerror (errno));
606 fputc ('\n', stderr);
607 exit (1);
608 }
609 session_workload = 1;
610 }
611 else if (flag == ¶m.wsesspage)
612 {
613 num_gen = 2; /* XXX fix me---somehow */
614 gen[0] = &wsesspage;
615
616 stat[num_stats++] = &session_stat;
617
618 errno = 0;
619 name = "bad number of sessions (1st param)";
620 param.wsesspage.num_sessions = strtoul (optarg, &end, 0);
621 if (end == optarg || errno == ERANGE)
622 goto bad_wsesspage_param;
623 optarg = end + 1;
624
625 name = "bad number of user requests per session (2nd param)";
626 if (*end != ',')
627 goto bad_wsesspage_param;
628 optarg = end + 1;
629
630 param.wsesspage.num_reqs = strtoul (optarg, &end, 0);
631 if (end == optarg || errno == ERANGE)
632 goto bad_wsesspage_param;
633
634 name = "bad user think time (3rd param)";
635 if (*end != ',')
636 goto bad_wsesspage_param;
637 optarg = end + 1;
638
639 param.wsesspage.think_time = strtod (optarg, &end);
640 if (end == optarg || errno == ERANGE
641 || param.wsesspage.think_time < 0.0)
642 goto bad_wsesspage_param;
643
644 name = "extraneous parameter";
645 if (*end)
646 {
647 bad_wsesspage_param:
648 fprintf (stderr, "%s: %s in --wsesspage arg (rest: `%s')",
649 prog_name, name, end);
650 if (errno)
651 fprintf (stderr, ": %s", strerror (errno));
652 fputc ('\n', stderr);
653 exit (1);
654 }
655 session_workload = 1;
656 }
657 else if (flag == ¶m.wsesslog)
658 {
659 num_gen = 1; /* XXX fix me---somehow */
660 gen[0] = &wsesslog;
661
662 stat[num_stats++] = &session_stat;
663
664 errno = 0;
665 name = "bad number of sessions (1st param)";
666 param.wsesslog.num_sessions = strtoul (optarg, &end, 0);
667 if (end == optarg || errno == ERANGE)
668 goto bad_wsesslog_param;
669 optarg = end + 1;
670
671 name = "bad user think time (2nd param)";
672 if (*end != ',')
673 goto bad_wsesslog_param;
674 optarg = end + 1;
675
676 param.wsesslog.think_time = strtod (optarg, &end);
677 if (end == optarg || errno == ERANGE
678 || param.wsesslog.think_time < 0.0)
679 goto bad_wsesslog_param;
680
681 name = "bad session filename (3rd param)";
682 if (*end != ',')
683 goto bad_wsesslog_param;
684 optarg = end + 1;
685
686 /* simulate parsing of string */
687 param.wsesslog.file = optarg;
688 if ((end = strchr (optarg, ',')) == NULL)
689 /* must be last param, position end at final \0 */
690 end = optarg + strlen(optarg);
691 else
692 /* terminate end of string */
693 *end++ = '\0';
694 optarg = end;
695
696 name = "extraneous parameter";
697 if (*end)
698 {
699 bad_wsesslog_param:
700 fprintf (stderr, "%s: %s in --wsesslog arg (rest: `%s')",
701 prog_name, name, end);
702 if (errno)
703 fprintf (stderr, ": %s", strerror (errno));
704 fputc ('\n', stderr);
705 exit (1);
706 }
707 session_workload = 1;
708 }
709 else if (flag == ¶m.wset)
710 {
711 gen[1] = &uri_wset; /* XXX fix me---somehow */
712
713 errno = 0;
714 name = "bad working set size (1st parameter)";
715 param.wset.num_files = strtoul (optarg, &end, 0);
716 if (end == optarg || errno == ERANGE)
717 goto bad_wset_param;
718
719 name = "bad target miss rate (2nd parameter)";
720 if (*end != ',')
721 goto bad_wset_param;
722 optarg = end + 1;
723
724 param.wset.target_miss_rate = strtod (optarg, &end);
725 if (end == optarg || errno == ERANGE
726 || param.wset.target_miss_rate < 0.0
727 || param.wset.target_miss_rate > 1.0)
728 goto bad_wset_param;
729
730 name = "extraneous parameter";
731 if (*end)
732 {
733 bad_wset_param:
734 fprintf (stderr, "%s: %s in --wset arg (rest: `%s')",
735 prog_name, name, optarg);
736 if (errno)
737 fprintf (stderr, ": %s", strerror (errno));
738 fputc ('\n', stderr);
739 exit (1);
740 }
741 }
742 break;
743
744 case 'd':
745 #ifdef DEBUG
746 errno = 0;
747 debug_level = strtoul (optarg, &end, 10);
748 if (errno == ERANGE || end == optarg || *end)
749 {
750 fprintf (stderr, "%s: illegal debug level %s\n",
751 prog_name, optarg);
752 exit (1);
753 }
754 #else
755 fprintf (stderr, "%s: sorry, need to recompile with -DDEBUG on...\n",
756 prog_name);
757 #endif
758 break;
759
760 case 'v':
761 ++verbose;
762 break;
763
764 case 'V':
765 printf ("%s: httperf-"VERSION" compiled "__DATE__" with"
766 #ifndef DEBUG
767 "out"
768 #endif
769 " DEBUG with"
770 #ifndef TIME_SYSCALLS
771 "out"
772 #endif
773 " TIME_SYSCALLS.\n", prog_name);
774 break;
775
776 case 'h':
777 usage ();
778 exit (0);
779
780 case ':':
781 fprintf (stderr, "%s: parameter missing for option %s\n",
782 prog_name, longopts[longindex].name);
783 exit (1);
784
785 case '?':
786 /* Invalid or ambiguous option name or extraneous parameter.
787 getopt_long () already issued an explanation to the user,
788 so all we do is call it quites. */
789 exit (1);
790
791 default:
792 fprintf (stderr,
793 "%s: getopt_long: unexpected value (%d)\n",
794 prog_name, ch);
795 exit (1);
796 }
797 }
798
799 #ifdef HAVE_SSL
800 if (param.use_ssl)
801 {
802 char buf[1024];
803
804 if (param.port < 0)
805 param.port = 443;
806
807 SSL_load_error_strings ();
808 SSLeay_add_ssl_algorithms ();
809
810 /* for some strange reason, SSLv23_client_method () doesn't work here */
811 ssl_ctx = SSL_CTX_new (SSLv3_client_method ());
812 if (!ssl_ctx)
813 {
814 ERR_print_errors_fp (stderr);
815 exit (-1);
816 }
817
818 memset (buf, 0, sizeof (buf));
819 RAND_seed (buf, sizeof (buf));
820 }
821 #endif
822 if (param.port < 0)
823 param.port = 80;
824
825 if (param.print_reply || param.print_request)
826 stat[num_stats++] = &stats_print_reply;
827
828 if (param.session_cookies)
829 {
830 if (!session_workload)
831 {
832 fprintf (stderr, "%s: --session-cookie requires session-oriented "
833 "workload (e.g., --wsess)\n", prog_name);
834 exit (-1);
835 }
836 gen[num_gen++] = &sess_cookie;
837 }
838
839 if (param.additional_header || param.method)
840 gen[num_gen++] = &misc;
841
842 /* echo command invocation for logging purposes: */
843 printf ("%s", prog_name);
844 if (verbose) printf (" --verbose");
845 switch (param.print_reply)
846 {
847 case 0: break;
848 case PRINT_HEADER: printf (" --print-reply=header"); break;
849 case PRINT_BODY: printf (" --print-reply=body"); break;
850 default: printf (" --print-reply"); break;
851 }
852 switch (param.print_request)
853 {
854 case 0: break;
855 case PRINT_HEADER: printf (" --print-request=header"); break;
856 case PRINT_BODY: printf (" --print-request=body"); break;
857 default: printf (" --print-request"); break;
858 }
859 if (param.hog) printf (" --hog");
860 if (param.close_with_reset) printf (" --close-with-reset");
861 if (param.think_timeout > 0) printf (" --think-timeout=%g",
862 param.think_timeout);
863 if (param.timeout > 0) printf (" --timeout=%g", param.timeout);
864 printf (" --client=%u/%u", param.client.id, param.client.num_clients);
865 if (param.server) printf (" --server=%s", param.server);
866 if (param.server_name) printf (" --server_name=%s", param.server_name);
867 if (param.port) printf (" --port=%d", param.port);
868 if (param.uri) printf (" --uri=%s", param.uri);
869 if (param.failure_status) printf (" --failure-status=%u",
870 param.failure_status);
871 if (param.http_version != 0x10001)
872 printf (" --http-version=%u.%u", param.http_version >> 16,
873 param.http_version & 0xffff);
874 if (param.max_conns)
875 printf (" --max-connections=%u", param.max_conns);
876 if (param.max_piped)
877 printf (" --max-piped-calls=%u", param.max_piped);
878 if (param.rate.rate_param > 0.0)
879 {
880 switch (param.rate.dist)
881 {
882 case DETERMINISTIC:
883 /* for backwards compatibility, continue to use --rate: */
884 printf (" --rate=%g", param.rate.rate_param);
885 break;
886
887 case UNIFORM:
888 printf (" --period=u%g,%g",
889 param.rate.min_iat, param.rate.max_iat);
890 break;
891
892 case EXPONENTIAL:
893 printf (" --period=e%g", param.rate.mean_iat);
894 break;
895
896 default:
897 printf("--period=??");
898 break;
899 }
900 }
901 printf (" --send-buffer=%d", param.send_buffer_size);
902 if (param.retry_on_failure) printf (" --retry-on-failure");
903 printf (" --recv-buffer=%d", param.recv_buffer_size);
904 if (param.session_cookies) printf (" --session-cookies");
905 #ifdef HAVE_SSL
906 if (param.use_ssl) printf (" --ssl");
907 if (param.ssl_cipher_list)
908 printf(" --ssl-ciphers=%s", param.ssl_cipher_list);
909 if (!param.ssl_reuse) printf (" --ssl-no-reuse");
910 #endif
911 if (param.additional_header)
912 printf (" --add-header='%s'", param.additional_header);
913 if (param.method) printf (" --method=%s", param.method);
914 if (param.wsesslog.num_sessions)
915 {
916 /* This overrides any --wsess, --num-conns, --num-calls,
917 --burst-length and any uri generator */
918 printf (" --wsesslog=%u,%.3f,%s", param.wsesslog.num_sessions,
919 param.wsesslog.think_time, param.wsesslog.file);
920 }
921 else if (param.wsesspage.num_sessions)
922 {
923 printf (" --wsesspage=%u,%u,%.3f", param.wsesspage.num_sessions,
924 param.wsesspage.num_reqs, param.wsesspage.think_time);
925 }
926 else
927 {
928 if (param.wsess.num_sessions)
929 printf (" --wsess=%u,%u,%.3f", param.wsess.num_sessions,
930 param.wsess.num_calls, param.wsess.think_time);
931 else
932 {
933 if (param.num_conns) printf (" --num-conns=%d", param.num_conns);
934 if (param.num_calls) printf (" --num-calls=%d",
935 param.num_calls);
936 }
937 if (param.burst_len != 1) printf (" --burst-length=%d", param.burst_len);
938 if (param.wset.num_files) printf (" --wset=%u,%.3f",
939 param.wset.num_files,
940 param.wset.target_miss_rate);
941 }
942 printf ("\n");
943
944 timer_init ();
945 core_init ();
946
947 signal (SIGINT, (void (*)()) core_exit);
948
949 for (i = 0; i < num_stats; ++i)
950 (*stat[i]->init)();
951 for (i = 0; i < num_gen; ++i)
952 (*gen[i]->init) ();
953
954 /* Update `now'. This is to keep things accurate even when some of
955 the initialization routines take a long time to execute. */
956 timer_tick ();
957
958 /* ensure that clients sample rates at different times: */
959 t = (param.client.id + 1.0)*RATE_INTERVAL/param.client.num_clients;
960 arg.l = 0;
961 timer_schedule (perf_sample, arg, t);
962 perf_sample_start = timer_now ();
963
964 for (i = 0; i < num_gen; ++i)
965 (*gen[i]->start) ();
966 for (i = 0; i < num_stats; ++i)
967 (*stat[i]->start)();
968
969 getrusage (RUSAGE_SELF, &test_rusage_start);
970 test_time_start = timer_now ();
971 core_loop ();
972 test_time_stop = timer_now ();
973 getrusage (RUSAGE_SELF, &test_rusage_stop);
974
975 for (i = 0; i < num_stats; ++i)
976 (*stat[i]->stop)();
977 for (i = 0; i < num_gen; ++i)
978 (*gen[i]->stop) ();
979 for (i = 0; i < num_stats; ++i)
980 (*stat[i]->dump)();
981
982 return 0;
983 }