/*
httperf -- a tool for measuring web server performance
Copyright (C) 2000 Hewlett-Packard Company
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA
*/
/* Creates sessions at the fixed rate PARAM.RATE. */
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <event.h>
#include <rate.h>
#include <session.h>
#include <timer.h>
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
typedef struct Sess_Private_Data
{
u_int num_calls_in_this_burst; /* # of calls created for this burst */
u_int num_calls_target; /* total # of calls desired */
u_int num_calls_destroyed; /* # of calls destroyed so far */
Timer *timer; /* timer for session think time */
}
Sess_Private_Data;
static size_t sess_private_data_offset;
static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;
static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
int i, to_create, retval;
Call *call;
/* Mimic browser behavior of fetching html object, then a couple of
embedded objects: */
to_create = 1;
if (priv->num_calls_in_this_burst > 0)
to_create = param.burst_len - priv->num_calls_in_this_burst;
priv->num_calls_in_this_burst += to_create;
for (i = 0; i < to_create; ++i)
{
call = call_new ();
if (!call)
{
sess_failure (sess);
return;
}
retval = session_issue_call (sess, call);
call_dec_ref (call);
if (retval < 0)
return;
}
}
static void
user_think_time_expired (Timer *t, Any_Type arg)
{
Sess *sess = arg.vp;
Sess_Private_Data *priv;
assert (object_is_sess (sess));
priv = SESS_PRIVATE_DATA (sess);
priv->timer = 0;
issue_calls (sess, priv);
}
static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Any_Type arg;
Sess *sess;
Call *call;
Sess_Private_Data *priv;
assert (et == EV_CALL_DESTROYED && object_is_call (obj));
call = (Call *) obj;
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
++priv->num_calls_destroyed;
if (priv->num_calls_destroyed >= param.wsess.num_calls)
{
/* we're done with this session */
if (!sess->failed)
sess_dec_ref (sess);
}
else if (priv->num_calls_in_this_burst < param.burst_len)
/* now that we received the reply to the first call in this burst,
create the remaining calls */
issue_calls (sess, priv);
else if (priv->num_calls_destroyed >= priv->num_calls_target)
{
/* we're done with this burst---schedule the user-think-time timer */
priv->num_calls_in_this_burst = 0;
priv->num_calls_target += param.burst_len;
assert (!priv->timer);
arg.vp = sess;
priv->timer = timer_schedule (user_think_time_expired, arg,
param.wsess.think_time);
}
}
/* Create a new session. */
static int
sess_create (Any_Type arg)
{
Sess_Private_Data *priv;
Sess *sess;
if (num_sessions_generated++ >= param.wsess.num_sessions)
return -1;
sess = sess_new ();
if (!sess)
return 1;
priv = SESS_PRIVATE_DATA (sess);
priv->num_calls_target = param.burst_len;
issue_calls (sess, SESS_PRIVATE_DATA (sess));
return 0;
}
static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
if (priv->timer)
{
timer_cancel (priv->timer);
priv->timer = 0;
}
if (++num_sessions_destroyed >= param.wsess.num_sessions)
core_exit ();
}
static void
init (void)
{
Any_Type arg;
session_init ();
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
rg_sess.rate = ¶m.rate;
rg_sess.tick = sess_create;
rg_sess.arg.l = 0;
arg.l = 0;
event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}
static void
start (void)
{
rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}
Load_Generator wsess =
{
"creates session workload",
init,
start,
no_op
};
syntax highlighted by Code2HTML, v. 0.9.1