/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * empweb.cc * Copyright (C) 2001 by the University of Southern California * $Id: empweb.cc,v 1.20 2005/09/18 23:33:32 tomh Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * 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. * * * The copyright of this module includes the following * linking-with-specific-other-licenses addition: * * In addition, as a special exception, the copyright holders of * this module give you permission to combine (via static or * dynamic linking) this module with free software programs or * libraries that are released under the GNU LGPL and with code * included in the standard release of ns-2 under the Apache 2.0 * license or under otherwise-compatible licenses with advertising * requirements (or modified versions of such code, with unchanged * license). You may copy and distribute such a system following the * terms of the GNU GPL for this module and the licenses of the * other code concerned, provided that you include the source code of * that other code when and as the GNU GPL requires distribution of * source code. * * Note that people who make modified versions of this module * are not obligated to grant this special exception for their * modified versions; it is their choice whether to do so. The GNU * General Public License gives permission to release a modified * version without this exception; this exception also makes it * possible to release a modified version which carries forward this * exception. * */ // // Empirical Web traffic model that simulates Web traffic based on a set of // CDF (Cumulative Distribution Function) data derived from live tcpdump trace // The structure of this file is largely borrowed from webtraf.cc // // $Header: /nfs/jade/vint/CVSROOT/ns-2/empweb/empweb.cc,v 1.20 2005/09/18 23:33:32 tomh Exp $ #include #include "empweb.h" // Data structures that are specific to this web traffic model and // should not be used outside this file. // // - EmpWebTrafPage // - EmpWebTrafObject class EmpWebPage : public TimerHandler { public: EmpWebPage(int id, EmpWebTrafSession* sess, int nObj, Node* dst, int svrId) : persistOption_(0), id_(id), sess_(sess), nObj_(nObj), curObj_(0), doneObj_(0), dst_(dst), svrId_(svrId) {} virtual ~EmpWebPage() {} inline void start() { // Call expire() and schedule the next one if needed status_ = TIMER_PENDING; handle(&event_); } inline int id() const { return id_; } inline int svrId() const { return svrId_; } Node* dst() { return dst_; } void doneObject() { if (sess_->mgr()->isdebug()) printf("doneObject: %g done=%d total=%d \n", Scheduler::instance().clock(), doneObj_, nObj_); if (++doneObj_ >= nObj_) { printf("doneObject: %g %d %d \n", Scheduler::instance().clock(), doneObj_, nObj_); sess_->donePage((void*)this); // } // sched(sess_->interObj()->value()); } else if (persistOption_) { sched(sess_->interObj()->value()); } } inline int curObj() const { return curObj_; } inline int doneObj() const { return doneObj_; } inline void set_persistOption(int opt) { persistOption_ = opt; } int persistOption_ ; //0: http1.0 1: http1.1 ; use http1.0 as default private: virtual void expire(Event* = 0) { // Launch a request. Make sure size is not 0! if (curObj_ >= nObj_) return; sess_->launchReq(this, LASTOBJ_++, (int)ceil(sess_->objSize()->value()), (int)ceil(sess_->reqSize()->value()), sess_->id(), persistOption_); if (sess_->mgr()->isdebug()) printf("expire: Session %d launched page %d obj %d nObj %d \n", sess_->id(), id_, curObj_, nObj_); } virtual void handle(Event *e) { if (sess_->mgr()->isdebug()) printf("handle: Session %d launched page %d obj %d\n", sess_->id(), id_, curObj_); TimerHandler::handle(e); curObj_++; if (!persistOption_) { if (curObj_ < nObj_) sched(sess_->interObj()->value()); } } int id_; EmpWebTrafSession* sess_; int nObj_, curObj_; int doneObj_; Node* dst_; int svrId_ ; static int LASTOBJ_; }; int EmpWebPage::LASTOBJ_ = 1; int EmpWebTrafSession::LASTPAGE_ = 1; int EmpWebTrafPool::LASTFLOW_ = 1; // XXX Must delete this after all pages are done!! EmpWebTrafSession::~EmpWebTrafSession() { if (donePage_ != curPage_) { fprintf(stderr, "done pages %d != all pages %d\n", donePage_, curPage_); abort(); } if (status_ != TIMER_IDLE) { fprintf(stderr, "EmpWebTrafSession must be idle when deleted.\n"); abort(); } /* if (rvInterPage_ != NULL) Tcl::instance().evalf("delete %s", rvInterPage_->name()); if (rvPageSize_ != NULL) Tcl::instance().evalf("delete %s", rvPageSize_->name()); if (rvInterObj_ != NULL) Tcl::instance().evalf("delete %s", rvInterObj_->name()); if (rvObjSize_ != NULL) Tcl::instance().evalf("delete %s", rvObjSize_->name()); if (rvReqSize_ != NULL) Tcl::instance().evalf("delete %s", rvReqSize_->name()); if (rvPersistSel_ != NULL) Tcl::instance().evalf("delete %s", rvPersistSel_->name()); if (rvServerSel_ != NULL) Tcl::instance().evalf("delete %s", rvServerSel_->name()); */ } void EmpWebTrafSession::donePage(void* ClntData) { EmpWebPage* pg = (EmpWebPage*)ClntData; if (mgr_->isdebug()) printf("Session %d done page %d\n", id_, pg->id()); if (pg->doneObj() != pg->curObj()) { fprintf(stderr, "done objects %d != all objects %d\n", pg->doneObj(), pg->curObj()); abort(); } //for HTTP1.1 persistent-connection if (pg->persistOption_) { if (!fulltcp_) { //recycle TCP connection mgr_->recycleTcp(ctcp_); mgr_->recycleTcp(stcp_); mgr_->recycleSink(csnk_); mgr_->recycleSink(ssnk_); } else { Tcl::instance().evalf("%s disconnect-full %s %s %s %s", mgr_->name(), src_->name(), pg->dst()->name(), ctcp_->name(), stcp_->name()); } } delete pg; // If all pages are done, tell my parent to delete myself if (++donePage_ >= nPage_) { mgr_->doneSession(id_); } else if (interPageOption_) { sched(rvInterPage_->value()); // printf("donePage: %g %d %d\n", Scheduler::instance().clock(), donePage_, curPage_); } } // Launch the current page void EmpWebTrafSession::expire(Event *) { // Pick destination for this page //temporary hack for isi traffic int n; if (clientIdx_ < mgr()->nClientL_) n = 0 ; //ISI server else n = int(ceil(serverSel()->value())); assert((n >= 0) && (n < mgr()->nSrc_)); Node* dst = mgr()->server_[n]; // Make sure page size is not 0! EmpWebPage* pg = new EmpWebPage(LASTPAGE_++, this, (int)ceil(rvPageSize_->value()), dst, n); //each page either use persistent or non-persistent connection int opt = (int)ceil(this->persistSel()->value()); pg->set_persistOption(opt); if (mgr_->isdebug()) printf("Session %d starting page %d, curpage %d \n", id_, LASTPAGE_-1, curPage_); if (pg->persistOption_) { //for HTTP1.1 persistent-connection mgr_->LASTFLOW_++; int wins = int(ceil(serverWin()->value())); int winc = int(ceil(clientWin()->value())); int window = (wins >= winc) ? wins : winc; int m = int(ceil(mtu()->value())); // Choose source and dest TCP agents for both source and destination if (fulltcp_) { ctcp_ = mgr_->picktcp(window,m); stcp_ = mgr_->picktcp(window,m); Tcl::instance().evalf("%s connect-full %s %s %s %s", mgr_->name(), src_->name(), pg->dst()->name(), ctcp_->name(), stcp_->name()); } else { ctcp_ = mgr_->picktcp(window,m); stcp_ = mgr_->picktcp(window,m); csnk_ = mgr_->picksink(); ssnk_ = mgr_->picksink(); } // Tcl::instance().evalf("%s set-fid %d %s %s", mgr_->name(), mgr_->LASTFLOW_-1, ctcp_->name(), stcp_->name()); Tcl::instance().evalf("%s set-fid %d %s %s", mgr_->name(), mgr_->color_, ctcp_->name(), stcp_->name()); } pg->start(); } void EmpWebTrafSession::handle(Event *e) { // If I haven't scheduled all my pages, do the next one TimerHandler::handle(e); ++curPage_; // XXX Notice before each page is done, it will schedule itself // one more time, this makes sure that this session will not be // deleted after the above call. Thus the following code will not // be executed in the context of a deleted object. if (!interPageOption_) { if (curPage_ < nPage_) { sched(rvInterPage_->value()); // printf("schedule: %g %d %d\n", Scheduler::instance().clock(), donePage_, curPage_); } } } // Launch a request for a particular object void EmpWebTrafSession::launchReq(void* ClntData, int obj, int size, int reqSize, int sid, int persist) { TcpAgent* ctcp; TcpAgent* stcp; TcpSink* csnk; TcpSink* ssnk; EmpWebPage* pg = (EmpWebPage*)ClntData; if (persist) { //for HTTP1.1 persistent-connection if (mgr_->isdebug()) { printf("HTTP1.1\n"); } // use the same connection ctcp = ctcp_; stcp = stcp_; if (fulltcp_) { csnk = 0; ssnk = 0; } else { csnk = csnk_; ssnk = ssnk_; } } else { //for HTTP1.0 non-consistent connection if (mgr_->isdebug()) { printf("HTTP1.0\n"); } mgr_->LASTFLOW_++; int wins = int(ceil(serverWin()->value())); int winc = int(ceil(clientWin()->value())); int window = (wins >= winc) ? wins : winc; int m = int(ceil(mtu()->value())); // Choose source and dest TCP agents for both source and destination ctcp = mgr_->picktcp(window,m); stcp = mgr_->picktcp(window,m); Tcl::instance().evalf("%s set-fid %d %s %s", mgr_->name(), mgr_->color_, ctcp->name(), stcp->name()); if (fulltcp_) { csnk = 0; ssnk = 0; } else { csnk = mgr_->picksink(); ssnk = mgr_->picksink(); } } // Setup new TCP connection and launch request // size and reqSize are in the unit of bytes in fulltcp mode // but in the unit of packet in halftcp mode if (fulltcp_) { Tcl::instance().evalf("%s launch-req-full %d %d %s %s %s %s %d %d %d %d", mgr_->name(), obj, pg->id(), src_->name(), pg->dst()->name(), ctcp->name(), stcp->name(), size, reqSize, ClntData,persist); } else { assert (csnk != 0 && ssnk != 0); Tcl::instance().evalf("%s launch-req %d %d %s %s %s %s %s %s %d %d %d %d", mgr_->name(), obj, pg->id(), src_->name(), pg->dst()->name(), ctcp->name(), csnk->name(), stcp->name(), ssnk->name(), size, reqSize, ClntData, persist); } if (mgr_->isdebug()) { printf("size=%d obj=%d page=%d sess=%d %g src=%d dst=%d\n", size, obj, pg->id(), id_, Scheduler::instance().clock(), src_->address(), pg->dst()->address()); } } static class EmpWebTrafPoolClass : public TclClass { public: EmpWebTrafPoolClass() : TclClass("PagePool/EmpWebTraf") {} TclObject* create(int, const char*const*) { return (new EmpWebTrafPool()); } } class_empwebtrafpool; EmpWebTrafPool::~EmpWebTrafPool() { if (session_ != NULL) { for (int i = 0; i < nSession_; i++) delete session_[i]; delete []session_; } if (server_ != NULL) delete []server_; if (client_ != NULL) delete []client_; // XXX Destroy tcpPool_ and sinkPool_ ? } void EmpWebTrafPool::delay_bind_init_all() { delay_bind_init_one("debug_"); PagePool::delay_bind_init_all(); } int EmpWebTrafPool::delay_bind_dispatch(const char *varName,const char *localName, TclObject *tracer) { if (delay_bind_bool(varName, localName, "debug_", &debug_, tracer)) return TCL_OK; return PagePool::delay_bind_dispatch(varName, localName, tracer); } EmpWebTrafPool::EmpWebTrafPool() : concurrentSess_(0), nSrc_(0), server_(NULL), session_(NULL), nClient_(0), client_(NULL), nTcp_(0), nSink_(0), fulltcp_(0) { bind("fulltcp_", &fulltcp_); LIST_INIT(&tcpPool_); LIST_INIT(&sinkPool_); } TcpAgent* EmpWebTrafPool::picktcp(int win, int mtu) { TcpAgent* a = (TcpAgent*)detachHead(&tcpPool_); if (a == NULL) { Tcl& tcl = Tcl::instance(); tcl.evalf("%s alloc-tcp %d %d", name(), win, mtu); a = (TcpAgent*)lookup_obj(tcl.result()); if (a == NULL) { fprintf(stderr, "Failed to allocate a TCP agent\n"); abort(); } } else nTcp_--; return a; } TcpSink* EmpWebTrafPool::picksink() { TcpSink* a = (TcpSink*)detachHead(&sinkPool_); if (a == NULL) { Tcl& tcl = Tcl::instance(); tcl.evalf("%s alloc-tcp-sink", name()); a = (TcpSink*)lookup_obj(tcl.result()); if (a == NULL) { fprintf(stderr, "Failed to allocate a TCP sink\n"); abort(); } } else nSink_--; return a; } void EmpWebTrafPool::recycleTcp(Agent* a) { if (fulltcp_) { delete a; } else { if (a == NULL) { fprintf(stderr, "Failed to recycle TCP agent\n"); abort(); } nTcp_++; insertAgent(&tcpPool_, a); } } void EmpWebTrafPool::recycleSink(Agent* a) { if (fulltcp_) { delete a; } else { if (a == NULL) { fprintf(stderr, "Failed to recycle Sink agent\n"); abort(); } nSink_++; insertAgent(&sinkPool_, a); } } int EmpWebTrafPool::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "set-num-session") == 0) { if (session_ != NULL) { for (int i = 0; i < nSession_; i++) delete session_[i]; delete []session_; } nSession_ = atoi(argv[2]); session_ = new EmpWebTrafSession*[nSession_]; memset(session_, 0, sizeof(EmpWebTrafSession*)*nSession_); return (TCL_OK); } else if (strcmp(argv[1], "set-num-server-lan") == 0) { nSrcL_ = atoi(argv[2]); if (nSrcL_ > nSrc_) { fprintf(stderr, "Wrong server index %d\n", nSrcL_); return TCL_ERROR; } return (TCL_OK); } else if (strcmp(argv[1], "set-num-remote-client") == 0) { nClientL_ = atoi(argv[2]); if (nClientL_ > nClient_) { fprintf(stderr, "Wrong client index %d\n", nClientL_); return TCL_ERROR; } return (TCL_OK); } else if (strcmp(argv[1], "set-num-server") == 0) { nSrc_ = atoi(argv[2]); if (server_ != NULL) delete []server_; server_ = new Node*[nSrc_]; return (TCL_OK); } else if (strcmp(argv[1], "set-num-client") == 0) { nClient_ = atoi(argv[2]); if (client_ != NULL) delete []client_; client_ = new Node*[nClient_]; return (TCL_OK); } else if (strcmp(argv[1], "set-interPageOption") == 0) { int option = atoi(argv[2]); if (session_ != NULL) { for (int i = 0; i < nSession_; i++) { EmpWebTrafSession* p = session_[i]; p->set_interPageOption(option); } } return (TCL_OK); } else if (strcmp(argv[1], "doneObj") == 0) { EmpWebPage* p = (EmpWebPage*)atol(argv[2]); p->doneObject(); return (TCL_OK); } } else if (argc == 4) { if (strcmp(argv[1], "set-server") == 0) { Node* cli = (Node*)lookup_obj(argv[3]); if (cli == NULL) return (TCL_ERROR); int nc = atoi(argv[2]); if (nc >= nSrc_) { fprintf(stderr, "Wrong server index %d\n", nc); return TCL_ERROR; } server_[nc] = cli; return (TCL_OK); } else if (strcmp(argv[1], "set-client") == 0) { Node* s = (Node*)lookup_obj(argv[3]); if (s == NULL) return (TCL_ERROR); int n = atoi(argv[2]); if (n >= nClient_) { fprintf(stderr, "Wrong client index %d\n", n); return TCL_ERROR; } client_[n] = s; return (TCL_OK); } else if (strcmp(argv[1], "recycle") == 0) { // recycle // // Recycle a TCP source/sink pair Agent* tcp = (Agent*)lookup_obj(argv[2]); Agent* snk = (Agent*)lookup_obj(argv[3]); if ((tcp == NULL) || (snk == NULL)) return (TCL_ERROR); // XXX TBA: recycle tcp agents if (fulltcp_) { delete tcp; delete snk; } else { nTcp_++, nSink_++; insertAgent(&tcpPool_, tcp); insertAgent(&sinkPool_, snk); } return (TCL_OK); } } else if (argc == 17) { if (strcmp(argv[1], "create-session") == 0) { // create-session // // // // // // int n = atoi(argv[2]); if ((n < 0)||(n >= nSession_)||(session_[n] != NULL)) { fprintf(stderr,"Invalid session index %d\n",n); return (TCL_ERROR); } int npg = (int)strtod(argv[3], NULL); double lt = strtod(argv[4], NULL); int flip = atoi(argv[15]); if ((flip < 0)||(flip > 1)) { fprintf(stderr,"Invalid I/O flag %d\n",flip); return (TCL_ERROR); } //for SPAWAR demo color_ = atoi(argv[16]); int cl; if (flip == 1) cl = int(floor(Random::uniform(0, nClientL_))); else cl = int(floor(Random::uniform(nClientL_, nClient_))); assert((cl >= 0) && (cl < nClient_)); Node* c=client_[cl]; EmpWebTrafSession* p = new EmpWebTrafSession(this, c, npg, n, nSrc_, cl,fulltcp_); int res = lookup_rv(p->interPage(), argv[5]); res = (res == TCL_OK) ? lookup_rv(p->pageSize(), argv[6]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->interObj(), argv[7]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->objSize(), argv[8]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->reqSize(), argv[9]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->persistSel(), argv[10]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->serverSel(), argv[11]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->serverWin(), argv[12]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->clientWin(), argv[13]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->mtu(), argv[14]) : TCL_ERROR; if (res == TCL_ERROR) { delete p; fprintf(stderr, "Invalid random variable\n"); return (TCL_ERROR); } p->sched(lt); session_[n] = p; return (TCL_OK); } } return PagePool::command(argc, argv); }