hash_stat.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996, 1997, 1998, 1999, 2000
00005  *      Sleepycat Software.  All rights reserved.
00006  */
00007 
00008 #include "config.h"
00009 
00010 #ifndef lint
00011 static const char revid[] = "$Id: hash__stat_8c-source.html,v 1.1 2008/06/08 10:19:38 sebdiaz Exp $";
00012 #endif /* not lint */
00013 
00014 #ifndef NO_SYSTEM_INCLUDES
00015 #include <sys/types.h>
00016 
00017 #include <errno.h>
00018 #include <string.h>
00019 #endif
00020 
00021 #include "db_int.h"
00022 #include "db_page.h"
00023 #include "db_shash.h"
00024 #include "btree.h"
00025 #include "hash.h"
00026 #include "lock.h"
00027 
00028 static int __ham_stat_callback __P((DB *, PAGE *, void *, int *));
00029 
00030 /*
00031  * CDB___ham_stat --
00032  *      Gather/print the hash statistics
00033  *
00034  * PUBLIC: int CDB___ham_stat __P((DB *, void *, void *(*)(size_t), u_int32_t));
00035  */
00036 int
00037 CDB___ham_stat(dbp, spp, db_malloc, flags)
00038         DB *dbp;
00039         void *spp, *(*db_malloc) __P((size_t));
00040         u_int32_t flags;
00041 {
00042         DB_HASH_STAT *sp;
00043         HASH_CURSOR *hcp;
00044         DBC *dbc;
00045         PAGE *h;
00046         db_pgno_t pgno;
00047         int ret;
00048 
00049         PANIC_CHECK(dbp->dbenv);
00050         DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->stat");
00051 
00052         sp = NULL;
00053 
00054         /* Check for invalid flags. */
00055         if ((ret = CDB___db_statchk(dbp, flags)) != 0)
00056                 return (ret);
00057 
00058         if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
00059                 return (ret);
00060         hcp = (HASH_CURSOR *)dbc->internal;
00061 
00062         if ((ret = CDB___ham_get_meta(dbc)) != 0)
00063                 goto err;
00064 
00065         /* Allocate and clear the structure. */
00066         if ((ret = CDB___os_malloc(dbp->dbenv, sizeof(*sp), db_malloc, &sp)) != 0)
00067                 goto err;
00068         memset(sp, 0, sizeof(*sp));
00069         if (flags == DB_CACHED_COUNTS) {
00070                 sp->hash_nkeys = hcp->hdr->dbmeta.key_count;
00071                 sp->hash_ndata = hcp->hdr->dbmeta.record_count;
00072                 goto done;
00073         }
00074 
00075         /* Copy the fields that we have. */
00076         sp->hash_pagesize = dbp->pgsize;
00077         sp->hash_buckets = hcp->hdr->max_bucket + 1;
00078         sp->hash_magic = hcp->hdr->dbmeta.magic;
00079         sp->hash_version = hcp->hdr->dbmeta.version;
00080         sp->hash_metaflags = hcp->hdr->dbmeta.flags;
00081         sp->hash_nelem = hcp->hdr->nelem;
00082         sp->hash_ffactor = hcp->hdr->ffactor;
00083 
00084         /* Walk the free list, counting pages. */
00085         for (sp->hash_free = 0, pgno = hcp->hdr->dbmeta.free;
00086             pgno != PGNO_INVALID;) {
00087                 ++sp->hash_free;
00088 
00089                 if ((ret = CDB_memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
00090                         goto err;
00091 
00092                 pgno = h->next_pgno;
00093                 (void)CDB_memp_fput(dbp->mpf, h, 0);
00094         }
00095 
00096         /* Now traverse the rest of the table. */
00097         if ((ret = CDB___ham_traverse(dbp,
00098             dbc, DB_LOCK_READ, __ham_stat_callback, sp)) != 0)
00099                 goto err;
00100 
00101         if (!F_ISSET(dbp, DB_AM_RDONLY)) {
00102                 if ((ret = CDB___ham_dirty_meta(dbc)) != 0)
00103                         goto err;
00104                 hcp->hdr->dbmeta.key_count = sp->hash_nkeys;
00105                 hcp->hdr->dbmeta.record_count = sp->hash_ndata;
00106         }
00107 
00108 done:
00109         if ((ret = CDB___ham_release_meta(dbc)) != 0)
00110                 goto err;
00111         if ((ret = dbc->c_close(dbc)) != 0)
00112                 goto err;
00113 
00114         *(DB_HASH_STAT **)spp = sp;
00115         return (0);
00116 
00117 err:    if (sp != NULL)
00118                 CDB___os_free(sp, sizeof(*sp));
00119         if (hcp->hdr != NULL)
00120                 (void)CDB___ham_release_meta(dbc);
00121         (void)dbc->c_close(dbc);
00122         return (ret);
00123 
00124 }
00125 
00126 /*
00127  * CDB___ham_traverse
00128  *       Traverse an entire hash table.  We use the callback so that we
00129  * can use this both for stat collection and for deallocation.
00130  *
00131  * PUBLIC:  int CDB___ham_traverse __P((DB *, DBC *, db_lockmode_t,
00132  * PUBLIC:     int (*)(DB *, PAGE *, void *, int *), void *));
00133  */
00134 int
00135 CDB___ham_traverse(dbp, dbc, mode, callback, cookie)
00136         DB *dbp;
00137         DBC *dbc;
00138         db_lockmode_t mode;
00139         int (*callback) __P((DB *, PAGE *, void *, int *));
00140         void *cookie;
00141 {
00142         HASH_CURSOR *hcp;
00143         HKEYDATA *hk;
00144         DBC *opd;
00145         db_pgno_t pgno, opgno;
00146         u_int32_t bucket;
00147         int did_put, i, ret, t_ret;
00148 
00149         hcp = (HASH_CURSOR *)dbc->internal;
00150         opd = NULL;
00151         ret = 0;
00152 
00153         /*
00154          * In a perfect world, we could simply read each page in the file
00155          * and look at its page type to tally the information necessary.
00156          * Unfortunately, the bucket locking that hash tables do to make
00157          * locking easy, makes this a pain in the butt.  We have to traverse
00158          * duplicate, overflow and big pages from the bucket so that we
00159          * don't access anything that isn't properly locked.
00160          */
00161         for (bucket = 0; bucket <= hcp->hdr->max_bucket; bucket++) {
00162                 hcp->bucket = bucket;
00163                 hcp->pgno = pgno = BUCKET_TO_PAGE(hcp, bucket);
00164                 for (ret = CDB___ham_get_cpage(dbc, mode); ret == 0;
00165                     ret = CDB___ham_next_cpage(dbc, pgno, 0)) {
00166                         pgno = NEXT_PGNO(hcp->page);
00167 
00168                         /*
00169                          * Go through each item on the page checking for
00170                          * duplicates (in which case we have to count the
00171                          * duplicate pages) or big key/data items (in which
00172                          * case we have to count those pages).
00173                          */
00174                         for (i = 0; i < NUM_ENT(hcp->page); i++) {
00175                                 hk = (HKEYDATA *)P_ENTRY(hcp->page, i);
00176                                 switch (HPAGE_PTYPE(hk)) {
00177                                 case H_OFFDUP:
00178                                         memcpy(&opgno, HOFFDUP_PGNO(hk),
00179                                             sizeof(db_pgno_t));
00180                                         if ((ret = CDB___db_icursor(dbp, dbc->txn,
00181                                             dbp->dup_compare == NULL ?
00182                                             DB_RECNO : DB_BTREE, opgno, 1,
00183                                             &opd)) != 0)
00184                                                 return (ret);
00185                                         if ((ret = CDB___bam_traverse(opd,
00186                                             DB_LOCK_READ, opgno,
00187                                             __ham_stat_callback, cookie))
00188                                             != 0)
00189                                                 goto err;
00190                                         if ((ret = opd->c_close(opd)) != 0)
00191                                                 return (ret);
00192                                         opd = NULL;
00193                                         break;
00194                                 case H_OFFPAGE:
00195                                         /*
00196                                          * We are about to get a big page
00197                                          * which will use the same spot that
00198                                          * the current page uses, so we need
00199                                          * to restore the current page before
00200                                          * looking at it again.
00201                                          */
00202                                         memcpy(&opgno, HOFFPAGE_PGNO(hk),
00203                                             sizeof(db_pgno_t));
00204                                         if ((ret = CDB___db_traverse_big(dbp,
00205                                             opgno, callback, cookie)) != 0)
00206                                                 goto err;
00207                                         break;
00208                                 case H_KEYDATA:
00209                                         break;
00210                                 }
00211                         }
00212 
00213                         /* Call the callback on CDB_main pages. */
00214                         if ((ret = callback(dbp,
00215                             hcp->page, cookie, &did_put)) != 0)
00216                                 goto err;
00217 
00218                         if (did_put)
00219                                 hcp->page = NULL;
00220                         if (pgno == PGNO_INVALID)
00221                                 break;
00222                 }
00223                 if (ret != 0)
00224                         goto err;
00225 
00226                 if (STD_LOCKING(dbc))
00227                         (void)CDB_lock_put(dbp->dbenv, &hcp->lock);
00228 
00229                 if (hcp->page != NULL) {
00230                         if ((ret = CDB___ham_put_page(dbc->dbp, hcp->page, 0)) != 0)
00231                                 return (ret);
00232                         hcp->page = NULL;
00233                 }
00234 
00235         }
00236 err:    if (opd != NULL &&
00237             (t_ret = opd->c_close(opd)) != 0 && ret == 0)
00238                 ret = t_ret;
00239         return (ret);
00240 }
00241 
00242 static int
00243 __ham_stat_callback(dbp, pagep, cookie, putp)
00244         DB *dbp;
00245         PAGE *pagep;
00246         void *cookie;
00247         int *putp;
00248 {
00249         DB_HASH_STAT *sp;
00250         DB_BTREE_STAT bstat;
00251         db_indx_t indx, len, off, tlen, top;
00252         u_int8_t *hk;
00253 
00254         *putp = 0;
00255         sp = cookie;
00256 
00257         switch (TYPE(pagep)) {
00258         case P_INVALID:
00259                 /* 
00260                  * Hash pages may be wholly zeroed;  this is not a bug. 
00261                  * Obviously such pages have no data, so we can just proceed.
00262                  */
00263                 break;
00264         case P_HASH:
00265                 /*
00266                  * We count the buckets and the overflow pages
00267                  * separately and tally their bytes separately
00268                  * as well.  We need to figure out if this page
00269                  * is a bucket.
00270                  */
00271                 if (PREV_PGNO(pagep) == PGNO_INVALID)
00272                         sp->hash_bfree += P_FREESPACE(pagep);
00273                 else {
00274                         sp->hash_overflows++;
00275                         sp->hash_ovfl_free += P_FREESPACE(pagep);
00276                 }
00277                 top = NUM_ENT(pagep);
00278                 /* Correct for on-page duplicates and deleted items. */
00279                 for (indx = 0; indx < top; indx += P_INDX) {
00280                         switch (*H_PAIRDATA(pagep, indx)) {
00281                         case H_OFFDUP:
00282                         case H_OFFPAGE:
00283                                 break;
00284                         case H_KEYDATA:
00285                                 sp->hash_ndata++;
00286                                 break;
00287                         case H_DUPLICATE:
00288                                 tlen = LEN_HDATA(pagep, 0, indx);
00289                                 hk = H_PAIRDATA(pagep, indx);
00290                                 for (off = 0; off < tlen;
00291                                     off += len + 2 * sizeof (db_indx_t)) {
00292                                         sp->hash_ndata++;
00293                                         memcpy(&len,
00294                                             HKEYDATA_DATA(hk)
00295                                             + off, sizeof(db_indx_t));
00296                                 }
00297                         }
00298                 }
00299                 sp->hash_nkeys += H_NUMPAIRS(pagep);
00300                 break;
00301         case P_IBTREE:
00302         case P_IRECNO:
00303         case P_LBTREE:
00304         case P_LRECNO:
00305         case P_LDUP:
00306                 /*
00307                  * These are all btree pages; get a correct
00308                  * cookie and call them.  Then add appropriate
00309                  * fields into our stat structure.
00310                  */
00311                 memset(&bstat, 0, sizeof(bstat));
00312                 bstat.bt_dup_pgfree = 0;
00313                 bstat.bt_int_pgfree = 0;
00314                 bstat.bt_leaf_pgfree = 0;
00315                 bstat.bt_ndata = 0;
00316                 CDB___bam_stat_callback(dbp, pagep, &bstat, putp);
00317                 sp->hash_dup++;
00318                 sp->hash_dup_free += bstat.bt_leaf_pgfree +
00319                     bstat.bt_dup_pgfree + bstat.bt_int_pgfree;
00320                 sp->hash_ndata += bstat.bt_ndata;
00321                 break;
00322         case P_OVERFLOW:
00323                 sp->hash_bigpages++;
00324                 sp->hash_big_bfree += P_OVFLSPACE(dbp->pgsize, pagep);
00325                 break;
00326         default:
00327                 return (CDB___db_unknown_type(dbp->dbenv,
00328                      "__ham_stat_callback", TYPE(pagep)));
00329         }
00330 
00331         return (0);
00332 }

Generated on Sun Jun 8 10:56:37 2008 for GNUmifluz by  doxygen 1.5.5