1 /*-
2  * Copyright (c) 1996, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  *
6  * $Id$
7  */
8 
9 #include "db_config.h"
10 
11 #include "db_int.h"
12 #include "dbinc/db_page.h"
13 #include "dbinc/db_am.h"
14 #include "dbinc/mp.h"
15 #include "dbinc/txn.h"
16 
17 #ifdef HAVE_STATISTICS
18 static void __memp_print_bh __P((ENV *,
19 		DB_MPOOL *, const char *, BH *, roff_t *));
20 static int  __memp_print_all __P((ENV *, u_int32_t));
21 static int  __memp_print_stats __P((ENV *, u_int32_t));
22 static int __memp_print_hash __P((ENV *,
23 		DB_MPOOL *, REGINFO *, roff_t *, u_int32_t));
24 static int  __memp_stat __P((ENV *,
25 		DB_MPOOL_STAT **, DB_MPOOL_FSTAT ***, u_int32_t));
26 static void __memp_stat_wait
27 		__P((ENV *, REGINFO *, MPOOL *, DB_MPOOL_STAT *, u_int32_t));
28 static int __memp_file_stats __P((ENV *,
29 		MPOOLFILE *, void *, u_int32_t *, u_int32_t));
30 static int __memp_count_files __P((ENV *,
31 		MPOOLFILE *, void *, u_int32_t *, u_int32_t));
32 static int __memp_get_files __P((ENV *,
33 		MPOOLFILE *, void *, u_int32_t *, u_int32_t));
34 static int __memp_print_files __P((ENV *,
35 		MPOOLFILE *, void *, u_int32_t *, u_int32_t));
36 
37 /*
38  * __memp_stat_pp --
39  *	DB_ENV->memp_stat pre/post processing.
40  *
41  * PUBLIC: int __memp_stat_pp
42  * PUBLIC:     __P((DB_ENV *, DB_MPOOL_STAT **, DB_MPOOL_FSTAT ***, u_int32_t));
43  */
44 int
__memp_stat_pp(dbenv,gspp,fspp,flags)45 __memp_stat_pp(dbenv, gspp, fspp, flags)
46 	DB_ENV *dbenv;
47 	DB_MPOOL_STAT **gspp;
48 	DB_MPOOL_FSTAT ***fspp;
49 	u_int32_t flags;
50 {
51 	DB_THREAD_INFO *ip;
52 	ENV *env;
53 	int ret;
54 
55 	env = dbenv->env;
56 
57 	ENV_REQUIRES_CONFIG(env,
58 	    env->mp_handle, "DB_ENV->memp_stat", DB_INIT_MPOOL);
59 
60 	if ((ret = __db_fchk(env,
61 	    "DB_ENV->memp_stat", flags, DB_STAT_CLEAR)) != 0)
62 		return (ret);
63 
64 	ENV_ENTER(env, ip);
65 	REPLICATION_WRAP(env, (__memp_stat(env, gspp, fspp, flags)), 0, ret);
66 	ENV_LEAVE(env, ip);
67 	return (ret);
68 }
69 
70 /*
71  * __memp_stat --
72  *	ENV->memp_stat
73  */
74 static int
__memp_stat(env,gspp,fspp,flags)75 __memp_stat(env, gspp, fspp, flags)
76 	ENV *env;
77 	DB_MPOOL_STAT **gspp;
78 	DB_MPOOL_FSTAT ***fspp;
79 	u_int32_t flags;
80 {
81 	DB_MPOOL *dbmp;
82 	DB_MPOOL_FSTAT **tfsp;
83 	DB_MPOOL_STAT *sp;
84 	MPOOL *c_mp, *mp;
85 	size_t len;
86 	int ret;
87 	u_int32_t i;
88 	uintmax_t tmp_wait, tmp_nowait;
89 
90 	/*
91 	 * The array holding the lengths related to the buffer allocated
92 	 * for *fspp.  The first element of the array holds the number of
93 	 * entries allocated.  The second element of the array holds the
94 	 * total number of bytes allocated.
95 	 */
96 	u_int32_t fsp_len[2];
97 
98 	dbmp = env->mp_handle;
99 	mp = dbmp->reginfo[0].primary;
100 	tfsp = NULL;
101 
102 	/* Global statistics. */
103 	if (gspp != NULL) {
104 		*gspp = NULL;
105 
106 		if ((ret = __os_umalloc(env, sizeof(**gspp), gspp)) != 0)
107 			return (ret);
108 		memset(*gspp, 0, sizeof(**gspp));
109 		sp = *gspp;
110 
111 		/*
112 		 * Initialization and information that is not maintained on
113 		 * a per-cache basis.  Note that configuration information
114 		 * may be modified at any time, and so we have to lock.
115 		 */
116 		sp->st_gbytes = mp->gbytes;
117 		sp->st_bytes = mp->bytes;
118 		sp->st_pagesize = mp->pagesize;
119 		sp->st_ncache = mp->nreg;
120 		sp->st_max_ncache = mp->max_nreg;
121 		sp->st_regsize = dbmp->reginfo[0].rp->size;
122 		sp->st_regmax = dbmp->reginfo[0].rp->max;
123 		sp->st_sync_interrupted = mp->stat.st_sync_interrupted;
124 
125 		MPOOL_SYSTEM_LOCK(env);
126 		sp->st_mmapsize = mp->mp_mmapsize;
127 		sp->st_maxopenfd = mp->mp_maxopenfd;
128 		sp->st_maxwrite = mp->mp_maxwrite;
129 		sp->st_maxwrite_sleep = mp->mp_maxwrite_sleep;
130 		MPOOL_SYSTEM_UNLOCK(env);
131 
132 		/* Walk the cache list and accumulate the global information. */
133 		for (i = 0; i < mp->nreg; ++i) {
134 			c_mp = dbmp->reginfo[i].primary;
135 
136 			sp->st_map += c_mp->stat.st_map;
137 			sp->st_cache_hit += c_mp->stat.st_cache_hit;
138 			sp->st_cache_miss += c_mp->stat.st_cache_miss;
139 			sp->st_page_create += c_mp->stat.st_page_create;
140 			sp->st_page_in += c_mp->stat.st_page_in;
141 			sp->st_page_out += c_mp->stat.st_page_out;
142 			sp->st_ro_evict += c_mp->stat.st_ro_evict;
143 			sp->st_rw_evict += c_mp->stat.st_rw_evict;
144 			sp->st_page_trickle += c_mp->stat.st_page_trickle;
145 			sp->st_mvcc_reused += c_mp->stat.st_mvcc_reused;
146 			sp->st_pages += c_mp->pages;
147 			/* Undocumented field used by tests only. */
148 			sp->st_oddfsize_detect +=
149 			    c_mp->stat.st_oddfsize_detect;
150 			/* Undocumented field used by tests only. */
151 			sp->st_oddfsize_resolve +=
152 			    c_mp->stat.st_oddfsize_resolve;
153 			/*
154 			 * st_page_dirty	calculated by __memp_stat_hash
155 			 * st_page_clean	calculated here
156 			 */
157 			__memp_stat_hash(
158 			    &dbmp->reginfo[i], c_mp, &sp->st_page_dirty);
159 			sp->st_page_clean = sp->st_pages - sp->st_page_dirty;
160 			sp->st_hash_buckets += c_mp->htab_buckets;
161 			sp->st_hash_mutexes += c_mp->htab_mutexes;
162 			sp->st_hash_searches += c_mp->stat.st_hash_searches;
163 			sp->st_hash_longest += c_mp->stat.st_hash_longest;
164 			sp->st_hash_examined += c_mp->stat.st_hash_examined;
165 			/*
166 			 * st_hash_nowait	calculated by __memp_stat_wait
167 			 * st_hash_wait
168 			 */
169 			__memp_stat_wait(
170 			    env, &dbmp->reginfo[i], c_mp, sp, flags);
171 			__mutex_set_wait_info(env,
172 			    c_mp->mtx_region, &tmp_wait, &tmp_nowait);
173 			sp->st_region_nowait += tmp_nowait;
174 			sp->st_region_wait += tmp_wait;
175 			sp->st_alloc += c_mp->stat.st_alloc;
176 			sp->st_alloc_buckets += c_mp->stat.st_alloc_buckets;
177 			if (sp->st_alloc_max_buckets <
178 			    c_mp->stat.st_alloc_max_buckets)
179 				sp->st_alloc_max_buckets =
180 				    c_mp->stat.st_alloc_max_buckets;
181 			sp->st_alloc_pages += c_mp->stat.st_alloc_pages;
182 			if (sp->st_alloc_max_pages <
183 			    c_mp->stat.st_alloc_max_pages)
184 				sp->st_alloc_max_pages =
185 				    c_mp->stat.st_alloc_max_pages;
186 
187 			if (LF_ISSET(DB_STAT_CLEAR)) {
188 				if (!LF_ISSET(DB_STAT_SUBSYSTEM))
189 					__mutex_clear(env, c_mp->mtx_region);
190 
191 				memset(&c_mp->stat, 0, sizeof(c_mp->stat));
192 			}
193 		}
194 
195 		/*
196 		 * We have duplicate statistics fields in per-file structures
197 		 * and the cache.  The counters are only incremented in the
198 		 * per-file structures, except if a file is flushed from the
199 		 * mpool, at which time we copy its information into the cache
200 		 * statistics.  We added the cache information above, now we
201 		 * add the per-file information.
202 		 */
203 		if ((ret = __memp_walk_files(env, mp, __memp_file_stats,
204 		    sp, NULL, fspp == NULL ? LF_ISSET(DB_STAT_CLEAR) : 0)) != 0)
205 			return (ret);
206 	}
207 
208 	/* Per-file statistics. */
209 	if (fspp != NULL) {
210 		*fspp = NULL;
211 
212 		while (*fspp == NULL) {
213 			/* Count the MPOOLFILE structures. */
214 			i = 0;
215 			/*
216 			 * Allow space for the first __memp_get_files() to
217 			 * align the structure array to uintmax_t,
218 			 * DB_MPOOL_STAT's most restrictive field.  [#23150]
219 			 */
220 			len = sizeof(uintmax_t);
221 			if ((ret = __memp_walk_files(env,
222 			     mp, __memp_count_files, &len, &i, flags)) != 0)
223 				return (ret);
224 
225 			if (i == 0)
226 				return (0);
227 
228 			/*
229 			 * Copy the number of DB_MPOOL_FSTAT entries and the
230 			 * number of bytes allocated for them into fsp_len. Do
231 			 * not count the space reserved for allignment.
232 			 */
233 			fsp_len[0] = i;
234 			fsp_len[1] = len - sizeof(uintmax_t);
235 
236 			/* Space for the trailing NULL. */
237 			len += sizeof(DB_MPOOL_FSTAT *);
238 
239 			/* Allocate space */
240 			if ((ret = __os_umalloc(env, len, fspp)) != 0)
241 				return (ret);
242 
243 			tfsp = *fspp;
244 			*tfsp = NULL;
245 
246 			/*
247 			 * Files may have been opened since we counted, if we
248 			 * walk off the end of the allocated space specified
249 			 * in fsp_len, retry.
250 			 */
251 			if ((ret = __memp_walk_files(env,
252 			    mp, __memp_get_files, &tfsp,
253 			    fsp_len, flags)) != 0) {
254 				if (ret == DB_BUFFER_SMALL) {
255 					__os_ufree(env, *fspp);
256 					*fspp = NULL;
257 					tfsp = NULL;
258 				} else
259 					return (ret);
260 			}
261 		}
262 
263 		*++tfsp = NULL;
264 	}
265 
266 	return (0);
267 }
268 
269 static int
__memp_file_stats(env,mfp,argp,countp,flags)270 __memp_file_stats(env, mfp, argp, countp, flags)
271 	ENV *env;
272 	MPOOLFILE *mfp;
273 	void *argp;
274 	u_int32_t *countp;
275 	u_int32_t flags;
276 {
277 	DB_MPOOL_STAT *sp;
278 
279 	COMPQUIET(env, NULL);
280 	COMPQUIET(countp, NULL);
281 
282 	sp = argp;
283 
284 	sp->st_map += mfp->stat.st_map;
285 	sp->st_cache_hit += mfp->stat.st_cache_hit;
286 	sp->st_cache_miss += mfp->stat.st_cache_miss;
287 	sp->st_page_create += mfp->stat.st_page_create;
288 	sp->st_page_in += mfp->stat.st_page_in;
289 	sp->st_page_out += mfp->stat.st_page_out;
290 	if (LF_ISSET(DB_STAT_CLEAR))
291 		memset(&mfp->stat, 0, sizeof(mfp->stat));
292 
293 	return (0);
294 }
295 
296 /*
297  * __memp_count_files --
298  *	This __memp_walk_files() iterator counts the number of files as well as
299  *	the space needed for their statistics, including file names.
300  */
301 static int
__memp_count_files(env,mfp,argp,countp,flags)302 __memp_count_files(env, mfp, argp, countp, flags)
303 	ENV *env;
304 	MPOOLFILE *mfp;
305 	void *argp;
306 	u_int32_t *countp;
307 	u_int32_t flags;
308 {
309 	DB_MPOOL *dbmp;
310 	size_t len;
311 
312 	COMPQUIET(flags, 0);
313 	dbmp = env->mp_handle;
314 	len = *(size_t *)argp;
315 
316 	(*countp)++;
317 	len += sizeof(DB_MPOOL_FSTAT *) +
318 	    sizeof(DB_MPOOL_FSTAT) + strlen(__memp_fns(dbmp, mfp)) + 1;
319 
320 	*(size_t *)argp = len;
321 	return (0);
322 }
323 
324 /*
325  * __memp_get_files --
326  *	get another file's specific statistics
327  *
328  * Add a file statistics entry to the current list. The chunk of memory
329  * starts with an array of DB_MPOOL_FSTAT pointers, a null pointer to mark
330  * the last one, then an aligned array of DB_MPOOL_FSTAT structures, then
331  * characters space for the file names.
332  *	+-----------------------------------------------+
333  *	| count * DB_MPOOL_FSTAT pointers		|
334  *	+-----------------------------------------------+
335  *	| null pointer					+
336  *	+-----------------------------------------------|
337  *	| [space for aligning DB_MPOOL_FSTAT array]	|
338  *	+-----------------------------------------------+
339  *	| count * DB_MPOOL_FSTAT structs		|
340  *	+-----------------------------------------------+
341  *	| first file name | second file name | third... |
342  *	+-----------------------------------------------+
343  *	| file name | ...				|
344  *	+-----------------------------------------------+
345  */
346 static int
__memp_get_files(env,mfp,argp,fsp_len,flags)347 __memp_get_files(env, mfp, argp, fsp_len, flags)
348 	ENV *env;
349 	MPOOLFILE *mfp;
350 	void *argp;
351 	u_int32_t fsp_len[];
352 	u_int32_t flags;
353 {
354 	DB_MPOOL *dbmp;
355 	DB_MPOOL_FSTAT **tfsp, *tstruct;
356 	char *name, *tname;
357 	size_t nlen, tlen;
358 
359 	/* We walked through more files than argp was allocated for. */
360 	if (fsp_len[0] == 0)
361 		return DB_BUFFER_SMALL;
362 
363 	dbmp = env->mp_handle;
364 	tfsp = *(DB_MPOOL_FSTAT ***)argp;
365 
366 	if (*tfsp == NULL) {
367 		/*
368 		 * Add 1 to count because to skip over the NULL end marker.
369 		 * Align it further for DB_MPOOL_STAT's most restrictive field
370 		 * because uintmax_t might require stricter alignment than
371 		 * pointers; e.g., IP32 LL64 SPARC. [#23150]
372 		 */
373 		tstruct = (DB_MPOOL_FSTAT *)&tfsp[fsp_len[0] + 1];
374 		tstruct = ALIGNP_INC(tstruct, sizeof(uintmax_t));
375 		tname = (char *)&tstruct[fsp_len[0]];
376 		*tfsp = tstruct;
377 	} else {
378 		/*
379 		 * This stat struct follows the previous one; the file name
380 		 * follows the previous entry's filename.
381 		 */
382 		tstruct = *tfsp + 1;
383 		tname = (*tfsp)->file_name + strlen((*tfsp)->file_name) + 1;
384 		*++tfsp = tstruct;
385 	}
386 
387 	name = __memp_fns(dbmp, mfp);
388 	nlen = strlen(name) + 1;
389 
390 	/* The space required for file names is larger than
391 	 *argp was allocated for.
392 	 */
393 	tlen = sizeof(DB_MPOOL_FSTAT *) + sizeof(DB_MPOOL_FSTAT) + nlen;
394 	if (fsp_len[1] < tlen)
395 		return DB_BUFFER_SMALL;
396 	else
397 		/* Count down the number of bytes left in argp. */
398 		fsp_len[1] -= tlen;
399 
400 	memcpy(tname, name, nlen);
401 	memcpy(tstruct, &mfp->stat, sizeof(mfp->stat));
402 	tstruct->file_name = tname;
403 
404 	/* Grab the pagesize from the mfp. */
405 	tstruct->st_pagesize = mfp->pagesize;
406 
407 	*(DB_MPOOL_FSTAT ***)argp = tfsp;
408 
409 	/* Count down the number of entries left in argp. */
410 	fsp_len[0]--;
411 
412 	if (LF_ISSET(DB_STAT_CLEAR))
413 		memset(&mfp->stat, 0, sizeof(mfp->stat));
414 
415 	return (0);
416 }
417 
418 /*
419  * __memp_stat_print_pp --
420  *	ENV->memp_stat_print pre/post processing.
421  *
422  * PUBLIC: int __memp_stat_print_pp __P((DB_ENV *, u_int32_t));
423  */
424 int
__memp_stat_print_pp(dbenv,flags)425 __memp_stat_print_pp(dbenv, flags)
426 	DB_ENV *dbenv;
427 	u_int32_t flags;
428 {
429 	DB_THREAD_INFO *ip;
430 	ENV *env;
431 	int ret;
432 
433 	env = dbenv->env;
434 
435 	ENV_REQUIRES_CONFIG(env,
436 	    env->mp_handle, "DB_ENV->memp_stat_print", DB_INIT_MPOOL);
437 
438 #define	DB_STAT_MEMP_FLAGS						\
439 	(DB_STAT_ALL | DB_STAT_ALLOC | DB_STAT_CLEAR | DB_STAT_MEMP_HASH)
440 	if ((ret = __db_fchk(env,
441 	    "DB_ENV->memp_stat_print", flags, DB_STAT_MEMP_FLAGS)) != 0)
442 		return (ret);
443 
444 	ENV_ENTER(env, ip);
445 	REPLICATION_WRAP(env, (__memp_stat_print(env, flags)), 0, ret);
446 	ENV_LEAVE(env, ip);
447 	return (ret);
448 }
449 
450 #define	FMAP_ENTRIES	200			/* Files we map. */
451 
452 /*
453  * __memp_stat_print --
454  *	ENV->memp_stat_print method.
455  *
456  * PUBLIC: int  __memp_stat_print __P((ENV *, u_int32_t));
457  */
458 int
__memp_stat_print(env,flags)459 __memp_stat_print(env, flags)
460 	ENV *env;
461 	u_int32_t flags;
462 {
463 	u_int32_t orig_flags;
464 	int ret;
465 
466 	orig_flags = flags;
467 	LF_CLR(DB_STAT_CLEAR | DB_STAT_SUBSYSTEM);
468 	if (flags == 0 || LF_ISSET(DB_STAT_ALL)) {
469 		ret = __memp_print_stats(env,
470 		    LF_ISSET(DB_STAT_ALL) ? flags : orig_flags);
471 		if (flags == 0 || ret != 0)
472 			return (ret);
473 	}
474 
475 	if (LF_ISSET(DB_STAT_ALL | DB_STAT_MEMP_HASH) &&
476 	    (ret = __memp_print_all(env, orig_flags)) != 0)
477 		return (ret);
478 
479 	return (0);
480 }
481 
482 /*
483  * __memp_print_stats --
484  *	Display default mpool region statistics.
485  */
486 static int
__memp_print_stats(env,flags)487 __memp_print_stats(env, flags)
488 	ENV *env;
489 	u_int32_t flags;
490 {
491 	DB_MPOOL_FSTAT **fsp, **tfsp;
492 	DB_MPOOL_STAT *gsp;
493 	int ret;
494 
495 	if ((ret = __memp_stat(env, &gsp, &fsp, flags)) != 0)
496 		return (ret);
497 
498 	if (LF_ISSET(DB_STAT_ALL))
499 		__db_msg(env, "Default cache region information:");
500 	__db_dlbytes(env, "Total cache size",
501 	    (u_long)gsp->st_gbytes, (u_long)0, (u_long)gsp->st_bytes);
502 	__db_dl(env, "Number of caches", (u_long)gsp->st_ncache);
503 	__db_dl(env, "Maximum number of caches", (u_long)gsp->st_max_ncache);
504 	__db_dlbytes(env, "Pool individual cache size",
505 	    (u_long)0, (u_long)0, (u_long)gsp->st_regsize);
506 	__db_dlbytes(env, "Pool individual cache max",
507 	    (u_long)0, (u_long)0, (u_long)gsp->st_regmax);
508 	__db_dlbytes(env, "Maximum memory-mapped file size",
509 	    (u_long)0, (u_long)0, (u_long)gsp->st_mmapsize);
510 	STAT_LONG("Maximum open file descriptors", gsp->st_maxopenfd);
511 	STAT_LONG("Maximum sequential buffer writes", gsp->st_maxwrite);
512 	STAT_LONG("Sleep after writing maximum sequential buffers",
513 	    gsp->st_maxwrite_sleep);
514 	__db_dl(env,
515 	    "Requested pages mapped into the process' address space",
516 	    (u_long)gsp->st_map);
517 	__db_dl_pct(env, "Requested pages found in the cache",
518 	    (u_long)gsp->st_cache_hit, DB_PCT(
519 	    gsp->st_cache_hit, gsp->st_cache_hit + gsp->st_cache_miss), NULL);
520 	__db_dl(env, "Requested pages not found in the cache",
521 	    (u_long)gsp->st_cache_miss);
522 	__db_dl(env,
523 	    "Pages created in the cache", (u_long)gsp->st_page_create);
524 	__db_dl(env, "Pages read into the cache", (u_long)gsp->st_page_in);
525 	__db_dl(env, "Pages written from the cache to the backing file",
526 	    (u_long)gsp->st_page_out);
527 	__db_dl(env, "Clean pages forced from the cache",
528 	    (u_long)gsp->st_ro_evict);
529 	__db_dl(env, "Dirty pages forced from the cache",
530 	    (u_long)gsp->st_rw_evict);
531 	__db_dl(env, "Dirty pages written by trickle-sync thread",
532 	    (u_long)gsp->st_page_trickle);
533 	__db_dl(env, "Current total page count",
534 	    (u_long)gsp->st_pages);
535 	__db_dl(env, "Current clean page count",
536 	    (u_long)gsp->st_page_clean);
537 	__db_dl(env, "Current dirty page count",
538 	    (u_long)gsp->st_page_dirty);
539 	__db_dl(env, "Number of hash buckets used for page location",
540 	    (u_long)gsp->st_hash_buckets);
541 	__db_dl(env, "Number of mutexes for the hash buckets",
542 	    (u_long)gsp->st_hash_mutexes);
543 	__db_dl(env, "Assumed page size used",
544 	    (u_long)gsp->st_pagesize);
545 	__db_dl(env,
546 	    "Total number of times hash chains searched for a page",
547 	    (u_long)gsp->st_hash_searches);
548 	__db_dl(env, "The longest hash chain searched for a page",
549 	    (u_long)gsp->st_hash_longest);
550 	__db_dl(env,
551 	    "Total number of hash chain entries checked for page",
552 	    (u_long)gsp->st_hash_examined);
553 	__db_dl_pct(env,
554 	    "The number of hash bucket locks that required waiting",
555 	    (u_long)gsp->st_hash_wait, DB_PCT(
556 	    gsp->st_hash_wait, gsp->st_hash_wait + gsp->st_hash_nowait), NULL);
557 	__db_dl_pct(env,
558     "The maximum number of times any hash bucket lock was waited for",
559 	    (u_long)gsp->st_hash_max_wait, DB_PCT(gsp->st_hash_max_wait,
560 	    gsp->st_hash_max_wait + gsp->st_hash_max_nowait), NULL);
561 	__db_dl_pct(env,
562 	    "The number of region locks that required waiting",
563 	    (u_long)gsp->st_region_wait, DB_PCT(gsp->st_region_wait,
564 	    gsp->st_region_wait + gsp->st_region_nowait), NULL);
565 	__db_dl(env, "The number of buffers frozen",
566 	    (u_long)gsp->st_mvcc_frozen);
567 	__db_dl(env, "The number of buffers thawed",
568 	    (u_long)gsp->st_mvcc_thawed);
569 	__db_dl(env, "The number of frozen buffers freed",
570 	    (u_long)gsp->st_mvcc_freed);
571 	__db_dl(env, "The number of outdated intermediate versions reused",
572 	    (u_long)gsp->st_mvcc_reused);
573 	__db_dl(env, "The number of page allocations", (u_long)gsp->st_alloc);
574 	__db_dl(env,
575 	    "The number of hash buckets examined during allocations",
576 	    (u_long)gsp->st_alloc_buckets);
577 	__db_dl(env,
578 	    "The maximum number of hash buckets examined for an allocation",
579 	    (u_long)gsp->st_alloc_max_buckets);
580 	__db_dl(env, "The number of pages examined during allocations",
581 	    (u_long)gsp->st_alloc_pages);
582 	__db_dl(env, "The max number of pages examined for an allocation",
583 	    (u_long)gsp->st_alloc_max_pages);
584 	__db_dl(env, "Threads waited on page I/O", (u_long)gsp->st_io_wait);
585 	__db_dl(env, "The number of times a sync is interrupted",
586 	    (u_long)gsp->st_sync_interrupted);
587 
588 	for (tfsp = fsp; fsp != NULL && *tfsp != NULL; ++tfsp) {
589 		if (LF_ISSET(DB_STAT_ALL))
590 			__db_msg(env, "%s", DB_GLOBAL(db_line));
591 		__db_msg(env, "Pool File: %s", (*tfsp)->file_name);
592 		__db_dl(env, "Page size", (u_long)(*tfsp)->st_pagesize);
593 		__db_dl(env,
594 		    "Requested pages mapped into the process' address space",
595 		    (u_long)(*tfsp)->st_map);
596 		__db_dl_pct(env, "Requested pages found in the cache",
597 		    (u_long)(*tfsp)->st_cache_hit, DB_PCT((*tfsp)->st_cache_hit,
598 		    (*tfsp)->st_cache_hit + (*tfsp)->st_cache_miss), NULL);
599 		__db_dl(env, "Requested pages not found in the cache",
600 		    (u_long)(*tfsp)->st_cache_miss);
601 		__db_dl(env, "Pages created in the cache",
602 		    (u_long)(*tfsp)->st_page_create);
603 		__db_dl(env, "Pages read into the cache",
604 		    (u_long)(*tfsp)->st_page_in);
605 		__db_dl(env,
606 		    "Pages written from the cache to the backing file",
607 		    (u_long)(*tfsp)->st_page_out);
608 		if ((*tfsp)->st_backup_spins != 0)
609 			__db_dl(env,
610 			    "Spins while trying to backup the file",
611 			    (u_long)(*tfsp)->st_backup_spins);
612 	}
613 
614 	__os_ufree(env, fsp);
615 	__os_ufree(env, gsp);
616 	return (0);
617 }
618 
619 /*
620  * __memp_print_all --
621  *	Display debugging mpool region statistics.
622  */
623 static int
__memp_print_all(env,flags)624 __memp_print_all(env, flags)
625 	ENV *env;
626 	u_int32_t flags;
627 {
628 	static const FN cfn[] = {
629 		{ DB_MPOOL_NOFILE,	"DB_MPOOL_NOFILE" },
630 		{ DB_MPOOL_UNLINK,	"DB_MPOOL_UNLINK" },
631 		{ 0,			NULL }
632 	};
633 	DB_MPOOL *dbmp;
634 	DB_MPOOLFILE *dbmfp;
635 	MPOOL *mp;
636 	roff_t fmap[FMAP_ENTRIES + 1];
637 	u_int32_t i, cnt;
638 	int ret;
639 
640 	dbmp = env->mp_handle;
641 	mp = dbmp->reginfo[0].primary;
642 	ret = 0;
643 
644 	MPOOL_SYSTEM_LOCK(env);
645 
646 	__db_print_reginfo(env, dbmp->reginfo, "Mpool", flags);
647 	__db_msg(env, "%s", DB_GLOBAL(db_line));
648 
649 	__db_msg(env, "MPOOL structure:");
650 	__mutex_print_debug_single(
651 	    env, "MPOOL region mutex", mp->mtx_region, flags);
652 	STAT_LSN("Maximum checkpoint LSN", &mp->lsn);
653 	STAT_ULONG("Hash table entries", mp->htab_buckets);
654 	STAT_ULONG("Hash table mutexes", mp->htab_mutexes);
655 
656 	__db_msg(env, "%s", DB_GLOBAL(db_line));
657 	__db_msg(env, "DB_MPOOL handle information:");
658 	__mutex_print_debug_single(
659 	    env, "DB_MPOOL handle mutex", dbmp->mutex, flags);
660 	STAT_ULONG("Underlying cache regions", mp->nreg);
661 
662 	__db_msg(env, "%s", DB_GLOBAL(db_line));
663 	__db_msg(env, "DB_MPOOLFILE structures:");
664 	for (cnt = 0, dbmfp = TAILQ_FIRST(&dbmp->dbmfq);
665 	    dbmfp != NULL; dbmfp = TAILQ_NEXT(dbmfp, q), ++cnt) {
666 		__db_msg(env, "File #%lu: %s: per-process, %s",
667 		    (u_long)cnt + 1, __memp_fn(dbmfp),
668 		    F_ISSET(dbmfp, MP_READONLY) ? "readonly" : "read/write");
669 		STAT_ULONG("Reference count", dbmfp->ref);
670 		STAT_ULONG("Pinned block reference count", dbmfp->ref);
671 		STAT_ULONG("Clear length", dbmfp->clear_len);
672 		__db_print_fileid(env, dbmfp->fileid, "\tID");
673 		STAT_ULONG("File type", dbmfp->ftype);
674 		STAT_ULONG("LSN offset", dbmfp->lsn_offset);
675 		STAT_ULONG("Max gbytes", dbmfp->gbytes);
676 		STAT_ULONG("Max bytes", dbmfp->bytes);
677 		STAT_ULONG("Cache priority", dbmfp->priority);
678 		STAT_POINTER("mmap address", dbmfp->addr);
679 		STAT_ULONG("mmap length", dbmfp->len);
680 		__db_prflags(env, NULL,
681 		    dbmfp->config_flags, cfn, NULL, "\tFlags");
682 		__db_print_fh(env, "File handle", dbmfp->fhp, flags);
683 	}
684 
685 	__db_msg(env, "%s", DB_GLOBAL(db_line));
686 	__db_msg(env, "MPOOLFILE structures:");
687 	cnt = 0;
688 	ret = __memp_walk_files(env, mp, __memp_print_files, fmap, &cnt, flags);
689 	MPOOL_SYSTEM_UNLOCK(env);
690 	if (ret != 0)
691 		return (ret);
692 
693 	if (cnt < FMAP_ENTRIES)
694 		fmap[cnt] = INVALID_ROFF;
695 	else
696 		fmap[FMAP_ENTRIES] = INVALID_ROFF;
697 
698 	/* Dump the individual caches. */
699 	for (i = 0; i < mp->nreg; ++i) {
700 		__db_msg(env, "%s", DB_GLOBAL(db_line));
701 		__db_msg(env, "Cache #%d:", i + 1);
702 		if (i > 0)
703 			__env_alloc_print(&dbmp->reginfo[i], flags);
704 		if ((ret = __memp_print_hash(
705 		    env, dbmp, &dbmp->reginfo[i], fmap, flags)) != 0)
706 			break;
707 	}
708 
709 	return (ret);
710 }
711 
712 static int
__memp_print_files(env,mfp,argp,countp,flags)713 __memp_print_files(env, mfp, argp, countp, flags)
714 	ENV *env;
715 	MPOOLFILE *mfp;
716 	void *argp;
717 	u_int32_t *countp;
718 	u_int32_t flags;
719 {
720 	roff_t *fmap;
721 	DB_MPOOL *dbmp;
722 	u_int32_t mfp_flags;
723 	static const FN fn[] = {
724 		{ MP_CAN_MMAP,		"MP_CAN_MMAP" },
725 		{ MP_DIRECT,		"MP_DIRECT" },
726 		{ MP_EXTENT,		"MP_EXTENT" },
727 		{ MP_FAKE_DEADFILE,	"deadfile" },
728 		{ MP_FAKE_FILEWRITTEN,	"file written" },
729 		{ MP_FAKE_NB,		"no backing file" },
730 		{ MP_FAKE_UOC,		"unlink on close" },
731 		{ MP_NOT_DURABLE,	"not durable" },
732 		{ MP_TEMP,		"MP_TEMP" },
733 		{ 0,			NULL }
734 	};
735 
736 	dbmp = env->mp_handle;
737 	fmap = argp;
738 
739 	__db_msg(env, "File #%d: %s", *countp + 1, __memp_fns(dbmp, mfp));
740 	__mutex_print_debug_single(env, "Mutex", mfp->mutex, flags);
741 
742 	MUTEX_LOCK(env, mfp->mutex);
743 	STAT_ULONG("Revision count", mfp->revision);
744 	STAT_ULONG("Reference count", mfp->mpf_cnt);
745 	STAT_ULONG("Sync/read only open count", mfp->neutral_cnt);
746 	STAT_ULONG("Block count", mfp->block_cnt);
747 	STAT_ULONG("Last page number", mfp->last_pgno);
748 	STAT_ULONG("Original last page number", mfp->orig_last_pgno);
749 	STAT_ULONG("Maximum page number", mfp->maxpgno);
750 	STAT_LONG("Type", mfp->ftype);
751 	STAT_LONG("Priority", mfp->priority);
752 	STAT_LONG("Page's LSN offset", mfp->lsn_off);
753 	STAT_LONG("Page's clear length", mfp->clear_len);
754 	STAT_ULONG("Multiversion reference count",
755 	    atomic_read(&mfp->multiversion));
756 
757 	__db_print_fileid(env,
758 	    R_ADDR(dbmp->reginfo, mfp->fileid_off), "\tID");
759 
760 	mfp_flags = 0;
761 	if (mfp->deadfile)
762 		FLD_SET(mfp_flags, MP_FAKE_DEADFILE);
763 	if (mfp->file_written)
764 		FLD_SET(mfp_flags, MP_FAKE_FILEWRITTEN);
765 	if (mfp->no_backing_file)
766 		FLD_SET(mfp_flags, MP_FAKE_NB);
767 	if (mfp->unlink_on_close)
768 		FLD_SET(mfp_flags, MP_FAKE_UOC);
769 	__db_prflags(env, NULL, mfp_flags, fn, NULL, "\tFlags");
770 
771 	if (*countp < FMAP_ENTRIES)
772 		fmap[*countp] = R_OFFSET(dbmp->reginfo, mfp);
773 	(*countp)++;
774 	MUTEX_UNLOCK(env, mfp->mutex);
775 	return (0);
776 }
777 
778 /*
779  * __memp_print_hash --
780  *	Display hash bucket statistics for a cache.
781  */
782 static int
__memp_print_hash(env,dbmp,reginfo,fmap,flags)783 __memp_print_hash(env, dbmp, reginfo, fmap, flags)
784 	ENV *env;
785 	DB_MPOOL *dbmp;
786 	REGINFO *reginfo;
787 	roff_t *fmap;
788 	u_int32_t flags;
789 {
790 	BH *bhp, *vbhp;
791 	DB_MPOOL_HASH *hp;
792 	DB_MSGBUF mb;
793 	MPOOL *c_mp;
794 	u_int32_t bucket;
795 
796 	c_mp = reginfo->primary;
797 	DB_MSGBUF_INIT(&mb);
798 	STAT_ULONG("Hash table last-checked", c_mp->last_checked);
799 	STAT_ULONG("Hash table LRU priority", c_mp->lru_priority);
800 	STAT_ULONG("Hash table LRU generation", c_mp->lru_generation);
801 	STAT_ULONG("Put counter", c_mp->put_counter);
802 
803 	/* Display the hash table list of BH's. */
804 	__db_msg(env,
805 	    "BH hash table (%lu hash slots)", (u_long)c_mp->htab_buckets);
806 	__db_msg(env, "bucket #: priority, I/O wait, [mutex]");
807 	__db_msg(env, "\tpageno, file, ref, LSN, address, priority, flags");
808 
809 	for (hp = R_ADDR(reginfo, c_mp->htab),
810 	    bucket = 0; bucket < c_mp->htab_buckets; ++hp, ++bucket) {
811 		MUTEX_READLOCK(env, hp->mtx_hash);
812 		if ((bhp = SH_TAILQ_FIRST(&hp->hash_bucket, __bh)) != NULL) {
813 			__db_msgadd(env, &mb,
814 			    "bucket %lu: %lu (%lu dirty)",
815 			    (u_long)bucket, (u_long)hp->hash_io_wait,
816 			    (u_long)atomic_read(&hp->hash_page_dirty));
817 			if (hp->hash_frozen != 0)
818 				__db_msgadd(env, &mb, "(MVCC %lu/%lu/%lu) ",
819 				    (u_long)hp->hash_frozen,
820 				    (u_long)hp->hash_thawed,
821 				    (u_long)hp->hash_frozen_freed);
822 			__mutex_print_debug_stats(
823 			    env, &mb, hp->mtx_hash, flags);
824 			DB_MSGBUF_FLUSH(env, &mb);
825 		}
826 		for (; bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, hq, __bh)) {
827 			__memp_print_bh(env, dbmp, NULL, bhp, fmap);
828 
829 			/* Print the version chain, if it exists. */
830 			for (vbhp = SH_CHAIN_PREV(bhp, vc, __bh);
831 			    vbhp != NULL;
832 			    vbhp = SH_CHAIN_PREV(vbhp, vc, __bh)) {
833 				__memp_print_bh(env, dbmp,
834 				    " prev:\t", vbhp, fmap);
835 			}
836 		}
837 		MUTEX_UNLOCK(env, hp->mtx_hash);
838 	}
839 #ifdef DIAGNOSTIC
840 	SH_TAILQ_FOREACH(bhp, &c_mp->free_frozen, hq, __bh) {
841 		__db_msg(env, "free frozen %lu pgno %lu mtx_buf %lu",
842 		    (u_long)R_OFFSET(dbmp->reginfo, bhp),
843 		    (u_long)bhp->pgno, (u_long)bhp->mtx_buf);
844 	}
845 #endif
846 
847 	return (0);
848 }
849 
850 /*
851  * __memp_print_bh --
852  *	Display a BH structure.
853  */
854 static void
__memp_print_bh(env,dbmp,prefix,bhp,fmap)855 __memp_print_bh(env, dbmp, prefix, bhp, fmap)
856 	ENV *env;
857 	DB_MPOOL *dbmp;
858 	const char *prefix;
859 	BH *bhp;
860 	roff_t *fmap;
861 {
862 	static const FN fn[] = {
863 		{ BH_CALLPGIN,		"callpgin" },
864 		{ BH_DIRTY,		"dirty" },
865 		{ BH_DIRTY_CREATE,	"created" },
866 		{ BH_DISCARD,		"discard" },
867 		{ BH_EXCLUSIVE,		"exclusive" },
868 		{ BH_FREED,		"freed" },
869 		{ BH_FROZEN,		"frozen" },
870 		{ BH_TRASH,		"trash" },
871 		{ BH_THAWED,		"thawed" },
872 		{ BH_UNREACHABLE,	"unreachable" },
873 		{ 0,			NULL }
874 	};
875 	DB_MSGBUF mb;
876 	int i;
877 
878 	DB_MSGBUF_INIT(&mb);
879 
880 	if (prefix != NULL)
881 		__db_msgadd(env, &mb, "%s", prefix);
882 	else
883 		__db_msgadd(env, &mb, "\t");
884 
885 	for (i = 0; i < FMAP_ENTRIES; ++i)
886 		if (fmap[i] == INVALID_ROFF || fmap[i] == bhp->mf_offset)
887 			break;
888 
889 	if (fmap[i] == INVALID_ROFF)
890 		__db_msgadd(env, &mb, "%5lu, %lu, ",
891 		    (u_long)bhp->pgno, (u_long)bhp->mf_offset);
892 	else
893 		__db_msgadd(
894 		    env, &mb, "%5lu, #%d, ", (u_long)bhp->pgno, i + 1);
895 
896 	__db_msgadd(env, &mb, "%2lu, %lu/%lu", (u_long)atomic_read(&bhp->ref),
897 	    F_ISSET(bhp, BH_FROZEN) ? 0 : (u_long)LSN(bhp->buf).file,
898 	    F_ISSET(bhp, BH_FROZEN) ? 0 : (u_long)LSN(bhp->buf).offset);
899 	if (bhp->td_off != INVALID_ROFF)
900 		__db_msgadd(env, &mb, " (@%lu/%lu 0x%x)",
901 		    (u_long)VISIBLE_LSN(env, bhp)->file,
902 		    (u_long)VISIBLE_LSN(env, bhp)->offset,
903 		    BH_OWNER(env, bhp)->txnid);
904 	__db_msgadd(env, &mb, ", %#08lx, %lu",
905 	    (u_long)R_OFFSET(dbmp->reginfo, bhp), (u_long)bhp->priority);
906 	__db_prflags(env, &mb, bhp->flags, fn, " (", ")");
907 	DB_MSGBUF_FLUSH(env, &mb);
908 }
909 
910 /*
911  * __memp_stat_wait --
912  *	Total hash bucket wait stats into the region.
913  */
914 static void
__memp_stat_wait(env,reginfo,mp,mstat,flags)915 __memp_stat_wait(env, reginfo, mp, mstat, flags)
916 	ENV *env;
917 	REGINFO *reginfo;
918 	MPOOL *mp;
919 	DB_MPOOL_STAT *mstat;
920 	u_int32_t flags;
921 {
922 	DB_MPOOL_HASH *hp;
923 	u_int32_t i;
924 	uintmax_t tmp_nowait, tmp_wait;
925 
926 	mstat->st_hash_max_wait = 0;
927 	hp = R_ADDR(reginfo, mp->htab);
928 	for (i = 0; i < mp->htab_buckets; i++, hp++) {
929 		__mutex_set_wait_info(
930 		    env, hp->mtx_hash, &tmp_wait, &tmp_nowait);
931 		mstat->st_hash_nowait += tmp_nowait;
932 		mstat->st_hash_wait += tmp_wait;
933 		if (tmp_wait > mstat->st_hash_max_wait) {
934 			mstat->st_hash_max_wait = tmp_wait;
935 			mstat->st_hash_max_nowait = tmp_nowait;
936 		}
937 		if (LF_ISSET(DB_STAT_CLEAR |
938 		    DB_STAT_SUBSYSTEM) == DB_STAT_CLEAR)
939 			__mutex_clear(env, hp->mtx_hash);
940 
941 		mstat->st_io_wait += hp->hash_io_wait;
942 		mstat->st_mvcc_frozen += hp->hash_frozen;
943 		mstat->st_mvcc_thawed += hp->hash_thawed;
944 		mstat->st_mvcc_freed += hp->hash_frozen_freed;
945 		if (LF_ISSET(DB_STAT_CLEAR)) {
946 			hp->hash_io_wait = 0;
947 			hp->hash_frozen = 0;
948 			hp->hash_thawed = 0;
949 			hp->hash_frozen_freed = 0;
950 		}
951 	}
952 }
953 
954 #else /* !HAVE_STATISTICS */
955 
956 int
__memp_stat_pp(dbenv,gspp,fspp,flags)957 __memp_stat_pp(dbenv, gspp, fspp, flags)
958 	DB_ENV *dbenv;
959 	DB_MPOOL_STAT **gspp;
960 	DB_MPOOL_FSTAT ***fspp;
961 	u_int32_t flags;
962 {
963 	COMPQUIET(gspp, NULL);
964 	COMPQUIET(fspp, NULL);
965 	COMPQUIET(flags, 0);
966 
967 	return (__db_stat_not_built(dbenv->env));
968 }
969 
970 int
__memp_stat_print_pp(dbenv,flags)971 __memp_stat_print_pp(dbenv, flags)
972 	DB_ENV *dbenv;
973 	u_int32_t flags;
974 {
975 	COMPQUIET(flags, 0);
976 
977 	return (__db_stat_not_built(dbenv->env));
978 }
979 #endif
980 
981 /*
982  * __memp_stat_hash --
983  *	Total hash bucket stats (other than mutex wait) into the region.
984  *
985  * PUBLIC: void __memp_stat_hash __P((REGINFO *, MPOOL *, u_int32_t *));
986  */
987 void
__memp_stat_hash(reginfo,mp,dirtyp)988 __memp_stat_hash(reginfo, mp, dirtyp)
989 	REGINFO *reginfo;
990 	MPOOL *mp;
991 	u_int32_t *dirtyp;
992 {
993 	DB_MPOOL_HASH *hp;
994 	u_int32_t dirty, i;
995 
996 	hp = R_ADDR(reginfo, mp->htab);
997 	for (i = 0, dirty = 0; i < mp->htab_buckets; i++, hp++)
998 		dirty += (u_int32_t)atomic_read(&hp->hash_page_dirty);
999 	*dirtyp = dirty;
1000 }
1001