"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 = ¶m.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 };