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
10static 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
31static void __memp_pbh __P((DB_MPOOL *, BH *, size_t *, FILE *));
32
33/*
34 * memp_stat --
35 *	Display MPOOL statistics.
36 */
37int
38memp_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 */
126char *
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 */
140char *
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 */
165void
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 */
270static 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