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
memp_stat(dbmp,gspp,fspp,db_malloc)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 *
__memp_fn(dbmfp)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 *
__memp_fns(dbmp,mfp)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
__memp_dump_region(dbmp,area,fp)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
__memp_pbh(dbmp,bhp,fmap,fp)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