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