Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members

pagepool.cc

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

Generated on Tue Apr 20 12:14:26 2004 for NS2.26SourcesOriginal by doxygen 1.3.3