1 // Part of the ht://Dig package <http://www.htdig.org/>
2 // Copyright (c) 1999-2004 The ht://Dig Group
3 // For copyright details, see the file COPYING in your distribution
4 // or the GNU Library General Public License (LGPL) version 2 or later
5 // <http://www.gnu.org/copyleft/lgpl.html>
6 //
7 /*-
8 * See the file LICENSE for redistribution information.
9 *
10 * Copyright (c) 1996, 1997, 1998, 1999
11 * Sleepycat Software. All rights reserved.
12 */
13
14 #include "db_config.h"
15 #include "htconfig.h"
16
17 #ifndef lint
18 static const char copyright[] =
19 "@(#) Copyright (c) 1996, 1997, 1998, 1999\n\
20 Sleepycat Software Inc. All rights reserved.\n";
21 static const char sccsid[] = "@(#)db_stat.c 11.2 (Sleepycat) 9/14/99";
22 #endif
23
24 #ifndef NO_SYSTEM_INCLUDES
25 #include <sys/types.h>
26
27 #if TIME_WITH_SYS_TIME
28 #include <sys/time.h>
29 #include <time.h>
30 #else
31 #if HAVE_SYS_TIME_H
32 #include <sys/time.h>
33 #else
34 #include <time.h>
35 #endif
36 #endif
37
38 #include <ctype.h>
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #ifdef HAVE_GETOPT_H
44 #include <getopt.h>
45 #endif /* HAVE_GETOPT_H */
46 #endif
47
48 extern "C" {
49 #include "db_int.h"
50 #include "db_page.h"
51 #include "db_shash.h"
52 #include "lock.h"
53 #include "mp.h"
54 }
55
56 #include "WordDBInfo.h"
57 #include "WordDBCompress.h"
58 #include "WordContext.h"
59
60 #define PCT(f, t, pgsize) \
61 ((t) == 0 ? 0 : \
62 (((double)(((t) * (pgsize)) - (f)) / ((t) * (pgsize))) * 100))
63
64 typedef enum { T_NOTSET, T_DB, T_ENV, T_LOCK, T_LOG, T_MPOOL, T_TXN } test_t;
65
66 int argcheck __P((char *, const char *));
67 int btree_stats __P((DB_ENV *, DB *));
68 int db_init __P((char *, test_t));
69 void dl __P((const char *, u_long));
70 void dl_bytes __P((const char *, u_long, u_long, u_long));
71 int env_stats __P((DB_ENV *));
72 int hash_stats __P((DB_ENV *, DB *));
73 int lock_ok __P((char *));
74 int lock_stats __P((DB_ENV *));
75 int log_stats __P((DB_ENV *));
76 int main __P((int, char *[]));
77 int mpool_ok __P((char *));
78 int mpool_stats __P((DB_ENV *));
79 void onint __P((int));
80 void prflags __P((u_int32_t, const FN *));
81 int queue_stats __P((DB_ENV *, DB *));
82 void siginit __P((void));
83 int txn_compare __P((const void *, const void *));
84 int txn_stats __P((DB_ENV *));
85 void usage __P((void));
86
87 DB_ENV *dbenv;
88 int interrupted;
89 char *stats_internal;
90 const char
91 *progname = "htdb_stat"; /* Program name. */
92
93 int
main(int argc,char * argv[])94 main(int argc, char *argv[])
95 {
96 extern char *optarg;
97 extern int optind;
98 DB *dbp;
99 test_t ttype;
100 int ch, d_close, e_close, exitval, Nflag, ret;
101 char *db, *home, *subdb;
102 int compress = 0;
103 int wordlist = 0;
104 Configuration *config = 0;
105
106 dbp = NULL;
107 ttype = T_NOTSET;
108 d_close = e_close = exitval = Nflag = 0;
109 db = home = subdb = NULL;
110 while ((ch = getopt(argc, argv, "C:cd:eh:lM:mNs:tzW")) != EOF)
111 switch (ch) {
112 case 'C':
113 ttype = T_LOCK;
114 if (!argcheck(stats_internal = optarg, "Acflmo"))
115 usage();
116 break;
117 case 'c':
118 ttype = T_LOCK;
119 break;
120 case 'd':
121 db = optarg;
122 ttype = T_DB;
123 break;
124 case 'e':
125 ttype = T_ENV;
126 break;
127 case 'h':
128 home = optarg;
129 break;
130 case 'l':
131 ttype = T_LOG;
132 break;
133 case 'M':
134 ttype = T_MPOOL;
135 if (!argcheck(stats_internal = optarg, "Ahlm"))
136 usage();
137 break;
138 case 'm':
139 ttype = T_MPOOL;
140 break;
141 case 'N':
142 Nflag = 1;
143 break;
144 case 's':
145 subdb = optarg;
146 ttype = T_DB;
147 break;
148 case 't':
149 ttype = T_TXN;
150 break;
151 case 'z':
152 compress = DB_COMPRESS;
153 break;
154 case 'W':
155 wordlist = 1;
156 break;
157 case '?':
158 default:
159 usage();
160 }
161 argc -= optind;
162 argv += optind;
163
164 switch (ttype) {
165 case T_DB:
166 if (db == NULL)
167 usage();
168 break;
169 case T_NOTSET:
170 usage();
171 /* NOTREACHED */
172 default:
173 break;
174 }
175
176 /* Handle possible interruptions. */
177 siginit();
178
179 if(wordlist && compress) {
180 static ConfigDefaults defaults[] = {
181 { "wordlist_wordkey_description", "Word/DocID 32/Flag 8/Location 16"},
182 { "wordlist_env_skip", "true"},
183 { 0, 0, 0 }
184 };
185 config = WordContext::Initialize(defaults);
186 }
187
188 /*
189 * Create an environment object and initialize it for error
190 * reporting.
191 */
192 if ((ret = CDB_db_env_create(&dbenv, 0)) != 0) {
193 fprintf(stderr,
194 "%s: CDB_db_env_create: %s\n", progname, CDB_db_strerror(ret));
195 exit (1);
196 }
197
198 dbenv->set_errfile(dbenv, stderr);
199 dbenv->set_errpfx(dbenv, progname);
200 if(wordlist && compress) dbenv->mp_cmpr_info = (new WordDBCompress)->CmprInfo();
201
202 /* Optionally turn mutexes and the panic checks off. */
203 if (Nflag) {
204 if ((ret = dbenv->set_mutexlocks(dbenv, 0)) != 0) {
205 dbenv->err(dbenv, ret, "set_mutexlocks");
206 goto shutdown;
207 }
208 if ((ret = dbenv->set_panic(dbenv, 0)) != 0) {
209 dbenv->err(dbenv, ret, "set_panic");
210 goto shutdown;
211 }
212 }
213
214 /* Initialize the environment. */
215 if (db_init(home, ttype) != 0)
216 goto shutdown;
217
218 e_close = 1;
219
220 switch (ttype) {
221 case T_DB:
222 /* Create the DB object and open the file. */
223 if ((ret = CDB_db_create(&dbp, dbenv, 0)) != 0) {
224 dbenv->err(dbenv, ret, "CDB_db_create");
225 goto shutdown;
226 }
227 if ((ret =
228 dbp->open(dbp, db, subdb, DB_UNKNOWN, (DB_RDONLY | compress), 0)) != 0) {
229 dbp->err(dbp, ret, "open: %s", db);
230 goto shutdown;
231 }
232 d_close = 1;
233 switch (dbp->type) {
234 case DB_BTREE:
235 case DB_RECNO:
236 if (btree_stats(dbenv, dbp))
237 goto shutdown;
238 break;
239 case DB_HASH:
240 if (hash_stats(dbenv, dbp))
241 goto shutdown;
242 break;
243 case DB_QUEUE:
244 if (queue_stats(dbenv, dbp))
245 goto shutdown;
246 break;
247 case DB_UNKNOWN:
248 abort(); /* Impossible. */
249 /* NOTREACHED */
250 }
251 break;
252 case T_ENV:
253 if (env_stats(dbenv))
254 exitval = 1;
255 break;
256 case T_LOCK:
257 if (lock_stats(dbenv))
258 exitval = 1;
259 break;
260 case T_LOG:
261 if (log_stats(dbenv))
262 exitval = 1;
263 break;
264 case T_MPOOL:
265 if (mpool_stats(dbenv))
266 exitval = 1;
267 break;
268 case T_TXN:
269 if (txn_stats(dbenv))
270 exitval = 1;
271 break;
272 case T_NOTSET:
273 abort(); /* Impossible. */
274 /* NOTREACHED */
275 }
276
277 if (0) {
278 shutdown: exitval = 1;
279 }
280 if (d_close && (ret = dbp->close(dbp, 0)) != 0) {
281 exitval = 1;
282 dbp->err(dbp, ret, "close");
283 }
284 if (e_close && (ret = dbenv->close(dbenv, 0)) != 0) {
285 exitval = 1;
286 fprintf(stderr,
287 "%s: dbenv->close: %s\n", progname, CDB_db_strerror(ret));
288 }
289
290 if (interrupted) {
291 (void)signal(interrupted, SIG_DFL);
292 (void)raise(interrupted);
293 /* NOTREACHED */
294 }
295
296 if(config) {
297 WordContext::Finish();
298 delete config;
299 }
300
301 return (exitval);
302 }
303
304 /*
305 * env_stats --
306 * Display environment statistics.
307 */
308 int
env_stats(DB_ENV * dbenvp)309 env_stats(DB_ENV *dbenvp)
310 {
311 REGENV renv;
312 REGION *rp, regs[1024];
313 int n, ret;
314 const char *lable;
315
316 n = sizeof(regs) / sizeof(regs[0]);
317 if ((ret = CDB___db_e_stat(dbenvp, &renv, regs, &n)) != 0) {
318 dbenvp->err(dbenvp, ret, "CDB___db_e_stat");
319 return (1);
320 }
321
322 printf("%d.%d.%d\tEnvironment version.\n",
323 renv.majver, renv.minver, renv.patch);
324 printf("%lx\tMagic number.\n", (u_long)renv.magic);
325 printf("%d\tPanic value.\n", renv.panic);
326
327 /* Adjust the reference count for us... */
328 printf("%d\tReferences.\n", renv.refcnt - 1);
329
330 dl("Locks granted without waiting.\n",
331 (u_long)renv.mutex.mutex_set_nowait);
332 dl("Locks granted after waiting.\n",
333 (u_long)renv.mutex.mutex_set_wait);
334
335 while (n > 0) {
336 printf("%s\n", DB_LINE);
337 rp = ®s[--n];
338 switch (rp->id) {
339 case REG_ID_ENV:
340 lable = "Environment";
341 break;
342 case REG_ID_LOCK:
343 lable = "Lock";
344 break;
345 case REG_ID_LOG:
346 lable = "Log";
347 break;
348 case REG_ID_MPOOL:
349 lable = "Mpool";
350 break;
351 case REG_ID_TXN:
352 lable = "Txn";
353 break;
354 default:
355 lable = "Unknown";
356 break;
357 }
358 printf("%s Region: %d.\n", lable, rp->id);
359 dl_bytes("Size.\n", (u_long)0, (u_long)0, (u_long)rp->size);
360 printf("%d\tSegment ID.\n", rp->segid);
361 dl("Locks granted without waiting.\n",
362 (u_long)rp->mutex.mutex_set_nowait);
363 dl("Locks granted after waiting.\n",
364 (u_long)rp->mutex.mutex_set_wait);
365 }
366
367 return (0);
368 }
369
370 /*
371 * btree_stats --
372 * Display btree/recno statistics.
373 */
374 int
btree_stats(DB_ENV * dbenvp,DB * dbp)375 btree_stats(DB_ENV *dbenvp, DB *dbp)
376 {
377 static const FN fn[] = {
378 { BTM_DUP, "duplicates" },
379 { BTM_FIXEDLEN, "fixed-length" },
380 { BTM_RECNO, "recno" },
381 { BTM_RECNUM, "record-numbers" },
382 { BTM_RENUMBER, "renumber" },
383 { BTM_SUBDB, "subdatabases" },
384 { 0, NULL }
385 };
386 DB_BTREE_STAT *sp;
387 int ret;
388
389 COMPQUIET(dbenvp, NULL);
390
391 if ((ret = dbp->stat(dbp, &sp, NULL, 0)) != 0) {
392 dbp->err(dbp, ret, "dbp->stat");
393 return (1);
394 }
395
396 printf("%lx\tBtree magic number.\n", (u_long)sp->bt_magic);
397 printf("%lu\tBtree version number.\n", (u_long)sp->bt_version);
398 prflags(sp->bt_metaflags, fn);
399 if (dbp->type == DB_BTREE) {
400 #ifdef NOT_IMPLEMENTED
401 dl("Maximum keys per-page.\n", (u_long)sp->bt_maxkey);
402 #endif
403 dl("Minimum keys per-page.\n", (u_long)sp->bt_minkey);
404 }
405 if (dbp->type == DB_RECNO) {
406 dl("Fixed-length record size.\n", (u_long)sp->bt_re_len);
407 if (isprint(sp->bt_re_pad))
408 printf("%c\tFixed-length record pad.\n",
409 (int)sp->bt_re_pad);
410 else
411 printf("0x%x\tFixed-length record pad.\n",
412 (int)sp->bt_re_pad);
413 }
414 dl("Underlying database page size.\n", (u_long)sp->bt_pagesize);
415 dl("Number of levels in the tree.\n", (u_long)sp->bt_levels);
416 dl("Number of keys in the tree.\n", (u_long)sp->bt_nrecs);
417
418 dl("Number of tree internal pages.\n", (u_long)sp->bt_int_pg);
419 dl("Number of bytes free in tree internal pages",
420 (u_long)sp->bt_int_pgfree);
421 printf(" (%.0f%% ff).\n",
422 PCT(sp->bt_int_pgfree, sp->bt_int_pg, sp->bt_pagesize));
423
424 dl("Number of tree leaf pages.\n", (u_long)sp->bt_leaf_pg);
425 dl("Number of bytes free in tree leaf pages",
426 (u_long)sp->bt_leaf_pgfree);
427 printf(" (%.0f%% ff).\n",
428 PCT(sp->bt_leaf_pgfree, sp->bt_leaf_pg, sp->bt_pagesize));
429
430 dl("Number of tree duplicate pages.\n", (u_long)sp->bt_dup_pg);
431 dl("Number of bytes free in tree duplicate pages",
432 (u_long)sp->bt_dup_pgfree);
433 printf(" (%.0f%% ff).\n",
434 PCT(sp->bt_dup_pgfree, sp->bt_dup_pg, sp->bt_pagesize));
435
436 dl("Number of tree overflow pages.\n", (u_long)sp->bt_over_pg);
437 dl("Number of bytes free in tree overflow pages",
438 (u_long)sp->bt_over_pgfree);
439 printf(" (%.0f%% ff).\n",
440 PCT(sp->bt_over_pgfree, sp->bt_over_pg, sp->bt_pagesize));
441
442 dl("Number of pages on the free list.\n", (u_long)sp->bt_free);
443
444 free(sp);
445
446 return (0);
447 }
448
449 /*
450 * hash_stats --
451 * Display hash statistics.
452 */
453 int
hash_stats(DB_ENV * dbenvp,DB * dbp)454 hash_stats(DB_ENV *dbenvp, DB *dbp)
455 {
456 static const FN fn[] = {
457 { DB_HASH_DUP, "duplicates" },
458 { DB_HASH_SUBDB,"subdatabases" },
459 { 0, NULL }
460 };
461 DB_HASH_STAT *sp;
462 int ret;
463
464 COMPQUIET(dbenvp, NULL);
465
466 if ((ret = dbp->stat(dbp, &sp, NULL, 0)) != 0) {
467 dbp->err(dbp, ret, "dbp->stat");
468 return (1);
469 }
470
471 printf("%lx\tHash magic number.\n", (u_long)sp->hash_magic);
472 printf("%lu\tHash version number.\n", (u_long)sp->hash_version);
473 prflags(sp->hash_metaflags, fn);
474 dl("Underlying database page size.\n", (u_long)sp->hash_pagesize);
475 dl("Number of keys in the database.\n", (u_long)sp->hash_nrecs);
476
477 dl("Number of hash buckets.\n", (u_long)sp->hash_buckets);
478 dl("Number of bytes free on bucket pages", (u_long)sp->hash_bfree);
479 printf(" (%.0f%% ff).\n",
480 PCT(sp->hash_bfree, sp->hash_buckets, sp->hash_pagesize));
481
482 dl("Number of overflow pages.\n", (u_long)sp->hash_bigpages);
483 dl("Number of bytes free in overflow pages",
484 (u_long)sp->hash_big_bfree);
485 printf(" (%.0f%% ff).\n",
486 PCT(sp->hash_big_bfree, sp->hash_bigpages, sp->hash_pagesize));
487
488 dl("Number of bucket overflow pages.\n", (u_long)sp->hash_overflows);
489 dl("Number of bytes free in bucket overflow pages",
490 (u_long)sp->hash_ovfl_free);
491 printf(" (%.0f%% ff).\n",
492 PCT(sp->hash_ovfl_free, sp->hash_overflows, sp->hash_pagesize));
493
494 dl("Number of duplicate pages.\n", (u_long)sp->hash_dup);
495 dl("Number of bytes free in duplicate pages",
496 (u_long)sp->hash_dup_free);
497 printf(" (%.0f%% ff).\n",
498 PCT(sp->hash_dup_free, sp->hash_dup, sp->hash_pagesize));
499
500 dl("Number of pages on the free list.\n", (u_long)sp->hash_free);
501
502 return (0);
503 }
504
505 /*
506 * queue_stats --
507 * Display queue statistics.
508 */
509 int
queue_stats(DB_ENV * dbenvp,DB * dbp)510 queue_stats(DB_ENV *dbenvp, DB *dbp)
511 {
512 DB_QUEUE_STAT *sp;
513 int ret;
514
515 COMPQUIET(dbenvp, NULL);
516
517 if ((ret = dbp->stat(dbp, &sp, NULL, 0)) != 0) {
518 dbp->err(dbp, ret, "dbp->stat");
519 return (1);
520 }
521
522 printf("%lx\tQueue magic number.\n", (u_long)sp->qs_magic);
523 printf("%lu\tQueue version number.\n", (u_long)sp->qs_version);
524 dl("Fixed-length record size.\n", (u_long)sp->qs_re_len);
525 if (isprint(sp->qs_re_pad))
526 printf("%c\tFixed-length record pad.\n", (int)sp->qs_re_pad);
527 else
528 printf("0x%x\tFixed-length record pad.\n", (int)sp->qs_re_pad);
529 dl("Underlying tree page size.\n", (u_long)sp->qs_pagesize);
530 dl("Number of records in the database.\n", (u_long)sp->qs_nrecs);
531 dl("Number of database pages.\n", (u_long)sp->qs_pages);
532 dl("Number of bytes free in database pages", (u_long)sp->qs_pgfree);
533 printf(" (%.0f%% ff).\n",
534 PCT(sp->qs_pgfree, sp->qs_pages, sp->qs_pagesize));
535 printf("%lu\tFirst undeleted record.\n", (u_long)sp->qs_first_recno);
536 printf(
537 "%lu\tLast allocated record number.\n", (u_long)sp->qs_cur_recno);
538 printf("%lu\tStart offset.\n", (u_long)sp->qs_start);
539
540 return (0);
541 }
542
543 /*
544 * lock_stats --
545 * Display lock statistics.
546 */
547 int
lock_stats(DB_ENV * dbenvp)548 lock_stats(DB_ENV *dbenvp)
549 {
550 DB_LOCK_STAT *sp;
551 int ret;
552
553 if (stats_internal != NULL) {
554 CDB___lock_dump_region(dbenvp, stats_internal, stdout);
555 return (0);
556 }
557
558 if ((ret = CDB_lock_stat(dbenvp, &sp, NULL)) != 0) {
559 dbenvp->err(dbenvp, ret, NULL);
560 return (1);
561 }
562
563 dl("Last allocated locker ID.\n", (u_long)sp->st_lastid);
564 dl("Number of lock modes.\n", (u_long)sp->st_nmodes);
565 dl("Maximum number of locks possible.\n", (u_long)sp->st_maxlocks);
566 dl("Current lockers.\n", (u_long)sp->st_nlockers);
567 dl("Maximum current lockers.\n", (u_long)sp->st_nlockers);
568 dl("Number of lock requests.\n", (u_long)sp->st_nrequests);
569 dl("Number of lock releases.\n", (u_long)sp->st_nreleases);
570 dl("Number of lock conflicts.\n", (u_long)sp->st_nconflicts);
571 dl("Number of deadlocks.\n", (u_long)sp->st_ndeadlocks);
572 dl_bytes("Lock region size.\n",
573 (u_long)0, (u_long)0, (u_long)sp->st_regsize);
574 dl("The number of region locks granted without waiting.\n",
575 (u_long)sp->st_region_nowait);
576 dl("The number of region locks granted after waiting.\n",
577 (u_long)sp->st_region_wait);
578
579 return (0);
580 }
581
582 /*
583 * log_stats --
584 * Display log statistics.
585 */
586 int
log_stats(DB_ENV * dbenvp)587 log_stats(DB_ENV *dbenvp)
588 {
589 DB_LOG_STAT *sp;
590 int ret;
591
592 if ((ret = CDB_log_stat(dbenvp, &sp, NULL)) != 0) {
593 dbenvp->err(dbenvp, ret, NULL);
594 return (1);
595 }
596
597 printf("%lx\tLog magic number.\n", (u_long)sp->st_magic);
598 printf("%lu\tLog version number.\n", (u_long)sp->st_version);
599 dl_bytes("Log region size.\n",
600 (u_long)0, (u_long)0, (u_long)sp->st_regsize);
601 dl_bytes("Log record cache size.\n",
602 (u_long)0, (u_long)0, (u_long)sp->st_lg_bsize);
603 printf("%#o\tLog file mode.\n", sp->st_mode);
604 if (sp->st_lg_max % MEGABYTE == 0)
605 printf("%luMb\tLog file size.\n",
606 (u_long)sp->st_lg_max / MEGABYTE);
607 else if (sp->st_lg_max % 1024 == 0)
608 printf("%luKb\tLog file size.\n", (u_long)sp->st_lg_max / 1024);
609 else
610 printf("%lu\tLog file size.\n", (u_long)sp->st_lg_max);
611 dl_bytes("Log bytes written.\n",
612 (u_long)0, (u_long)sp->st_w_mbytes, (u_long)sp->st_w_bytes);
613 dl_bytes("Log bytes written since last checkpoint.\n",
614 (u_long)0, (u_long)sp->st_wc_mbytes, (u_long)sp->st_wc_bytes);
615 dl("Total log file writes.\n", (u_long)sp->st_wcount);
616 dl("Total log file write due to overflow.\n",
617 (u_long)sp->st_wcount_fill);
618 dl("Total log file flushes.\n", (u_long)sp->st_scount);
619 printf("%lu\tCurrent log file number.\n", (u_long)sp->st_cur_file);
620 printf("%lu\tCurrent log file offset.\n", (u_long)sp->st_cur_offset);
621 dl("The number of region locks granted without waiting.\n",
622 (u_long)sp->st_region_nowait);
623 dl("The number of region locks granted after waiting.\n",
624 (u_long)sp->st_region_wait);
625
626 return (0);
627 }
628
629 /*
630 * mpool_stats --
631 * Display mpool statistics.
632 */
633 int
mpool_stats(DB_ENV * dbenvp)634 mpool_stats(DB_ENV *dbenvp)
635 {
636 DB_MPOOL_FSTAT **fsp;
637 DB_MPOOL_STAT *gsp;
638 int ret;
639
640 if (stats_internal != NULL) {
641 CDB___memp_dump_region(dbenvp, stats_internal, stdout);
642 return (1);
643 }
644
645 if ((ret = CDB_memp_stat(dbenvp, &gsp, &fsp, NULL)) != 0) {
646 dbenvp->err(dbenvp, ret, NULL);
647 return (1);
648 }
649
650 dl("Pool region size.\n", (u_long)gsp->st_regsize);
651 dl_bytes("Cache size.\n",
652 (u_long)gsp->st_gbytes, (u_long)0, (u_long)gsp->st_bytes);
653 dl("Requested pages found in the cache", (u_long)gsp->st_cache_hit);
654 if (gsp->st_cache_hit + gsp->st_cache_miss != 0)
655 printf(" (%.0f%%)", ((double)gsp->st_cache_hit /
656 (gsp->st_cache_hit + gsp->st_cache_miss)) * 100);
657 printf(".\n");
658 dl("Requested pages mapped into the process' address space.\n",
659 (u_long)gsp->st_map);
660 dl("Requested pages not found in the cache.\n",
661 (u_long)gsp->st_cache_miss);
662 dl("Pages created in the cache.\n", (u_long)gsp->st_page_create);
663 dl("Pages read into the cache.\n", (u_long)gsp->st_page_in);
664 dl("Pages written from the cache to the backing file.\n",
665 (u_long)gsp->st_page_out);
666 dl("Clean pages forced from the cache.\n",
667 (u_long)gsp->st_ro_evict);
668 dl("Dirty pages forced from the cache.\n",
669 (u_long)gsp->st_rw_evict);
670 dl("Dirty buffers written by trickle-sync thread.\n",
671 (u_long)gsp->st_page_trickle);
672 dl("Current clean buffer count.\n",
673 (u_long)gsp->st_page_clean);
674 dl("Current dirty buffer count.\n",
675 (u_long)gsp->st_page_dirty);
676 dl("Number of hash buckets used for page location.\n",
677 (u_long)gsp->st_hash_buckets);
678 dl("Total number of times hash chains searched for a page.\n",
679 (u_long)gsp->st_hash_searches);
680 dl("The longest hash chain searched for a page.\n",
681 (u_long)gsp->st_hash_longest);
682 dl("Total number of hash buckets examined for page location.\n",
683 (u_long)gsp->st_hash_examined);
684 dl("The number of region locks granted without waiting.\n",
685 (u_long)gsp->st_region_nowait);
686 dl("The number of region locks granted after waiting.\n",
687 (u_long)gsp->st_region_wait);
688
689 for (; fsp != NULL && *fsp != NULL; ++fsp) {
690 printf("%s\n", DB_LINE);
691 printf("Pool File: %s\n", (*fsp)->file_name);
692 dl("Page size.\n", (u_long)(*fsp)->st_pagesize);
693 dl("Requested pages found in the cache",
694 (u_long)(*fsp)->st_cache_hit);
695 if ((*fsp)->st_cache_hit + (*fsp)->st_cache_miss != 0)
696 printf(" (%.0f%%)", ((double)(*fsp)->st_cache_hit /
697 ((*fsp)->st_cache_hit + (*fsp)->st_cache_miss)) *
698 100);
699 printf(".\n");
700 dl("Requested pages mapped into the process' address space.\n",
701 (u_long)(*fsp)->st_map);
702 dl("Requested pages not found in the cache.\n",
703 (u_long)(*fsp)->st_cache_miss);
704 dl("Pages created in the cache.\n",
705 (u_long)(*fsp)->st_page_create);
706 dl("Pages read into the cache.\n",
707 (u_long)(*fsp)->st_page_in);
708 dl("Pages written from the cache to the backing file.\n",
709 (u_long)(*fsp)->st_page_out);
710 }
711
712 return (0);
713 }
714
715 /*
716 * txn_stats --
717 * Display transaction statistics.
718 */
719 int
txn_stats(DB_ENV * dbenvp)720 txn_stats(DB_ENV *dbenvp)
721 {
722 DB_TXN_STAT *sp;
723 u_int32_t i;
724 int ret;
725 const char *p;
726
727 if ((ret = CDB_txn_stat(dbenvp, &sp, NULL)) != 0) {
728 dbenvp->err(dbenvp, ret, NULL);
729 return (1);
730 }
731
732 p = sp->st_last_ckp.file == 0 ?
733 "No checkpoint LSN." : "File/offset for last checkpoint LSN.";
734 printf("%lu/%lu\t%s\n",
735 (u_long)sp->st_last_ckp.file, (u_long)sp->st_last_ckp.offset, p);
736 p = sp->st_pending_ckp.file == 0 ?
737 "No pending checkpoint LSN." :
738 "File/offset for last pending checkpoint LSN.";
739 printf("%lu/%lu\t%s\n",
740 (u_long)sp->st_pending_ckp.file,
741 (u_long)sp->st_pending_ckp.offset, p);
742 if (sp->st_time_ckp == 0)
743 printf("0\tNo checkpoint timestamp.\n");
744 else
745 printf("%.24s\tCheckpoint timestamp.\n",
746 ctime(&sp->st_time_ckp));
747 printf("%lx\tLast transaction ID allocated.\n",
748 (u_long)sp->st_last_txnid);
749 dl("Maximum number of active transactions possible.\n",
750 (u_long)sp->st_maxtxns);
751 dl("Active transactions.\n", (u_long)sp->st_nactive);
752 dl("Maximum active transactions.\n", (u_long)sp->st_maxnactive);
753 dl("Number of transactions begun.\n", (u_long)sp->st_nbegins);
754 dl("Number of transactions aborted.\n", (u_long)sp->st_naborts);
755 dl("Number of transactions committed.\n", (u_long)sp->st_ncommits);
756 dl_bytes("Transaction region size.\n",
757 (u_long)0, (u_long)0, (u_long)sp->st_regsize);
758 dl("The number of region locks granted without waiting.\n",
759 (u_long)sp->st_region_nowait);
760 dl("The number of region locks granted after waiting.\n",
761 (u_long)sp->st_region_wait);
762 qsort(sp->st_txnarray,
763 sp->st_nactive, sizeof(sp->st_txnarray[0]), txn_compare);
764 for (i = 0; i < sp->st_nactive; ++i)
765 printf("\tid: %lx; initial LSN file/offest %lu/%lu\n",
766 (u_long)sp->st_txnarray[i].txnid,
767 (u_long)sp->st_txnarray[i].lsn.file,
768 (u_long)sp->st_txnarray[i].lsn.offset);
769
770 return (0);
771 }
772
773 int
txn_compare(const void * a1,const void * b1)774 txn_compare(const void *a1, const void *b1)
775 {
776 const DB_TXN_ACTIVE *a, *b;
777
778 a = (DB_TXN_ACTIVE*)a1;
779 b = (DB_TXN_ACTIVE*)b1;
780
781 if (a->txnid > b->txnid)
782 return (1);
783 if (a->txnid < b->txnid)
784 return (-1);
785 return (0);
786 }
787
788 /*
789 * dl --
790 * Display a big value.
791 */
792 void
dl(const char * msg,u_long value)793 dl(const char *msg, u_long value)
794 {
795 /*
796 * Two formats: if less than 10 million, display as the number, if
797 * greater than 10 million display as ###M.
798 */
799 if (value < 10000000)
800 printf("%lu\t%s", value, msg);
801 else
802 printf("%luM\t%s", value / 1000000, msg);
803 }
804
805 /*
806 * dl_bytes --
807 * Display a big number of bytes.
808 */
809 void
dl_bytes(const char * msg,u_long gbytes,u_long mbytes,u_long bytes)810 dl_bytes(const char *msg, u_long gbytes, u_long mbytes, u_long bytes)
811 {
812 const char *sep;
813
814 while (bytes > MEGABYTE) {
815 ++mbytes;
816 bytes -= MEGABYTE;
817 }
818 while (mbytes > GIGABYTE / MEGABYTE) {
819 ++gbytes;
820 --mbytes;
821 }
822
823 sep = "";
824 if (gbytes > 0) {
825 printf("%luGB", gbytes);
826 sep = " ";
827 }
828 if (mbytes > 0) {
829 printf("%s%luMB", sep, bytes);
830 sep = " ";
831 }
832 if (bytes > 1024) {
833 printf("%s%luKB", sep, bytes / 1024);
834 bytes %= 1024;
835 sep = " ";
836 }
837 if (bytes > 0)
838 printf("%s%lu", sep, bytes);
839 printf("\t%s", msg);
840 }
841
842 /*
843 * prflags --
844 * Print out flag values.
845 */
846 void
prflags(u_int32_t flags,const FN * fnp)847 prflags(u_int32_t flags, const FN *fnp)
848 {
849 const char *sep;
850
851 sep = "\t";
852 printf("Flags:");
853 for (; fnp->mask != 0; ++fnp)
854 if (fnp->mask & flags) {
855 printf("%s%s", sep, fnp->name);
856 sep = ", ";
857 }
858 printf("\n");
859 }
860
861 /*
862 * db_init --
863 * Initialize the environment.
864 */
865 int
db_init(char * home,test_t ttype)866 db_init(char *home, test_t ttype)
867 {
868 u_int32_t flags;
869 int ret;
870
871 /*
872 * Try and use the shared memory pool region when reporting statistics
873 * on the DB databases, so our information is as up-to-date as possible,
874 * even if the mpool cache hasn't been flushed.
875 */
876 flags = DB_USE_ENVIRON;
877 switch (ttype) {
878 case T_ENV:
879 break;
880 case T_DB:
881 case T_MPOOL:
882 LF_SET(DB_INIT_MPOOL);
883 break;
884 case T_LOCK:
885 LF_SET(DB_INIT_LOCK);
886 break;
887 case T_LOG:
888 LF_SET(DB_INIT_LOG);
889 break;
890 case T_TXN:
891 LF_SET(DB_INIT_TXN);
892 break;
893 case T_NOTSET:
894 abort();
895 /* NOTREACHED */
896 }
897
898 /*
899 * If that fails, and we're trying to look at a shared region, it's
900 * a hard failure.
901 */
902 if ((ret = dbenv->open(dbenv, home, NULL, flags, 0)) == 0)
903 return (0);
904 if (ttype != T_DB) {
905 dbenv->err(dbenv, ret, "open");
906 return (1);
907 }
908
909 /*
910 * We're trying to look at a database.
911 *
912 * An environment is required because we may be trying to look at
913 * databases in directories other than the current one. We could
914 * avoid using an environment iff the -h option wasn't specified,
915 * but that seems like more work than it's worth.
916 *
917 *
918 * No environment exists (or, at least no environment that includes
919 * an mpool region exists). Create one, but make it private so that
920 * no files are actually created.
921 */
922 LF_SET(DB_CREATE | DB_PRIVATE);
923 if ((ret = dbenv->open(dbenv, home, NULL, flags, 0)) == 0)
924 return (0);
925
926 /* An environment is required. */
927 dbenv->err(dbenv, ret, "open");
928 return (1);
929 }
930
931 /*
932 * argcheck --
933 * Return if argument flags are okay.
934 */
935 int
argcheck(char * arg,const char * ok_args)936 argcheck(char *arg, const char *ok_args)
937 {
938 for (; *arg != '\0'; ++arg)
939 if (strchr(ok_args, *arg) == NULL)
940 return (0);
941 return (1);
942 }
943
944 /*
945 * siginit --
946 * Initialize the set of signals for which we want to clean up.
947 * Generally, we try not to leave the shared regions locked if
948 * we can.
949 */
950 void
siginit()951 siginit()
952 {
953 #ifdef SIGHUP
954 (void)signal(SIGHUP, onint);
955 #endif
956 (void)signal(SIGINT, onint);
957 #ifdef SIGPIPE
958 (void)signal(SIGPIPE, onint);
959 #endif
960 (void)signal(SIGTERM, onint);
961 }
962
963 /*
964 * onint --
965 * Interrupt signal handler.
966 */
967 void
onint(int signo)968 onint(int signo)
969 {
970 if ((interrupted = signo) == 0)
971 interrupted = SIGINT;
972 }
973
974 void
usage()975 usage()
976 {
977 fprintf(stderr,
978 "usage: htdb_stat [-celmNtzW] [-C Acflmo] [-d file [-s file]] [-h home] [-M Ahlm]\n");
979 exit (1);
980 }
981