test_cache_digest.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:15k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: test_cache_digest.c,v 1.23 1998/07/22 20:38:01 wessels Exp $
  3.  *
  4.  * AUTHOR: Alex Rousskov
  5.  *
  6.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  7.  * ----------------------------------------------------------
  8.  *
  9.  *  Squid is the result of efforts by numerous individuals from the
  10.  *  Internet community.  Development is led by Duane Wessels of the
  11.  *  National Laboratory for Applied Network Research and funded by the
  12.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  13.  *  Duane Wessels and the University of California San Diego.  Please
  14.  *  see the COPYRIGHT file for full details.  Squid incorporates
  15.  *  software developed and/or copyrighted by other sources.  Please see
  16.  *  the CREDITS file for full details.
  17.  *
  18.  *  This program is free software; you can redistribute it and/or modify
  19.  *  it under the terms of the GNU General Public License as published by
  20.  *  the Free Software Foundation; either version 2 of the License, or
  21.  *  (at your option) any later version.
  22.  *  
  23.  *  This program is distributed in the hope that it will be useful,
  24.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  25.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26.  *  GNU General Public License for more details.
  27.  *  
  28.  *  You should have received a copy of the GNU General Public License
  29.  *  along with this program; if not, write to the Free Software
  30.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  31.  *
  32.  */
  33. /*
  34.  * Test-suite for playing with cache digests
  35.  */
  36. #include "squid.h"
  37. typedef struct {
  38.     int query_count;
  39.     int true_hit_count;
  40.     int true_miss_count;
  41.     int false_hit_count;
  42.     int false_miss_count;
  43. } CacheQueryStats;
  44. typedef struct _Cache Cache;
  45. struct _Cache {
  46.     const char *name;
  47.     hash_table *hash;
  48.     CacheDigest *digest;
  49.     Cache *peer;
  50.     CacheQueryStats qstats;
  51.     int count; /* #currently cached entries */
  52.     int req_count; /* #requests to this cache */
  53.     int bad_add_count; /* #duplicate adds */
  54.     int bad_del_count; /* #dels with no prior add */
  55. };
  56. typedef struct _CacheEntry {
  57.     const cache_key *key;
  58.     struct _CacheEntry *next;
  59.     unsigned char key_arr[MD5_DIGEST_CHARS];
  60.     /* storeSwapLogData s; */
  61. } CacheEntry;
  62. /* parsed access log entry */
  63. typedef struct {
  64.     cache_key key[MD5_DIGEST_CHARS];
  65.     time_t timestamp;
  66.     short int use_icp; /* true/false */
  67. } RawAccessLogEntry;
  68. typedef enum {
  69.     frError = -2, frMore = -1, frEof = 0, frOk = 1
  70. } fr_result;
  71. typedef struct _FileIterator FileIterator;
  72. typedef fr_result(*FI_READER) (FileIterator * fi);
  73. struct _FileIterator {
  74.     const char *fname;
  75.     FILE *file;
  76.     time_t inner_time; /* timestamp of the current entry */
  77.     time_t time_offset; /* to adjust time set by reader */
  78.     int line_count; /* number of lines scanned */
  79.     int bad_line_count; /* number of parsing errors */
  80.     int time_warp_count; /* number of out-of-order entries in the file */
  81.     FI_READER reader; /* reads next entry and updates inner_time */
  82.     void *entry; /* buffer for the current entry, freed with xfree() */
  83. };
  84. /* globals */
  85. static time_t cur_time = -1; /* timestamp of the current log entry */
  86. /* copied from url.c */
  87. const char *RequestMethodStr[] =
  88. {
  89.     "NONE",
  90.     "GET",
  91.     "POST",
  92.     "PUT",
  93.     "HEAD",
  94.     "CONNECT",
  95.     "TRACE",
  96.     "PURGE"
  97. };
  98. /* copied from url.c */
  99. static method_t
  100. methodStrToId(const char *s)
  101. {
  102.     if (strcasecmp(s, "GET") == 0) {
  103. return METHOD_GET;
  104.     } else if (strcasecmp(s, "POST") == 0) {
  105. return METHOD_POST;
  106.     } else if (strcasecmp(s, "PUT") == 0) {
  107. return METHOD_PUT;
  108.     } else if (strcasecmp(s, "HEAD") == 0) {
  109. return METHOD_HEAD;
  110.     } else if (strcasecmp(s, "CONNECT") == 0) {
  111. return METHOD_CONNECT;
  112.     } else if (strcasecmp(s, "TRACE") == 0) {
  113. return METHOD_TRACE;
  114.     } else if (strcasecmp(s, "PURGE") == 0) {
  115. return METHOD_PURGE;
  116.     }
  117.     return METHOD_NONE;
  118. }
  119. /* FileIterator */
  120. static void fileIteratorAdvance(FileIterator * fi);
  121. static FileIterator *
  122. fileIteratorCreate(const char *fname, FI_READER reader)
  123. {
  124.     FileIterator *fi = xcalloc(1, sizeof(FileIterator));
  125.     assert(fname && reader);
  126.     fi->fname = fname;
  127.     fi->reader = reader;
  128.     fi->file = fopen(fname, "r");
  129.     if (!fi->file) {
  130. fprintf(stderr, "cannot open %s: %sn", fname, strerror(errno));
  131. return NULL;
  132.     } else
  133. fprintf(stderr, "opened %sn", fname);
  134.     fileIteratorAdvance(fi);
  135.     return fi;
  136. }
  137. static void
  138. fileIteratorDestroy(FileIterator * fi)
  139. {
  140.     assert(fi);
  141.     if (fi->file) {
  142. fclose(fi->file);
  143. fprintf(stderr, "closed %sn", fi->fname);
  144.     }
  145.     xfree(fi->entry);
  146.     xfree(fi);
  147. }
  148. static void
  149. fileIteratorSetCurTime(FileIterator * fi, time_t ct)
  150. {
  151.     assert(fi);
  152.     assert(fi->inner_time > 0);
  153.     fi->time_offset = ct - fi->inner_time;
  154. }
  155. static void
  156. fileIteratorAdvance(FileIterator * fi)
  157. {
  158.     int res;
  159.     assert(fi);
  160.     do {
  161. const time_t last_time = fi->inner_time;
  162. fi->inner_time = -1;
  163. res = fi->reader(fi);
  164. fi->line_count++;
  165. if (fi->inner_time < 0)
  166.     fi->inner_time = last_time;
  167. else
  168.     fi->inner_time += fi->time_offset;
  169. if (res == frError)
  170.     fi->bad_line_count++;
  171. else if (res == frEof) {
  172.     fprintf(stderr, "exhausted %s (%d entries) at %s",
  173. fi->fname, fi->line_count, ctime(&fi->inner_time));
  174.     fi->inner_time = -1;
  175. } else if (fi->inner_time < last_time) {
  176.     assert(last_time >= 0);
  177.     fi->time_warp_count++;
  178.     fi->inner_time = last_time;
  179. }
  180. /* report progress */
  181. if (!(fi->line_count % 50000))
  182.     fprintf(stderr, "%s scanned %d K entries (%d bad) at %s",
  183. fi->fname, fi->line_count / 1000, fi->bad_line_count,
  184. ctime(&fi->inner_time));
  185.     } while (res < 0);
  186. }
  187. /* CacheEntry */
  188. static CacheEntry *
  189. cacheEntryCreate(const storeSwapLogData * s)
  190. {
  191.     CacheEntry *e = xcalloc(1, sizeof(CacheEntry));
  192.     assert(s);
  193.     /* e->s = *s; */
  194.     xmemcpy(e->key_arr, s->key, MD5_DIGEST_CHARS);
  195.     e->key = &e->key_arr[0];
  196.     return e;
  197. }
  198. static void
  199. cacheEntryDestroy(CacheEntry * e)
  200. {
  201.     assert(e);
  202.     xfree(e);
  203. }
  204. /* Cache */
  205. static Cache *
  206. cacheCreate(const char *name)
  207. {
  208.     Cache *c;
  209.     assert(name && strlen(name));
  210.     c = xcalloc(1, sizeof(Cache));
  211.     c->name = name;
  212.     c->hash = hash_create(storeKeyHashCmp, 2e6, storeKeyHashHash);
  213.     return c;
  214. }
  215. static void
  216. cacheDestroy(Cache * cache)
  217. {
  218.     CacheEntry *e = NULL;
  219.     hash_table *hash;
  220.     assert(cache);
  221.     hash = cache->hash;
  222.     /* destroy hash table contents */
  223.     hash_first(hash);
  224.     while (e = hash_next(hash)) {
  225. hash_remove_link(hash, (hash_link *) e);
  226. cacheEntryDestroy(e);
  227.     }
  228.     /* destroy the hash table itself */
  229.     hashFreeMemory(hash);
  230.     if (cache->digest)
  231. cacheDigestDestroy(cache->digest);
  232.     xfree(cache);
  233. }
  234. /* re-digests currently hashed entries */
  235. static void
  236. cacheResetDigest(Cache * cache)
  237. {
  238.     CacheEntry *e = NULL;
  239.     hash_table *hash;
  240.     struct timeval t_start, t_end;
  241.     assert(cache);
  242.     fprintf(stderr, "%s: init-ing digest with %d entriesn", cache->name, cache->count);
  243.     if (cache->digest)
  244. cacheDigestDestroy(cache->digest);
  245.     hash = cache->hash;
  246.     cache->digest = cacheDigestCreate(cache->count + 1, 6);
  247.     if (!cache->count)
  248. return;
  249.     gettimeofday(&t_start, NULL);
  250.     hash_first(hash);
  251.     while (e = hash_next(hash)) {
  252. cacheDigestAdd(cache->digest, e->key);
  253.     }
  254.     gettimeofday(&t_end, NULL);
  255.     assert(cache->digest->count == cache->count);
  256.     fprintf(stderr, "%s: init-ed  digest with %d entriesn",
  257. cache->name, cache->digest->count);
  258.     fprintf(stderr, "%s: init took: %f sec, %f sec/Mn",
  259. cache->name,
  260. tvSubDsec(t_start, t_end),
  261. (double) 1e6 * tvSubDsec(t_start, t_end) / cache->count);
  262.     /* check how long it takes to traverse the hash */
  263.     gettimeofday(&t_start, NULL);
  264.     for (e = hash_first(hash); e; e = hash_next(hash)) {
  265.     }
  266.     gettimeofday(&t_end, NULL);
  267.     fprintf(stderr, "%s: hash scan took: %f sec, %f sec/Mn",
  268. cache->name,
  269. tvSubDsec(t_start, t_end),
  270. (double) 1e6 * tvSubDsec(t_start, t_end) / cache->count);
  271. }
  272. static void
  273. cacheQueryPeer(Cache * cache, const cache_key * key)
  274. {
  275.     const int peer_has_it = hash_lookup(cache->peer->hash, key) != NULL;
  276.     const int we_think_we_have_it = cacheDigestTest(cache->digest, key);
  277.     cache->qstats.query_count++;
  278.     if (peer_has_it) {
  279. if (we_think_we_have_it)
  280.     cache->qstats.true_hit_count++;
  281. else
  282.     cache->qstats.false_miss_count++;
  283.     } else {
  284. if (we_think_we_have_it)
  285.     cache->qstats.false_hit_count++;
  286. else
  287.     cache->qstats.true_miss_count++;
  288.     }
  289. }
  290. static void
  291. cacheQueryReport(Cache * cache, CacheQueryStats * stats)
  292. {
  293.     fprintf(stdout, "%s: peer queries: %d (%d%%)n",
  294. cache->name,
  295. stats->query_count, xpercentInt(stats->query_count, cache->req_count)
  296. );
  297.     fprintf(stdout, "%s: t-hit: %d (%d%%) t-miss: %d (%d%%) t-*: %d (%d%%)n",
  298. cache->name,
  299. stats->true_hit_count, xpercentInt(stats->true_hit_count, stats->query_count),
  300. stats->true_miss_count, xpercentInt(stats->true_miss_count, stats->query_count),
  301. stats->true_hit_count + stats->true_miss_count,
  302. xpercentInt(stats->true_hit_count + stats->true_miss_count, stats->query_count)
  303. );
  304.     fprintf(stdout, "%s: f-hit: %d (%d%%) f-miss: %d (%d%%) f-*: %d (%d%%)n",
  305. cache->name,
  306. stats->false_hit_count, xpercentInt(stats->false_hit_count, stats->query_count),
  307. stats->false_miss_count, xpercentInt(stats->false_miss_count, stats->query_count),
  308. stats->false_hit_count + stats->false_miss_count,
  309. xpercentInt(stats->false_hit_count + stats->false_miss_count, stats->query_count)
  310. );
  311. }
  312. static void
  313. cacheReport(Cache * cache)
  314. {
  315.     fprintf(stdout, "%s: entries: %d reqs: %d bad-add: %d bad-del: %dn",
  316. cache->name, cache->count, cache->req_count,
  317. cache->bad_add_count, cache->bad_del_count);
  318. }
  319. static void
  320. cacheFetch(Cache * cache, const RawAccessLogEntry * e)
  321. {
  322.     assert(e);
  323.     cache->req_count++;
  324.     if (e->use_icp)
  325. cacheQueryPeer(cache, e->key);
  326. }
  327. static fr_result
  328. swapStateReader(FileIterator * fi)
  329. {
  330.     storeSwapLogData *entry;
  331.     if (!fi->entry)
  332. fi->entry = xcalloc(1, sizeof(storeSwapLogData));
  333.     entry = fi->entry;
  334.     if (fread(entry, sizeof(*entry), 1, fi->file) != 1)
  335. return frEof;
  336.     fi->inner_time = entry->lastref;
  337.     if (entry->op != SWAP_LOG_ADD && entry->op != SWAP_LOG_DEL) {
  338. fprintf(stderr, "%s:%d: unknown swap log actionn", fi->fname, fi->line_count);
  339. exit(-3);
  340.     }
  341.     return frOk;
  342. }
  343. static fr_result
  344. accessLogReader(FileIterator * fi)
  345. {
  346.     static char buf[4096];
  347.     RawAccessLogEntry *entry;
  348.     char *url;
  349.     char *method;
  350.     int method_id = METHOD_NONE;
  351.     char *hier = NULL;
  352.     assert(fi);
  353.     if (!fi->entry)
  354. fi->entry = xcalloc(1, sizeof(RawAccessLogEntry));
  355.     else
  356. memset(fi->entry, 0, sizeof(RawAccessLogEntry));
  357.     entry = fi->entry;
  358.     if (!fgets(buf, sizeof(buf), fi->file))
  359. return frEof; /* eof */
  360.     entry->timestamp = fi->inner_time = (time_t) atoi(buf);
  361.     url = strstr(buf, "://");
  362.     hier = url ? strstr(url, " - ") : NULL;
  363.     if (!url || !hier) {
  364. /*fprintf(stderr, "%s:%d: strange access log entry '%s'n", 
  365.  * fname, scanned_count, buf); */
  366. return frError;
  367.     }
  368.     method = url;
  369.     while (!isdigit(*method)) {
  370. if (*method == ' ')
  371.     *method = '';
  372. --method;
  373.     }
  374.     method += 2;
  375.     method_id = methodStrToId(method);
  376.     if (method_id == METHOD_NONE) {
  377. /*fprintf(stderr, "%s:%d: invalid method %s in '%s'n", 
  378.  * fname, scanned_count, method, buf); */
  379. return frError;
  380.     }
  381.     while (*url)
  382. url--;
  383.     url++;
  384.     *hier = '';
  385.     hier += 3;
  386.     *strchr(hier, '/') = '';
  387.     /*fprintf(stdout, "%s:%d: %s %s %sn",
  388.      * fname, count, method, url, hier); */
  389.     entry->use_icp = strcmp(hier, "NONE");
  390.     /* no ICP lookup for these status codes */
  391. /*      strcmp(hier, "NONE") &&
  392.  * strcmp(hier, "DIRECT") &&
  393.  * strcmp(hier, "FIREWALL_IP_DIRECT") &&
  394.  * strcmp(hier, "LOCAL_IP_DIRECT") &&
  395.  * strcmp(hier, "NO_DIRECT_FAIL") &&
  396.  * strcmp(hier, "NO_PARENT_DIRECT") &&
  397.  * strcmp(hier, "SINGLE_PARENT") &&
  398.  * strcmp(hier, "PASSTHROUGH_PARENT") &&
  399.  * strcmp(hier, "SSL_PARENT_MISS") &&
  400.  * strcmp(hier, "DEFAULT_PARENT");
  401.  */
  402.     memcpy(entry->key, storeKeyPublic(url, method_id), sizeof(entry->key));
  403.     /*fprintf(stdout, "%s:%d: %s %s %s %sn",
  404.      * fname, count, method, storeKeyText(entry->key), url, hier); */
  405.     return frOk;
  406. }
  407. static void
  408. cachePurge(Cache * cache, storeSwapLogData * s, int update_digest)
  409. {
  410.     CacheEntry *olde = (CacheEntry *) hash_lookup(cache->hash, s->key);
  411.     if (!olde) {
  412. cache->bad_del_count++;
  413.     } else {
  414. assert(cache->count);
  415. hash_remove_link(cache->hash, (hash_link *) olde);
  416. if (update_digest)
  417.     cacheDigestDel(cache->digest, s->key);
  418. cacheEntryDestroy(olde);
  419. cache->count--;
  420.     }
  421. }
  422. static void
  423. cacheStore(Cache * cache, storeSwapLogData * s, int update_digest)
  424. {
  425.     CacheEntry *olde = (CacheEntry *) hash_lookup(cache->hash, s->key);
  426.     if (olde) {
  427. cache->bad_add_count++;
  428.     } else {
  429. CacheEntry *e = cacheEntryCreate(s);
  430. hash_join(cache->hash, (hash_link *) e);
  431. cache->count++;
  432. if (update_digest)
  433.     cacheDigestAdd(cache->digest, e->key);
  434.     }
  435. }
  436. static void
  437. cacheUpdateStore(Cache * cache, storeSwapLogData * s, int update_digest)
  438. {
  439.     switch (s->op) {
  440.     case SWAP_LOG_ADD:
  441. cacheStore(cache, s, update_digest);
  442. break;
  443.     case SWAP_LOG_DEL:
  444. cachePurge(cache, s, update_digest);
  445. break;
  446.     default:
  447. assert(0);
  448.     }
  449. }
  450. static int
  451. usage(const char *prg_name)
  452. {
  453.     fprintf(stderr, "usage: %s <access_log> <swap_state> ...n",
  454. prg_name);
  455.     return -1;
  456. }
  457. int
  458. main(int argc, char *argv[])
  459. {
  460.     FileIterator **fis = NULL;
  461.     const int fi_count = argc - 1;
  462.     int active_fi_count = 0;
  463.     time_t ready_time;
  464.     Cache *them, *us;
  465.     int i;
  466.     if (argc < 3)
  467. return usage(argv[0]);
  468.     them = cacheCreate("them");
  469.     us = cacheCreate("us");
  470.     them->peer = us;
  471.     us->peer = them;
  472.     fis = xcalloc(fi_count, sizeof(FileIterator *));
  473.     /* init iterators with files */
  474.     fis[0] = fileIteratorCreate(argv[1], accessLogReader);
  475.     for (i = 2; i < argc; ++i)
  476. fis[i - 1] = fileIteratorCreate(argv[i], swapStateReader);
  477.     /* check that all files were found */
  478.     for (i = 0; i < fi_count; ++i)
  479. if (!fis[i])
  480.     return -2;
  481.     /* read prefix to get start-up contents of the peer cache */
  482.     ready_time = -1;
  483.     for (i = 1; i < fi_count; ++i) {
  484. FileIterator *fi = fis[i];
  485. while (fi->inner_time > 0) {
  486.     if (((storeSwapLogData *) fi->entry)->op == SWAP_LOG_DEL) {
  487. cachePurge(them, fi->entry, 0);
  488. if (ready_time < 0)
  489.     ready_time = fi->inner_time;
  490.     } else {
  491. if (ready_time > 0 && fi->inner_time > ready_time)
  492.     break;
  493. cacheStore(them, fi->entry, 0);
  494.     }
  495.     fileIteratorAdvance(fi);
  496. }
  497.     }
  498.     /* digest peer cache content */
  499.     cacheResetDigest(them);
  500.     us->digest = cacheDigestClone(them->digest); /* @netw@ */
  501.     /* shift the time in access log to match ready_time */
  502.     fileIteratorSetCurTime(fis[0], ready_time);
  503.     /* iterate, use the iterator with the smallest positive inner_time */
  504.     cur_time = -1;
  505.     do {
  506. int next_i = -1;
  507. time_t next_time = -1;
  508. active_fi_count = 0;
  509. for (i = 0; i < fi_count; ++i) {
  510.     if (fis[i]->inner_time >= 0) {
  511. if (!active_fi_count || fis[i]->inner_time < next_time) {
  512.     next_i = i;
  513.     next_time = fis[i]->inner_time;
  514. }
  515. active_fi_count++;
  516.     }
  517. }
  518. if (next_i >= 0) {
  519.     cur_time = next_time;
  520.     /*fprintf(stderr, "%2d time: %d %s", next_i, (int)cur_time, ctime(&cur_time)); */
  521.     if (next_i == 0)
  522. cacheFetch(us, fis[next_i]->entry);
  523.     else
  524. cacheUpdateStore(them, fis[next_i]->entry, 1);
  525.     fileIteratorAdvance(fis[next_i]);
  526. }
  527.     } while (active_fi_count);
  528.     /* report */
  529.     cacheReport(them);
  530.     cacheReport(us);
  531.     cacheQueryReport(us, &us->qstats);
  532.     /* clean */
  533.     for (i = 0; i < argc - 1; ++i) {
  534. fileIteratorDestroy(fis[i]);
  535.     }
  536.     xfree(fis);
  537.     cacheDestroy(them);
  538.     cacheDestroy(us);
  539.     return 0;
  540. }