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 #include "config.h" 00008 00009 #ifndef lint 00010 static const char revid[] = "$Id: mp__stat_8c-source.html,v 1.1 2008年06月08日 10:20:50 sebdiaz Exp $"; 00011 #endif /* not lint */ 00012 00013 #ifndef NO_SYSTEM_INCLUDES 00014 #include <sys/types.h> 00015 00016 #include <errno.h> 00017 #include <stdio.h> 00018 #include <string.h> 00019 #include <unistd.h> 00020 #endif 00021 00022 #ifdef HAVE_RPC 00023 #include "db_server.h" 00024 #endif 00025 00026 #include "db_int.h" 00027 #include "db_page.h" 00028 #include "db_shash.h" 00029 #include "db_am.h" 00030 #include "mp.h" 00031 00032 #ifdef HAVE_RPC 00033 #include "gen_client_ext.h" 00034 #include "rpc_client_ext.h" 00035 #endif 00036 00037 static void __memp_dumpcache 00038 __P((DB_MPOOL *, REGINFO *, size_t *, FILE *, u_int32_t)); 00039 static void __memp_pbh __P((DB_MPOOL *, BH *, size_t *, FILE *)); 00040 00041 /* 00042 * CDB_memp_stat -- 00043 * Display MPOOL statistics. 00044 */ 00045 int 00046 CDB_memp_stat(dbenv, gspp, fspp, db_malloc) 00047 DB_ENV *dbenv; 00048 DB_MPOOL_STAT **gspp; 00049 DB_MPOOL_FSTAT ***fspp; 00050 void *(*db_malloc) __P((size_t)); 00051 { 00052 DB_MPOOL *dbmp; 00053 DB_MPOOL_FSTAT **tfsp; 00054 DB_MPOOL_STAT *sp; 00055 MPOOL *c_mp, *mp; 00056 MPOOLFILE *mfp; 00057 size_t len, nlen; 00058 u_int32_t i; 00059 int ret; 00060 char *name; 00061 00062 #ifdef HAVE_RPC 00063 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) 00064 return (__dbcl_memp_stat(dbenv, gspp, fspp, db_malloc)); 00065 #endif 00066 00067 PANIC_CHECK(dbenv); 00068 ENV_REQUIRES_CONFIG(dbenv, dbenv->mp_handle, DB_INIT_MPOOL); 00069 00070 dbmp = dbenv->mp_handle; 00071 sp = NULL; 00072 00073 /* Global statistics. */ 00074 mp = dbmp->reginfo[0].primary; 00075 if (gspp != NULL) { 00076 *gspp = NULL; 00077 00078 if ((ret = CDB___os_calloc(dbenv, 1, sizeof(**gspp), gspp)) != 0) 00079 return (ret); 00080 sp = *gspp; 00081 00082 /* 00083 * Initialization and information that is not maintained on 00084 * a per-cache basis. 00085 */ 00086 sp->st_hash_longest = 0; 00087 sp->st_region_wait = dbmp->reginfo[0].rp->mutex.mutex_set_wait; 00088 sp->st_region_nowait = 00089 dbmp->reginfo[0].rp->mutex.mutex_set_nowait; 00090 sp->st_gbytes = dbenv->mp_gbytes; 00091 sp->st_bytes = dbenv->mp_bytes; 00092 sp->st_ncache = dbmp->nreg; 00093 sp->st_regsize = dbmp->reginfo[0].rp->size; 00094 00095 R_LOCK(dbenv, dbmp->reginfo); 00096 00097 /* Walk the cache list and accumulate the global information. */ 00098 for (i = 0; i < mp->nreg; ++i) { 00099 c_mp = dbmp->reginfo[i].primary; 00100 sp->st_cache_hit += c_mp->stat.st_cache_hit; 00101 sp->st_cache_miss += c_mp->stat.st_cache_miss; 00102 sp->st_map += c_mp->stat.st_map; 00103 sp->st_page_create += c_mp->stat.st_page_create; 00104 sp->st_page_in += c_mp->stat.st_page_in; 00105 sp->st_page_out += c_mp->stat.st_page_out; 00106 sp->st_ro_evict += c_mp->stat.st_ro_evict; 00107 sp->st_rw_evict += c_mp->stat.st_rw_evict; 00108 sp->st_hash_buckets += c_mp->stat.st_hash_buckets; 00109 sp->st_hash_searches += c_mp->stat.st_hash_searches; 00110 if (c_mp->stat.st_hash_longest > sp->st_hash_longest) 00111 sp->st_hash_longest = 00112 c_mp->stat.st_hash_longest; 00113 sp->st_hash_examined += c_mp->stat.st_hash_examined; 00114 sp->st_page_clean += c_mp->stat.st_page_clean; 00115 sp->st_page_dirty += c_mp->stat.st_page_dirty; 00116 sp->st_page_trickle += c_mp->stat.st_page_trickle; 00117 sp->st_region_wait += c_mp->stat.st_region_wait; 00118 sp->st_region_nowait += c_mp->stat.st_region_nowait; 00119 } 00120 00121 R_UNLOCK(dbenv, dbmp->reginfo); 00122 } 00123 00124 /* Per-file statistics. */ 00125 if (fspp != NULL) { 00126 *fspp = NULL; 00127 00128 R_LOCK(dbenv, dbmp->reginfo); 00129 00130 /* Count the MPOOLFILE structures. */ 00131 for (len = 0, 00132 mfp = SH_TAILQ_FIRST(&mp->mpfq, __mpoolfile); 00133 mfp != NULL; 00134 ++len, mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile)) 00135 ; 00136 00137 R_UNLOCK(dbenv, dbmp->reginfo); 00138 00139 if (len == 0) 00140 return (0); 00141 00142 /* Allocate space for the pointers. */ 00143 len = (len + 1) * sizeof(DB_MPOOL_FSTAT *); 00144 if ((ret = CDB___os_malloc(dbenv, len, db_malloc, fspp)) != 0) 00145 return (ret); 00146 00147 R_LOCK(dbenv, dbmp->reginfo); 00148 00149 /* Build each individual entry. */ 00150 for (tfsp = *fspp, 00151 mfp = SH_TAILQ_FIRST(&mp->mpfq, __mpoolfile); 00152 mfp != NULL; 00153 ++tfsp, mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile)) { 00154 name = CDB___memp_fns(dbmp, mfp); 00155 nlen = strlen(name); 00156 len = sizeof(DB_MPOOL_FSTAT) + nlen + 1; 00157 if ((ret = 00158 CDB___os_malloc(dbenv, len, db_malloc, tfsp)) != 0) 00159 return (ret); 00160 **tfsp = mfp->stat; 00161 (*tfsp)->file_name = (char *) 00162 (u_int8_t *)*tfsp + sizeof(DB_MPOOL_FSTAT); 00163 memcpy((*tfsp)->file_name, name, nlen + 1); 00164 00165 /* 00166 * We have duplicate statistics fields in the cache 00167 * and per-file structures. The counters are only 00168 * incremented in the per-file structures, though. 00169 * The intent is that if we ever flush files from 00170 * the pool we can save their last known totals in 00171 * the cache structure. 00172 */ 00173 if (sp != NULL) { 00174 sp->st_cache_hit += mfp->stat.st_cache_hit; 00175 sp->st_cache_miss += mfp->stat.st_cache_miss; 00176 sp->st_map += mfp->stat.st_map; 00177 sp->st_page_create += mfp->stat.st_page_create; 00178 sp->st_page_in += mfp->stat.st_page_in; 00179 sp->st_page_out += mfp->stat.st_page_out; 00180 } 00181 } 00182 *tfsp = NULL; 00183 00184 R_UNLOCK(dbenv, dbmp->reginfo); 00185 } 00186 return (0); 00187 } 00188 00189 #define FMAP_ENTRIES 200 /* Files we map. */ 00190 00191 #define MPOOL_DUMP_HASH 0x01 /* Debug hash chains. */ 00192 #define MPOOL_DUMP_LRU 0x02 /* Debug LRU chains. */ 00193 #define MPOOL_DUMP_MEM 0x04 /* Debug region memory. */ 00194 #define MPOOL_DUMP_ALL 0x07 /* Debug all. */ 00195 00196 /* 00197 * CDB___memp_dump_region -- 00198 * Display MPOOL structures. 00199 * 00200 * PUBLIC: void CDB___memp_dump_region __P((DB_ENV *, char *, FILE *)); 00201 */ 00202 void 00203 CDB___memp_dump_region(dbenv, area, fp) 00204 DB_ENV *dbenv; 00205 char *area; 00206 FILE *fp; 00207 { 00208 DB_MPOOL *dbmp; 00209 DB_MPOOLFILE *dbmfp; 00210 MPOOL *mp; 00211 MPOOLFILE *mfp; 00212 size_t fmap[FMAP_ENTRIES + 1]; 00213 u_int32_t i, flags; 00214 int cnt; 00215 u_int8_t *p; 00216 00217 dbmp = dbenv->mp_handle; 00218 00219 /* Make it easy to call from the debugger. */ 00220 if (fp == NULL) 00221 fp = stderr; 00222 00223 for (flags = 0; *area != '0円'; ++area) 00224 switch (*area) { 00225 case 'A': 00226 LF_SET(MPOOL_DUMP_ALL); 00227 break; 00228 case 'h': 00229 LF_SET(MPOOL_DUMP_HASH); 00230 break; 00231 case 'l': 00232 LF_SET(MPOOL_DUMP_LRU); 00233 break; 00234 case 'm': 00235 LF_SET(MPOOL_DUMP_MEM); 00236 break; 00237 } 00238 00239 R_LOCK(dbenv, dbmp->reginfo); 00240 00241 mp = dbmp->reginfo[0].primary; 00242 00243 /* Display MPOOL structures. */ 00244 (void)fprintf(fp, "%s\nPool (region addr 0x%lx)\n", 00245 DB_LINE, (u_long)dbmp->reginfo[0].addr); 00246 00247 /* Display the MPOOLFILE structures. */ 00248 cnt = 0; 00249 for (mfp = SH_TAILQ_FIRST(&mp->mpfq, __mpoolfile); 00250 mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile), ++cnt) { 00251 (void)fprintf(fp, "File #%d: %s: type %ld, %s\n\t [UID: ", 00252 cnt + 1, CDB___memp_fns(dbmp, mfp), (long)mfp->ftype, 00253 F_ISSET(mfp, MP_CAN_MMAP) ? "mmap" : "read/write"); 00254 p = R_ADDR(dbmp->reginfo, mfp->fileid_off); 00255 for (i = 0; i < DB_FILE_ID_LEN; ++i) { 00256 (void)fprintf(fp, "%x", *p++); 00257 if (i < DB_FILE_ID_LEN - 1) 00258 (void)fprintf(fp, " "); 00259 } 00260 (void)fprintf(fp, "]\n"); 00261 if (cnt < FMAP_ENTRIES) 00262 fmap[cnt] = R_OFFSET(dbmp->reginfo, mfp); 00263 } 00264 00265 for (dbmfp = TAILQ_FIRST(&dbmp->dbmfq); 00266 dbmfp != NULL; dbmfp = TAILQ_NEXT(dbmfp, q), ++cnt) { 00267 (void)fprintf(fp, "File #%d: %s: per-process, %s\n", 00268 cnt + 1, CDB___memp_fn(dbmfp), 00269 F_ISSET(dbmfp, MP_READONLY) ? "readonly" : "read/write"); 00270 if (cnt < FMAP_ENTRIES) 00271 fmap[cnt] = R_OFFSET(dbmp->reginfo, mfp); 00272 } 00273 if (cnt < FMAP_ENTRIES) 00274 fmap[cnt] = INVALID_ROFF; 00275 else 00276 fmap[FMAP_ENTRIES] = INVALID_ROFF; 00277 00278 /* Dump the memory pools. */ 00279 for (i = 0; i < mp->nreg; ++i) { 00280 (void)fprintf(fp, "%s\nCache #%d:\n", DB_LINE, i + 1); 00281 __memp_dumpcache(dbmp, &dbmp->reginfo[i], fmap, fp, flags); 00282 } 00283 00284 R_UNLOCK(dbenv, dbmp->reginfo); 00285 00286 /* Flush in case we're debugging. */ 00287 (void)fflush(fp); 00288 } 00289 00290 /* 00291 * __memp_dumpcache -- 00292 * Display statistics for a cache. 00293 */ 00294 static void 00295 __memp_dumpcache(dbmp, reginfo, fmap, fp, flags) 00296 DB_MPOOL *dbmp; 00297 REGINFO *reginfo; 00298 size_t *fmap; 00299 FILE *fp; 00300 u_int32_t flags; 00301 { 00302 BH *bhp; 00303 DB_HASHTAB *dbht; 00304 MPOOL *c_mp; 00305 int bucket; 00306 00307 c_mp = reginfo->primary; 00308 00309 /* Display the hash table list of BH's. */ 00310 if (LF_ISSET(MPOOL_DUMP_HASH)) { 00311 (void)fprintf(fp, 00312 "%s\nBH hash table (%lu hash slots)\npageno, file, ref, address\n", 00313 DB_LINE, (u_long)c_mp->htab_buckets); 00314 for (dbht = R_ADDR(reginfo, c_mp->htab), 00315 bucket = 0; bucket < c_mp->htab_buckets; ++dbht, ++bucket) { 00316 if (SH_TAILQ_FIRST(dbht, __bh) != NULL) 00317 (void)fprintf(fp, "%lu:\n", (u_long)bucket); 00318 for (bhp = SH_TAILQ_FIRST(dbht, __bh); 00319 bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, hq, __bh)) 00320 __memp_pbh(dbmp, bhp, fmap, fp); 00321 } 00322 } 00323 00324 /* Display the LRU list of BH's. */ 00325 if (LF_ISSET(MPOOL_DUMP_LRU)) { 00326 (void)fprintf(fp, "%s\nBH LRU list\n", DB_LINE); 00327 (void)fprintf(fp, "pageno, file, ref, address\n"); 00328 for (bhp = SH_TAILQ_FIRST(&c_mp->bhq, __bh); 00329 bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, q, __bh)) 00330 __memp_pbh(dbmp, bhp, fmap, fp); 00331 } 00332 00333 /* Dump the memory pool. */ 00334 if (LF_ISSET(MPOOL_DUMP_MEM)) 00335 CDB___db_shalloc_dump(reginfo->addr, fp); 00336 } 00337 00338 /* 00339 * __memp_pbh -- 00340 * Display a BH structure. 00341 */ 00342 static void 00343 __memp_pbh(dbmp, bhp, fmap, fp) 00344 DB_MPOOL *dbmp; 00345 BH *bhp; 00346 size_t *fmap; 00347 FILE *fp; 00348 { 00349 static const FN fn[] = { 00350 { BH_CALLPGIN, "callpgin" }, 00351 { BH_DIRTY, "dirty" }, 00352 { BH_DISCARD, "discard" }, 00353 { BH_LOCKED, "locked" }, 00354 { BH_TRASH, "trash" }, 00355 { BH_WRITE, "write" }, 00356 { 0, NULL } 00357 }; 00358 int i; 00359 00360 for (i = 0; i < FMAP_ENTRIES; ++i) 00361 if (fmap[i] == INVALID_ROFF || fmap[i] == bhp->mf_offset) 00362 break; 00363 00364 if (fmap[i] == INVALID_ROFF) 00365 (void)fprintf(fp, " %4lu, %lu, %2lu, %lu", 00366 (u_long)bhp->pgno, (u_long)bhp->mf_offset, 00367 (u_long)bhp->ref, (u_long)R_OFFSET(dbmp->reginfo, bhp)); 00368 else 00369 (void)fprintf(fp, " %4lu, #%d, %2lu, %lu", 00370 (u_long)bhp->pgno, i + 1, 00371 (u_long)bhp->ref, (u_long)R_OFFSET(dbmp->reginfo, bhp)); 00372 00373 CDB___db_prflags(bhp->flags, fn, fp); 00374 00375 (void)fprintf(fp, "\n"); 00376 }