1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996, 1997, 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 #include "config.h" 8 9 #ifndef lint 10 static const char sccsid[] = "@(#)mp_pr.c 10.30 (Sleepycat) 10/1/98"; 11 #endif /* not lint */ 12 13 #ifndef NO_SYSTEM_INCLUDES 14 #include <sys/types.h> 15 16 #include <errno.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <unistd.h> 20 #endif 21 22 #include "db_int.h" 23 #include "db_page.h" 24 #include "shqueue.h" 25 #include "db_shash.h" 26 #include "mp.h" 27 #include "db_auto.h" 28 #include "db_ext.h" 29 #include "common_ext.h" 30 31 static void __memp_pbh __P((DB_MPOOL *, BH *, size_t *, FILE *)); 32 33 /* 34 * memp_stat -- 35 * Display MPOOL statistics. 36 */ 37 int 38 memp_stat(dbmp, gspp, fspp, db_malloc) 39 DB_MPOOL *dbmp; 40 DB_MPOOL_STAT **gspp; 41 DB_MPOOL_FSTAT ***fspp; 42 void *(*db_malloc) __P((size_t)); 43 { 44 DB_MPOOL_FSTAT **tfsp; 45 MPOOLFILE *mfp; 46 size_t len, nlen; 47 int ret; 48 char *name; 49 50 MP_PANIC_CHECK(dbmp); 51 52 /* Allocate space for the global statistics. */ 53 if (gspp != NULL) { 54 *gspp = NULL; 55 56 if ((ret = __os_malloc(sizeof(**gspp), db_malloc, gspp)) != 0) 57 return (ret); 58 59 LOCKREGION(dbmp); 60 61 /* Copy out the global statistics. */ 62 **gspp = dbmp->mp->stat; 63 (*gspp)->st_hash_buckets = dbmp->mp->htab_buckets; 64 (*gspp)->st_region_wait = 65 dbmp->mp->rlayout.lock.mutex_set_wait; 66 (*gspp)->st_region_nowait = 67 dbmp->mp->rlayout.lock.mutex_set_nowait; 68 (*gspp)->st_refcnt = dbmp->mp->rlayout.refcnt; 69 (*gspp)->st_regsize = dbmp->mp->rlayout.size; 70 71 UNLOCKREGION(dbmp); 72 } 73 74 if (fspp != NULL) { 75 *fspp = NULL; 76 77 LOCKREGION(dbmp); 78 79 /* Count the MPOOLFILE structures. */ 80 for (len = 0, 81 mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile); 82 mfp != NULL; 83 ++len, mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile)) 84 ; 85 86 UNLOCKREGION(dbmp); 87 88 if (len == 0) 89 return (0); 90 91 /* Allocate space for the pointers. */ 92 len = (len + 1) * sizeof(DB_MPOOL_FSTAT *); 93 if ((ret = __os_malloc(len, db_malloc, fspp)) != 0) 94 return (ret); 95 96 LOCKREGION(dbmp); 97 98 /* Build each individual entry. */ 99 for (tfsp = *fspp, 100 mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile); 101 mfp != NULL; 102 ++tfsp, mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile)) { 103 name = __memp_fns(dbmp, mfp); 104 nlen = strlen(name); 105 len = sizeof(DB_MPOOL_FSTAT) + nlen + 1; 106 if ((ret = __os_malloc(len, db_malloc, tfsp)) != 0) 107 return (ret); 108 **tfsp = mfp->stat; 109 (*tfsp)->file_name = (char *) 110 (u_int8_t *)*tfsp + sizeof(DB_MPOOL_FSTAT); 111 memcpy((*tfsp)->file_name, name, nlen + 1); 112 } 113 *tfsp = NULL; 114 115 UNLOCKREGION(dbmp); 116 } 117 return (0); 118 } 119 120 /* 121 * __memp_fn -- 122 * On errors we print whatever is available as the file name. 123 * 124 * PUBLIC: char * __memp_fn __P((DB_MPOOLFILE *)); 125 */ 126 char * 127 __memp_fn(dbmfp) 128 DB_MPOOLFILE *dbmfp; 129 { 130 return (__memp_fns(dbmfp->dbmp, dbmfp->mfp)); 131 } 132 133 /* 134 * __memp_fns -- 135 * On errors we print whatever is available as the file name. 136 * 137 * PUBLIC: char * __memp_fns __P((DB_MPOOL *, MPOOLFILE *)); 138 * 139 */ 140 char * 141 __memp_fns(dbmp, mfp) 142 DB_MPOOL *dbmp; 143 MPOOLFILE *mfp; 144 { 145 if (mfp->path_off == 0) 146 return ((char *)"temporary"); 147 148 return ((char *)R_ADDR(dbmp, mfp->path_off)); 149 } 150 151 #define FMAP_ENTRIES 200 /* Files we map. */ 152 153 #define MPOOL_DUMP_HASH 0x01 /* Debug hash chains. */ 154 #define MPOOL_DUMP_LRU 0x02 /* Debug LRU chains. */ 155 #define MPOOL_DUMP_MEM 0x04 /* Debug region memory. */ 156 #define MPOOL_DUMP_ALL 0x07 /* Debug all. */ 157 158 159 /* 160 * __memp_dump_region -- 161 * Display MPOOL structures. 162 * 163 * PUBLIC: void __memp_dump_region __P((DB_MPOOL *, char *, FILE *)); 164 */ 165 void 166 __memp_dump_region(dbmp, area, fp) 167 DB_MPOOL *dbmp; 168 char *area; 169 FILE *fp; 170 { 171 BH *bhp; 172 DB_HASHTAB *htabp; 173 DB_MPOOLFILE *dbmfp; 174 MPOOL *mp; 175 MPOOLFILE *mfp; 176 size_t bucket, fmap[FMAP_ENTRIES + 1]; 177 u_int32_t flags; 178 int cnt; 179 180 /* Make it easy to call from the debugger. */ 181 if (fp == NULL) 182 fp = stderr; 183 184 for (flags = 0; *area != '\0'; ++area) 185 switch (*area) { 186 case 'A': 187 LF_SET(MPOOL_DUMP_ALL); 188 break; 189 case 'h': 190 LF_SET(MPOOL_DUMP_HASH); 191 break; 192 case 'l': 193 LF_SET(MPOOL_DUMP_LRU); 194 break; 195 case 'm': 196 LF_SET(MPOOL_DUMP_MEM); 197 break; 198 } 199 200 LOCKREGION(dbmp); 201 202 mp = dbmp->mp; 203 204 /* Display MPOOL structures. */ 205 (void)fprintf(fp, "%s\nPool (region addr 0x%lx, alloc addr 0x%lx)\n", 206 DB_LINE, (u_long)dbmp->reginfo.addr, (u_long)dbmp->addr); 207 208 /* Display the MPOOLFILE structures. */ 209 cnt = 0; 210 for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile); 211 mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile), ++cnt) { 212 (void)fprintf(fp, "file #%d: %s: refs %lu, type %ld, %s\n", 213 cnt + 1, __memp_fns(dbmp, mfp), (u_long)mfp->ref, 214 (long)mfp->ftype, 215 F_ISSET(mfp, MP_CAN_MMAP) ? "mmap" : "read/write"); 216 if (cnt < FMAP_ENTRIES) 217 fmap[cnt] = R_OFFSET(dbmp, mfp); 218 } 219 220 for (dbmfp = TAILQ_FIRST(&dbmp->dbmfq); 221 dbmfp != NULL; dbmfp = TAILQ_NEXT(dbmfp, q), ++cnt) { 222 (void)fprintf(fp, "file #%d: %s: fd: %d: per-process, %s\n", 223 cnt + 1, __memp_fn(dbmfp), dbmfp->fd, 224 F_ISSET(dbmfp, MP_READONLY) ? "readonly" : "read/write"); 225 if (cnt < FMAP_ENTRIES) 226 fmap[cnt] = R_OFFSET(dbmp, mfp); 227 } 228 if (cnt < FMAP_ENTRIES) 229 fmap[cnt] = INVALID; 230 else 231 fmap[FMAP_ENTRIES] = INVALID; 232 233 /* Display the hash table list of BH's. */ 234 if (LF_ISSET(MPOOL_DUMP_HASH)) { 235 (void)fprintf(fp, 236 "%s\nBH hash table (%lu hash slots)\npageno, file, ref, address\n", 237 DB_LINE, (u_long)mp->htab_buckets); 238 for (htabp = dbmp->htab, 239 bucket = 0; bucket < mp->htab_buckets; ++htabp, ++bucket) { 240 if (SH_TAILQ_FIRST(&dbmp->htab[bucket], __bh) != NULL) 241 (void)fprintf(fp, "%lu:\n", (u_long)bucket); 242 for (bhp = SH_TAILQ_FIRST(&dbmp->htab[bucket], __bh); 243 bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, hq, __bh)) 244 __memp_pbh(dbmp, bhp, fmap, fp); 245 } 246 } 247 248 /* Display the LRU list of BH's. */ 249 if (LF_ISSET(MPOOL_DUMP_LRU)) { 250 (void)fprintf(fp, "%s\nBH LRU list\n", DB_LINE); 251 (void)fprintf(fp, "pageno, file, ref, address\n"); 252 for (bhp = SH_TAILQ_FIRST(&dbmp->mp->bhq, __bh); 253 bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, q, __bh)) 254 __memp_pbh(dbmp, bhp, fmap, fp); 255 } 256 257 if (LF_ISSET(MPOOL_DUMP_MEM)) 258 __db_shalloc_dump(dbmp->addr, fp); 259 260 UNLOCKREGION(dbmp); 261 262 /* Flush in case we're debugging. */ 263 (void)fflush(fp); 264 } 265 266 /* 267 * __memp_pbh -- 268 * Display a BH structure. 269 */ 270 static void 271 __memp_pbh(dbmp, bhp, fmap, fp) 272 DB_MPOOL *dbmp; 273 BH *bhp; 274 size_t *fmap; 275 FILE *fp; 276 { 277 static const FN fn[] = { 278 { BH_CALLPGIN, "callpgin" }, 279 { BH_DIRTY, "dirty" }, 280 { BH_DISCARD, "discard" }, 281 { BH_LOCKED, "locked" }, 282 { BH_TRASH, "trash" }, 283 { BH_WRITE, "write" }, 284 { 0 }, 285 }; 286 int i; 287 288 for (i = 0; i < FMAP_ENTRIES; ++i) 289 if (fmap[i] == INVALID || fmap[i] == bhp->mf_offset) 290 break; 291 292 if (fmap[i] == INVALID) 293 (void)fprintf(fp, " %4lu, %lu, %2lu, %lu", 294 (u_long)bhp->pgno, (u_long)bhp->mf_offset, 295 (u_long)bhp->ref, (u_long)R_OFFSET(dbmp, bhp)); 296 else 297 (void)fprintf(fp, " %4lu, #%d, %2lu, %lu", 298 (u_long)bhp->pgno, i + 1, 299 (u_long)bhp->ref, (u_long)R_OFFSET(dbmp, bhp)); 300 301 __db_prflags(bhp->flags, fn, fp); 302 303 (void)fprintf(fp, "\n"); 304 } 305