"Fossies" - the Fresh Open Source Software Archive

Member "httperf-0.9.0/src/gen/wsesslog.c" (7 Apr 2007, 17210 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 "wsesslog.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 /* Creates sessions at the fixed rate PARAM.RATE.  The session descriptions
   36    are read in from a configuration file.
   37 
   38    There is currently no tool that translates from standard log
   39    formats to the format accepted by this module.
   40 
   41    An example input file follows:
   42 
   43    #
   44    # This file specifies the potentially-bursty uri sequence for a number of
   45    # user sessions.  The format rules of this file are as follows:
   46    #
   47    # Comment lines start with a '#' as the first character.  # anywhere else
   48    # is considered part of the uri.
   49    #
   50    # Lines with only whitespace delimit session definitions (multiple blank
   51    # lines do not generate "null" sessions).
   52    #
   53    # Lines otherwise specify a uri-sequence (1 uri per line).  If the
   54    # first character of the line is whitespace (e.g. space or tab), the
   55    # uri is considered to be part of a burst that is sent out after the
   56    # previous non-burst uri.
   57    #
   58 
   59    # session 1 definition (this is a comment)
   60 
   61    /foo.html
   62     /pict1.gif
   63     /pict2.gif
   64    /foo2.html
   65     /pict3.gif
   66     /pict4.gif
   67 
   68    #session 2 definition
   69 
   70    /foo3.html
   71    /foo4.html
   72     /pict5.gif
   73 
   74    Any comment on this module contact carter@hpl.hp.com.  */
   75 
   76 #include <assert.h>
   77 #include <ctype.h>
   78 #include <errno.h>
   79 #include <stdio.h>
   80 #include <stdlib.h>
   81 #include <string.h>
   82 
   83 #include <httperf.h>
   84 #include <conn.h>
   85 #include <core.h>
   86 #include <event.h>
   87 #include <rate.h>
   88 #include <session.h>
   89 #include <timer.h>
   90 
   91 /* Maximum number of sessions that can be defined in the configuration
   92    file.  */
   93 #define MAX_SESSION_TEMPLATES   1000
   94 
   95 #ifndef TRUE
   96 #define TRUE  (1)
   97 #endif
   98 #ifndef FALSE
   99 #define FALSE (0)
  100 #endif
  101 
  102 #define SESS_PRIVATE_DATA(c)                        \
  103   ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
  104 
  105 typedef struct req REQ;
  106 struct req
  107   {
  108     REQ *next;
  109     int method;
  110     char *uri;
  111     int uri_len;
  112     char *contents;
  113     int contents_len;
  114     char extra_hdrs[50];    /* plenty for "Content-length: 1234567890" */
  115     int extra_hdrs_len;
  116   };
  117 
  118 typedef struct burst BURST;
  119 struct burst
  120   {
  121     BURST *next;
  122     int num_reqs;
  123     Time user_think_time;
  124     REQ *req_list;
  125   };
  126 
  127 typedef struct Sess_Private_Data Sess_Private_Data;
  128 struct Sess_Private_Data
  129   {
  130     u_int num_calls_in_this_burst; /* # of calls created for this burst */
  131     u_int num_calls_target; /* total # of calls desired */
  132     u_int num_calls_destroyed;  /* # of calls destroyed so far */
  133     Timer *timer;       /* timer for session think time */
  134 
  135     int total_num_reqs;     /* total number of requests in this session */
  136 
  137     BURST *current_burst;   /* the current burst we're working on */
  138     REQ *current_req;       /* the current request we're working on */
  139   };
  140 
  141 /* Methods allowed for a request: */
  142 enum
  143   {
  144     HM_DELETE, HM_GET, HM_HEAD, HM_OPTIONS, HM_POST, HM_PUT, HM_TRACE,
  145     HM_LEN
  146   };
  147 
  148 static const char *call_method_name[] =
  149   {
  150     "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "TRACE"
  151   };
  152 
  153 static size_t sess_private_data_offset;
  154 static int num_sessions_generated;
  155 static int num_sessions_destroyed;
  156 static Rate_Generator rg_sess;
  157 
  158 /* This is an array rather than a list because we may want different
  159    httperf clients to start at different places in the sequence of
  160    sessions. */
  161 static int num_templates;
  162 static int next_session_template;
  163 static Sess_Private_Data session_templates[MAX_SESSION_TEMPLATES] =
  164   {
  165     { 0, }
  166   };
  167 
  168 static void
  169 sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  170 {
  171   Sess_Private_Data *priv;
  172   Sess *sess;
  173 
  174   assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  175   sess = (Sess *) obj;
  176 
  177   priv = SESS_PRIVATE_DATA (sess);
  178   if (priv->timer)
  179     {
  180       timer_cancel (priv->timer);
  181       priv->timer = 0;
  182     }
  183 
  184   if (++num_sessions_destroyed >= param.wsesslog.num_sessions)
  185     core_exit ();
  186 }
  187 
  188 static void
  189 issue_calls (Sess *sess, Sess_Private_Data *priv)
  190 {
  191   int i, to_create, retval, n;
  192   const char *method_str;
  193   Call *call;
  194   REQ *req;
  195 
  196   /* Mimic browser behavior of fetching html object, then a couple of
  197      embedded objects: */
  198 
  199   to_create = 1;
  200   if (priv->num_calls_in_this_burst > 0)
  201     to_create = priv->current_burst->num_reqs - priv->num_calls_in_this_burst;
  202 
  203   n = session_max_qlen (sess) - session_current_qlen (sess);
  204   if (n < to_create)
  205     to_create = n;
  206 
  207   priv->num_calls_in_this_burst += to_create;
  208 
  209   for (i = 0; i < to_create; ++i)
  210     {
  211       call = call_new ();
  212       if (!call)
  213     {
  214       sess_failure (sess);
  215       return;
  216     }
  217 
  218       /* fill in the new call: */
  219       req = priv->current_req;
  220       if (req == NULL)
  221     panic ("%s: internal error, requests ran past end of burst\n",
  222            prog_name);
  223 
  224       method_str = call_method_name[req->method];
  225       call_set_method (call, method_str, strlen (method_str));
  226       call_set_uri (call, req->uri, req->uri_len);
  227       if (req->contents_len > 0)
  228     {
  229       /* add "Content-length:" header and contents, if necessary: */
  230       call_append_request_header (call, req->extra_hdrs,
  231                       req->extra_hdrs_len);
  232       call_set_contents (call, req->contents, req->contents_len);
  233     }
  234       priv->current_req = req->next;
  235 
  236       if (DBG > 0)
  237     fprintf (stderr, "%s: accessing URI `%s'\n", prog_name, req->uri);
  238 
  239       retval = session_issue_call (sess, call);
  240       call_dec_ref (call);
  241 
  242       if (retval < 0)
  243     return;
  244     }
  245 }
  246 
  247 static void
  248 user_think_time_expired (Timer *t, Any_Type arg)
  249 {
  250   Sess *sess = arg.vp;
  251   Sess_Private_Data *priv;
  252 
  253   assert (object_is_sess (sess));
  254 
  255   priv = SESS_PRIVATE_DATA (sess);
  256   priv->timer = 0;
  257   issue_calls (sess, priv);
  258 }
  259 
  260 /* Create a new session and fill in our private information.  */
  261 static int
  262 sess_create (Any_Type arg)
  263 {
  264   Sess_Private_Data *priv, *template;
  265   Sess *sess;
  266 
  267   if (num_sessions_generated++ >= param.wsesslog.num_sessions)
  268     return -1;
  269 
  270   sess = sess_new ();
  271 
  272   template = &session_templates[next_session_template];
  273   if (++next_session_template >= num_templates)
  274     next_session_template = 0;
  275 
  276   priv = SESS_PRIVATE_DATA (sess);
  277   priv->current_burst = template->current_burst;
  278   priv->current_req = priv->current_burst->req_list;
  279   priv->total_num_reqs = template->total_num_reqs;
  280   priv->num_calls_target = priv->current_burst->num_reqs;
  281 
  282   if (DBG > 0)
  283     fprintf (stderr, "Starting session, first burst_len = %d\n",
  284          priv->num_calls_target);
  285 
  286   issue_calls (sess, SESS_PRIVATE_DATA (sess));
  287   return 0;
  288 }
  289 
  290 static void
  291 prepare_for_next_burst (Sess *sess, Sess_Private_Data *priv)
  292 {
  293   Time think_time;
  294   Any_Type arg;
  295 
  296   if (priv->current_burst != NULL)
  297     {
  298       think_time = priv->current_burst->user_think_time;
  299 
  300       /* advance to next burst: */
  301       priv->current_burst = priv->current_burst->next;
  302 
  303       if (priv->current_burst != NULL)
  304     {
  305       priv->current_req = priv->current_burst->req_list;
  306       priv->num_calls_in_this_burst = 0;
  307       priv->num_calls_target += priv->current_burst->num_reqs;
  308 
  309       assert (!priv->timer);
  310       arg.vp = sess;
  311       priv->timer = timer_schedule (user_think_time_expired,
  312                     arg, think_time);
  313     }
  314     }
  315 }
  316 
  317 static void
  318 call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  319 {
  320   Sess_Private_Data *priv;
  321   Sess *sess;
  322   Call *call;
  323 
  324   assert (et == EV_CALL_DESTROYED && object_is_call (obj));
  325   call = (Call *) obj;
  326   sess = session_get_sess_from_call (call);
  327   priv = SESS_PRIVATE_DATA (sess);
  328 
  329   if (sess->failed)
  330     return;
  331 
  332   ++priv->num_calls_destroyed;
  333 
  334   if (priv->num_calls_destroyed >= priv->total_num_reqs)
  335     /* we're done with this session */
  336     sess_dec_ref (sess);
  337   else if (priv->num_calls_in_this_burst < priv->current_burst->num_reqs)
  338     issue_calls (sess, priv);
  339   else if (priv->num_calls_destroyed >= priv->num_calls_target)
  340     prepare_for_next_burst (sess, priv);
  341 }
  342 
  343 /* Allocates memory for a REQ and assigns values to data members.
  344    This is used during configuration file parsing only.  */
  345 static REQ*
  346 new_request (char *uristr)
  347 {
  348   REQ *retptr;
  349 
  350   retptr = (REQ *) malloc (sizeof (*retptr));
  351   if (retptr == NULL || uristr == NULL)
  352     panic ("%s: ran out of memory while parsing %s\n",
  353        prog_name, param.wsesslog.file);  
  354 
  355   memset (retptr, 0, sizeof (*retptr));
  356   retptr->uri = uristr;
  357   retptr->uri_len = strlen (uristr);
  358   retptr->method = HM_GET;
  359   return retptr;
  360 }
  361 
  362 /* Like new_request except this is for burst descriptors.  */
  363 static BURST*
  364 new_burst (REQ *r)
  365 {
  366   BURST *retptr;
  367     
  368   retptr = (BURST *) malloc (sizeof (*retptr));
  369   if (retptr == NULL)
  370     panic ("%s: ran out of memory while parsing %s\n",
  371        prog_name, param.wsesslog.file);  
  372   memset (retptr, 0, sizeof (*retptr));
  373   retptr->user_think_time = param.wsesslog.think_time;
  374   retptr->req_list = r;
  375   return retptr;
  376 }
  377 
  378 /* Read in session-defining configuration file and create in-memory
  379    data structures from which to assign uri_s to calls. */
  380 static void
  381 parse_config (void)
  382 {
  383   FILE *fp;
  384   int lineno, i, reqnum;
  385   Sess_Private_Data *sptr;
  386   char line[10000]; /* some uri's get pretty long */
  387   char uri[10000];  /* some uri's get pretty long */
  388   char method_str[1000];
  389   char this_arg[10000];
  390   char contents[10000];
  391   double think_time;
  392   int bytes_read;
  393   REQ *reqptr;
  394   BURST *bptr, *current_burst = 0;
  395   char *from, *to, *parsed_so_far;
  396   int ch;
  397   int single_quoted, double_quoted, escaped, done;
  398 
  399   fp = fopen (param.wsesslog.file, "r");
  400   if (fp == NULL)
  401     panic ("%s: can't open %s\n", prog_name, param.wsesslog.file);  
  402 
  403   num_templates = 0;
  404   sptr = &session_templates[0];
  405 
  406   for (lineno = 1; fgets (line, sizeof (line), fp); lineno++)
  407     {
  408       if (line[0] == '#')
  409     continue;       /* skip over comment lines */
  410 
  411       if (sscanf (line,"%s%n", uri, &bytes_read) != 1)
  412     {
  413       /* must be a session-delimiting blank line */
  414       if (sptr->current_req != NULL)
  415         sptr++;     /* advance to next session */
  416       continue;
  417     }
  418       /* looks like a request-specifying line */
  419       reqptr = new_request (strdup (uri));
  420 
  421       if (sptr->current_req == NULL)
  422     {
  423       num_templates++;
  424       if (num_templates > MAX_SESSION_TEMPLATES)
  425         panic ("%s: too many sessions (%d) specified in %s\n",
  426            prog_name, num_templates, param.wsesslog.file);  
  427       current_burst = sptr->current_burst = new_burst (reqptr);
  428     }
  429       else
  430     {
  431       if (!isspace (line[0]))
  432         /* this uri starts a new burst */
  433         current_burst = (current_burst->next = new_burst (reqptr));
  434       else
  435         sptr->current_req->next = reqptr;
  436     }
  437       /* do some common steps for all new requests */
  438       current_burst->num_reqs++;
  439       sptr->total_num_reqs++;
  440       sptr->current_req = reqptr;
  441 
  442       /* parse rest of line to specify additional parameters of this
  443      request and burst */
  444       parsed_so_far = line + bytes_read;
  445       while (sscanf (parsed_so_far, " %s%n", this_arg, &bytes_read) == 1)
  446     {
  447       if (sscanf (this_arg, "method=%s", method_str) == 1)
  448         {
  449           for (i = 0; i < HM_LEN; i++)
  450         {
  451           if (!strncmp (method_str,call_method_name[i],
  452                 strlen (call_method_name[i])))
  453             {
  454               sptr->current_req->method = i;
  455               break;
  456             }
  457         }
  458           if (i == HM_LEN)
  459         panic ("%s: did not recognize method '%s' in %s\n",
  460                prog_name, method_str, param.wsesslog.file);  
  461         }
  462       else if (sscanf (this_arg, "think=%lf", &think_time) == 1)
  463         current_burst->user_think_time = think_time;
  464       else if (sscanf (this_arg, "contents=%s", contents) == 1)
  465         {
  466           /* this is tricky since contents might be a quoted
  467          string with embedded spaces or escaped quotes.  We
  468          should parse this carefully from parsed_so_far */
  469           from = strchr (parsed_so_far, '=') + 1;
  470           to = contents;
  471           single_quoted = FALSE;
  472           double_quoted = FALSE;
  473           escaped = FALSE;
  474           done = FALSE;
  475           while ((ch = *from++) != '\0' && !done)
  476         {
  477           if (escaped == TRUE)
  478             {
  479               switch (ch)
  480             {
  481             case 'n':
  482               *to++ = '\n';
  483               break;
  484             case 'r':
  485               *to++ = '\r';
  486               break;
  487             case 't':
  488               *to++ = '\t';
  489               break;
  490             case '\n':
  491               *to++ = '\n';
  492               /* this allows an escaped newline to
  493                  continue the parsing to the next line. */
  494               if (fgets(line,sizeof(line),fp) == NULL)
  495                 {
  496                   lineno++;
  497                   panic ("%s: premature EOF seen in '%s'\n",
  498                      prog_name, param.wsesslog.file);  
  499                 }
  500               parsed_so_far = from = line;
  501               break;
  502             default:
  503               *to++ = ch;
  504               break;
  505             }
  506               escaped = FALSE;
  507             }
  508           else if (ch == '"' && double_quoted)
  509             {
  510               double_quoted = FALSE;
  511             }
  512           else if (ch == '\'' && single_quoted)
  513             {
  514               single_quoted = FALSE;
  515             }
  516           else
  517             {
  518               switch (ch)
  519             {
  520             case '\t':
  521             case '\n':
  522             case ' ':
  523               if (single_quoted == FALSE &&
  524                   double_quoted == FALSE)
  525                 done = TRUE;    /* we are done */
  526               else
  527                 *to++ = ch;
  528               break;
  529             case '\\':      /* backslash */
  530               escaped = TRUE;
  531               break;
  532             case '"':       /* double quote */
  533               if (single_quoted)
  534                 *to++ = ch;
  535               else
  536                 double_quoted = TRUE;
  537               break;
  538             case '\'':      /* single quote */
  539               if (double_quoted)
  540                 *to++ = ch;
  541               else
  542                 single_quoted = TRUE;
  543               break;
  544             default:
  545               *to++ = ch;
  546               break;
  547             }
  548             }
  549         }
  550           *to = '\0';
  551           from--;       /* back up 'from' to '\0' or white-space */
  552           bytes_read = from - parsed_so_far;
  553           if ((sptr->current_req->contents_len = strlen (contents)) != 0)
  554         {
  555           sptr->current_req->contents = strdup (contents);
  556           snprintf (sptr->current_req->extra_hdrs,
  557                 sizeof(sptr->current_req->extra_hdrs),
  558                 "Content-length: %d\r\n",
  559                sptr->current_req->contents_len);
  560           sptr->current_req->extra_hdrs_len =
  561             strlen (sptr->current_req->extra_hdrs);
  562         }
  563         }
  564       else
  565         {
  566           /* do not recognize this arg */
  567           panic ("%s: did not recognize arg '%s' in %s\n",
  568              prog_name, this_arg, param.wsesslog.file);  
  569         }
  570       parsed_so_far += bytes_read;
  571     }
  572     }
  573   fclose (fp);
  574 
  575   if (DBG > 3)
  576     {
  577       fprintf (stderr,"%s: session list follows:\n\n", prog_name);
  578 
  579       for (i = 0; i < num_templates; i++)
  580     {
  581       sptr = &session_templates[i];
  582       fprintf (stderr, "#session %d (total_reqs=%d):\n",
  583            i, sptr->total_num_reqs);
  584         
  585       for (bptr = sptr->current_burst; bptr; bptr = bptr->next)
  586         {
  587           for (reqptr = bptr->req_list, reqnum = 0;
  588            reqptr;
  589            reqptr = reqptr->next, reqnum++)
  590         {
  591           if (reqnum >= bptr->num_reqs)
  592             panic ("%s: internal error detected in parsing %s\n",
  593                prog_name, param.wsesslog.file);  
  594           if (reqnum > 0)
  595             fprintf (stderr, "\t");
  596           fprintf (stderr, "%s", reqptr->uri);
  597           if (reqnum == 0
  598               && bptr->user_think_time != param.wsesslog.think_time)
  599             fprintf (stderr, " think=%0.2f",
  600                  (double) bptr->user_think_time);
  601           if (reqptr->method != HM_GET)
  602             fprintf (stderr," method=%s",
  603                  call_method_name[reqptr->method]);
  604           if (reqptr->contents != NULL)
  605             fprintf (stderr, " contents='%s'", reqptr->contents);
  606           fprintf (stderr, "\n");
  607         }
  608         }
  609       fprintf (stderr, "\n");
  610     }
  611     }
  612 }
  613 
  614 static void
  615 init (void)
  616 {
  617   Any_Type arg;
  618 
  619   parse_config ();
  620 
  621   sess_private_data_offset = object_expand (OBJ_SESS,
  622                         sizeof (Sess_Private_Data));
  623   rg_sess.rate = &param.rate;
  624   rg_sess.tick = sess_create;
  625   rg_sess.arg.l = 0;
  626 
  627   arg.l = 0;
  628   event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
  629   event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
  630 
  631   /* This must come last so the session event handlers are executed
  632      before this module's handlers.  */
  633   session_init ();
  634 }
  635 
  636 static void
  637 start (void)
  638 {
  639   rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
  640 }
  641 
  642 Load_Generator wsesslog =
  643   {
  644     "creates log-based session workload",
  645     init,
  646     start,
  647     no_op
  648   };