1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: mut_stat.c,v 12.30 2008/01/08 20:58:43 bostic Exp $
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/mutex_int.h"
15
16#ifdef HAVE_STATISTICS
17static int __mutex_print_all __P((ENV *, u_int32_t));
18static const char *__mutex_print_id __P((int));
19static int __mutex_print_stats __P((ENV *, u_int32_t));
20static void __mutex_print_summary __P((ENV *));
21static int __mutex_stat __P((ENV *, DB_MUTEX_STAT **, u_int32_t));
22
23/*
24 * __mutex_stat_pp --
25 *	ENV->mutex_stat pre/post processing.
26 *
27 * PUBLIC: int __mutex_stat_pp __P((DB_ENV *, DB_MUTEX_STAT **, u_int32_t));
28 */
29int
30__mutex_stat_pp(dbenv, statp, flags)
31	DB_ENV *dbenv;
32	DB_MUTEX_STAT **statp;
33	u_int32_t flags;
34{
35	DB_THREAD_INFO *ip;
36	ENV *env;
37	int ret;
38
39	env = dbenv->env;
40
41	if ((ret = __db_fchk(env,
42	    "DB_ENV->mutex_stat", flags, DB_STAT_CLEAR)) != 0)
43		return (ret);
44
45	ENV_ENTER(env, ip);
46	REPLICATION_WRAP(env, (__mutex_stat(env, statp, flags)), 0, ret);
47	ENV_LEAVE(env, ip);
48	return (ret);
49}
50
51/*
52 * __mutex_stat --
53 *	ENV->mutex_stat.
54 */
55static int
56__mutex_stat(env, statp, flags)
57	ENV *env;
58	DB_MUTEX_STAT **statp;
59	u_int32_t flags;
60{
61	DB_MUTEXMGR *mtxmgr;
62	DB_MUTEXREGION *mtxregion;
63	DB_MUTEX_STAT *stats;
64	int ret;
65
66	*statp = NULL;
67	mtxmgr = env->mutex_handle;
68	mtxregion = mtxmgr->reginfo.primary;
69
70	if ((ret = __os_umalloc(env, sizeof(DB_MUTEX_STAT), &stats)) != 0)
71		return (ret);
72
73	MUTEX_SYSTEM_LOCK(env);
74
75	/*
76	 * Most fields are maintained in the underlying region structure.
77	 * Region size and region mutex are not.
78	 */
79	*stats = mtxregion->stat;
80	stats->st_regsize = mtxmgr->reginfo.rp->size;
81	__mutex_set_wait_info(env, mtxregion->mtx_region,
82	    &stats->st_region_wait, &stats->st_region_nowait);
83	if (LF_ISSET(DB_STAT_CLEAR))
84		__mutex_clear(env, mtxregion->mtx_region);
85
86	MUTEX_SYSTEM_UNLOCK(env);
87
88	*statp = stats;
89	return (0);
90}
91
92/*
93 * __mutex_stat_print_pp --
94 *	ENV->mutex_stat_print pre/post processing.
95 *
96 * PUBLIC: int __mutex_stat_print_pp __P((DB_ENV *, u_int32_t));
97 */
98int
99__mutex_stat_print_pp(dbenv, flags)
100	DB_ENV *dbenv;
101	u_int32_t flags;
102{
103	DB_THREAD_INFO *ip;
104	ENV *env;
105	int ret;
106
107	env = dbenv->env;
108
109	if ((ret = __db_fchk(env, "DB_ENV->mutex_stat_print",
110	    flags, DB_STAT_ALL | DB_STAT_CLEAR)) != 0)
111		return (ret);
112
113	ENV_ENTER(env, ip);
114	REPLICATION_WRAP(env, (__mutex_stat_print(env, flags)), 0, ret);
115	ENV_LEAVE(env, ip);
116	return (ret);
117}
118
119/*
120 * __mutex_stat_print
121 *	ENV->mutex_stat_print method.
122 *
123 * PUBLIC: int __mutex_stat_print __P((ENV *, u_int32_t));
124 */
125int
126__mutex_stat_print(env, flags)
127	ENV *env;
128	u_int32_t flags;
129{
130	u_int32_t orig_flags;
131	int ret;
132
133	orig_flags = flags;
134	LF_CLR(DB_STAT_CLEAR | DB_STAT_SUBSYSTEM);
135	if (flags == 0 || LF_ISSET(DB_STAT_ALL)) {
136		ret = __mutex_print_stats(env, orig_flags);
137		__mutex_print_summary(env);
138		if (flags == 0 || ret != 0)
139			return (ret);
140	}
141
142	if (LF_ISSET(DB_STAT_ALL))
143		ret = __mutex_print_all(env, orig_flags);
144
145	return (0);
146}
147
148static void
149__mutex_print_summary(env)
150	ENV *env;
151{
152	DB_MUTEX *mutexp;
153	DB_MUTEXMGR *mtxmgr;
154	DB_MUTEXREGION *mtxregion;
155	db_mutex_t i;
156	u_int32_t counts[MTX_MAX_ENTRY + 2];
157	int alloc_id;
158
159	mtxmgr = env->mutex_handle;
160	mtxregion = mtxmgr->reginfo.primary;
161	memset(counts, 0, sizeof(counts));
162
163	for (i = 1; i <= mtxregion->stat.st_mutex_cnt; ++i, ++mutexp) {
164		mutexp = MUTEXP_SET(i);
165
166		if (!F_ISSET(mutexp, DB_MUTEX_ALLOCATED))
167			counts[0]++;
168		else if (mutexp->alloc_id > MTX_MAX_ENTRY)
169			counts[MTX_MAX_ENTRY + 1]++;
170		else
171			counts[mutexp->alloc_id]++;
172	}
173	__db_msg(env, "Mutex counts");
174	__db_msg(env, "%d\tUnallocated", counts[0]);
175	for (alloc_id = 1; alloc_id <= MTX_TXN_REGION + 1; alloc_id++)
176		if (counts[alloc_id] != 0)
177			__db_msg(env, "%lu\t%s",
178			    (u_long)counts[alloc_id],
179			    __mutex_print_id(alloc_id));
180
181}
182
183/*
184 * __mutex_print_stats --
185 *	Display default mutex region statistics.
186 */
187static int
188__mutex_print_stats(env, flags)
189	ENV *env;
190	u_int32_t flags;
191{
192	DB_MUTEX_STAT *sp;
193	int ret;
194
195	if ((ret = __mutex_stat(env, &sp, LF_ISSET(DB_STAT_CLEAR))) != 0)
196		return (ret);
197
198	if (LF_ISSET(DB_STAT_ALL))
199		__db_msg(env, "Default mutex region information:");
200
201	__db_dlbytes(env, "Mutex region size",
202	    (u_long)0, (u_long)0, (u_long)sp->st_regsize);
203	__db_dl_pct(env,
204	    "The number of region locks that required waiting",
205	    (u_long)sp->st_region_wait, DB_PCT(sp->st_region_wait,
206	    sp->st_region_wait + sp->st_region_nowait), NULL);
207	STAT_ULONG("Mutex alignment", sp->st_mutex_align);
208	STAT_ULONG("Mutex test-and-set spins", sp->st_mutex_tas_spins);
209	STAT_ULONG("Mutex total count", sp->st_mutex_cnt);
210	STAT_ULONG("Mutex free count", sp->st_mutex_free);
211	STAT_ULONG("Mutex in-use count", sp->st_mutex_inuse);
212	STAT_ULONG("Mutex maximum in-use count", sp->st_mutex_inuse_max);
213
214	__os_ufree(env, sp);
215
216	return (0);
217}
218
219/*
220 * __mutex_print_all --
221 *	Display debugging mutex region statistics.
222 */
223static int
224__mutex_print_all(env, flags)
225	ENV *env;
226	u_int32_t flags;
227{
228	static const FN fn[] = {
229		{ DB_MUTEX_ALLOCATED,		"alloc" },
230		{ DB_MUTEX_LOCKED,		"locked" },
231		{ DB_MUTEX_LOGICAL_LOCK,	"logical" },
232		{ DB_MUTEX_PROCESS_ONLY,	"process-private" },
233		{ DB_MUTEX_SELF_BLOCK,		"self-block" },
234		{ 0,				NULL }
235	};
236	DB_MSGBUF mb, *mbp;
237	DB_MUTEX *mutexp;
238	DB_MUTEXMGR *mtxmgr;
239	DB_MUTEXREGION *mtxregion;
240	db_mutex_t i;
241
242	DB_MSGBUF_INIT(&mb);
243	mbp = &mb;
244
245	mtxmgr = env->mutex_handle;
246	mtxregion = mtxmgr->reginfo.primary;
247
248	__db_print_reginfo(env, &mtxmgr->reginfo, "Mutex", flags);
249	__db_msg(env, "%s", DB_GLOBAL(db_line));
250
251	__db_msg(env, "DB_MUTEXREGION structure:");
252	__mutex_print_debug_single(env,
253	    "DB_MUTEXREGION region mutex", mtxregion->mtx_region, flags);
254	STAT_ULONG("Size of the aligned mutex", mtxregion->mutex_size);
255	STAT_ULONG("Next free mutex", mtxregion->mutex_next);
256
257	/*
258	 * The OOB mutex (MUTEX_INVALID) is 0, skip it.
259	 *
260	 * We're not holding the mutex region lock, so we're racing threads of
261	 * control allocating mutexes.  That's OK, it just means we display or
262	 * clear statistics while mutexes are moving.
263	 */
264	__db_msg(env, "%s", DB_GLOBAL(db_line));
265	__db_msg(env, "mutex\twait/nowait, pct wait, holder, flags");
266	for (i = 1; i <= mtxregion->stat.st_mutex_cnt; ++i, ++mutexp) {
267		mutexp = MUTEXP_SET(i);
268
269		if (!F_ISSET(mutexp, DB_MUTEX_ALLOCATED))
270			continue;
271
272		__db_msgadd(env, mbp, "%5lu\t", (u_long)i);
273
274		__mutex_print_debug_stats(env, mbp, i, flags);
275
276		if (mutexp->alloc_id != 0)
277			__db_msgadd(env,
278			    mbp, ", %s", __mutex_print_id(mutexp->alloc_id));
279
280		__db_prflags(env, mbp, mutexp->flags, fn, " (", ")");
281
282		DB_MSGBUF_FLUSH(env, mbp);
283	}
284
285	return (0);
286}
287
288/*
289 * __mutex_print_debug_single --
290 *	Print mutex internal debugging statistics for a single mutex on a
291 *	single output line.
292 *
293 * PUBLIC: void __mutex_print_debug_single
294 * PUBLIC:          __P((ENV *, const char *, db_mutex_t, u_int32_t));
295 */
296void
297__mutex_print_debug_single(env, tag, mutex, flags)
298	ENV *env;
299	const char *tag;
300	db_mutex_t mutex;
301	u_int32_t flags;
302{
303	DB_MSGBUF mb, *mbp;
304
305	DB_MSGBUF_INIT(&mb);
306	mbp = &mb;
307
308	if (LF_ISSET(DB_STAT_SUBSYSTEM))
309		LF_CLR(DB_STAT_CLEAR);
310	__db_msgadd(env, mbp, "%lu\t%s ", (u_long)mutex, tag);
311	__mutex_print_debug_stats(env, mbp, mutex, flags);
312	DB_MSGBUF_FLUSH(env, mbp);
313}
314
315/*
316 * __mutex_print_debug_stats --
317 *	Print mutex internal debugging statistics, that is, the statistics
318 *	in the [] square brackets.
319 *
320 * PUBLIC: void __mutex_print_debug_stats
321 * PUBLIC:          __P((ENV *, DB_MSGBUF *, db_mutex_t, u_int32_t));
322 */
323void
324__mutex_print_debug_stats(env, mbp, mutex, flags)
325	ENV *env;
326	DB_MSGBUF *mbp;
327	db_mutex_t mutex;
328	u_int32_t flags;
329{
330	DB_ENV *dbenv;
331	DB_MUTEX *mutexp;
332	DB_MUTEXMGR *mtxmgr;
333	DB_MUTEXREGION *mtxregion;
334	u_long value;
335	char buf[DB_THREADID_STRLEN];
336
337	if (mutex == MUTEX_INVALID) {
338		__db_msgadd(env, mbp, "[!Set]");
339		return;
340	}
341
342	dbenv = env->dbenv;
343	mtxmgr = env->mutex_handle;
344	mtxregion = mtxmgr->reginfo.primary;
345	mutexp = MUTEXP_SET(mutex);
346
347	__db_msgadd(env, mbp, "[");
348	if ((value = mutexp->mutex_set_wait) < 10000000)
349		__db_msgadd(env, mbp, "%lu", value);
350	else
351		__db_msgadd(env, mbp, "%luM", value / 1000000);
352	if ((value = mutexp->mutex_set_nowait) < 10000000)
353		__db_msgadd(env, mbp, "/%lu", value);
354	else
355		__db_msgadd(env, mbp, "/%luM", value / 1000000);
356
357	__db_msgadd(env, mbp, " %d%%",
358	    DB_PCT(mutexp->mutex_set_wait,
359	    mutexp->mutex_set_wait + mutexp->mutex_set_nowait));
360
361	if (F_ISSET(mutexp, DB_MUTEX_LOCKED))
362		__db_msgadd(env, mbp, " %s]",
363		    dbenv->thread_id_string(dbenv,
364		    mutexp->pid, mutexp->tid, buf));
365	else
366		__db_msgadd(env, mbp, " !Own]");
367
368	if (LF_ISSET(DB_STAT_CLEAR))
369		__mutex_clear(env, mutex);
370}
371
372static const char *
373__mutex_print_id(alloc_id)
374	int alloc_id;
375{
376	switch (alloc_id) {
377	case MTX_APPLICATION:		return ("application allocated");
378	case MTX_DB_HANDLE:		return ("db handle");
379	case MTX_ENV_DBLIST:		return ("env dblist");
380	case MTX_ENV_HANDLE:		return ("env handle");
381	case MTX_ENV_REGION:		return ("env region");
382	case MTX_LOCK_REGION:		return ("lock region");
383	case MTX_LOGICAL_LOCK:		return ("logical lock");
384	case MTX_LOG_FILENAME:		return ("log filename");
385	case MTX_LOG_FLUSH:		return ("log flush");
386	case MTX_LOG_HANDLE:		return ("log handle");
387	case MTX_LOG_REGION:		return ("log region");
388	case MTX_MPOOLFILE_HANDLE:	return ("mpoolfile handle");
389	case MTX_MPOOL_FH:		return ("mpool filehandle");
390	case MTX_MPOOL_FILE_BUCKET:	return ("mpool file bucket");
391	case MTX_MPOOL_HANDLE:		return ("mpool handle");
392	case MTX_MPOOL_HASH_BUCKET:	return ("mpool hash bucket");
393	case MTX_MPOOL_IO:		return ("mpool buffer I/O");
394	case MTX_MPOOL_REGION:		return ("mpool region");
395	case MTX_MUTEX_REGION:		return ("mutex region");
396	case MTX_MUTEX_TEST:		return ("mutex test");
397	case MTX_REP_CHKPT:		return ("replication checkpoint");
398	case MTX_REP_DATABASE:		return ("replication database");
399	case MTX_REP_EVENT:		return ("replication event");
400	case MTX_REP_REGION:		return ("replication region");
401	case MTX_SEQUENCE:		return ("sequence");
402	case MTX_TWISTER:		return ("twister");
403	case MTX_TXN_ACTIVE:		return ("txn active list");
404	case MTX_TXN_CHKPT:		return ("transaction checkpoint");
405	case MTX_TXN_COMMIT:		return ("txn commit");
406	case MTX_TXN_MVCC:		return ("txn mvcc");
407	case MTX_TXN_REGION:		return ("txn region");
408	default:			return ("unknown mutex type");
409	/* NOTREACHED */
410	}
411}
412
413/*
414 * __mutex_set_wait_info --
415 *	Return mutex statistics.
416 *
417 * PUBLIC: void __mutex_set_wait_info
418 * PUBLIC:	__P((ENV *, db_mutex_t, u_int32_t *, u_int32_t *));
419 */
420void
421__mutex_set_wait_info(env, mutex, waitp, nowaitp)
422	ENV *env;
423	db_mutex_t mutex;
424	u_int32_t *waitp, *nowaitp;
425{
426	DB_MUTEX *mutexp;
427	DB_MUTEXMGR *mtxmgr;
428	DB_MUTEXREGION *mtxregion;
429
430	mtxmgr = env->mutex_handle;
431	mtxregion = mtxmgr->reginfo.primary;
432	mutexp = MUTEXP_SET(mutex);
433
434	*waitp = mutexp->mutex_set_wait;
435	*nowaitp = mutexp->mutex_set_nowait;
436}
437
438/*
439 * __mutex_clear --
440 *	Clear mutex statistics.
441 *
442 * PUBLIC: void __mutex_clear __P((ENV *, db_mutex_t));
443 */
444void
445__mutex_clear(env, mutex)
446	ENV *env;
447	db_mutex_t mutex;
448{
449	DB_MUTEX *mutexp;
450	DB_MUTEXMGR *mtxmgr;
451	DB_MUTEXREGION *mtxregion;
452
453	mtxmgr = env->mutex_handle;
454	mtxregion = mtxmgr->reginfo.primary;
455	mutexp = MUTEXP_SET(mutex);
456
457	mutexp->mutex_set_wait = mutexp->mutex_set_nowait = 0;
458}
459
460#else /* !HAVE_STATISTICS */
461
462int
463__mutex_stat_pp(dbenv, statp, flags)
464	DB_ENV *dbenv;
465	DB_MUTEX_STAT **statp;
466	u_int32_t flags;
467{
468	COMPQUIET(statp, NULL);
469	COMPQUIET(flags, 0);
470
471	return (__db_stat_not_built(dbenv->env));
472}
473
474int
475__mutex_stat_print_pp(dbenv, flags)
476	DB_ENV *dbenv;
477	u_int32_t flags;
478{
479	COMPQUIET(flags, 0);
480
481	return (__db_stat_not_built(dbenv->env));
482}
483#endif
484