1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_stati.c,v 12.37 2008/01/08 20:58:10 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/btree.h"
14#include "dbinc/hash.h"
15#include "dbinc/qam.h"
16#include "dbinc/lock.h"
17#include "dbinc/log.h"
18#include "dbinc/mp.h"
19
20#ifdef HAVE_STATISTICS
21static int __db_print_all __P((DB *, u_int32_t));
22static int __db_print_citem __P((DBC *));
23static int __db_print_cursor __P((DB *));
24static int __db_print_stats __P((DB *, DB_THREAD_INFO *, u_int32_t));
25static int __db_stat __P((DB *, DB_THREAD_INFO *, DB_TXN *, void *, u_int32_t));
26static int __db_stat_arg __P((DB *, u_int32_t));
27
28/*
29 * __db_stat_pp --
30 *	DB->stat pre/post processing.
31 *
32 * PUBLIC: int __db_stat_pp __P((DB *, DB_TXN *, void *, u_int32_t));
33 */
34int
35__db_stat_pp(dbp, txn, spp, flags)
36	DB *dbp;
37	DB_TXN *txn;
38	void *spp;
39	u_int32_t flags;
40{
41	DB_THREAD_INFO *ip;
42	ENV *env;
43	int handle_check, ret, t_ret;
44
45	env = dbp->env;
46
47	DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->stat");
48
49	if ((ret = __db_stat_arg(dbp, flags)) != 0)
50		return (ret);
51
52	ENV_ENTER(env, ip);
53
54	/* Check for replication block. */
55	handle_check = IS_ENV_REPLICATED(env);
56	if (handle_check && (ret = __db_rep_enter(dbp, 1, 0, 0)) != 0) {
57		handle_check = 0;
58		goto err;
59	}
60
61	ret = __db_stat(dbp, ip, txn, spp, flags);
62
63	/* Release replication block. */
64	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
65		ret = t_ret;
66
67err:	ENV_LEAVE(env, ip);
68	return (ret);
69}
70
71/*
72 * __db_stat --
73 *	DB->stat.
74 *
75 */
76static int
77__db_stat(dbp, ip, txn, spp, flags)
78	DB *dbp;
79	DB_THREAD_INFO *ip;
80	DB_TXN *txn;
81	void *spp;
82	u_int32_t flags;
83{
84	DBC *dbc;
85	ENV *env;
86	int ret, t_ret;
87
88	env = dbp->env;
89
90	/* Acquire a cursor. */
91	if ((ret = __db_cursor(dbp, ip, txn,
92	     &dbc, LF_ISSET(DB_READ_COMMITTED | DB_READ_UNCOMMITTED))) != 0)
93		return (ret);
94
95	DEBUG_LWRITE(dbc, NULL, "DB->stat", NULL, NULL, flags);
96	LF_CLR(DB_READ_COMMITTED | DB_READ_UNCOMMITTED);
97
98	switch (dbp->type) {
99	case DB_BTREE:
100	case DB_RECNO:
101		ret = __bam_stat(dbc, spp, flags);
102		break;
103	case DB_HASH:
104		ret = __ham_stat(dbc, spp, flags);
105		break;
106	case DB_QUEUE:
107		ret = __qam_stat(dbc, spp, flags);
108		break;
109	case DB_UNKNOWN:
110	default:
111		ret = (__db_unknown_type(env, "DB->stat", dbp->type));
112		break;
113	}
114
115	if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
116		ret = t_ret;
117
118	return (ret);
119}
120
121/*
122 * __db_stat_arg --
123 *	Check DB->stat arguments.
124 */
125static int
126__db_stat_arg(dbp, flags)
127	DB *dbp;
128	u_int32_t flags;
129{
130	ENV *env;
131
132	env = dbp->env;
133
134	/* Check for invalid function flags. */
135	LF_CLR(DB_READ_COMMITTED | DB_READ_UNCOMMITTED);
136	switch (flags) {
137	case 0:
138	case DB_FAST_STAT:
139		break;
140	default:
141		return (__db_ferr(env, "DB->stat", 0));
142	}
143
144	return (0);
145}
146
147/*
148 * __db_stat_print_pp --
149 *	DB->stat_print pre/post processing.
150 *
151 * PUBLIC: int __db_stat_print_pp __P((DB *, u_int32_t));
152 */
153int
154__db_stat_print_pp(dbp, flags)
155	DB *dbp;
156	u_int32_t flags;
157{
158	DB_THREAD_INFO *ip;
159	ENV *env;
160	int handle_check, ret, t_ret;
161
162	env = dbp->env;
163
164	DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->stat_print");
165
166	/*
167	 * !!!
168	 * The actual argument checking is simple, do it inline.
169	 */
170	if ((ret = __db_fchk(env,
171	    "DB->stat_print", flags, DB_FAST_STAT | DB_STAT_ALL)) != 0)
172		return (ret);
173
174	ENV_ENTER(env, ip);
175
176	/* Check for replication block. */
177	handle_check = IS_ENV_REPLICATED(env);
178	if (handle_check && (ret = __db_rep_enter(dbp, 1, 0, 0)) != 0) {
179		handle_check = 0;
180		goto err;
181	}
182
183	ret = __db_stat_print(dbp, ip, flags);
184
185	/* Release replication block. */
186	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
187		ret = t_ret;
188
189err:	ENV_LEAVE(env, ip);
190	return (ret);
191}
192
193/*
194 * __db_stat_print --
195 *	DB->stat_print.
196 *
197 * PUBLIC: int __db_stat_print __P((DB *, DB_THREAD_INFO *, u_int32_t));
198 */
199int
200__db_stat_print(dbp, ip, flags)
201	DB *dbp;
202	DB_THREAD_INFO *ip;
203	u_int32_t flags;
204{
205	time_t now;
206	int ret;
207	char time_buf[CTIME_BUFLEN];
208
209	(void)time(&now);
210	__db_msg(dbp->env, "%.24s\tLocal time", __os_ctime(&now, time_buf));
211
212	if (LF_ISSET(DB_STAT_ALL) && (ret = __db_print_all(dbp, flags)) != 0)
213		return (ret);
214
215	if ((ret = __db_print_stats(dbp, ip, flags)) != 0)
216		return (ret);
217
218	return (0);
219}
220
221/*
222 * __db_print_stats --
223 *	Display default DB handle statistics.
224 */
225static int
226__db_print_stats(dbp, ip, flags)
227	DB *dbp;
228	DB_THREAD_INFO *ip;
229	u_int32_t flags;
230{
231	DBC *dbc;
232	ENV *env;
233	int ret, t_ret;
234
235	env = dbp->env;
236
237	/* Acquire a cursor. */
238	if ((ret = __db_cursor(dbp, ip, NULL, &dbc, 0)) != 0)
239		return (ret);
240
241	DEBUG_LWRITE(dbc, NULL, "DB->stat_print", NULL, NULL, 0);
242
243	switch (dbp->type) {
244	case DB_BTREE:
245	case DB_RECNO:
246		ret = __bam_stat_print(dbc, flags);
247		break;
248	case DB_HASH:
249		ret = __ham_stat_print(dbc, flags);
250		break;
251	case DB_QUEUE:
252		ret = __qam_stat_print(dbc, flags);
253		break;
254	case DB_UNKNOWN:
255	default:
256		ret = (__db_unknown_type(env, "DB->stat_print", dbp->type));
257		break;
258	}
259
260	if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
261		ret = t_ret;
262
263	return (ret);
264}
265
266/*
267 * __db_print_all --
268 *	Display debugging DB handle statistics.
269 */
270static int
271__db_print_all(dbp, flags)
272	DB *dbp;
273	u_int32_t flags;
274{
275	static const FN fn[] = {
276		{ DB_AM_CHKSUM,			"DB_AM_CHKSUM" },
277		{ DB_AM_COMPENSATE,		"DB_AM_COMPENSATE" },
278		{ DB_AM_CREATED,		"DB_AM_CREATED" },
279		{ DB_AM_CREATED_MSTR,		"DB_AM_CREATED_MSTR" },
280		{ DB_AM_DBM_ERROR,		"DB_AM_DBM_ERROR" },
281		{ DB_AM_DELIMITER,		"DB_AM_DELIMITER" },
282		{ DB_AM_DISCARD,		"DB_AM_DISCARD" },
283		{ DB_AM_DUP,			"DB_AM_DUP" },
284		{ DB_AM_DUPSORT,		"DB_AM_DUPSORT" },
285		{ DB_AM_ENCRYPT,		"DB_AM_ENCRYPT" },
286		{ DB_AM_FIXEDLEN,		"DB_AM_FIXEDLEN" },
287		{ DB_AM_INMEM,			"DB_AM_INMEM" },
288		{ DB_AM_IN_RENAME,		"DB_AM_IN_RENAME" },
289		{ DB_AM_NOT_DURABLE,		"DB_AM_NOT_DURABLE" },
290		{ DB_AM_OPEN_CALLED,		"DB_AM_OPEN_CALLED" },
291		{ DB_AM_PAD,			"DB_AM_PAD" },
292		{ DB_AM_PGDEF,			"DB_AM_PGDEF" },
293		{ DB_AM_RDONLY,			"DB_AM_RDONLY" },
294		{ DB_AM_READ_UNCOMMITTED,	"DB_AM_READ_UNCOMMITTED" },
295		{ DB_AM_RECNUM,			"DB_AM_RECNUM" },
296		{ DB_AM_RECOVER,		"DB_AM_RECOVER" },
297		{ DB_AM_RENUMBER,		"DB_AM_RENUMBER" },
298		{ DB_AM_REVSPLITOFF,		"DB_AM_REVSPLITOFF" },
299		{ DB_AM_SECONDARY,		"DB_AM_SECONDARY" },
300		{ DB_AM_SNAPSHOT,		"DB_AM_SNAPSHOT" },
301		{ DB_AM_SUBDB,			"DB_AM_SUBDB" },
302		{ DB_AM_SWAP,			"DB_AM_SWAP" },
303		{ DB_AM_TXN,			"DB_AM_TXN" },
304		{ DB_AM_VERIFYING,		"DB_AM_VERIFYING" },
305		{ 0,				NULL }
306	};
307	ENV *env;
308	char time_buf[CTIME_BUFLEN];
309
310	env = dbp->env;
311
312	__db_msg(env, "%s", DB_GLOBAL(db_line));
313	__db_msg(env, "DB handle information:");
314	STAT_ULONG("Page size", dbp->pgsize);
315	STAT_ISSET("Append recno", dbp->db_append_recno);
316	STAT_ISSET("Feedback", dbp->db_feedback);
317	STAT_ISSET("Dup compare", dbp->dup_compare);
318	STAT_ISSET("App private", dbp->app_private);
319	STAT_ISSET("DbEnv", dbp->env);
320	STAT_STRING("Type", __db_dbtype_to_string(dbp->type));
321
322	__mutex_print_debug_single(env, "Thread mutex", dbp->mutex, flags);
323
324	STAT_STRING("File", dbp->fname);
325	STAT_STRING("Database", dbp->dname);
326	STAT_HEX("Open flags", dbp->open_flags);
327
328	__db_print_fileid(env, dbp->fileid, "\tFile ID");
329
330	STAT_ULONG("Cursor adjust ID", dbp->adj_fileid);
331	STAT_ULONG("Meta pgno", dbp->meta_pgno);
332	if (dbp->locker != NULL)
333		STAT_ULONG("Locker ID", dbp->locker->id);
334	if (dbp->cur_locker != NULL)
335		STAT_ULONG("Handle lock", dbp->cur_locker->id);
336	if (dbp->associate_locker != NULL)
337		STAT_ULONG("Associate lock", dbp->associate_locker->id);
338	STAT_ULONG("RPC remote ID", dbp->cl_id);
339
340	__db_msg(env,
341	    "%.24s\tReplication handle timestamp",
342	    dbp->timestamp == 0 ? "0" : __os_ctime(&dbp->timestamp, time_buf));
343
344	STAT_ISSET("Secondary callback", dbp->s_callback);
345	STAT_ISSET("Primary handle", dbp->s_primary);
346
347	STAT_ISSET("api internal", dbp->api_internal);
348	STAT_ISSET("Btree/Recno internal", dbp->bt_internal);
349	STAT_ISSET("Hash internal", dbp->h_internal);
350	STAT_ISSET("Queue internal", dbp->q_internal);
351	STAT_ISSET("XA internal", dbp->xa_internal);
352
353	__db_prflags(env, NULL, dbp->flags, fn, NULL, "\tFlags");
354
355	if (dbp->log_filename == NULL)
356		STAT_ISSET("File naming information", dbp->log_filename);
357	else
358		__dbreg_print_fname(env, dbp->log_filename);
359
360	(void)__db_print_cursor(dbp);
361
362	return (0);
363}
364
365/*
366 * __db_print_cursor --
367 *	Display the cursor active and free queues.
368 */
369static int
370__db_print_cursor(dbp)
371	DB *dbp;
372{
373	DBC *dbc;
374	ENV *env;
375	int ret, t_ret;
376
377	env = dbp->env;
378
379	__db_msg(env, "%s", DB_GLOBAL(db_line));
380	__db_msg(env, "DB handle cursors:");
381
382	ret = 0;
383	MUTEX_LOCK(dbp->env, dbp->mutex);
384	__db_msg(env, "Active queue:");
385	TAILQ_FOREACH(dbc, &dbp->active_queue, links)
386		if ((t_ret = __db_print_citem(dbc)) != 0 && ret == 0)
387			ret = t_ret;
388	__db_msg(env, "Join queue:");
389	TAILQ_FOREACH(dbc, &dbp->join_queue, links)
390		if ((t_ret = __db_print_citem(dbc)) != 0 && ret == 0)
391			ret = t_ret;
392	__db_msg(env, "Free queue:");
393	TAILQ_FOREACH(dbc, &dbp->free_queue, links)
394		if ((t_ret = __db_print_citem(dbc)) != 0 && ret == 0)
395			ret = t_ret;
396	MUTEX_UNLOCK(dbp->env, dbp->mutex);
397
398	return (ret);
399}
400
401static int
402__db_print_citem(dbc)
403	DBC *dbc;
404{
405	static const FN fn[] = {
406		{ DBC_ACTIVE,		"DBC_ACTIVE" },
407		{ DBC_DONTLOCK,		"DBC_DONTLOCK" },
408		{ DBC_MULTIPLE,		"DBC_MULTIPLE" },
409		{ DBC_MULTIPLE_KEY,	"DBC_MULTIPLE_KEY" },
410		{ DBC_OPD,		"DBC_OPD" },
411		{ DBC_OWN_LID,		"DBC_OWN_LID" },
412		{ DBC_READ_COMMITTED,	"DBC_READ_COMMITTED" },
413		{ DBC_READ_UNCOMMITTED,	"DBC_READ_UNCOMMITTED" },
414		{ DBC_RECOVER,		"DBC_RECOVER" },
415		{ DBC_RMW,		"DBC_RMW" },
416		{ DBC_TRANSIENT,	"DBC_TRANSIENT" },
417		{ DBC_WRITECURSOR,	"DBC_WRITECURSOR" },
418		{ DBC_WRITER,		"DBC_WRITER" },
419		{ 0,			NULL }
420	};
421	DB *dbp;
422	DBC_INTERNAL *cp;
423	ENV *env;
424
425	dbp = dbc->dbp;
426	env = dbp->env;
427	cp = dbc->internal;
428
429	STAT_POINTER("DBC", dbc);
430	STAT_POINTER("Associated dbp", dbc->dbp);
431	STAT_POINTER("Associated txn", dbc->txn);
432	STAT_POINTER("Internal", cp);
433	STAT_HEX("Default locker ID", dbc->lref == NULL ? 0 : dbc->lref->id);
434	STAT_HEX("Locker", P_TO_ULONG(dbc->locker));
435	STAT_STRING("Type", __db_dbtype_to_string(dbc->dbtype));
436
437	STAT_POINTER("Off-page duplicate cursor", cp->opd);
438	STAT_POINTER("Referenced page", cp->page);
439	STAT_ULONG("Root", cp->root);
440	STAT_ULONG("Page number", cp->pgno);
441	STAT_ULONG("Page index", cp->indx);
442	STAT_STRING("Lock mode", __db_lockmode_to_string(cp->lock_mode));
443	__db_prflags(env, NULL, dbc->flags, fn, NULL, "\tFlags");
444
445	switch (dbc->dbtype) {
446	case DB_BTREE:
447	case DB_RECNO:
448		__bam_print_cursor(dbc);
449		break;
450	case DB_HASH:
451		__ham_print_cursor(dbc);
452		break;
453	case DB_UNKNOWN:
454		DB_ASSERT(env, dbp->type != DB_UNKNOWN);
455		/* FALLTHROUGH */
456	case DB_QUEUE:
457	default:
458		break;
459	}
460	return (0);
461}
462
463#else /* !HAVE_STATISTICS */
464
465int
466__db_stat_pp(dbp, txn, spp, flags)
467	DB *dbp;
468	DB_TXN *txn;
469	void *spp;
470	u_int32_t flags;
471{
472	COMPQUIET(spp, NULL);
473	COMPQUIET(txn, NULL);
474	COMPQUIET(flags, 0);
475
476	return (__db_stat_not_built(dbp->env));
477}
478
479int
480__db_stat_print_pp(dbp, flags)
481	DB *dbp;
482	u_int32_t flags;
483{
484	COMPQUIET(flags, 0);
485
486	return (__db_stat_not_built(dbp->env));
487}
488#endif
489