1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: lock_stat.c,v 12.40 2008/04/11 16:13:53 ubell Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/lock.h"
14#include "dbinc/log.h"
15#include "dbinc/db_am.h"
16
17#ifdef HAVE_STATISTICS
18static int  __lock_dump_locker
19		__P((ENV *, DB_MSGBUF *, DB_LOCKTAB *, DB_LOCKER *));
20static int  __lock_dump_object __P((DB_LOCKTAB *, DB_MSGBUF *, DB_LOCKOBJ *));
21static int  __lock_print_all __P((ENV *, u_int32_t));
22static int  __lock_print_stats __P((ENV *, u_int32_t));
23static void __lock_print_header __P((ENV *));
24static int  __lock_stat __P((ENV *, DB_LOCK_STAT **, u_int32_t));
25
26/*
27 * __lock_stat_pp --
28 *	ENV->lock_stat pre/post processing.
29 *
30 * PUBLIC: int __lock_stat_pp __P((DB_ENV *, DB_LOCK_STAT **, u_int32_t));
31 */
32int
33__lock_stat_pp(dbenv, statp, flags)
34	DB_ENV *dbenv;
35	DB_LOCK_STAT **statp;
36	u_int32_t flags;
37{
38	DB_THREAD_INFO *ip;
39	ENV *env;
40	int ret;
41
42	env = dbenv->env;
43
44	ENV_REQUIRES_CONFIG(env,
45	    env->lk_handle, "DB_ENV->lock_stat", DB_INIT_LOCK);
46
47	if ((ret = __db_fchk(env,
48	    "DB_ENV->lock_stat", flags, DB_STAT_CLEAR)) != 0)
49		return (ret);
50
51	ENV_ENTER(env, ip);
52	REPLICATION_WRAP(env, (__lock_stat(env, statp, flags)), 0, ret);
53	ENV_LEAVE(env, ip);
54	return (ret);
55}
56
57/*
58 * __lock_stat --
59 *	ENV->lock_stat.
60 */
61static int
62__lock_stat(env, statp, flags)
63	ENV *env;
64	DB_LOCK_STAT **statp;
65	u_int32_t flags;
66{
67	DB_LOCKREGION *region;
68	DB_LOCKTAB *lt;
69	DB_LOCK_STAT *stats, tmp;
70	DB_LOCK_HSTAT htmp;
71	DB_LOCK_PSTAT ptmp;
72	int ret;
73	u_int32_t i, tmp_wait, tmp_nowait;
74
75	*statp = NULL;
76	lt = env->lk_handle;
77
78	if ((ret = __os_umalloc(env, sizeof(*stats), &stats)) != 0)
79		return (ret);
80
81	/* Copy out the global statistics. */
82	LOCK_REGION_LOCK(env);
83
84	region = lt->reginfo.primary;
85	memcpy(stats, &region->stat, sizeof(*stats));
86	stats->st_locktimeout = region->lk_timeout;
87	stats->st_txntimeout = region->tx_timeout;
88
89	for (i = 0; i < region->object_t_size; i++) {
90		stats->st_nrequests += lt->obj_stat[i].st_nrequests;
91		stats->st_nreleases += lt->obj_stat[i].st_nreleases;
92		stats->st_nupgrade += lt->obj_stat[i].st_nupgrade;
93		stats->st_ndowngrade += lt->obj_stat[i].st_ndowngrade;
94		stats->st_lock_wait += lt->obj_stat[i].st_lock_wait;
95		stats->st_lock_nowait += lt->obj_stat[i].st_lock_nowait;
96		stats->st_nlocktimeouts += lt->obj_stat[i].st_nlocktimeouts;
97		stats->st_ntxntimeouts += lt->obj_stat[i].st_ntxntimeouts;
98		if (stats->st_maxhlocks < lt->obj_stat[i].st_maxnlocks)
99			stats->st_maxhlocks = lt->obj_stat[i].st_maxnlocks;
100		if (stats->st_maxhobjects < lt->obj_stat[i].st_maxnobjects)
101			stats->st_maxhobjects = lt->obj_stat[i].st_maxnobjects;
102		if (stats->st_hash_len < lt->obj_stat[i].st_hash_len)
103			stats->st_hash_len = lt->obj_stat[i].st_hash_len;
104		if (LF_ISSET(DB_STAT_CLEAR)) {
105			htmp = lt->obj_stat[i];
106			memset(&lt->obj_stat[i], 0, sizeof(lt->obj_stat[i]));
107			lt->obj_stat[i].st_nlocks = htmp.st_nlocks;
108			lt->obj_stat[i].st_maxnlocks = htmp.st_nlocks;
109			lt->obj_stat[i].st_nobjects = htmp.st_nobjects;
110			lt->obj_stat[i].st_maxnobjects = htmp.st_nobjects;
111
112		}
113	}
114
115	for (i = 0; i < region->part_t_size; i++) {
116		stats->st_nlocks += lt->part_array[i].part_stat.st_nlocks;
117		stats->st_maxnlocks +=
118		     lt->part_array[i].part_stat.st_maxnlocks;
119		stats->st_nobjects += lt->part_array[i].part_stat.st_nobjects;
120		stats->st_maxnobjects +=
121		    lt->part_array[i].part_stat.st_maxnobjects;
122		stats->st_locksteals +=
123		    lt->part_array[i].part_stat.st_locksteals;
124		if (stats->st_maxlsteals <
125		    lt->part_array[i].part_stat.st_locksteals)
126			stats->st_maxlsteals =
127			    lt->part_array[i].part_stat.st_locksteals;
128		stats->st_objectsteals +=
129		    lt->part_array[i].part_stat.st_objectsteals;
130		if (stats->st_maxosteals <
131		    lt->part_array[i].part_stat.st_objectsteals)
132			stats->st_maxosteals =
133			    lt->part_array[i].part_stat.st_objectsteals;
134		__mutex_set_wait_info(env,
135		     lt->part_array[i].mtx_part, &tmp_wait, &tmp_nowait);
136		stats->st_part_nowait += tmp_nowait;
137		stats->st_part_wait += tmp_wait;
138		if (tmp_wait > stats->st_part_max_wait) {
139			stats->st_part_max_nowait = tmp_nowait;
140			stats->st_part_max_wait = tmp_wait;
141		}
142
143		if (LF_ISSET(DB_STAT_CLEAR)) {
144			ptmp = lt->part_array[i].part_stat;
145			memset(&lt->part_array[i].part_stat,
146			    0, sizeof(lt->part_array[i].part_stat));
147			lt->part_array[i].part_stat.st_nlocks =
148			     ptmp.st_nlocks;
149			lt->part_array[i].part_stat.st_maxnlocks =
150			     ptmp.st_nlocks;
151			lt->part_array[i].part_stat.st_nobjects =
152			     ptmp.st_nobjects;
153			lt->part_array[i].part_stat.st_maxnobjects =
154			     ptmp.st_nobjects;
155		}
156	}
157
158	__mutex_set_wait_info(env, region->mtx_region,
159	    &stats->st_region_wait, &stats->st_region_nowait);
160	__mutex_set_wait_info(env, region->mtx_dd,
161	    &stats->st_objs_wait, &stats->st_objs_nowait);
162	__mutex_set_wait_info(env, region->mtx_lockers,
163	    &stats->st_lockers_wait, &stats->st_lockers_nowait);
164	stats->st_regsize = lt->reginfo.rp->size;
165	if (LF_ISSET(DB_STAT_CLEAR)) {
166		tmp = region->stat;
167		memset(&region->stat, 0, sizeof(region->stat));
168		if (!LF_ISSET(DB_STAT_SUBSYSTEM)) {
169			__mutex_clear(env, region->mtx_region);
170			__mutex_clear(env, region->mtx_dd);
171			__mutex_clear(env, region->mtx_lockers);
172			for (i = 0; i < region->part_t_size; i++)
173				__mutex_clear(env, lt->part_array[i].mtx_part);
174		}
175
176		region->stat.st_id = tmp.st_id;
177		region->stat.st_cur_maxid = tmp.st_cur_maxid;
178		region->stat.st_maxlocks = tmp.st_maxlocks;
179		region->stat.st_maxlockers = tmp.st_maxlockers;
180		region->stat.st_maxobjects = tmp.st_maxobjects;
181		region->stat.st_nlocks =
182		    region->stat.st_maxnlocks = tmp.st_nlocks;
183		region->stat.st_nlockers =
184		    region->stat.st_maxnlockers = tmp.st_nlockers;
185		region->stat.st_nobjects =
186		    region->stat.st_maxnobjects = tmp.st_nobjects;
187		region->stat.st_nmodes = tmp.st_nmodes;
188	}
189
190	LOCK_REGION_UNLOCK(env);
191
192	*statp = stats;
193	return (0);
194}
195
196/*
197 * __lock_stat_print_pp --
198 *	ENV->lock_stat_print pre/post processing.
199 *
200 * PUBLIC: int __lock_stat_print_pp __P((DB_ENV *, u_int32_t));
201 */
202int
203__lock_stat_print_pp(dbenv, flags)
204	DB_ENV *dbenv;
205	u_int32_t flags;
206{
207	DB_THREAD_INFO *ip;
208	ENV *env;
209	int ret;
210
211	env = dbenv->env;
212
213	ENV_REQUIRES_CONFIG(env,
214	    env->lk_handle, "DB_ENV->lock_stat_print", DB_INIT_LOCK);
215
216#define	DB_STAT_LOCK_FLAGS						\
217	(DB_STAT_ALL | DB_STAT_CLEAR | DB_STAT_LOCK_CONF |		\
218	 DB_STAT_LOCK_LOCKERS |	DB_STAT_LOCK_OBJECTS | DB_STAT_LOCK_PARAMS)
219	if ((ret = __db_fchk(env, "DB_ENV->lock_stat_print",
220	    flags, DB_STAT_CLEAR | DB_STAT_LOCK_FLAGS)) != 0)
221		return (ret);
222
223	ENV_ENTER(env, ip);
224	REPLICATION_WRAP(env, (__lock_stat_print(env, flags)), 0, ret);
225	ENV_LEAVE(env, ip);
226	return (ret);
227}
228
229/*
230 * __lock_stat_print --
231 *	ENV->lock_stat_print method.
232 *
233 * PUBLIC: int  __lock_stat_print __P((ENV *, u_int32_t));
234 */
235int
236__lock_stat_print(env, flags)
237	ENV *env;
238	u_int32_t flags;
239{
240	u_int32_t orig_flags;
241	int ret;
242
243	orig_flags = flags;
244	LF_CLR(DB_STAT_CLEAR | DB_STAT_SUBSYSTEM);
245	if (flags == 0 || LF_ISSET(DB_STAT_ALL)) {
246		ret = __lock_print_stats(env, orig_flags);
247		if (flags == 0 || ret != 0)
248			return (ret);
249	}
250
251	if (LF_ISSET(DB_STAT_ALL | DB_STAT_LOCK_CONF | DB_STAT_LOCK_LOCKERS |
252	    DB_STAT_LOCK_OBJECTS | DB_STAT_LOCK_PARAMS) &&
253	    (ret = __lock_print_all(env, orig_flags)) != 0)
254		return (ret);
255
256	return (0);
257}
258
259/*
260 * __lock_print_stats --
261 *	Display default lock region statistics.
262 */
263static int
264__lock_print_stats(env, flags)
265	ENV *env;
266	u_int32_t flags;
267{
268	DB_LOCK_STAT *sp;
269	int ret;
270
271#ifdef LOCK_DIAGNOSTIC
272	DB_LOCKTAB *lt;
273	DB_LOCKREGION *region;
274	u_int32_t i;
275	u_int32_t wait, nowait;
276
277	lt = env->lk_handle;
278	region = lt->reginfo.primary;
279
280	for (i = 0; i < region->object_t_size; i++) {
281		if (lt->obj_stat[i].st_hash_len == 0)
282			continue;
283		__db_dl(env,
284		    "Hash bucket", (u_long)i);
285		__db_dl(env, "Partition", (u_long)LOCK_PART(region, i));
286		__mutex_set_wait_info(env,
287		    lt->part_array[LOCK_PART(region, i)].mtx_part,
288		    &wait, &nowait);
289		__db_dl_pct(env,
290	    "The number of partition mutex requests that required waiting",
291		    (u_long)wait, DB_PCT(wait, wait + nowait), NULL);
292		__db_dl(env,
293		    "Maximum hash bucket length",
294		    (u_long)lt->obj_stat[i].st_hash_len);
295		__db_dl(env,
296		    "Total number of locks requested",
297		    (u_long)lt->obj_stat[i].st_nrequests);
298		__db_dl(env,
299		    "Total number of locks released",
300		    (u_long)lt->obj_stat[i].st_nreleases);
301		__db_dl(env,
302		    "Total number of locks upgraded",
303		    (u_long)lt->obj_stat[i].st_nupgrade);
304		__db_dl(env,
305		    "Total number of locks downgraded",
306		    (u_long)lt->obj_stat[i].st_ndowngrade);
307		__db_dl(env,
308	  "Lock requests not available due to conflicts, for which we waited",
309		    (u_long)lt->obj_stat[i].st_lock_wait);
310		__db_dl(env,
311  "Lock requests not available due to conflicts, for which we did not wait",
312		    (u_long)lt->obj_stat[i].st_lock_nowait);
313		__db_dl(env, "Number of locks that have timed out",
314		    (u_long)lt->obj_stat[i].st_nlocktimeouts);
315		__db_dl(env, "Number of transactions that have timed out",
316		    (u_long)lt->obj_stat[i].st_ntxntimeouts);
317	}
318#endif
319	if ((ret = __lock_stat(env, &sp, flags)) != 0)
320		return (ret);
321
322	if (LF_ISSET(DB_STAT_ALL))
323		__db_msg(env, "Default locking region information:");
324	__db_dl(env, "Last allocated locker ID", (u_long)sp->st_id);
325	__db_msg(env, "%#lx\tCurrent maximum unused locker ID",
326	    (u_long)sp->st_cur_maxid);
327	__db_dl(env, "Number of lock modes", (u_long)sp->st_nmodes);
328	__db_dl(env,
329	    "Maximum number of locks possible", (u_long)sp->st_maxlocks);
330	__db_dl(env,
331	    "Maximum number of lockers possible", (u_long)sp->st_maxlockers);
332	__db_dl(env, "Maximum number of lock objects possible",
333	    (u_long)sp->st_maxobjects);
334	__db_dl(env, "Number of lock object partitions",
335	    (u_long)sp->st_partitions);
336	__db_dl(env, "Number of current locks", (u_long)sp->st_nlocks);
337	__db_dl(env, "Maximum number of locks at any one time",
338	    (u_long)sp->st_maxnlocks);
339	__db_dl(env, "Maximum number of locks in any one bucket",
340	    (u_long)sp->st_maxhlocks);
341	__db_dl(env, "Maximum number of locks stolen by for an empty partition",
342	    (u_long)sp->st_locksteals);
343	__db_dl(env, "Maximum number of locks stolen for any one partition",
344	    (u_long)sp->st_maxlsteals);
345	__db_dl(env, "Number of current lockers", (u_long)sp->st_nlockers);
346	__db_dl(env, "Maximum number of lockers at any one time",
347	    (u_long)sp->st_maxnlockers);
348	__db_dl(env,
349	    "Number of current lock objects", (u_long)sp->st_nobjects);
350	__db_dl(env, "Maximum number of lock objects at any one time",
351	    (u_long)sp->st_maxnobjects);
352	__db_dl(env, "Maximum number of lock objects in any one bucket",
353	    (u_long)sp->st_maxhobjects);
354	__db_dl(env,
355	    "Maximum number of objects stolen by for an empty partition",
356	    (u_long)sp->st_objectsteals);
357	__db_dl(env, "Maximum number of objects stolen for any one partition",
358	    (u_long)sp->st_maxosteals);
359	__db_dl(env,
360	    "Total number of locks requested", (u_long)sp->st_nrequests);
361	__db_dl(env,
362	    "Total number of locks released", (u_long)sp->st_nreleases);
363	__db_dl(env,
364	    "Total number of locks upgraded", (u_long)sp->st_nupgrade);
365	__db_dl(env,
366	    "Total number of locks downgraded", (u_long)sp->st_ndowngrade);
367	__db_dl(env,
368	  "Lock requests not available due to conflicts, for which we waited",
369	    (u_long)sp->st_lock_wait);
370	__db_dl(env,
371  "Lock requests not available due to conflicts, for which we did not wait",
372	    (u_long)sp->st_lock_nowait);
373	__db_dl(env, "Number of deadlocks", (u_long)sp->st_ndeadlocks);
374	__db_dl(env, "Lock timeout value", (u_long)sp->st_locktimeout);
375	__db_dl(env, "Number of locks that have timed out",
376	    (u_long)sp->st_nlocktimeouts);
377	__db_dl(env,
378	    "Transaction timeout value", (u_long)sp->st_txntimeout);
379	__db_dl(env, "Number of transactions that have timed out",
380	    (u_long)sp->st_ntxntimeouts);
381
382	__db_dlbytes(env, "The size of the lock region",
383	    (u_long)0, (u_long)0, (u_long)sp->st_regsize);
384	__db_dl_pct(env,
385	    "The number of partition locks that required waiting",
386	    (u_long)sp->st_part_wait, DB_PCT(
387	    sp->st_part_wait, sp->st_part_wait + sp->st_part_nowait), NULL);
388	__db_dl_pct(env,
389    "The maximum number of times any partition lock was waited for",
390	    (u_long)sp->st_part_max_wait, DB_PCT(sp->st_part_max_wait,
391	    sp->st_part_max_wait + sp->st_part_max_nowait), NULL);
392	__db_dl_pct(env,
393	    "The number of object queue operations that required waiting",
394	    (u_long)sp->st_objs_wait, DB_PCT(sp->st_objs_wait,
395	    sp->st_objs_wait + sp->st_objs_nowait), NULL);
396	__db_dl_pct(env,
397	    "The number of locker allocations that required waiting",
398	    (u_long)sp->st_lockers_wait, DB_PCT(sp->st_lockers_wait,
399	    sp->st_lockers_wait + sp->st_lockers_nowait), NULL);
400	__db_dl_pct(env,
401	    "The number of region locks that required waiting",
402	    (u_long)sp->st_region_wait, DB_PCT(sp->st_region_wait,
403	    sp->st_region_wait + sp->st_region_nowait), NULL);
404	__db_dl(env, "Maximum hash bucket length",
405	    (u_long)sp->st_hash_len);
406
407	__os_ufree(env, sp);
408
409	return (0);
410}
411
412/*
413 * __lock_print_all --
414 *	Display debugging lock region statistics.
415 */
416static int
417__lock_print_all(env, flags)
418	ENV *env;
419	u_int32_t flags;
420{
421	DB_LOCKER *lip;
422	DB_LOCKOBJ *op;
423	DB_LOCKREGION *lrp;
424	DB_LOCKTAB *lt;
425	DB_MSGBUF mb;
426	int i, j;
427	u_int32_t k;
428
429	lt = env->lk_handle;
430	lrp = lt->reginfo.primary;
431	DB_MSGBUF_INIT(&mb);
432
433	LOCK_REGION_LOCK(env);
434	__db_print_reginfo(env, &lt->reginfo, "Lock", flags);
435
436	if (LF_ISSET(DB_STAT_ALL | DB_STAT_LOCK_PARAMS)) {
437		__db_msg(env, "%s", DB_GLOBAL(db_line));
438		__db_msg(env, "Lock region parameters:");
439		__mutex_print_debug_single(env,
440		    "Lock region region mutex", lrp->mtx_region, flags);
441		STAT_ULONG("locker table size", lrp->locker_t_size);
442		STAT_ULONG("object table size", lrp->object_t_size);
443		STAT_ULONG("obj_off", lrp->obj_off);
444		STAT_ULONG("locker_off", lrp->locker_off);
445		STAT_ULONG("need_dd", lrp->need_dd);
446		if (timespecisset(&lrp->next_timeout)) {
447#ifdef HAVE_STRFTIME
448			time_t t = (time_t)lrp->next_timeout.tv_sec;
449			char tbuf[64];
450			if (strftime(tbuf, sizeof(tbuf),
451			    "%m-%d-%H:%M:%S", localtime(&t)) != 0)
452				__db_msg(env, "next_timeout: %s.%09lu",
453				     tbuf, (u_long)lrp->next_timeout.tv_nsec);
454			else
455#endif
456				__db_msg(env, "next_timeout: %lu.%09lu",
457				     (u_long)lrp->next_timeout.tv_sec,
458				     (u_long)lrp->next_timeout.tv_nsec);
459		}
460	}
461
462	if (LF_ISSET(DB_STAT_ALL | DB_STAT_LOCK_CONF)) {
463		__db_msg(env, "%s", DB_GLOBAL(db_line));
464		__db_msg(env, "Lock conflict matrix:");
465		for (i = 0; i < lrp->stat.st_nmodes; i++) {
466			for (j = 0; j < lrp->stat.st_nmodes; j++)
467				__db_msgadd(env, &mb, "%lu\t", (u_long)
468				    lt->conflicts[i * lrp->stat.st_nmodes + j]);
469			DB_MSGBUF_FLUSH(env, &mb);
470		}
471	}
472	LOCK_REGION_UNLOCK(env);
473
474	if (LF_ISSET(DB_STAT_ALL | DB_STAT_LOCK_LOCKERS)) {
475		__db_msg(env, "%s", DB_GLOBAL(db_line));
476		__db_msg(env, "Locks grouped by lockers:");
477		__lock_print_header(env);
478		LOCK_LOCKERS(env, lrp);
479		for (k = 0; k < lrp->locker_t_size; k++)
480			SH_TAILQ_FOREACH(
481			    lip, &lt->locker_tab[k], links, __db_locker)
482				(void)__lock_dump_locker(env, &mb, lt, lip);
483		UNLOCK_LOCKERS(env, lrp);
484	}
485
486	if (LF_ISSET(DB_STAT_ALL | DB_STAT_LOCK_OBJECTS)) {
487		__db_msg(env, "%s", DB_GLOBAL(db_line));
488		__db_msg(env, "Locks grouped by object:");
489		__lock_print_header(env);
490		for (k = 0; k < lrp->object_t_size; k++) {
491			OBJECT_LOCK_NDX(lt, lrp, k);
492			SH_TAILQ_FOREACH(
493			    op, &lt->obj_tab[k], links, __db_lockobj) {
494				(void)__lock_dump_object(lt, &mb, op);
495				__db_msg(env, "%s", "");
496			}
497			OBJECT_UNLOCK(lt, lrp, k);
498		}
499	}
500
501	return (0);
502}
503
504static int
505__lock_dump_locker(env, mbp, lt, lip)
506	ENV *env;
507	DB_MSGBUF *mbp;
508	DB_LOCKTAB *lt;
509	DB_LOCKER *lip;
510{
511	DB_LOCKREGION *lrp;
512	struct __db_lock *lp;
513	char buf[DB_THREADID_STRLEN];
514	u_int32_t ndx;
515
516	lrp = lt->reginfo.primary;
517
518	__db_msgadd(env,
519	    mbp, "%8lx dd=%2ld locks held %-4d write locks %-4d pid/thread %s",
520	    (u_long)lip->id, (long)lip->dd_id, lip->nlocks, lip->nwrites,
521	    env->dbenv->thread_id_string(env->dbenv, lip->pid, lip->tid, buf));
522	if (timespecisset(&lip->tx_expire)) {
523#ifdef HAVE_STRFTIME
524		time_t t = (time_t)lip->tx_expire.tv_sec;
525		char tbuf[64];
526		if (strftime(tbuf, sizeof(tbuf),
527		    "%m-%d-%H:%M:%S", localtime(&t)) != 0)
528			__db_msgadd(env, mbp, "expires %s.%09lu",
529			    tbuf, (u_long)lip->tx_expire.tv_nsec);
530		else
531#endif
532			__db_msgadd(env, mbp, "expires %lu.%09lu",
533			    (u_long)lip->tx_expire.tv_sec,
534			    (u_long)lip->tx_expire.tv_nsec);
535	}
536	if (F_ISSET(lip, DB_LOCKER_TIMEOUT))
537		__db_msgadd(
538		    env, mbp, " lk timeout %lu", (u_long)lip->lk_timeout);
539	if (timespecisset(&lip->lk_expire)) {
540#ifdef HAVE_STRFTIME
541		time_t t = (time_t)lip->lk_expire.tv_sec;
542		char tbuf[64];
543		if (strftime(tbuf,
544		    sizeof(tbuf), "%m-%d-%H:%M:%S", localtime(&t)) != 0)
545			__db_msgadd(env, mbp, " lk expires %s.%09lu",
546			    tbuf, (u_long)lip->lk_expire.tv_nsec);
547		else
548#endif
549			__db_msgadd(env, mbp, " lk expires %lu.%09lu",
550			    (u_long)lip->lk_expire.tv_sec,
551			    (u_long)lip->lk_expire.tv_nsec);
552	}
553	DB_MSGBUF_FLUSH(env, mbp);
554
555	/*
556	 * We need some care here since the list may change while we
557	 * look.
558	 */
559retry:	SH_LIST_FOREACH(lp, &lip->heldby, locker_links, __db_lock) {
560		if (!SH_LIST_EMPTY(&lip->heldby) && lp != NULL) {
561			ndx = lp->indx;
562			OBJECT_LOCK_NDX(lt, lrp, ndx);
563			if (lp->indx == ndx)
564				__lock_printlock(lt, mbp, lp, 1);
565			else {
566				OBJECT_UNLOCK(lt, lrp, ndx);
567				goto retry;
568			}
569			OBJECT_UNLOCK(lt, lrp, ndx);
570		}
571	}
572	return (0);
573}
574
575static int
576__lock_dump_object(lt, mbp, op)
577	DB_LOCKTAB *lt;
578	DB_MSGBUF *mbp;
579	DB_LOCKOBJ *op;
580{
581	struct __db_lock *lp;
582
583	SH_TAILQ_FOREACH(lp, &op->holders, links, __db_lock)
584		__lock_printlock(lt, mbp, lp, 1);
585	SH_TAILQ_FOREACH(lp, &op->waiters, links, __db_lock)
586		__lock_printlock(lt, mbp, lp, 1);
587	return (0);
588}
589
590/*
591 * __lock_print_header --
592 */
593static void
594__lock_print_header(env)
595	ENV *env;
596{
597	__db_msg(env, "%-8s %-10s%-4s %-7s %s",
598	    "Locker", "Mode",
599	    "Count", "Status", "----------------- Object ---------------");
600}
601
602/*
603 * __lock_printlock --
604 *
605 * PUBLIC: void __lock_printlock
606 * PUBLIC:     __P((DB_LOCKTAB *, DB_MSGBUF *mbp, struct __db_lock *, int));
607 */
608void
609__lock_printlock(lt, mbp, lp, ispgno)
610	DB_LOCKTAB *lt;
611	DB_MSGBUF *mbp;
612	struct __db_lock *lp;
613	int ispgno;
614{
615	DB_LOCKOBJ *lockobj;
616	DB_MSGBUF mb;
617	ENV *env;
618	db_pgno_t pgno;
619	u_int32_t *fidp, type;
620	u_int8_t *ptr;
621	char *fname, *dname, *p, namebuf[26];
622	const char *mode, *status;
623
624	env = lt->env;
625
626	if (mbp == NULL) {
627		DB_MSGBUF_INIT(&mb);
628		mbp = &mb;
629	}
630
631	switch (lp->mode) {
632	case DB_LOCK_IREAD:
633		mode = "IREAD";
634		break;
635	case DB_LOCK_IWR:
636		mode = "IWR";
637		break;
638	case DB_LOCK_IWRITE:
639		mode = "IWRITE";
640		break;
641	case DB_LOCK_NG:
642		mode = "NG";
643		break;
644	case DB_LOCK_READ:
645		mode = "READ";
646		break;
647	case DB_LOCK_READ_UNCOMMITTED:
648		mode = "READ_UNCOMMITTED";
649		break;
650	case DB_LOCK_WRITE:
651		mode = "WRITE";
652		break;
653	case DB_LOCK_WWRITE:
654		mode = "WAS_WRITE";
655		break;
656	case DB_LOCK_WAIT:
657		mode = "WAIT";
658		break;
659	default:
660		mode = "UNKNOWN";
661		break;
662	}
663	switch (lp->status) {
664	case DB_LSTAT_ABORTED:
665		status = "ABORT";
666		break;
667	case DB_LSTAT_EXPIRED:
668		status = "EXPIRED";
669		break;
670	case DB_LSTAT_FREE:
671		status = "FREE";
672		break;
673	case DB_LSTAT_HELD:
674		status = "HELD";
675		break;
676	case DB_LSTAT_PENDING:
677		status = "PENDING";
678		break;
679	case DB_LSTAT_WAITING:
680		status = "WAIT";
681		break;
682	default:
683		status = "UNKNOWN";
684		break;
685	}
686	__db_msgadd(env, mbp, "%8lx %-10s %4lu %-7s ",
687	    (u_long)((DB_LOCKER *)R_ADDR(&lt->reginfo, lp->holder))->id,
688	    mode, (u_long)lp->refcount, status);
689
690	lockobj = (DB_LOCKOBJ *)((u_int8_t *)lp + lp->obj);
691	ptr = SH_DBT_PTR(&lockobj->lockobj);
692	if (ispgno && lockobj->lockobj.size == sizeof(struct __db_ilock)) {
693		/* Assume this is a DBT lock. */
694		memcpy(&pgno, ptr, sizeof(db_pgno_t));
695		fidp = (u_int32_t *)(ptr + sizeof(db_pgno_t));
696		type = *(u_int32_t *)(ptr + sizeof(db_pgno_t) + DB_FILE_ID_LEN);
697		(void)__dbreg_get_name(
698		    lt->env, (u_int8_t *)fidp, &fname, &dname);
699		if (fname == NULL && dname == NULL)
700			__db_msgadd(env, mbp, "(%lx %lx %lx %lx %lx) ",
701			    (u_long)fidp[0], (u_long)fidp[1], (u_long)fidp[2],
702			    (u_long)fidp[3], (u_long)fidp[4]);
703		else {
704			if (fname != NULL && dname != NULL) {
705				(void)snprintf(namebuf, sizeof(namebuf),
706				    "%14s:%-10s", fname, dname);
707				p = namebuf;
708			} else if (fname != NULL)
709				p = fname;
710			else
711				p = dname;
712			__db_msgadd(env, mbp, "%-25s ", p);
713		}
714		__db_msgadd(env, mbp, "%-7s %7lu",
715			type == DB_PAGE_LOCK ? "page" :
716			type == DB_RECORD_LOCK ? "record" : "handle",
717			(u_long)pgno);
718	} else {
719		__db_msgadd(env, mbp, "0x%lx ",
720		    (u_long)R_OFFSET(&lt->reginfo, lockobj));
721		__db_prbytes(env, mbp, ptr, lockobj->lockobj.size);
722	}
723	DB_MSGBUF_FLUSH(env, mbp);
724}
725
726#else /* !HAVE_STATISTICS */
727
728int
729__lock_stat_pp(dbenv, statp, flags)
730	DB_ENV *dbenv;
731	DB_LOCK_STAT **statp;
732	u_int32_t flags;
733{
734	COMPQUIET(statp, NULL);
735	COMPQUIET(flags, 0);
736
737	return (__db_stat_not_built(dbenv->env));
738}
739
740int
741__lock_stat_print_pp(dbenv, flags)
742	DB_ENV *dbenv;
743	u_int32_t flags;
744{
745	COMPQUIET(flags, 0);
746
747	return (__db_stat_not_built(dbenv->env));
748}
749#endif
750