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