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 }