"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 *) &param.additional_header, 0},
  109     {"burst-length", required_argument, &param.burst_len,       0},
  110     {"client",       required_argument, (int *) &param.client,      0},
  111     {"close-with-reset", no_argument,   &param.close_with_reset,    1},
  112     {"debug",        required_argument, 0,              'd'},
  113     {"failure-status", required_argument, &param.failure_status,    0},
  114     {"help",         no_argument,   0,              'h'},
  115     {"hog",      no_argument,   &param.hog,         1},
  116     {"http-version", required_argument, (int *) &param.http_version,    0},
  117     {"max-connections", required_argument, &param.max_conns,        0},
  118     {"max-piped-calls", required_argument, &param.max_piped,        0},
  119     {"method",       required_argument, (int *) &param.method,      0},
  120     {"no-host-hdr",  no_argument,   &param.no_host_hdr,     1},
  121     {"num-calls",    required_argument, (int *) &param.num_calls,   0},
  122     {"num-conns",    required_argument, (int *) &param.num_conns,   0},
  123     {"period",       required_argument, (int *) &param.rate.mean_iat,   0},
  124     {"port",         required_argument, (int *) &param.port,        0},
  125     {"print-reply",  optional_argument, &param.print_reply,     0},
  126     {"print-request",optional_argument, &param.print_request,       0},
  127     {"rate",         required_argument, (int *) &param.rate,        0},
  128     {"recv-buffer",  required_argument, (int *) &param.recv_buffer_size, 0},
  129     {"retry-on-failure", no_argument,   &param.retry_on_failure,    1},
  130     {"send-buffer",  required_argument, (int *) &param.send_buffer_size, 0},
  131     {"server",       required_argument, (int *) &param.server,      0},
  132     {"server-name",  required_argument, (int *) &param.server_name, 0},
  133     {"session-cookies", no_argument,    (int *) &param.session_cookies, 1},
  134 #ifdef HAVE_SSL
  135     {"ssl",      no_argument,   &param.use_ssl,         1},
  136     {"ssl-ciphers",  required_argument, (int *) &param.ssl_cipher_list, 0},
  137     {"ssl-no-reuse", no_argument,   &param.ssl_reuse,       0},
  138 #endif
  139     {"think-timeout",required_argument, (int *) &param.think_timeout,   0},
  140     {"timeout",      required_argument, (int *) &param.timeout,     0},
  141     {"uri",      required_argument, (int *) &param.uri,     0},
  142     {"verbose",      no_argument,   0,              'v'},
  143     {"version",      no_argument,   0,              'V'},
  144     {"wlog",         required_argument, (int *) &param.wlog,            0},
  145     {"wsess",        required_argument, (int *) &param.wsess,       0},
  146     {"wsesslog",     required_argument, (int *) &param.wsesslog,    0},
  147     {"wsesspage",    required_argument, (int *) &param.wsesspage,   0},
  148     {"wset",         required_argument, (int *) &param.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 == &param.method)
  277         param.method = optarg;
  278       else if (flag == &param.additional_header)
  279         param.additional_header = optarg;
  280       else if (flag == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.print_request || flag == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.server)
  527         param.server = optarg;
  528       else if (flag == &param.server_name)
  529         param.server_name = optarg;
  530 #ifdef HAVE_SSL
  531       else if (flag == &param.ssl_cipher_list)
  532         param.ssl_cipher_list = optarg;
  533 #endif
  534       else if (flag == &param.uri)
  535         param.uri = optarg;
  536       else if (flag == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 == &param.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 }