pagepool.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) Xerox Corporation 1998. All rights reserved.
00003  *
00004  * This program is free software; you can redistribute it and/or modify it
00005  * under the terms of the GNU General Public License as published by the
00006  * Free Software Foundation; either version 2 of the License, or (at your
00007  * option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License along
00015  * with this program; if not, write to the Free Software Foundation, Inc.,
00016  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00017  *
00018  * Linking this file statically or dynamically with other modules is making
00019  * a combined work based on this file.  Thus, the terms and conditions of
00020  * the GNU General Public License cover the whole combination.
00021  *
00022  * In addition, as a special exception, the copyright holders of this file
00023  * give you permission to combine this file with free software programs or
00024  * libraries that are released under the GNU LGPL and with code included in
00025  * the standard release of ns-2 under the Apache 2.0 license or under
00026  * otherwise-compatible licenses with advertising requirements (or modified
00027  * versions of such code, with unchanged license).  You may copy and
00028  * distribute such a system following the terms of the GNU GPL for this
00029  * file and the licenses of the other code concerned, provided that you
00030  * include the source code of that other code when and as the GNU GPL
00031  * requires distribution of source code.
00032  *
00033  * Note that people who make modified versions of this file are not
00034  * obligated to grant this special exception for their modified versions;
00035  * it is their choice whether to do so.  The GNU General Public License
00036  * gives permission to release a modified version without this exception;
00037  * this exception also makes it possible to release a modified version
00038  * which carries forward this exception.
00039  *
00040  * $Header: /nfs/jade/vint/CVSROOT/ns-2/webcache/pagepool.cc,v 1.16 2005/09/18 23:33:35 tomh Exp $
00041  */
00042 
00043 #include <stdlib.h>
00044 #include <sys/types.h>
00045 #include <fcntl.h>
00046 #ifdef WIN32
00047 #include <windows.h>
00048 #include <io.h>
00049 #else 
00050 #include <unistd.h>
00051 #include <sys/file.h>
00052 #endif
00053 #include <sys/stat.h>
00054 
00055 #include <stdio.h>
00056 #include <limits.h>
00057 #include <ctype.h>
00058 
00059 extern "C" {
00060 #include <otcl.h>
00061 }
00062 #include "pagepool.h"
00063 #include "http.h"
00064 
00065 // Static/global variables
00066 int ClientPage::PUSHALL_ = 0;   // Initialized to selective push
00067 
00068 void ServerPage::set_mtime(int *mt, int n)
00069 {
00070     if (mtime_ != NULL) 
00071         delete []mtime_;
00072     mtime_ = new int[n];
00073     memcpy(mtime_, mt, sizeof(int)*n);
00074 }
00075 
00076 ClientPage::ClientPage(const char *n, int s, double mt, double et, double a) :
00077         Page(s), age_(a), mtime_(mt), etime_(et), 
00078         status_(HTTP_VALID_PAGE), counter_(0), 
00079         mpushTime_(0)
00080 {
00081     // Parse name to get server and page id
00082     char *buf = new char[strlen(n) + 1];
00083     strcpy(buf, n);
00084     char *tmp = strtok(buf, ":");
00085     server_ = (HttpApp*)TclObject::lookup(tmp);
00086     if (server_ == NULL) {
00087         fprintf(stderr, "Non-exitent server name for page %s", n);
00088         abort();
00089     }
00090     tmp = strtok(NULL, ":");
00091     id_ = atol(tmp);
00092     delete []buf;
00093 }
00094 
00095 void ClientPage::print_name(char* name, PageID& id)
00096 {
00097     sprintf(name, "%s:%-d", id.s_->name(), id.id_);
00098 }
00099 
00100 void ClientPage::split_name(const char* name, PageID& id)
00101 {
00102     char *buf = new char[strlen(name)+1];
00103     strcpy(buf, name);
00104     char *tmp = strtok(buf, ":");
00105     id.s_ = (HttpApp*)TclObject::lookup(tmp);
00106     if (id.s_ == NULL) {
00107         fprintf(stderr, "Non-exitent server name for page %s\n", name);
00108         abort();
00109     }
00110     tmp = strtok(NULL, ":");
00111     id.id_ = atol(tmp);
00112     delete []buf;
00113 }
00114 
00115 void ClientPage::print_info(char *buf)
00116 {
00117     sprintf(buf, "size %d modtime %.17g time %.17g age %.17g",
00118         size(), mtime(), etime(), age());
00119     if (is_uncacheable())
00120         strcat(buf, " noc 1");
00121 }
00122 
00123 void ClientPage::name(char* buf) 
00124 {
00125     sprintf(buf, "%s:%d", server_->name(), id());
00126 }
00127 
00128 
00129 static class PagePoolClass : public TclClass {
00130 public:
00131         PagePoolClass() : TclClass("PagePool") {}
00132         TclObject* create(int, const char*const*) {
00133         return (new PagePool());
00134     }
00135 } class_pagepool_agent;
00136 
00137 int PagePool::command(int argc, const char*const* argv)
00138 {
00139     if (argc == 2) {
00140         // XXX Should be static class variables... 
00141         if (strcmp(argv[1], "set-allpush") == 0) {
00142             ClientPage::PUSHALL_ = 1;
00143             return (TCL_OK);
00144         }
00145         if (strcmp(argv[1], "set-selpush") == 0) {
00146             ClientPage::PUSHALL_ = 0;
00147             return (TCL_OK);
00148         }
00149     }
00150     return TclObject::command(argc, argv);
00151 }
00152 
00153 
00154 // TracePagePool
00155 // Used for Worrell's filtered server traces only. For handling general 
00156 // web server traces and proxy traces, have a look at ProxyTracePagePool below.
00157 //
00158 // Load a trace statistics file, and randomly generate requests and 
00159 // page lifetimes from the trace.
00160 //
00161 // Trace statistics file format:
00162 // <URL> <size> {<modification time>}
00163 
00164 static class TracePagePoolClass : public TclClass {
00165 public:
00166         TracePagePoolClass() : TclClass("PagePool/Trace") {}
00167         TclObject* create(int argc, const char*const* argv) {
00168         if (argc >= 5)
00169             return (new TracePagePool(argv[4]));
00170         return 0;
00171     }
00172 } class_tracepagepool_agent;
00173 
00174 TracePagePool::TracePagePool(const char *fn) : 
00175     PagePool(), ranvar_(0)
00176 {
00177     FILE *fp = fopen(fn, "r");
00178     if (fp == NULL) {
00179         fprintf(stderr, 
00180             "TracePagePool: couldn't open trace file %s\n", fn);
00181         abort();    // What else can we do?
00182     }
00183 
00184     namemap_ = new Tcl_HashTable;
00185     Tcl_InitHashTable(namemap_, TCL_STRING_KEYS);
00186     idmap_ = new Tcl_HashTable;
00187     Tcl_InitHashTable(idmap_, TCL_ONE_WORD_KEYS);
00188 
00189     while (load_page(fp));
00190     change_time();
00191 }
00192 
00193 TracePagePool::~TracePagePool()
00194 {
00195     if (namemap_ != NULL) {
00196         Tcl_DeleteHashTable(namemap_);
00197         delete namemap_;
00198     }
00199     if (idmap_ != NULL) {
00200         Tcl_DeleteHashTable(idmap_);
00201         delete idmap_;
00202     }
00203 }
00204 
00205 void TracePagePool::change_time()
00206 {
00207     Tcl_HashEntry *he;
00208     Tcl_HashSearch hs;
00209     ServerPage *pg;
00210     int i, j;
00211 
00212     for (i = 0, he = Tcl_FirstHashEntry(idmap_, &hs);
00213          he != NULL;
00214          he = Tcl_NextHashEntry(&hs), i++) {
00215         pg = (ServerPage *) Tcl_GetHashValue(he);
00216         for (j = 0; j < pg->num_mtime(); j++) 
00217             pg->mtime(j) -= (int)start_time_;
00218     }
00219     end_time_ -= start_time_;
00220     start_time_ = 0;
00221     duration_ = (int)end_time_;
00222 }
00223 
00224 ServerPage* TracePagePool::load_page(FILE *fp)
00225 {
00226     static char buf[TRACEPAGEPOOL_MAXBUF];
00227     char *delim = " \t\n";
00228     char *tmp1, *tmp2;
00229     ServerPage *pg;
00230 
00231     // XXX Use internal variables of struct Page
00232     if (!fgets(buf, TRACEPAGEPOOL_MAXBUF, fp))
00233         return NULL;
00234 
00235     // URL
00236     tmp1 = strtok(buf, delim);
00237     // Size
00238     tmp2 = strtok(NULL, delim);
00239     pg = new ServerPage(atoi(tmp2), num_pages_++);
00240 
00241     if (add_page(tmp1, pg)) {
00242         delete pg;
00243         return NULL;
00244     }
00245 
00246     // Modtimes, assuming they are in ascending time order
00247     int num = 0;
00248     int *nmd = new int[5];
00249     while ((tmp1 = strtok(NULL, delim)) != NULL) {
00250         if (num >= 5) {
00251             int *tt = new int[num+5];
00252             memcpy(tt, nmd, sizeof(int)*num);
00253             delete []nmd;
00254             nmd = tt;
00255         }
00256         nmd[num] = atoi(tmp1);
00257         if (nmd[num] < start_time_)
00258             start_time_ = nmd[num];
00259         if (nmd[num] > end_time_)
00260             end_time_ = nmd[num];
00261         num++;
00262     }
00263     pg->num_mtime() = num;
00264     pg->set_mtime(nmd, num);
00265     delete []nmd;
00266     return pg;
00267 }
00268 
00269 int TracePagePool::add_page(const char* name, ServerPage *pg)
00270 {
00271     int newEntry = 1;
00272     Tcl_HashEntry *he = Tcl_CreateHashEntry(namemap_, 
00273                         (const char *)name,
00274                         &newEntry);
00275     if (he == NULL)
00276         return -1;
00277     if (newEntry)
00278         Tcl_SetHashValue(he, (ClientData)pg);
00279     else 
00280         fprintf(stderr, "TracePagePool: Duplicate entry %s\n", 
00281             name);
00282 
00283     long key = pg->id();
00284     Tcl_HashEntry *hf = 
00285         Tcl_CreateHashEntry(idmap_, (const char *)key, &newEntry);
00286     if (hf == NULL) {
00287         Tcl_DeleteHashEntry(he);
00288         return -1;
00289     }
00290     if (newEntry)
00291         Tcl_SetHashValue(hf, (ClientData)pg);
00292     else 
00293         fprintf(stderr, "TracePagePool: Duplicate entry %d\n", 
00294             pg->id());
00295 
00296     return 0;
00297 }
00298 
00299 ServerPage* TracePagePool::get_page(int id)
00300 {
00301     if ((id < 0) || (id >= num_pages_))
00302         return NULL;
00303     long key = id;
00304     Tcl_HashEntry *he = Tcl_FindHashEntry(idmap_, (const char *)key);
00305     if (he == NULL)
00306         return NULL;
00307     return (ServerPage *)Tcl_GetHashValue(he);
00308 }
00309 
00310 int TracePagePool::command(int argc, const char *const* argv)
00311 {
00312     Tcl &tcl = Tcl::instance();
00313 
00314     if (argc == 2) {
00315         if (strcmp(argv[1], "get-poolsize") == 0) {
00316             /* 
00317              * <pgpool> get-poolsize
00318              * Get the number of pages currently in pool
00319              */
00320             tcl.resultf("%d", num_pages_);
00321             return TCL_OK;
00322         } else if (strcmp(argv[1], "get-start-time") == 0) {
00323             tcl.resultf("%.17g", start_time_);
00324             return TCL_OK;
00325         } else if (strcmp(argv[1], "get-duration") == 0) {
00326             tcl.resultf("%d", duration_);
00327             return TCL_OK;
00328         }
00329     } else if (argc == 3) {
00330         if (strcmp(argv[1], "gen-pageid") == 0) {
00331             /* 
00332              * <pgpool> gen-pageid <client_id>
00333              * Randomly generate a page id from page pool
00334              */
00335             double tmp = ranvar_ ? ranvar_->value() : 
00336                 Random::uniform();
00337             // tmp should be in [0, num_pages_-1]
00338             tmp = (tmp < 0) ? 0 : (tmp >= num_pages_) ? 
00339                 (num_pages_-1):tmp;
00340             if ((int)tmp >= num_pages_) abort();
00341             tcl.resultf("%d", (int)tmp);
00342             return TCL_OK;
00343         } else if (strcmp(argv[1], "gen-size") == 0) {
00344             /*
00345              * <pgpool> gen-size <pageid>
00346              */
00347             int id = atoi(argv[2]);
00348             ServerPage *pg = get_page(id);
00349             if (pg == NULL) {
00350                 tcl.add_errorf("TracePagePool %s: page %d doesn't exists.\n",
00351                            name_, id);
00352                 return TCL_ERROR;
00353             }
00354             tcl.resultf("%d", pg->size());
00355             return TCL_OK;
00356         } else if (strcmp(argv[1], "ranvar") == 0) {
00357             /* 
00358              * <pgpool> ranvar <ranvar> 
00359              * Set a random var which is used to randomly pick 
00360              * a page from the page pool.
00361              */
00362             ranvar_ = (RandomVariable *)TclObject::lookup(argv[2]);
00363             return TCL_OK;
00364         } else if (strcmp(argv[1], "set-start-time") == 0) {
00365             double st = strtod(argv[2], NULL);
00366             start_time_ = st;
00367             end_time_ += st;
00368         } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
00369             tcl.resultf("%.17g", Scheduler::instance().clock());
00370             return TCL_OK;
00371         }
00372     } else {
00373         if (strcmp(argv[1], "gen-modtime") == 0) {
00374             /* 
00375              * <pgpool> get-modtime <pageid> <modtime>
00376              * 
00377              * Return next modtime that is larger than modtime
00378              * To retrieve the first modtime (creation time), set 
00379              * <modtime> to -1 in the request.
00380              */
00381             int id = atoi(argv[2]);
00382             double mt = strtod(argv[3], NULL);
00383             ServerPage *pg = get_page(id);
00384             if (pg == NULL) {
00385                 tcl.add_errorf("TracePagePool %s: page %d doesn't exists.\n",
00386                            name_, id);
00387                 return TCL_ERROR;
00388             }
00389             for (int i = 0; i < pg->num_mtime(); i++) 
00390                 if (pg->mtime(i) > mt) {
00391                     tcl.resultf("%.17g", 
00392                             pg->mtime(i)+start_time_);
00393                     return TCL_OK;
00394                 }
00395             // When get to the last modtime, return -1
00396             tcl.resultf("%d", INT_MAX);
00397             return TCL_OK;
00398         }
00399     }
00400     return PagePool::command(argc, argv);
00401 }
00402 
00403 
00404 
00405 static class MathPagePoolClass : public TclClass {
00406 public:
00407         MathPagePoolClass() : TclClass("PagePool/Math") {}
00408         TclObject* create(int, const char*const*) {
00409         return (new MathPagePool());
00410     }
00411 } class_mathpagepool_agent;
00412 
00413 // Use 3 ranvars to generate requests, mod times and page size
00414 
00415 int MathPagePool::command(int argc, const char *const* argv)
00416 {
00417     Tcl& tcl = Tcl::instance();
00418 
00419     // Keep the same tcl interface as PagePool/Trace
00420     if (argc == 2) {
00421         if (strcmp(argv[1], "get-poolsize") == 0) { 
00422             tcl.result("1");
00423             return TCL_OK;
00424         } else if (strcmp(argv[1], "get-start-time") == 0) {
00425             tcl.resultf("%.17g", start_time_);
00426             return TCL_OK;
00427         } else if (strcmp(argv[1], "get-duration") == 0) {
00428             tcl.resultf("%d", duration_);
00429             return TCL_OK;
00430         }
00431     } else if (argc == 3) {
00432         if (strcmp(argv[1], "gen-pageid") == 0) {
00433             // Single page
00434             tcl.result("0");
00435             return TCL_OK;
00436         } else if (strcmp(argv[1], "gen-size") == 0) {
00437             if (rvSize_ == 0) {
00438                 tcl.add_errorf("%s: no page size generator", 
00439                            name_);
00440                 return TCL_ERROR;
00441             }
00442             int size = (int) rvSize_->value();
00443             if (size == 0)
00444                 // XXX do not allow page size 0, because TcpApp
00445                 // doesn't behave correctly when sending 0 byte
00446                 size = 1;
00447             tcl.resultf("%d", size);
00448             return TCL_OK;
00449         } else if (strcmp(argv[1], "ranvar-size") == 0) {
00450             rvSize_ = (RandomVariable*)TclObject::lookup(argv[2]);
00451             return TCL_OK;
00452         } else if (strcmp(argv[1], "ranvar-age") == 0) {
00453             rvAge_ = (RandomVariable*)TclObject::lookup(argv[2]);
00454             return TCL_OK;
00455         } else if (strcmp(argv[1], "set-start-time") == 0) {
00456             double st = strtod(argv[2], NULL);
00457             start_time_ = st;
00458             end_time_ += st;
00459             return TCL_OK;
00460         } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
00461             tcl.resultf("%.17g", Scheduler::instance().clock());
00462             return TCL_OK;
00463         }
00464     } else {
00465         if (strcmp(argv[1], "gen-modtime") == 0) {
00466             if (rvAge_ == 0) {
00467                 tcl.add_errorf("%s: no page age generator", 
00468                            name_);
00469                 return TCL_ERROR;
00470             }
00471             double mt = strtod(argv[3], NULL);
00472             tcl.resultf("%.17g", mt + rvAge_->value());
00473             return TCL_OK;
00474         }
00475     }
00476 
00477     return PagePool::command(argc, argv);
00478 }
00479 
00480 
00481 // Assume one main page, which changes often, and multiple component pages
00482 static class CompMathPagePoolClass : public TclClass {
00483 public:
00484         CompMathPagePoolClass() : TclClass("PagePool/CompMath") {}
00485         TclObject* create(int, const char*const*) {
00486         return (new CompMathPagePool());
00487     }
00488 } class_compmathpagepool_agent;
00489 
00490 
00491 CompMathPagePool::CompMathPagePool()
00492 {
00493     bind("num_pages_", &num_pages_);
00494     bind("main_size_", &main_size_);
00495     bind("comp_size_", &comp_size_);
00496 }
00497 
00498 int CompMathPagePool::command(int argc, const char *const* argv)
00499 {
00500     Tcl& tcl = Tcl::instance();
00501 
00502     // Keep the same tcl interface as PagePool/Trace
00503     if (argc == 2) {
00504         if (strcmp(argv[1], "get-poolsize") == 0) { 
00505             tcl.result("1");
00506             return TCL_OK;
00507         } else if (strcmp(argv[1], "get-start-time") == 0) {
00508             tcl.resultf("%.17g", start_time_);
00509             return TCL_OK;
00510         } else if (strcmp(argv[1], "get-duration") == 0) {
00511             tcl.resultf("%d", duration_);
00512             return TCL_OK;
00513         }
00514 
00515     } else if (argc == 3) {
00516         if (strcmp(argv[1], "gen-pageid") == 0) {
00517             // Main pageid, never return id of component pages
00518             tcl.result("0");
00519             return TCL_OK;
00520         } else if (strcmp(argv[1], "gen-size") == 0) {
00521             int id = atoi(argv[2]);
00522             if (id == 0) 
00523                 tcl.resultf("%d", main_size_);
00524             else 
00525                 tcl.resultf("%d", comp_size_);
00526             return TCL_OK;
00527         } else if (strcmp(argv[1], "gen-obj-size") == 0) {
00528             tcl.resultf("%d", comp_size_);
00529             return (TCL_OK);
00530         } else if (strcmp(argv[1], "get-next-objs") == 0) {
00531             PageID id;
00532             ClientPage::split_name(argv[2], id);
00533             // If we want simultaneous requests of multiple
00534             // objects, return a list; otherwise return a single
00535             // pageid. 
00536             for (int i = id.id_+1; i < num_pages_; i++) {
00537                 tcl.resultf("%s %s:%d", tcl.result(), 
00538                         id.s_->name(), i);
00539             }
00540             return TCL_OK;
00541         } else if (strcmp(argv[1], "ranvar-main-age") == 0) {
00542             rvMainAge_ = 
00543                 (RandomVariable*)TclObject::lookup(argv[2]);
00544             return TCL_OK;
00545         } else if (strcmp(argv[1], "ranvar-obj-age") == 0) {
00546             rvCompAge_ =
00547                 (RandomVariable*)TclObject::lookup(argv[2]);
00548             return TCL_OK;
00549         } else if (strcmp(argv[1], "set-start-time") == 0) {
00550             double st = strtod(argv[2], NULL);
00551             start_time_ = st;
00552             end_time_ += st;
00553             return TCL_OK;
00554         } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
00555             tcl.resultf("%.17g", Scheduler::instance().clock());
00556             return TCL_OK;
00557         } else if (strcmp(argv[1], "is-mainpage") == 0) {
00558             // Tell if the given page is a main page or an 
00559             // embedded object. 
00560             // XXX Here because we have only one page, so only 
00561             // page id 0 is the main page. If we have multiple 
00562             // pages, we need something else to do this.
00563             PageID t1;
00564             ClientPage::split_name(argv[2], t1);
00565             if (t1.id_ == 0)
00566                 tcl.result("1");
00567             else 
00568                 tcl.result("0");
00569             return TCL_OK;
00570         } else if (strcmp(argv[1], "get-mainpage") == 0) {
00571             // Get the main page of an embedded object
00572             // XXX Should maintain a mapping between embedded
00573             // objects and main pages. It can be an algorithmic
00574             // one, e.g., using page id intervals. It's simple 
00575             // here because we have only one page.
00576             PageID t1;
00577             ClientPage::split_name(argv[2], t1);
00578             tcl.resultf("%s:0", t1.s_->name());
00579             return TCL_OK;
00580         } else if (strcmp(argv[1], "get-obj-num") == 0) {
00581             // Returns the number of embedded objects of the page
00582             // given in argv[1]. Here because we have only one 
00583             // page, we return a fixed value.
00584             tcl.resultf("%d", num_pages_-1);
00585             return TCL_OK;
00586         }
00587 
00588     } else {
00589         // argc > 3
00590         if (strcmp(argv[1], "gen-modtime") == 0) {
00591             int id = atoi(argv[2]);
00592             if (id == 0) {
00593                 if (rvMainAge_ == 0) {
00594                   tcl.add_errorf("%s: no page age generator", 
00595                          name_);
00596                     return TCL_ERROR;
00597                 }
00598                 double mt = strtod(argv[3], NULL);
00599                 tcl.resultf("%.17g", mt + rvMainAge_->value());
00600             } else {
00601                 if (rvCompAge_ == 0) {
00602                    tcl.add_errorf("%s: no page age generator", 
00603                           name_);
00604                    return TCL_ERROR;
00605                 }
00606                 double mt = atoi(argv[3]);
00607                 tcl.resultf("%.17g", mt + rvCompAge_->value());
00608             }
00609             return TCL_OK;
00610         } else if (strcmp(argv[1], "gen-obj-modtime") == 0) {
00611             if (rvCompAge_ == 0) {
00612                 tcl.add_errorf("%s: no page age generator", 
00613                            name_);
00614                 return TCL_ERROR;
00615             }
00616             double mt = atoi(argv[3]);
00617             tcl.resultf("%.17g", mt + rvCompAge_->value());
00618             return TCL_OK;
00619         }
00620     }
00621 
00622     return PagePool::command(argc, argv);
00623 }
00624 
00625 
00626 static class ClientPagePoolClass : public TclClass {
00627 public:
00628         ClientPagePoolClass() : TclClass("PagePool/Client") {}
00629         TclObject* create(int, const char*const*) {
00630         return (new ClientPagePool());
00631     }
00632 } class_clientpagepool_agent;
00633 
00634 ClientPagePool::ClientPagePool()
00635 {
00636     namemap_ = new Tcl_HashTable;
00637     Tcl_InitHashTable(namemap_, 2);
00638 }
00639 
00640 ClientPagePool::~ClientPagePool()
00641 {
00642     if (namemap_ != NULL) {
00643         Tcl_DeleteHashTable(namemap_);
00644         delete namemap_;
00645     }
00646 }
00647 
00648 // In case client/cache/server needs details, e.g., page listing
00649 int ClientPagePool::command(int argc, const char*const* argv)
00650 {
00651     Tcl& tcl = Tcl::instance();
00652     if (argc == 2) {
00653         if (strcmp(argv[1], "list-pages") == 0) {
00654             Tcl_HashEntry *he;
00655             Tcl_HashSearch hs;
00656             char *buf = new char[num_pages_*20];
00657             char *p = buf;
00658             for (he = Tcl_FirstHashEntry(namemap_, &hs); 
00659                  he != NULL;
00660                  he = Tcl_NextHashEntry(&hs)) {
00661                 int* t2 = (int*)Tcl_GetHashKey(namemap_, he);
00662                 PageID t1(t2);
00663 #ifdef NEED_SUNOS_PROTOS
00664                 sprintf(p, "%s:%-d ", t1.s_->name(),t1.id_);
00665                 p += strlen(p);
00666 #else
00667                 p += sprintf(p,"%s:%-d ",t1.s_->name(),t1.id_);
00668 #endif
00669             }
00670             tcl.resultf("%s", buf);
00671             delete []buf;
00672             return TCL_OK;
00673         }
00674     }
00675     return PagePool::command(argc, argv);
00676 }
00677 
00678 ClientPage* ClientPagePool::get_page(const char *name)
00679 {
00680     PageID t1;
00681     ClientPage::split_name(name, t1);
00682 
00683     Tcl_HashEntry *he = Tcl_FindHashEntry(namemap_, (const char *)&t1);
00684     if (he == NULL)
00685         return NULL;
00686     return (ClientPage *)Tcl_GetHashValue(he);
00687 }
00688 
00689 int ClientPagePool::get_pageinfo(const char *name, char *buf)
00690 {
00691     ClientPage *pg = get_page(name);
00692     if (pg == NULL) 
00693         return -1;
00694     pg->print_info(buf);
00695     return 0;
00696 }
00697 
00698 ClientPage* ClientPagePool::enter_page(int argc, const char*const* argv)
00699 {
00700     double mt = -1, et, age = -1, noc = 0;
00701     int size = -1;
00702     for (int i = 3; i < argc; i+=2) {
00703         if (strcmp(argv[i], "modtime") == 0)
00704             mt = strtod(argv[i+1], NULL);
00705         else if (strcmp(argv[i], "size") == 0) 
00706             size = atoi(argv[i+1]);
00707         else if (strcmp(argv[i], "age") == 0)
00708             age = strtod(argv[i+1], NULL);
00709         else if (strcmp(argv[i], "noc") == 0)
00710                 // non-cacheable flag
00711             noc = 1;
00712     }
00713     // XXX allow mod time < 0 and age < 0!!
00714     if (size < 0) {
00715         fprintf(stderr, "PagePool %s: wrong information for page %s\n",
00716             name_, argv[2]);
00717         return NULL;
00718     }
00719     et = Scheduler::instance().clock();
00720     ClientPage* pg = new ClientPage(argv[2], size, mt, et, age);
00721     if (add_page(pg) < 0) {
00722         delete pg; 
00723         return NULL;
00724     }
00725     if (noc) 
00726         pg->set_uncacheable();
00727     return pg;
00728 }
00729 
00730 ClientPage* ClientPagePool::enter_page(const char *name, int size, double mt, 
00731                        double et, double age)
00732 {
00733     ClientPage* pg = new ClientPage(name, size, mt, et, age);
00734     if (add_page(pg) < 0) {
00735         delete pg; 
00736         return NULL;
00737     }
00738     return pg;
00739 }
00740 
00741 // XXX We don't need parsing "noc" here because a non-cacheable
00742 // page won't be processed by a cache.
00743 ClientPage* ClientPagePool::enter_metadata(int argc, const char*const* argv)
00744 {
00745     ClientPage *pg = enter_page(argc, argv);
00746     if (pg != NULL)
00747         pg->set_valid_hdr();
00748     return pg;
00749 }
00750 
00751 ClientPage* ClientPagePool::enter_metadata(const char *name, int size, 
00752                        double mt, double et, double age)
00753 {
00754     ClientPage *pg = enter_page(name, size, mt, et, age);
00755     if (pg != NULL) 
00756         pg->set_valid_hdr();
00757     return pg;
00758 }
00759 
00760 int ClientPagePool::add_page(ClientPage* pg)
00761 {
00762     if (pg == NULL)
00763         return -1;
00764 
00765     char buf[HTTP_MAXURLLEN];
00766     pg->name(buf);
00767 
00768     PageID t1;
00769     ClientPage::split_name(buf, t1);
00770 
00771     int newEntry = 1;
00772     Tcl_HashEntry *he = Tcl_CreateHashEntry(namemap_, 
00773                         (const char *)&t1,
00774                         &newEntry);
00775     if (he == NULL)
00776         return -1;
00777 
00778     // XXX If cache replacement algorithm is added, should change 
00779     // cache size here!!
00780     if (newEntry) {
00781         Tcl_SetHashValue(he, (ClientData)pg);
00782         num_pages_++;
00783     } else {
00784         // Replace the old one
00785         ClientPage *q = (ClientPage *)Tcl_GetHashValue(he);
00786         // XXX must copy the counter value
00787         pg->counter() = q->counter();
00788         // XXX must copy the mpush values
00789         if (q->is_mpush())
00790             pg->set_mpush(q->mpush_time());
00791         Tcl_SetHashValue(he, (ClientData)pg);
00792         delete q;
00793     }
00794     return 0;
00795 }
00796 
00797 int ClientPagePool::remove_page(const char *name)
00798 {
00799     PageID t1;
00800     ClientPage::split_name(name, t1);
00801 
00802     // Find out which client we are seeking
00803     Tcl_HashEntry *he = Tcl_FindHashEntry(namemap_, (const char *)&t1);
00804     if (he == NULL)
00805         return -1;
00806     ClientPage *pg = (ClientPage *)Tcl_GetHashValue(he);
00807     Tcl_DeleteHashEntry(he);
00808     delete pg;
00809     num_pages_--;
00810     // XXX If cache replacement algorithm is added, should change
00811     // cache size here!!
00812     return 0;
00813 }
00814 
00815 int ClientPagePool::set_mtime(const char *name, double mt)
00816 {
00817     ClientPage *pg = (ClientPage *)get_page(name);
00818     if (pg == NULL) 
00819         return -1;
00820     pg->mtime() = mt;
00821     return 0;
00822 }
00823 
00824 int ClientPagePool::get_mtime(const char *name, double& mt)
00825 {
00826     ClientPage *pg = (ClientPage *)get_page(name);
00827     if (pg == NULL) 
00828         return -1;
00829     mt = pg->mtime();
00830     return 0;
00831 }
00832 
00833 int ClientPagePool::set_etime(const char *name, double et)
00834 {
00835     ClientPage *pg = (ClientPage *)get_page(name);
00836     if (pg == NULL) 
00837         return -1;
00838     pg->etime() = et;
00839     return 0;
00840 }
00841 
00842 int ClientPagePool::get_etime(const char *name, double& et)
00843 {
00844     ClientPage *pg = (ClientPage *)get_page(name);
00845     if (pg == NULL) 
00846         return -1;
00847     et = pg->etime();
00848     return 0;
00849 }
00850 
00851 int ClientPagePool::get_size(const char *name, int& size) 
00852 {
00853     ClientPage *pg = (ClientPage *)get_page(name);
00854     if (pg == NULL) 
00855         return -1;
00856     size = pg->size();
00857     return 0;
00858 }
00859 
00860 int ClientPagePool::get_age(const char *name, double& age) 
00861 {
00862     ClientPage *pg = (ClientPage *)get_page(name);
00863     if (pg == NULL) 
00864         return -1;
00865     age = pg->age();
00866     return 0;
00867 }
00868 
00869 void ClientPagePool::invalidate_server(int sid)
00870 {
00871     Tcl_HashEntry *he;
00872     Tcl_HashSearch hs;
00873     ClientPage *pg;
00874     int i;
00875 
00876     for (i = 0, he = Tcl_FirstHashEntry(namemap_, &hs);
00877          he != NULL;
00878          he = Tcl_NextHashEntry(&hs), i++) {
00879         pg = (ClientPage *) Tcl_GetHashValue(he);
00880         if (pg->server()->id() == sid)
00881             pg->server_down();
00882     }
00883 }
00884 
00885 
00886 // Proxy traces. Request file format:
00887 //
00888 // [<time> <clientID> <serverID> <URL_ID>]
00889 // i <Duration> <Number_of_unique_URLs>
00890 //
00891 // <time> is guaranteed to start from 0. It needs to be adjusted
00892 //
00893 // Page file format (sorted by access counts)
00894 // 
00895 // <serverID> <URL_ID> <PageSize> <AccessCount>
00896 
00897 static class ProxyTracePagePoolClass : public TclClass {
00898 public:
00899         ProxyTracePagePoolClass() : TclClass("PagePool/ProxyTrace") {}
00900         TclObject* create(int, const char*const*) {
00901         return (new ProxyTracePagePool());
00902     }
00903 } class_ProxyTracepagepool_agent;
00904 
00905 ProxyTracePagePool::ProxyTracePagePool() : 
00906     rvDyn_(NULL), rvStatic_(NULL), br_(0), 
00907     size_(NULL), reqfile_(NULL), req_(NULL), lastseq_(0)
00908 {
00909 }
00910 
00911 ProxyTracePagePool::~ProxyTracePagePool()
00912 {
00913     if (size_ != NULL) 
00914         delete []size_;
00915     if (reqfile_ != NULL) 
00916         fclose(reqfile_);
00917     if (req_ != NULL) {
00918         Tcl_DeleteHashTable(req_);
00919         delete req_;
00920     }
00921 }
00922 
00923 int ProxyTracePagePool::init_req(const char *fn) 
00924 {
00925     reqfile_ = fopen(fn, "r");
00926     if (reqfile_ == NULL) {
00927         fprintf(stderr, 
00928           "ProxyTracePagePool: couldn't open trace file %s\n", fn);
00929         return TCL_ERROR;
00930     }
00931 
00932     // Discover information about the trace, e.g., number of pages,
00933     // start time, end time, etc. They should be available at the 
00934     // first line of the trace file.
00935     return find_info();
00936 }
00937 
00938 int ProxyTracePagePool::find_info()
00939 {
00940     // Read the last line of the file
00941     fseek(reqfile_, -128, SEEK_END);
00942     char buf[129];
00943     if (fread(buf, 1, 128, reqfile_) != 128) {
00944         fprintf(stderr,
00945             "ProxyTracePagePool: cannot read file information\n");
00946         return TCL_ERROR;
00947     }
00948     int i;
00949     // ignore the last RETURN
00950     buf[128] = 0;
00951     if (buf[127] == '\n')
00952         buf[127] = 0; 
00953     for (i = 127; i >= 0; i--)
00954         if (buf[i] == '\n') {
00955             i++; 
00956             break;
00957         }
00958     if (buf[i] != 'i') {
00959         fprintf(stderr, 
00960     "ProxyTracePagePool: trace file doesn't contain statistics.\n");
00961         abort();
00962     }
00963     double len;
00964     sscanf(buf+i+1, "%lf %u", &len, &num_pages_);
00965     duration_ = (int)ceil(len);
00966 #if 0
00967     printf("ProxyTracePagePool: duration %d pages %u\n",
00968            duration_, num_pages_);
00969 #endif
00970     rewind(reqfile_);
00971     return TCL_OK;
00972 }
00973 
00974 // Load page size info. Assuming request stream has already been loaded
00975 int ProxyTracePagePool::init_page(const char *fn)
00976 {
00977     FILE *fp = fopen(fn, "r");
00978     if (fp == NULL) {
00979         fprintf(stderr, 
00980           "ProxyTracePagePool: couldn't open trace file %s\n", fn);
00981         return TCL_ERROR;
00982     }
00983     if (size_ != NULL) 
00984         delete []size_;
00985     int* p = new int[num_pages_];
00986     size_ = p;
00987     for (int i = 0; i < num_pages_; i++, p++)
00988         fscanf(fp, "%*d %*d %d %*u\n", p);
00989     fclose(fp);
00990     return TCL_OK;
00991 }
00992 
00993 ProxyTracePagePool::ClientRequest* ProxyTracePagePool::load_req(int cid)
00994 {
00995     // Find out which client we are seeking
00996     Tcl_HashEntry *he;
00997     ClientRequest *p;
00998     int dummy; 
00999     long key = cid;
01000     if ((he = Tcl_FindHashEntry(req_, (const char*)key)) == NULL) {
01001         // New entry
01002         p = new ClientRequest();
01003         p->seq_ = lastseq_++;
01004         he = Tcl_CreateHashEntry(req_, (const char*)key, &dummy);
01005         Tcl_SetHashValue(he, (const char*)p);
01006         // Search from the beginning of file for this new client
01007         fseek(reqfile_, 0, SEEK_SET);
01008     } else {
01009         p = (ClientRequest*)Tcl_GetHashValue(he);
01010         if (p->nrt_ == -1)
01011             // No more requests for this client
01012             return p;
01013         // Clear EOF status
01014         fseek(reqfile_, p->fpos_, SEEK_SET);
01015     }
01016 
01017     // Looking for the next available request for this client
01018     double nrt;
01019     int ncid = -1, nurl;
01020     char buf[256];
01021     while (fgets(buf, 256, reqfile_)) {
01022         if (isalpha(buf[0])) {
01023             // Last line, break;
01024             ncid = -1;
01025             break;
01026         }
01027         sscanf(buf, "%lf %d %*d %d\n", &nrt, &ncid, &nurl);
01028         if ((ncid % nclient_) == p->seq_)
01029             break;
01030     }
01031     if ((ncid % nclient_) != p->seq_)
01032         // Didn't find the next request for this client
01033         p->nrt_ = -1;
01034     else {
01035         p->nrt_ = nrt, p->nurl_ = nurl;
01036         p->nrt_ += start_time_;
01037     }
01038     p->fpos_ = ftell(reqfile_);
01039     return p;
01040 }
01041 
01042 // Provide a tcl interface compatible with MathPagePool
01043 int ProxyTracePagePool::command(int argc, const char*const* argv)
01044 {
01045     Tcl& tcl = Tcl::instance();
01046 
01047     if (argc == 2) {
01048         if (strcmp(argv[1], "get-poolsize") == 0) { 
01049             tcl.resultf("%u", num_pages_);
01050             return TCL_OK;
01051         } else if (strcmp(argv[1], "get-start-time") == 0) {
01052             tcl.resultf("%.17g", start_time_);
01053             return TCL_OK;
01054         } else if (strcmp(argv[1], "get-duration") == 0) {
01055             tcl.resultf("%d", duration_);
01056             return TCL_OK;
01057         } else if (strcmp(argv[1], "bimodal-ratio") == 0) {
01058             tcl.resultf("%g", br_ / 10);
01059             return TCL_OK;
01060         }
01061     } else if (argc == 3) {
01062         if (strcmp(argv[1], "set-client-num") == 0) {
01063             // Set the number of clients it'll access
01064             // Cannot be changed once set
01065             if (req_ != NULL)
01066                 return TCL_ERROR;
01067             int num = atoi(argv[2]);
01068             req_ = new Tcl_HashTable;
01069             Tcl_InitHashTable(req_, TCL_ONE_WORD_KEYS);
01070             nclient_ = num;
01071             return TCL_OK;
01072         } else if (strcmp(argv[1], "gen-request") == 0) {
01073             // Use client id to get a corresponding request
01074             int id = atoi(argv[2]);
01075             ClientRequest *p = load_req(id);
01076             if ((p->nrt_ >= 0) && 
01077                 (p->nrt_ < Scheduler::instance().clock())) {
01078                 // XXX Do NOT treat this as an error, also
01079                 // do NOT disable further requests from this 
01080                 // client.
01081                 fprintf(stderr,
01082                     "%.17g: Wrong request time %g.\n",
01083                     Scheduler::instance().clock(),
01084                     p->nrt_);
01085                 // XXX If it's a little bit older than current 
01086                 // time, let it be a little bit later than now
01087                 p->nrt_ = Scheduler::instance().clock()+0.001;
01088             }
01089             tcl.resultf("%lf %d", 
01090                     p->nrt_ - Scheduler::instance().clock(), 
01091                     p->nurl_);
01092             return TCL_OK;
01093         } else if (strcmp(argv[1], "gen-size") == 0) {
01094             int id = atoi(argv[2]);
01095             if ((id < 0) || (id > num_pages_)) {
01096                 tcl.result("PagePool: id out of range.\n");
01097                 return TCL_ERROR;
01098             }
01099             tcl.resultf("%d", size_[id]);
01100             return TCL_OK;
01101         } else if (strcmp(argv[1], "set-start-time") == 0) {
01102             start_time_ = strtod(argv[2], NULL);
01103             return TCL_OK;
01104         } else if (strcmp(argv[1], "bimodal-ratio") == 0) {
01105             // XXX Codes in Http/Server::gen-page{} also depends
01106             // on this dyn/static page algorithm. If this is 
01107             // changed, that instproc must be changed too.
01108             //
01109             // percentage of dynamic pages. E.g., 
01110             // if this ratio is 5, then page 0-4 is 
01111             // dynamic, and page 4-99 is static, and so on.
01112             double ratio = strtod(argv[2], NULL);
01113             //br_ = (int)ceil(ratio*100);
01114             br_ = (int)ceil(ratio*10);
01115             return TCL_OK;
01116         } else if (strcmp(argv[1], "ranvar-dp") == 0) {
01117             // Page mod ranvar for dynamic pages
01118             rvDyn_ = (RandomVariable*)TclObject::lookup(argv[2]);
01119             return TCL_OK;
01120         } else if (strcmp(argv[1], "ranvar-sp") == 0) {
01121             // page mod ranvar for static pages
01122             rvStatic_= (RandomVariable*)TclObject::lookup(argv[2]);
01123             return TCL_OK;
01124         } else if (strcmp(argv[1], "set-reqfile") == 0) {
01125             return init_req(argv[2]);
01126         } else if (strcmp(argv[1], "set-pagefile") == 0) {
01127             return init_page(argv[2]);
01128         } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
01129             int id = atoi(argv[2]) % 10;
01130             if (id >= br_)
01131                 // Static page
01132                 tcl.result("0");
01133             else
01134                 // Dynamic page
01135                 tcl.resultf("%.17g", 
01136                         Scheduler::instance().clock());
01137             return TCL_OK;
01138         }
01139     } else {
01140         if (strcmp(argv[1], "gen-modtime") == 0) {
01141             if ((rvDyn_ == 0) || (rvStatic_ == 0)) {
01142                 tcl.add_errorf("%s: no page age generator", 
01143                            name_);
01144                 return TCL_ERROR;
01145             }
01146             // int id = atoi(argv[2]) % 100;
01147             int id = atoi(argv[2]) % 10;
01148             double mt = strtod(argv[3], NULL);
01149             if (id >= br_) 
01150                 tcl.resultf("%.17g", mt + rvStatic_->value());
01151             else 
01152                 tcl.resultf("%.17g", mt + rvDyn_->value());
01153             return TCL_OK;
01154         }
01155     }
01156 
01157     return PagePool::command(argc, argv);
01158 }
01159 
01160 
01161 // Proxy trace with special method for page modification
01162 static class EPAPagePoolClass : public TclClass {
01163 public:
01164     EPAPagePoolClass() : TclClass("PagePool/ProxyTrace/epa") {}
01165     TclObject* create(int, const char*const*) {
01166         return (new EPATracePagePool());
01167     }
01168 } class_epapagepool_agent;
01169 
01170 int EPATracePagePool::command(int argc, const char*const* argv)
01171 {
01172     Tcl& tcl = Tcl::instance();
01173     if (argc == 2) {
01174         if (strcmp(argv[1], "pick-pagemod") == 0) {
01175             if (rvDyn_ == 0) {
01176                 tcl.add_errorf("%s: no page age generator",
01177                            name_);
01178                 return (TCL_ERROR);
01179             }
01180             int j = (int)floor(rvDyn_->value());
01181             //fprintf(stderr, "mod id = %d\n", j/br_*10 + j % br_);
01182             tcl.resultf("%d", j/br_*10 + j % br_);
01183             return TCL_OK;
01184         }
01185     } else if (argc == 3) {
01186         if (strcmp(argv[1], "ranvar-dp") == 0) {
01187             rvDyn_ = (RandomVariable*)TclObject::lookup(argv[2]);
01188             if (rvDyn_ == 0) {
01189                 tcl.add_errorf("%s: no page age generator",
01190                            name_);
01191                 return (TCL_ERROR);
01192             }
01193             ((UniformRandomVariable*)rvDyn_)->setmin(0);
01194             ((UniformRandomVariable*)rvDyn_)->setmax(num_pages_/10*br_ + num_pages_%br_ - 1);
01195             return TCL_OK;
01196         }
01197     } else {
01198         if (strcmp(argv[1], "gen-modtime") == 0) {
01199             // Return a very large number
01200             tcl.resultf("%d", INT_MAX);
01201             return TCL_OK;
01202         }
01203     }
01204     return ProxyTracePagePool::command(argc, argv);
01205 }
01206 

Generated on Tue Mar 6 16:47:48 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6