pagepool.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:31k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) Xerox Corporation 1998. All rights reserved.
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify it
  5.  * under the terms of the GNU General Public License as published by the
  6.  * Free Software Foundation; either version 2 of the License, or (at your
  7.  * option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful, but
  10.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12.  * General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License along
  15.  * with this program; if not, write to the Free Software Foundation, Inc.,
  16.  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17.  *
  18.  * Linking this file statically or dynamically with other modules is making
  19.  * a combined work based on this file.  Thus, the terms and conditions of
  20.  * the GNU General Public License cover the whole combination.
  21.  *
  22.  * In addition, as a special exception, the copyright holders of this file
  23.  * give you permission to combine this file with free software programs or
  24.  * libraries that are released under the GNU LGPL and with code included in
  25.  * the standard release of ns-2 under the Apache 2.0 license or under
  26.  * otherwise-compatible licenses with advertising requirements (or modified
  27.  * versions of such code, with unchanged license).  You may copy and
  28.  * distribute such a system following the terms of the GNU GPL for this
  29.  * file and the licenses of the other code concerned, provided that you
  30.  * include the source code of that other code when and as the GNU GPL
  31.  * requires distribution of source code.
  32.  *
  33.  * Note that people who make modified versions of this file are not
  34.  * obligated to grant this special exception for their modified versions;
  35.  * it is their choice whether to do so.  The GNU General Public License
  36.  * gives permission to release a modified version without this exception;
  37.  * this exception also makes it possible to release a modified version
  38.  * which carries forward this exception.
  39.  *
  40.  * $Header: /cvsroot/nsnam/ns-2/webcache/pagepool.cc,v 1.17 2006/02/21 15:20:20 mahrenho Exp $
  41.  */
  42. #include <stdlib.h>
  43. #include <sys/types.h>
  44. #include <fcntl.h>
  45. #ifdef WIN32
  46. #include <windows.h>
  47. #include <io.h>
  48. #else 
  49. #include <unistd.h>
  50. #include <sys/file.h>
  51. #endif
  52. #include <sys/stat.h>
  53. #include <stdio.h>
  54. #include <limits.h>
  55. #include <ctype.h>
  56. extern "C" {
  57. #include <otcl.h>
  58. }
  59. #include "pagepool.h"
  60. #include "http.h"
  61. // Static/global variables
  62. int ClientPage::PUSHALL_ = 0; // Initialized to selective push
  63. void ServerPage::set_mtime(int *mt, int n)
  64. {
  65. if (mtime_ != NULL) 
  66. delete []mtime_;
  67. mtime_ = new int[n];
  68. memcpy(mtime_, mt, sizeof(int)*n);
  69. }
  70. ClientPage::ClientPage(const char *n, int s, double mt, double et, double a) :
  71. Page(s), age_(a), mtime_(mt), etime_(et), 
  72. status_(HTTP_VALID_PAGE), counter_(0), 
  73. mpushTime_(0)
  74. {
  75. // Parse name to get server and page id
  76. char *buf = new char[strlen(n) + 1];
  77. strcpy(buf, n);
  78. char *tmp = strtok(buf, ":");
  79. server_ = (HttpApp*)TclObject::lookup(tmp);
  80. if (server_ == NULL) {
  81. fprintf(stderr, "Non-exitent server name for page %s", n);
  82. abort();
  83. }
  84. tmp = strtok(NULL, ":");
  85. id_ = atol(tmp);
  86. delete []buf;
  87. }
  88. void ClientPage::print_name(char* name, PageID& id)
  89. {
  90. sprintf(name, "%s:%-d", id.s_->name(), id.id_);
  91. }
  92. void ClientPage::split_name(const char* name, PageID& id)
  93. {
  94. char *buf = new char[strlen(name)+1];
  95. strcpy(buf, name);
  96. char *tmp = strtok(buf, ":");
  97. id.s_ = (HttpApp*)TclObject::lookup(tmp);
  98. if (id.s_ == NULL) {
  99. fprintf(stderr, "Non-exitent server name for page %sn", name);
  100. abort();
  101. }
  102. tmp = strtok(NULL, ":");
  103. id.id_ = atol(tmp);
  104. delete []buf;
  105. }
  106. void ClientPage::print_info(char *buf)
  107. {
  108. sprintf(buf, "size %d modtime %.17g time %.17g age %.17g",
  109. size(), mtime(), etime(), age());
  110. if (is_uncacheable())
  111. strcat(buf, " noc 1");
  112. }
  113. void ClientPage::name(char* buf) 
  114. {
  115. sprintf(buf, "%s:%d", server_->name(), id());
  116. }
  117. static class PagePoolClass : public TclClass {
  118. public:
  119.         PagePoolClass() : TclClass("PagePool") {}
  120.         TclObject* create(int, const char*const*) {
  121. return (new PagePool());
  122. }
  123. } class_pagepool_agent;
  124. int PagePool::command(int argc, const char*const* argv)
  125. {
  126. if (argc == 2) {
  127. // XXX Should be static class variables... 
  128. if (strcmp(argv[1], "set-allpush") == 0) {
  129. ClientPage::PUSHALL_ = 1;
  130. return (TCL_OK);
  131. }
  132. if (strcmp(argv[1], "set-selpush") == 0) {
  133. ClientPage::PUSHALL_ = 0;
  134. return (TCL_OK);
  135. }
  136. }
  137. return TclObject::command(argc, argv);
  138. }
  139. // TracePagePool
  140. // Used for Worrell's filtered server traces only. For handling general 
  141. // web server traces and proxy traces, have a look at ProxyTracePagePool below.
  142. //
  143. // Load a trace statistics file, and randomly generate requests and 
  144. // page lifetimes from the trace.
  145. //
  146. // Trace statistics file format:
  147. // <URL> <size> {<modification time>}
  148. static class TracePagePoolClass : public TclClass {
  149. public:
  150.         TracePagePoolClass() : TclClass("PagePool/Trace") {}
  151.         TclObject* create(int argc, const char*const* argv) {
  152. if (argc >= 5)
  153. return (new TracePagePool(argv[4]));
  154. return 0;
  155. }
  156. } class_tracepagepool_agent;
  157. TracePagePool::TracePagePool(const char *fn) : 
  158. PagePool(), ranvar_(0)
  159. {
  160. FILE *fp = fopen(fn, "r");
  161. if (fp == NULL) {
  162. fprintf(stderr, 
  163. "TracePagePool: couldn't open trace file %sn", fn);
  164. abort(); // What else can we do?
  165. }
  166. namemap_ = new Tcl_HashTable;
  167. Tcl_InitHashTable(namemap_, TCL_STRING_KEYS);
  168. idmap_ = new Tcl_HashTable;
  169. Tcl_InitHashTable(idmap_, TCL_ONE_WORD_KEYS);
  170. while (load_page(fp));
  171. change_time();
  172. }
  173. TracePagePool::~TracePagePool()
  174. {
  175. if (namemap_ != NULL) {
  176. Tcl_DeleteHashTable(namemap_);
  177. delete namemap_;
  178. }
  179. if (idmap_ != NULL) {
  180. Tcl_DeleteHashTable(idmap_);
  181. delete idmap_;
  182. }
  183. }
  184. void TracePagePool::change_time()
  185. {
  186. Tcl_HashEntry *he;
  187. Tcl_HashSearch hs;
  188. ServerPage *pg;
  189. int i, j;
  190. for (i = 0, he = Tcl_FirstHashEntry(idmap_, &hs);
  191.      he != NULL;
  192.      he = Tcl_NextHashEntry(&hs), i++) {
  193. pg = (ServerPage *) Tcl_GetHashValue(he);
  194. for (j = 0; j < pg->num_mtime(); j++) 
  195. pg->mtime(j) -= (int)start_time_;
  196. }
  197. end_time_ -= start_time_;
  198. start_time_ = 0;
  199. duration_ = (int)end_time_;
  200. }
  201. ServerPage* TracePagePool::load_page(FILE *fp)
  202. {
  203. static char buf[TRACEPAGEPOOL_MAXBUF];
  204. char *delim = " tn";
  205. char *tmp1, *tmp2;
  206. ServerPage *pg;
  207. // XXX Use internal variables of struct Page
  208. if (!fgets(buf, TRACEPAGEPOOL_MAXBUF, fp))
  209. return NULL;
  210. // URL
  211. tmp1 = strtok(buf, delim);
  212. // Size
  213. tmp2 = strtok(NULL, delim);
  214. pg = new ServerPage(atoi(tmp2), num_pages_++);
  215. if (add_page(tmp1, pg)) {
  216. delete pg;
  217. return NULL;
  218. }
  219. // Modtimes, assuming they are in ascending time order
  220. int num = 0;
  221. int *nmd = new int[5];
  222. while ((tmp1 = strtok(NULL, delim)) != NULL) {
  223. if (num >= 5) {
  224. int *tt = new int[num+5];
  225. memcpy(tt, nmd, sizeof(int)*num);
  226. delete []nmd;
  227. nmd = tt;
  228. }
  229. nmd[num] = atoi(tmp1);
  230. if (nmd[num] < start_time_)
  231. start_time_ = nmd[num];
  232. if (nmd[num] > end_time_)
  233. end_time_ = nmd[num];
  234. num++;
  235. }
  236. pg->num_mtime() = num;
  237. pg->set_mtime(nmd, num);
  238. delete []nmd;
  239. return pg;
  240. }
  241. int TracePagePool::add_page(const char* name, ServerPage *pg)
  242. {
  243. int newEntry = 1;
  244. Tcl_HashEntry *he = Tcl_CreateHashEntry(namemap_, 
  245. (const char *)name,
  246. &newEntry);
  247. if (he == NULL)
  248. return -1;
  249. if (newEntry)
  250. Tcl_SetHashValue(he, (ClientData)pg);
  251. else 
  252. fprintf(stderr, "TracePagePool: Duplicate entry %sn", 
  253. name);
  254. long key = pg->id();
  255. Tcl_HashEntry *hf = 
  256. Tcl_CreateHashEntry(idmap_, (const char *)key, &newEntry);
  257. if (hf == NULL) {
  258. Tcl_DeleteHashEntry(he);
  259. return -1;
  260. }
  261. if (newEntry)
  262. Tcl_SetHashValue(hf, (ClientData)pg);
  263. else 
  264. fprintf(stderr, "TracePagePool: Duplicate entry %dn", 
  265. pg->id());
  266. return 0;
  267. }
  268. ServerPage* TracePagePool::get_page(int id)
  269. {
  270. if ((id < 0) || (id >= num_pages_))
  271. return NULL;
  272. long key = id;
  273. Tcl_HashEntry *he = Tcl_FindHashEntry(idmap_, (const char *)key);
  274. if (he == NULL)
  275. return NULL;
  276. return (ServerPage *)Tcl_GetHashValue(he);
  277. }
  278. int TracePagePool::command(int argc, const char *const* argv)
  279. {
  280. Tcl &tcl = Tcl::instance();
  281. if (argc == 2) {
  282. if (strcmp(argv[1], "get-poolsize") == 0) {
  283. /* 
  284.  * <pgpool> get-poolsize
  285.  * Get the number of pages currently in pool
  286.  */
  287. tcl.resultf("%d", num_pages_);
  288. return TCL_OK;
  289. } else if (strcmp(argv[1], "get-start-time") == 0) {
  290. tcl.resultf("%.17g", start_time_);
  291. return TCL_OK;
  292. } else if (strcmp(argv[1], "get-duration") == 0) {
  293. tcl.resultf("%d", duration_);
  294. return TCL_OK;
  295. }
  296. } else if (argc == 3) {
  297. if (strcmp(argv[1], "gen-pageid") == 0) {
  298. /* 
  299.  * <pgpool> gen-pageid <client_id>
  300.  * Randomly generate a page id from page pool
  301.  */
  302. double tmp = ranvar_ ? ranvar_->value() : 
  303. Random::uniform();
  304. // tmp should be in [0, num_pages_-1]
  305. tmp = (tmp < 0) ? 0 : (tmp >= num_pages_) ? 
  306. (num_pages_-1):tmp;
  307. if ((int)tmp >= num_pages_) abort();
  308. tcl.resultf("%d", (int)tmp);
  309. return TCL_OK;
  310. } else if (strcmp(argv[1], "gen-size") == 0) {
  311. /*
  312.  * <pgpool> gen-size <pageid>
  313.  */
  314. int id = atoi(argv[2]);
  315. ServerPage *pg = get_page(id);
  316. if (pg == NULL) {
  317. tcl.add_errorf("TracePagePool %s: page %d doesn't exists.n",
  318.        name_, id);
  319. return TCL_ERROR;
  320. }
  321. tcl.resultf("%d", pg->size());
  322. return TCL_OK;
  323. } else if (strcmp(argv[1], "ranvar") == 0) {
  324. /* 
  325.  * <pgpool> ranvar <ranvar> 
  326.  * Set a random var which is used to randomly pick 
  327.  * a page from the page pool.
  328.  */
  329. ranvar_ = (RandomVariable *)TclObject::lookup(argv[2]);
  330. return TCL_OK;
  331. } else if (strcmp(argv[1], "set-start-time") == 0) {
  332. double st = strtod(argv[2], NULL);
  333. start_time_ = st;
  334. end_time_ += st;
  335. } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
  336. tcl.resultf("%.17g", Scheduler::instance().clock());
  337. return TCL_OK;
  338. }
  339. } else {
  340. if (strcmp(argv[1], "gen-modtime") == 0) {
  341. /* 
  342.  * <pgpool> get-modtime <pageid> <modtime>
  343.  * 
  344.  * Return next modtime that is larger than modtime
  345.  * To retrieve the first modtime (creation time), set 
  346.  * <modtime> to -1 in the request.
  347.  */
  348. int id = atoi(argv[2]);
  349. double mt = strtod(argv[3], NULL);
  350. ServerPage *pg = get_page(id);
  351. if (pg == NULL) {
  352. tcl.add_errorf("TracePagePool %s: page %d doesn't exists.n",
  353.        name_, id);
  354. return TCL_ERROR;
  355. }
  356. for (int i = 0; i < pg->num_mtime(); i++) 
  357. if (pg->mtime(i) > mt) {
  358. tcl.resultf("%.17g", 
  359.     pg->mtime(i)+start_time_);
  360. return TCL_OK;
  361. }
  362. // When get to the last modtime, return -1
  363. tcl.resultf("%d", INT_MAX);
  364. return TCL_OK;
  365. }
  366. }
  367. return PagePool::command(argc, argv);
  368. }
  369. static class MathPagePoolClass : public TclClass {
  370. public:
  371.         MathPagePoolClass() : TclClass("PagePool/Math") {}
  372.         TclObject* create(int, const char*const*) {
  373. return (new MathPagePool());
  374. }
  375. } class_mathpagepool_agent;
  376. // Use 3 ranvars to generate requests, mod times and page size
  377. int MathPagePool::command(int argc, const char *const* argv)
  378. {
  379. Tcl& tcl = Tcl::instance();
  380. // Keep the same tcl interface as PagePool/Trace
  381. if (argc == 2) {
  382. if (strcmp(argv[1], "get-poolsize") == 0) { 
  383. tcl.result("1");
  384. return TCL_OK;
  385. } else if (strcmp(argv[1], "get-start-time") == 0) {
  386. tcl.resultf("%.17g", start_time_);
  387. return TCL_OK;
  388. } else if (strcmp(argv[1], "get-duration") == 0) {
  389. tcl.resultf("%d", duration_);
  390. return TCL_OK;
  391. }
  392. } else if (argc == 3) {
  393. if (strcmp(argv[1], "gen-pageid") == 0) {
  394. // Single page
  395. tcl.result("0");
  396. return TCL_OK;
  397. } else if (strcmp(argv[1], "gen-size") == 0) {
  398. if (rvSize_ == 0) {
  399. tcl.add_errorf("%s: no page size generator", 
  400.        name_);
  401. return TCL_ERROR;
  402. }
  403. int size = (int) rvSize_->value();
  404. if (size == 0)
  405. // XXX do not allow page size 0, because TcpApp
  406. // doesn't behave correctly when sending 0 byte
  407. size = 1;
  408. tcl.resultf("%d", size);
  409. return TCL_OK;
  410. } else if (strcmp(argv[1], "ranvar-size") == 0) {
  411. rvSize_ = (RandomVariable*)TclObject::lookup(argv[2]);
  412. return TCL_OK;
  413. } else if (strcmp(argv[1], "ranvar-age") == 0) {
  414. rvAge_ = (RandomVariable*)TclObject::lookup(argv[2]);
  415. return TCL_OK;
  416. } else if (strcmp(argv[1], "set-start-time") == 0) {
  417. double st = strtod(argv[2], NULL);
  418. start_time_ = st;
  419. end_time_ += st;
  420. return TCL_OK;
  421. } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
  422. tcl.resultf("%.17g", Scheduler::instance().clock());
  423. return TCL_OK;
  424. }
  425. } else {
  426. if (strcmp(argv[1], "gen-modtime") == 0) {
  427. if (rvAge_ == 0) {
  428. tcl.add_errorf("%s: no page age generator", 
  429.        name_);
  430. return TCL_ERROR;
  431. }
  432. double mt = strtod(argv[3], NULL);
  433. tcl.resultf("%.17g", mt + rvAge_->value());
  434. return TCL_OK;
  435. }
  436. }
  437. return PagePool::command(argc, argv);
  438. }
  439. // Assume one main page, which changes often, and multiple component pages
  440. static class CompMathPagePoolClass : public TclClass {
  441. public:
  442.         CompMathPagePoolClass() : TclClass("PagePool/CompMath") {}
  443.         TclObject* create(int, const char*const*) {
  444. return (new CompMathPagePool());
  445. }
  446. } class_compmathpagepool_agent;
  447. CompMathPagePool::CompMathPagePool()
  448. {
  449. bind("num_pages_", &num_pages_);
  450. bind("main_size_", &main_size_);
  451. bind("comp_size_", &comp_size_);
  452. }
  453. int CompMathPagePool::command(int argc, const char *const* argv)
  454. {
  455. Tcl& tcl = Tcl::instance();
  456. // Keep the same tcl interface as PagePool/Trace
  457. if (argc == 2) {
  458. if (strcmp(argv[1], "get-poolsize") == 0) { 
  459. tcl.result("1");
  460. return TCL_OK;
  461. } else if (strcmp(argv[1], "get-start-time") == 0) {
  462. tcl.resultf("%.17g", start_time_);
  463. return TCL_OK;
  464. } else if (strcmp(argv[1], "get-duration") == 0) {
  465. tcl.resultf("%d", duration_);
  466. return TCL_OK;
  467. }
  468. } else if (argc == 3) {
  469. if (strcmp(argv[1], "gen-pageid") == 0) {
  470. // Main pageid, never return id of component pages
  471. tcl.result("0");
  472. return TCL_OK;
  473. } else if (strcmp(argv[1], "gen-size") == 0) {
  474. int id = atoi(argv[2]);
  475. if (id == 0) 
  476. tcl.resultf("%d", main_size_);
  477. else 
  478. tcl.resultf("%d", comp_size_);
  479. return TCL_OK;
  480. } else if (strcmp(argv[1], "gen-obj-size") == 0) {
  481. tcl.resultf("%d", comp_size_);
  482. return (TCL_OK);
  483. } else if (strcmp(argv[1], "get-next-objs") == 0) {
  484. PageID id;
  485. ClientPage::split_name(argv[2], id);
  486. // If we want simultaneous requests of multiple
  487. // objects, return a list; otherwise return a single
  488. // pageid. 
  489. for (int i = id.id_+1; i < num_pages_; i++) {
  490. tcl.resultf("%s %s:%d", tcl.result(), 
  491.     id.s_->name(), i);
  492. }
  493. return TCL_OK;
  494. } else if (strcmp(argv[1], "ranvar-main-age") == 0) {
  495. rvMainAge_ = 
  496. (RandomVariable*)TclObject::lookup(argv[2]);
  497. return TCL_OK;
  498. } else if (strcmp(argv[1], "ranvar-obj-age") == 0) {
  499. rvCompAge_ =
  500. (RandomVariable*)TclObject::lookup(argv[2]);
  501. return TCL_OK;
  502. } else if (strcmp(argv[1], "set-start-time") == 0) {
  503. double st = strtod(argv[2], NULL);
  504. start_time_ = st;
  505. end_time_ += st;
  506. return TCL_OK;
  507. } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
  508. tcl.resultf("%.17g", Scheduler::instance().clock());
  509. return TCL_OK;
  510. } else if (strcmp(argv[1], "is-mainpage") == 0) {
  511. // Tell if the given page is a main page or an 
  512. // embedded object. 
  513. // XXX Here because we have only one page, so only 
  514. // page id 0 is the main page. If we have multiple 
  515. // pages, we need something else to do this.
  516. PageID t1;
  517. ClientPage::split_name(argv[2], t1);
  518. if (t1.id_ == 0)
  519. tcl.result("1");
  520. else 
  521. tcl.result("0");
  522. return TCL_OK;
  523. } else if (strcmp(argv[1], "get-mainpage") == 0) {
  524. // Get the main page of an embedded object
  525. // XXX Should maintain a mapping between embedded
  526. // objects and main pages. It can be an algorithmic
  527. // one, e.g., using page id intervals. It's simple 
  528. // here because we have only one page.
  529. PageID t1;
  530. ClientPage::split_name(argv[2], t1);
  531. tcl.resultf("%s:0", t1.s_->name());
  532. return TCL_OK;
  533. } else if (strcmp(argv[1], "get-obj-num") == 0) {
  534. // Returns the number of embedded objects of the page
  535. // given in argv[1]. Here because we have only one 
  536. // page, we return a fixed value.
  537. tcl.resultf("%d", num_pages_-1);
  538. return TCL_OK;
  539. }
  540. } else {
  541. // argc > 3
  542. if (strcmp(argv[1], "gen-modtime") == 0) {
  543. int id = atoi(argv[2]);
  544. if (id == 0) {
  545. if (rvMainAge_ == 0) {
  546.   tcl.add_errorf("%s: no page age generator", 
  547.  name_);
  548. return TCL_ERROR;
  549. }
  550. double mt = strtod(argv[3], NULL);
  551. tcl.resultf("%.17g", mt + rvMainAge_->value());
  552. } else {
  553. if (rvCompAge_ == 0) {
  554.    tcl.add_errorf("%s: no page age generator", 
  555.   name_);
  556.    return TCL_ERROR;
  557. }
  558. double mt = atoi(argv[3]);
  559. tcl.resultf("%.17g", mt + rvCompAge_->value());
  560. }
  561. return TCL_OK;
  562. } else if (strcmp(argv[1], "gen-obj-modtime") == 0) {
  563. if (rvCompAge_ == 0) {
  564. tcl.add_errorf("%s: no page age generator", 
  565.        name_);
  566. return TCL_ERROR;
  567. }
  568. double mt = atoi(argv[3]);
  569. tcl.resultf("%.17g", mt + rvCompAge_->value());
  570. return TCL_OK;
  571. }
  572. }
  573. return PagePool::command(argc, argv);
  574. }
  575. static class ClientPagePoolClass : public TclClass {
  576. public:
  577.         ClientPagePoolClass() : TclClass("PagePool/Client") {}
  578.         TclObject* create(int, const char*const*) {
  579. return (new ClientPagePool());
  580. }
  581. } class_clientpagepool_agent;
  582. ClientPagePool::ClientPagePool()
  583. {
  584. namemap_ = new Tcl_HashTable;
  585. Tcl_InitHashTable(namemap_, 2);
  586. }
  587. ClientPagePool::~ClientPagePool()
  588. {
  589. if (namemap_ != NULL) {
  590. Tcl_DeleteHashTable(namemap_);
  591. delete namemap_;
  592. }
  593. }
  594. // In case client/cache/server needs details, e.g., page listing
  595. int ClientPagePool::command(int argc, const char*const* argv)
  596. {
  597. Tcl& tcl = Tcl::instance();
  598. if (argc == 2) {
  599. if (strcmp(argv[1], "list-pages") == 0) {
  600. Tcl_HashEntry *he;
  601. Tcl_HashSearch hs;
  602. char *buf = new char[num_pages_*20];
  603. char *p = buf;
  604. for (he = Tcl_FirstHashEntry(namemap_, &hs); 
  605.      he != NULL;
  606.      he = Tcl_NextHashEntry(&hs)) {
  607. int* t2 = (int*)Tcl_GetHashKey(namemap_, he);
  608. PageID t1(t2);
  609. #ifdef NEED_SUNOS_PROTOS
  610. sprintf(p, "%s:%-d ", t1.s_->name(),t1.id_);
  611. p += strlen(p);
  612. #else
  613. p += sprintf(p,"%s:%-d ",t1.s_->name(),t1.id_);
  614. #endif
  615. }
  616. tcl.resultf("%s", buf);
  617. delete []buf;
  618. return TCL_OK;
  619. }
  620. }
  621. return PagePool::command(argc, argv);
  622. }
  623. ClientPage* ClientPagePool::get_page(const char *name)
  624. {
  625. PageID t1;
  626. ClientPage::split_name(name, t1);
  627. Tcl_HashEntry *he = Tcl_FindHashEntry(namemap_, (const char *)&t1);
  628. if (he == NULL)
  629. return NULL;
  630. return (ClientPage *)Tcl_GetHashValue(he);
  631. }
  632. int ClientPagePool::get_pageinfo(const char *name, char *buf)
  633. {
  634. ClientPage *pg = get_page(name);
  635. if (pg == NULL) 
  636. return -1;
  637. pg->print_info(buf);
  638. return 0;
  639. }
  640. ClientPage* ClientPagePool::enter_page(int argc, const char*const* argv)
  641. {
  642. double mt = -1, et, age = -1, noc = 0;
  643. int size = -1;
  644. for (int i = 3; i < argc; i+=2) {
  645. if (strcmp(argv[i], "modtime") == 0)
  646. mt = strtod(argv[i+1], NULL);
  647. else if (strcmp(argv[i], "size") == 0) 
  648. size = atoi(argv[i+1]);
  649. else if (strcmp(argv[i], "age") == 0)
  650. age = strtod(argv[i+1], NULL);
  651. else if (strcmp(argv[i], "noc") == 0)
  652. // non-cacheable flag
  653. noc = 1;
  654. }
  655. // XXX allow mod time < 0 and age < 0!!
  656. if (size < 0) {
  657. fprintf(stderr, "PagePool %s: wrong information for page %sn",
  658. name_, argv[2]);
  659. return NULL;
  660. }
  661. et = Scheduler::instance().clock();
  662. ClientPage* pg = new ClientPage(argv[2], size, mt, et, age);
  663. if (add_page(pg) < 0) {
  664. delete pg; 
  665. return NULL;
  666. }
  667. if (noc) 
  668. pg->set_uncacheable();
  669. return pg;
  670. }
  671. ClientPage* ClientPagePool::enter_page(const char *name, int size, double mt, 
  672.        double et, double age)
  673. {
  674. ClientPage* pg = new ClientPage(name, size, mt, et, age);
  675. if (add_page(pg) < 0) {
  676. delete pg; 
  677. return NULL;
  678. }
  679. return pg;
  680. }
  681. // XXX We don't need parsing "noc" here because a non-cacheable
  682. // page won't be processed by a cache.
  683. ClientPage* ClientPagePool::enter_metadata(int argc, const char*const* argv)
  684. {
  685. ClientPage *pg = enter_page(argc, argv);
  686. if (pg != NULL)
  687. pg->set_valid_hdr();
  688. return pg;
  689. }
  690. ClientPage* ClientPagePool::enter_metadata(const char *name, int size, 
  691.    double mt, double et, double age)
  692. {
  693. ClientPage *pg = enter_page(name, size, mt, et, age);
  694. if (pg != NULL) 
  695. pg->set_valid_hdr();
  696. return pg;
  697. }
  698. int ClientPagePool::add_page(ClientPage* pg)
  699. {
  700. if (pg == NULL)
  701. return -1;
  702. char buf[HTTP_MAXURLLEN];
  703. pg->name(buf);
  704. PageID t1;
  705. ClientPage::split_name(buf, t1);
  706. int newEntry = 1;
  707. Tcl_HashEntry *he = Tcl_CreateHashEntry(namemap_, 
  708. (const char *)&t1,
  709. &newEntry);
  710. if (he == NULL)
  711. return -1;
  712. // XXX If cache replacement algorithm is added, should change 
  713. // cache size here!!
  714. if (newEntry) {
  715. Tcl_SetHashValue(he, (ClientData)pg);
  716. num_pages_++;
  717.   } else {
  718. // Replace the old one
  719. ClientPage *q = (ClientPage *)Tcl_GetHashValue(he);
  720. // XXX must copy the counter value
  721. pg->counter() = q->counter();
  722. // XXX must copy the mpush values
  723. if (q->is_mpush())
  724. pg->set_mpush(q->mpush_time());
  725. Tcl_SetHashValue(he, (ClientData)pg);
  726. delete q;
  727. }
  728. return 0;
  729. }
  730. int ClientPagePool::remove_page(const char *name)
  731. {
  732. PageID t1;
  733. ClientPage::split_name(name, t1);
  734. // Find out which client we are seeking
  735. Tcl_HashEntry *he = Tcl_FindHashEntry(namemap_, (const char *)&t1);
  736. if (he == NULL)
  737. return -1;
  738. ClientPage *pg = (ClientPage *)Tcl_GetHashValue(he);
  739. Tcl_DeleteHashEntry(he);
  740. delete pg;
  741. num_pages_--;
  742. // XXX If cache replacement algorithm is added, should change
  743. // cache size here!!
  744. return 0;
  745. }
  746. int ClientPagePool::set_mtime(const char *name, double mt)
  747. {
  748. ClientPage *pg = (ClientPage *)get_page(name);
  749. if (pg == NULL) 
  750. return -1;
  751. pg->mtime() = mt;
  752. return 0;
  753. }
  754. int ClientPagePool::get_mtime(const char *name, double& mt)
  755. {
  756. ClientPage *pg = (ClientPage *)get_page(name);
  757. if (pg == NULL) 
  758. return -1;
  759. mt = pg->mtime();
  760. return 0;
  761. }
  762. int ClientPagePool::set_etime(const char *name, double et)
  763. {
  764. ClientPage *pg = (ClientPage *)get_page(name);
  765. if (pg == NULL) 
  766. return -1;
  767. pg->etime() = et;
  768. return 0;
  769. }
  770. int ClientPagePool::get_etime(const char *name, double& et)
  771. {
  772. ClientPage *pg = (ClientPage *)get_page(name);
  773. if (pg == NULL) 
  774. return -1;
  775. et = pg->etime();
  776. return 0;
  777. }
  778. int ClientPagePool::get_size(const char *name, int& size) 
  779. {
  780. ClientPage *pg = (ClientPage *)get_page(name);
  781. if (pg == NULL) 
  782. return -1;
  783. size = pg->size();
  784. return 0;
  785. }
  786. int ClientPagePool::get_age(const char *name, double& age) 
  787. {
  788. ClientPage *pg = (ClientPage *)get_page(name);
  789. if (pg == NULL) 
  790. return -1;
  791. age = pg->age();
  792. return 0;
  793. }
  794. void ClientPagePool::invalidate_server(int sid)
  795. {
  796. Tcl_HashEntry *he;
  797. Tcl_HashSearch hs;
  798. ClientPage *pg;
  799. int i;
  800. for (i = 0, he = Tcl_FirstHashEntry(namemap_, &hs);
  801.      he != NULL;
  802.      he = Tcl_NextHashEntry(&hs), i++) {
  803. pg = (ClientPage *) Tcl_GetHashValue(he);
  804. if (pg->server()->id() == sid)
  805. pg->server_down();
  806. }
  807. }
  808. // Proxy traces. Request file format:
  809. //
  810. // [<time> <clientID> <serverID> <URL_ID>]
  811. // i <Duration> <Number_of_unique_URLs>
  812. //
  813. // <time> is guaranteed to start from 0. It needs to be adjusted
  814. //
  815. // Page file format (sorted by access counts)
  816. // 
  817. // <serverID> <URL_ID> <PageSize> <AccessCount>
  818. static class ProxyTracePagePoolClass : public TclClass {
  819. public:
  820.         ProxyTracePagePoolClass() : TclClass("PagePool/ProxyTrace") {}
  821.         TclObject* create(int, const char*const*) {
  822. return (new ProxyTracePagePool());
  823. }
  824. } class_ProxyTracepagepool_agent;
  825. ProxyTracePagePool::ProxyTracePagePool() : 
  826. rvDyn_(NULL), rvStatic_(NULL), br_(0), 
  827. size_(NULL), reqfile_(NULL), req_(NULL), lastseq_(0)
  828. {
  829. }
  830. ProxyTracePagePool::~ProxyTracePagePool()
  831. {
  832. if (size_ != NULL) 
  833. delete []size_;
  834. if (reqfile_ != NULL) 
  835. fclose(reqfile_);
  836. if (req_ != NULL) {
  837. Tcl_DeleteHashTable(req_);
  838. delete req_;
  839. }
  840. }
  841. int ProxyTracePagePool::init_req(const char *fn) 
  842. {
  843. reqfile_ = fopen(fn, "r");
  844. if (reqfile_ == NULL) {
  845. fprintf(stderr, 
  846.   "ProxyTracePagePool: couldn't open trace file %sn", fn);
  847. return TCL_ERROR;
  848. }
  849. // Discover information about the trace, e.g., number of pages,
  850. // start time, end time, etc. They should be available at the 
  851. // first line of the trace file.
  852. return find_info();
  853. }
  854. int ProxyTracePagePool::find_info()
  855. {
  856. // Read the last line of the file
  857. fseek(reqfile_, -128, SEEK_END);
  858. char buf[129];
  859. if (fread(buf, 1, 128, reqfile_) != 128) {
  860. fprintf(stderr,
  861. "ProxyTracePagePool: cannot read file informationn");
  862. return TCL_ERROR;
  863. }
  864. int i;
  865. // ignore the last RETURN
  866. buf[128] = 0;
  867. if (buf[127] == 'n')
  868. buf[127] = 0; 
  869. for (i = 127; i >= 0; i--)
  870. if (buf[i] == 'n') {
  871. i++; 
  872. break;
  873. }
  874. if (buf[i] != 'i') {
  875. fprintf(stderr, 
  876. "ProxyTracePagePool: trace file doesn't contain statistics.n");
  877. abort();
  878. }
  879. double len;
  880. sscanf(buf+i+1, "%lf %i", &len, &num_pages_);
  881. duration_ = (int)ceil(len);
  882. #if 0
  883. printf("ProxyTracePagePool: duration %d pages %un",
  884.        duration_, num_pages_);
  885. #endif
  886. rewind(reqfile_);
  887. return TCL_OK;
  888. }
  889. // Load page size info. Assuming request stream has already been loaded
  890. int ProxyTracePagePool::init_page(const char *fn)
  891. {
  892. FILE *fp = fopen(fn, "r");
  893. if (fp == NULL) {
  894. fprintf(stderr, 
  895.   "ProxyTracePagePool: couldn't open trace file %sn", fn);
  896. return TCL_ERROR;
  897. }
  898. if (size_ != NULL) 
  899. delete []size_;
  900. int* p = new int[num_pages_];
  901. size_ = p;
  902. for (int i = 0; i < num_pages_; i++, p++)
  903. fscanf(fp, "%*d %*d %d %*un", p);
  904. fclose(fp);
  905. return TCL_OK;
  906. }
  907. ProxyTracePagePool::ClientRequest* ProxyTracePagePool::load_req(int cid)
  908. {
  909. // Find out which client we are seeking
  910. Tcl_HashEntry *he;
  911. ClientRequest *p;
  912. int dummy; 
  913. long key = cid;
  914. if ((he = Tcl_FindHashEntry(req_, (const char*)key)) == NULL) {
  915. // New entry
  916. p = new ClientRequest();
  917. p->seq_ = lastseq_++;
  918. he = Tcl_CreateHashEntry(req_, (const char*)key, &dummy);
  919. Tcl_SetHashValue(he, (const char*)p);
  920. // Search from the beginning of file for this new client
  921. fseek(reqfile_, 0, SEEK_SET);
  922. } else {
  923. p = (ClientRequest*)Tcl_GetHashValue(he);
  924. if (p->nrt_ == -1)
  925. // No more requests for this client
  926. return p;
  927. // Clear EOF status
  928. fseek(reqfile_, p->fpos_, SEEK_SET);
  929. }
  930. // Looking for the next available request for this client
  931. double nrt;
  932. int ncid = -1, nurl;
  933. char buf[256];
  934. while (fgets(buf, 256, reqfile_)) {
  935. if (isalpha(buf[0])) {
  936. // Last line, break;
  937. ncid = -1;
  938. break;
  939. }
  940. sscanf(buf, "%lf %d %*d %dn", &nrt, &ncid, &nurl);
  941. if ((ncid % nclient_) == p->seq_)
  942. break;
  943. }
  944. if ((ncid % nclient_) != p->seq_)
  945. // Didn't find the next request for this client
  946. p->nrt_ = -1;
  947. else {
  948. p->nrt_ = nrt, p->nurl_ = nurl;
  949. p->nrt_ += start_time_;
  950. }
  951. p->fpos_ = ftell(reqfile_);
  952. return p;
  953. }
  954. // Provide a tcl interface compatible with MathPagePool
  955. int ProxyTracePagePool::command(int argc, const char*const* argv)
  956. {
  957. Tcl& tcl = Tcl::instance();
  958. if (argc == 2) {
  959. if (strcmp(argv[1], "get-poolsize") == 0) { 
  960. tcl.resultf("%u", num_pages_);
  961. return TCL_OK;
  962. } else if (strcmp(argv[1], "get-start-time") == 0) {
  963. tcl.resultf("%.17g", start_time_);
  964. return TCL_OK;
  965. } else if (strcmp(argv[1], "get-duration") == 0) {
  966. tcl.resultf("%d", duration_);
  967. return TCL_OK;
  968. } else if (strcmp(argv[1], "bimodal-ratio") == 0) {
  969. tcl.resultf("%g", br_ / 10);
  970. return TCL_OK;
  971. }
  972. } else if (argc == 3) {
  973. if (strcmp(argv[1], "set-client-num") == 0) {
  974. // Set the number of clients it'll access
  975. // Cannot be changed once set
  976. if (req_ != NULL)
  977. return TCL_ERROR;
  978. int num = atoi(argv[2]);
  979. req_ = new Tcl_HashTable;
  980. Tcl_InitHashTable(req_, TCL_ONE_WORD_KEYS);
  981. nclient_ = num;
  982. return TCL_OK;
  983. } else if (strcmp(argv[1], "gen-request") == 0) {
  984. // Use client id to get a corresponding request
  985. int id = atoi(argv[2]);
  986. ClientRequest *p = load_req(id);
  987. if ((p->nrt_ >= 0) && 
  988.     (p->nrt_ < Scheduler::instance().clock())) {
  989. // XXX Do NOT treat this as an error, also
  990. // do NOT disable further requests from this 
  991. // client.
  992. fprintf(stderr,
  993. "%.17g: Wrong request time %g.n",
  994. Scheduler::instance().clock(),
  995. p->nrt_);
  996. // XXX If it's a little bit older than current 
  997. // time, let it be a little bit later than now
  998. p->nrt_ = Scheduler::instance().clock()+0.001;
  999. }
  1000. tcl.resultf("%lf %d", 
  1001.     p->nrt_ - Scheduler::instance().clock(), 
  1002.     p->nurl_);
  1003. return TCL_OK;
  1004. } else if (strcmp(argv[1], "gen-size") == 0) {
  1005. int id = atoi(argv[2]);
  1006. if ((id < 0) || (id > num_pages_)) {
  1007. tcl.result("PagePool: id out of range.n");
  1008. return TCL_ERROR;
  1009. }
  1010. tcl.resultf("%d", size_[id]);
  1011. return TCL_OK;
  1012. } else if (strcmp(argv[1], "set-start-time") == 0) {
  1013. start_time_ = strtod(argv[2], NULL);
  1014. return TCL_OK;
  1015. } else if (strcmp(argv[1], "bimodal-ratio") == 0) {
  1016. // XXX Codes in Http/Server::gen-page{} also depends
  1017. // on this dyn/static page algorithm. If this is 
  1018. // changed, that instproc must be changed too.
  1019. //
  1020. // percentage of dynamic pages. E.g., 
  1021. // if this ratio is 5, then page 0-4 is 
  1022. // dynamic, and page 4-99 is static, and so on.
  1023. double ratio = strtod(argv[2], NULL);
  1024. //br_ = (int)ceil(ratio*100);
  1025. br_ = (int)ceil(ratio*10);
  1026. return TCL_OK;
  1027. } else if (strcmp(argv[1], "ranvar-dp") == 0) {
  1028. // Page mod ranvar for dynamic pages
  1029. rvDyn_ = (RandomVariable*)TclObject::lookup(argv[2]);
  1030. return TCL_OK;
  1031. } else if (strcmp(argv[1], "ranvar-sp") == 0) {
  1032. // page mod ranvar for static pages
  1033. rvStatic_= (RandomVariable*)TclObject::lookup(argv[2]);
  1034. return TCL_OK;
  1035. } else if (strcmp(argv[1], "set-reqfile") == 0) {
  1036. return init_req(argv[2]);
  1037. } else if (strcmp(argv[1], "set-pagefile") == 0) {
  1038. return init_page(argv[2]);
  1039. } else if (strcmp(argv[1], "gen-init-modtime") == 0) {
  1040. int id = atoi(argv[2]) % 10;
  1041. if (id >= br_)
  1042. // Static page
  1043. tcl.result("0");
  1044. else
  1045. // Dynamic page
  1046. tcl.resultf("%.17g", 
  1047.     Scheduler::instance().clock());
  1048. return TCL_OK;
  1049. }
  1050. } else {
  1051. if (strcmp(argv[1], "gen-modtime") == 0) {
  1052. if ((rvDyn_ == 0) || (rvStatic_ == 0)) {
  1053. tcl.add_errorf("%s: no page age generator", 
  1054.        name_);
  1055. return TCL_ERROR;
  1056. }
  1057. // int id = atoi(argv[2]) % 100;
  1058. int id = atoi(argv[2]) % 10;
  1059. double mt = strtod(argv[3], NULL);
  1060. if (id >= br_) 
  1061. tcl.resultf("%.17g", mt + rvStatic_->value());
  1062. else 
  1063. tcl.resultf("%.17g", mt + rvDyn_->value());
  1064. return TCL_OK;
  1065. }
  1066. }
  1067. return PagePool::command(argc, argv);
  1068. }
  1069. // Proxy trace with special method for page modification
  1070. static class EPAPagePoolClass : public TclClass {
  1071. public:
  1072. EPAPagePoolClass() : TclClass("PagePool/ProxyTrace/epa") {}
  1073. TclObject* create(int, const char*const*) {
  1074. return (new EPATracePagePool());
  1075. }
  1076. } class_epapagepool_agent;
  1077. int EPATracePagePool::command(int argc, const char*const* argv)
  1078. {
  1079. Tcl& tcl = Tcl::instance();
  1080. if (argc == 2) {
  1081. if (strcmp(argv[1], "pick-pagemod") == 0) {
  1082. if (rvDyn_ == 0) {
  1083. tcl.add_errorf("%s: no page age generator",
  1084.        name_);
  1085. return (TCL_ERROR);
  1086. }
  1087. int j = (int)floor(rvDyn_->value());
  1088. //fprintf(stderr, "mod id = %dn", j/br_*10 + j % br_);
  1089. tcl.resultf("%d", j/br_*10 + j % br_);
  1090. return TCL_OK;
  1091. }
  1092. } else if (argc == 3) {
  1093. if (strcmp(argv[1], "ranvar-dp") == 0) {
  1094. rvDyn_ = (RandomVariable*)TclObject::lookup(argv[2]);
  1095. if (rvDyn_ == 0) {
  1096. tcl.add_errorf("%s: no page age generator",
  1097.        name_);
  1098. return (TCL_ERROR);
  1099. }
  1100. ((UniformRandomVariable*)rvDyn_)->setmin(0);
  1101. ((UniformRandomVariable*)rvDyn_)->setmax(num_pages_/10*br_ + num_pages_%br_ - 1);
  1102. return TCL_OK;
  1103. }
  1104. } else {
  1105. if (strcmp(argv[1], "gen-modtime") == 0) {
  1106. // Return a very large number
  1107. tcl.resultf("%d", INT_MAX);
  1108. return TCL_OK;
  1109. }
  1110. }
  1111. return ProxyTracePagePool::command(argc, argv);
  1112. }