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 = &regs[--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