Locking implementation from 2.4.14.
authordrepper <drepper>
Tue, 9 Jun 1998 15:01:16 +0000 (15:01 +0000)
committerdrepper <drepper>
Tue, 9 Jun 1998 15:01:16 +0000 (15:01 +0000)
db2/lock/lock_region.c [new file with mode: 0644]

diff --git a/db2/lock/lock_region.c b/db2/lock/lock_region.c
new file mode 100644 (file)
index 0000000..b597560
--- /dev/null
@@ -0,0 +1,726 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996, 1997, 1998
+ *     Sleepycat Software.  All rights reserved.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)lock_region.c        10.15 (Sleepycat) 6/2/98";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#endif
+
+#include "db_int.h"
+#include "shqueue.h"
+#include "db_shash.h"
+#include "lock.h"
+#include "common_ext.h"
+
+static u_int32_t __lock_count_locks __P((DB_LOCKREGION *));
+static u_int32_t __lock_count_objs __P((DB_LOCKREGION *));
+static void     __lock_dump_locker __P((DB_LOCKTAB *, DB_LOCKOBJ *, FILE *));
+static void     __lock_dump_object __P((DB_LOCKTAB *, DB_LOCKOBJ *, FILE *));
+static const char *__lock_dump_status __P((db_status_t));
+static void     __lock_reset_region __P((DB_LOCKTAB *));
+static int      __lock_tabinit __P((DB_ENV *, DB_LOCKREGION *));
+
+int
+lock_open(path, flags, mode, dbenv, ltp)
+       const char *path;
+       u_int32_t flags;
+       int mode;
+       DB_ENV *dbenv;
+       DB_LOCKTAB **ltp;
+{
+       DB_LOCKTAB *lt;
+       u_int32_t lock_modes, maxlocks, regflags;
+       int ret;
+
+       /* Validate arguments. */
+#ifdef HAVE_SPINLOCKS
+#define        OKFLAGS (DB_CREATE | DB_THREAD)
+#else
+#define        OKFLAGS (DB_CREATE)
+#endif
+       if ((ret = __db_fchk(dbenv, "lock_open", flags, OKFLAGS)) != 0)
+               return (ret);
+
+       /* Create the lock table structure. */
+       if ((lt = (DB_LOCKTAB *)__db_calloc(1, sizeof(DB_LOCKTAB))) == NULL) {
+               __db_err(dbenv, "%s", strerror(ENOMEM));
+               return (ENOMEM);
+       }
+       lt->dbenv = dbenv;
+
+       /* Grab the values that we need to compute the region size. */
+       lock_modes = DB_LOCK_RW_N;
+       maxlocks = DB_LOCK_DEFAULT_N;
+       regflags = REGION_SIZEDEF;
+       if (dbenv != NULL) {
+               if (dbenv->lk_modes != 0) {
+                       lock_modes = dbenv->lk_modes;
+                       regflags = 0;
+               }
+               if (dbenv->lk_max != 0) {
+                       maxlocks = dbenv->lk_max;
+                       regflags = 0;
+               }
+       }
+
+       /* Join/create the lock region. */
+       lt->reginfo.dbenv = dbenv;
+       lt->reginfo.appname = DB_APP_NONE;
+       if (path == NULL)
+               lt->reginfo.path = NULL;
+       else
+               if ((lt->reginfo.path = (char *)__db_strdup(path)) == NULL)
+                       goto err;
+       lt->reginfo.file = DB_DEFAULT_LOCK_FILE;
+       lt->reginfo.mode = mode;
+       lt->reginfo.size =
+           LOCK_REGION_SIZE(lock_modes, maxlocks, __db_tablesize(maxlocks));
+       lt->reginfo.dbflags = flags;
+       lt->reginfo.addr = NULL;
+       lt->reginfo.fd = -1;
+       lt->reginfo.flags = regflags;
+
+       if ((ret = __db_rattach(&lt->reginfo)) != 0)
+               goto err;
+
+       /* Now set up the pointer to the region. */
+       lt->region = lt->reginfo.addr;
+
+       /* Initialize the region if we created it. */
+       if (F_ISSET(&lt->reginfo, REGION_CREATED)) {
+               lt->region->maxlocks = maxlocks;
+               lt->region->nmodes = lock_modes;
+               if ((ret = __lock_tabinit(dbenv, lt->region)) != 0)
+                       goto err;
+       } else {
+               /* Check for an unexpected region. */
+               if (lt->region->magic != DB_LOCKMAGIC) {
+                       __db_err(dbenv,
+                           "lock_open: %s: bad magic number", path);
+                       ret = EINVAL;
+                       goto err;
+               }
+       }
+
+       /* Check for automatic deadlock detection. */
+       if (dbenv != NULL && dbenv->lk_detect != DB_LOCK_NORUN) {
+               if (lt->region->detect != DB_LOCK_NORUN &&
+                   dbenv->lk_detect != DB_LOCK_DEFAULT &&
+                   lt->region->detect != dbenv->lk_detect) {
+                       __db_err(dbenv,
+                   "lock_open: incompatible deadlock detector mode");
+                       ret = EINVAL;
+                       goto err;
+               }
+               if (lt->region->detect == DB_LOCK_NORUN)
+                       lt->region->detect = dbenv->lk_detect;
+       }
+
+       /* Set up remaining pointers into region. */
+       lt->conflicts = (u_int8_t *)lt->region + sizeof(DB_LOCKREGION);
+       lt->hashtab =
+           (DB_HASHTAB *)((u_int8_t *)lt->region + lt->region->hash_off);
+       lt->mem = (void *)((u_int8_t *)lt->region + lt->region->mem_off);
+
+       UNLOCK_LOCKREGION(lt);
+       *ltp = lt;
+       return (0);
+
+err:   if (lt->reginfo.addr != NULL) {
+               UNLOCK_LOCKREGION(lt);
+               (void)__db_rdetach(&lt->reginfo);
+               if (F_ISSET(&lt->reginfo, REGION_CREATED))
+                       (void)lock_unlink(path, 1, dbenv);
+       }
+
+       if (lt->reginfo.path != NULL)
+               FREES(lt->reginfo.path);
+       FREE(lt, sizeof(*lt));
+       return (ret);
+}
+
+/*
+ * __lock_tabinit --
+ *     Initialize the lock region.
+ */
+static int
+__lock_tabinit(dbenv, lrp)
+       DB_ENV *dbenv;
+       DB_LOCKREGION *lrp;
+{
+       struct __db_lock *lp;
+       struct lock_header *tq_head;
+       struct obj_header *obj_head;
+       DB_LOCKOBJ *op;
+       u_int32_t i, nelements;
+       const u_int8_t *conflicts;
+       u_int8_t *curaddr;
+
+       conflicts = dbenv == NULL || dbenv->lk_conflicts == NULL ?
+           db_rw_conflicts : dbenv->lk_conflicts;
+
+       lrp->table_size = __db_tablesize(lrp->maxlocks);
+       lrp->magic = DB_LOCKMAGIC;
+       lrp->version = DB_LOCKVERSION;
+       lrp->id = 0;
+       /*
+        * These fields (lrp->maxlocks, lrp->nmodes) are initialized
+        * in the caller, since we had to grab those values to size
+        * the region.
+        */
+       lrp->need_dd = 0;
+       lrp->detect = DB_LOCK_NORUN;
+       lrp->numobjs = lrp->maxlocks;
+       lrp->nlockers = 0;
+       lrp->mem_bytes = ALIGN(STRING_SIZE(lrp->maxlocks), sizeof(size_t));
+       lrp->increment = lrp->hdr.size / 2;
+       lrp->nconflicts = 0;
+       lrp->nrequests = 0;
+       lrp->nreleases = 0;
+       lrp->ndeadlocks = 0;
+
+       /*
+        * As we write the region, we've got to maintain the alignment
+        * for the structures that follow each chunk.  This information
+        * ends up being encapsulated both in here as well as in the
+        * lock.h file for the XXX_SIZE macros.
+        */
+       /* Initialize conflict matrix. */
+       curaddr = (u_int8_t *)lrp + sizeof(DB_LOCKREGION);
+       memcpy(curaddr, conflicts, lrp->nmodes * lrp->nmodes);
+       curaddr += lrp->nmodes * lrp->nmodes;
+
+       /*
+        * Initialize hash table.
+        */
+       curaddr = (u_int8_t *)ALIGNP(curaddr, LOCK_HASH_ALIGN);
+       lrp->hash_off = curaddr - (u_int8_t *)lrp;
+       nelements = lrp->table_size;
+       __db_hashinit(curaddr, nelements);
+       curaddr += nelements * sizeof(DB_HASHTAB);
+
+       /*
+        * Initialize locks onto a free list. Since locks contains mutexes,
+        * we need to make sure that each lock is aligned on a MUTEX_ALIGNMENT
+        * boundary.
+        */
+       curaddr = (u_int8_t *)ALIGNP(curaddr, MUTEX_ALIGNMENT);
+       tq_head = &lrp->free_locks;
+       SH_TAILQ_INIT(tq_head);
+
+       for (i = 0; i++ < lrp->maxlocks;
+           curaddr += ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT)) {
+               lp = (struct __db_lock *)curaddr;
+               lp->status = DB_LSTAT_FREE;
+               SH_TAILQ_INSERT_HEAD(tq_head, lp, links, __db_lock);
+       }
+
+       /* Initialize objects onto a free list.  */
+       obj_head = &lrp->free_objs;
+       SH_TAILQ_INIT(obj_head);
+
+       for (i = 0; i++ < lrp->maxlocks; curaddr += sizeof(DB_LOCKOBJ)) {
+               op = (DB_LOCKOBJ *)curaddr;
+               SH_TAILQ_INSERT_HEAD(obj_head, op, links, __db_lockobj);
+       }
+
+       /*
+        * Initialize the string space; as for all shared memory allocation
+        * regions, this requires size_t alignment, since we store the
+        * lengths of malloc'd areas in the area.
+        */
+       curaddr = (u_int8_t *)ALIGNP(curaddr, sizeof(size_t));
+       lrp->mem_off = curaddr - (u_int8_t *)lrp;
+       __db_shalloc_init(curaddr, lrp->mem_bytes);
+       return (0);
+}
+
+int
+lock_close(lt)
+       DB_LOCKTAB *lt;
+{
+       int ret;
+
+       if ((ret = __db_rdetach(&lt->reginfo)) != 0)
+               return (ret);
+
+       if (lt->reginfo.path != NULL)
+               FREES(lt->reginfo.path);
+       FREE(lt, sizeof(*lt));
+
+       return (0);
+}
+
+int
+lock_unlink(path, force, dbenv)
+       const char *path;
+       int force;
+       DB_ENV *dbenv;
+{
+       REGINFO reginfo;
+       int ret;
+
+       memset(&reginfo, 0, sizeof(reginfo));
+       reginfo.dbenv = dbenv;
+       reginfo.appname = DB_APP_NONE;
+       if (path != NULL && (reginfo.path = (char *)__db_strdup(path)) == NULL)
+               return (ENOMEM);
+       reginfo.file = DB_DEFAULT_LOCK_FILE;
+       ret = __db_runlink(&reginfo, force);
+       if (reginfo.path != NULL)
+               FREES(reginfo.path);
+       return (ret);
+}
+
+/*
+ * __lock_validate_region --
+ *     Called at every interface to verify if the region has changed size,
+ *     and if so, to remap the region in and reset the process' pointers.
+ *
+ * PUBLIC: int __lock_validate_region __P((DB_LOCKTAB *));
+ */
+int
+__lock_validate_region(lt)
+       DB_LOCKTAB *lt;
+{
+       int ret;
+
+       if (lt->reginfo.size == lt->region->hdr.size)
+               return (0);
+
+       /* Detach/reattach the region. */
+       if ((ret = __db_rreattach(&lt->reginfo, lt->region->hdr.size)) != 0)
+               return (ret);
+
+       /* Reset region information. */
+       lt->region = lt->reginfo.addr;
+       __lock_reset_region(lt);
+
+       return (0);
+}
+
+/*
+ * __lock_grow_region --
+ *     We have run out of space; time to grow the region.
+ *
+ * PUBLIC: int __lock_grow_region __P((DB_LOCKTAB *, int, size_t));
+ */
+int
+__lock_grow_region(lt, which, howmuch)
+       DB_LOCKTAB *lt;
+       int which;
+       size_t howmuch;
+{
+       struct __db_lock *newl;
+       struct lock_header *lock_head;
+       struct obj_header *obj_head;
+       DB_LOCKOBJ *op;
+       DB_LOCKREGION *lrp;
+       float lock_ratio, obj_ratio;
+       size_t incr, oldsize, used, usedmem;
+       u_int32_t i, newlocks, newmem, newobjs, usedlocks, usedobjs;
+       u_int8_t *curaddr;
+       int ret;
+
+       lrp = lt->region;
+       oldsize = lrp->hdr.size;
+       incr = lrp->increment;
+
+       /* Figure out how much of each sort of space we have. */
+       usedmem = lrp->mem_bytes - __db_shalloc_count(lt->mem);
+       usedobjs = lrp->numobjs - __lock_count_objs(lrp);
+       usedlocks = lrp->maxlocks - __lock_count_locks(lrp);
+
+       /*
+        * Figure out what fraction of the used space belongs to each
+        * different type of "thing" in the region.  Then partition the
+        * new space up according to this ratio.
+        */
+       used = usedmem +
+           usedlocks * ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT) +
+           usedobjs * sizeof(DB_LOCKOBJ);
+
+       lock_ratio = usedlocks *
+           ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT) / (float)used;
+       obj_ratio = usedobjs * sizeof(DB_LOCKOBJ) / (float)used;
+
+       newlocks = (u_int32_t)(lock_ratio *
+           incr / ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT));
+       newobjs = (u_int32_t)(obj_ratio * incr / sizeof(DB_LOCKOBJ));
+       newmem = incr -
+           (newobjs * sizeof(DB_LOCKOBJ) +
+           newlocks * ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT));
+
+       /*
+        * Make sure we allocate enough memory for the object being
+        * requested.
+        */
+       switch (which) {
+       case DB_LOCK_LOCK:
+               if (newlocks == 0) {
+                       newlocks = 10;
+                       incr += newlocks * sizeof(struct __db_lock);
+               }
+               break;
+       case DB_LOCK_OBJ:
+               if (newobjs == 0) {
+                       newobjs = 10;
+                       incr += newobjs * sizeof(DB_LOCKOBJ);
+               }
+               break;
+       case DB_LOCK_MEM:
+               if (newmem < howmuch * 2) {
+                       incr += howmuch * 2 - newmem;
+                       newmem = howmuch * 2;
+               }
+               break;
+       }
+
+       newmem += ALIGN(incr, sizeof(size_t)) - incr;
+       incr = ALIGN(incr, sizeof(size_t));
+
+       /*
+        * Since we are going to be allocating locks at the beginning of the
+        * new chunk, we need to make sure that the chunk is MUTEX_ALIGNMENT
+        * aligned.  We did not guarantee this when we created the region, so
+        * we may need to pad the old region by extra bytes to ensure this
+        * alignment.
+        */
+       incr += ALIGN(oldsize, MUTEX_ALIGNMENT) - oldsize;
+
+       __db_err(lt->dbenv,
+           "Growing lock region: %lu locks %lu objs %lu bytes",
+           (u_long)newlocks, (u_long)newobjs, (u_long)newmem);
+
+       if ((ret = __db_rgrow(&lt->reginfo, oldsize + incr)) != 0)
+               return (ret);
+       lt->region = lt->reginfo.addr;
+       __lock_reset_region(lt);
+
+       /* Update region parameters. */
+       lrp = lt->region;
+       lrp->increment = incr << 1;
+       lrp->maxlocks += newlocks;
+       lrp->numobjs += newobjs;
+       lrp->mem_bytes += newmem;
+
+       curaddr = (u_int8_t *)lrp + oldsize;
+       curaddr = (u_int8_t *)ALIGNP(curaddr, MUTEX_ALIGNMENT);
+
+       /* Put new locks onto the free list. */
+       lock_head = &lrp->free_locks;
+       for (i = 0; i++ < newlocks;
+           curaddr += ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT)) {
+               newl = (struct __db_lock *)curaddr;
+               SH_TAILQ_INSERT_HEAD(lock_head, newl, links, __db_lock);
+       }
+
+       /* Put new objects onto the free list.  */
+       obj_head = &lrp->free_objs;
+       for (i = 0; i++ < newobjs; curaddr += sizeof(DB_LOCKOBJ)) {
+               op = (DB_LOCKOBJ *)curaddr;
+               SH_TAILQ_INSERT_HEAD(obj_head, op, links, __db_lockobj);
+       }
+
+       *((size_t *)curaddr) = newmem - sizeof(size_t);
+       curaddr += sizeof(size_t);
+       __db_shalloc_free(lt->mem, curaddr);
+
+       return (0);
+}
+
+static void
+__lock_reset_region(lt)
+       DB_LOCKTAB *lt;
+{
+       lt->conflicts = (u_int8_t *)lt->region + sizeof(DB_LOCKREGION);
+       lt->hashtab =
+           (DB_HASHTAB *)((u_int8_t *)lt->region + lt->region->hash_off);
+       lt->mem = (void *)((u_int8_t *)lt->region + lt->region->mem_off);
+}
+
+/*
+ * lock_stat --
+ *     Return LOCK statistics.
+ */
+int
+lock_stat(lt, gspp, db_malloc)
+       DB_LOCKTAB *lt;
+       DB_LOCK_STAT **gspp;
+       void *(*db_malloc) __P((size_t));
+{
+       DB_LOCKREGION *rp;
+
+       *gspp = NULL;
+
+       if ((*gspp = db_malloc == NULL ?
+           (DB_LOCK_STAT *)__db_malloc(sizeof(**gspp)) :
+           (DB_LOCK_STAT *)db_malloc(sizeof(**gspp))) == NULL)
+               return (ENOMEM);
+
+       /* Copy out the global statistics. */
+       LOCK_LOCKREGION(lt);
+
+       rp = lt->region;
+       (*gspp)->st_magic = rp->magic;
+       (*gspp)->st_version = rp->version;
+       (*gspp)->st_maxlocks = rp->maxlocks;
+       (*gspp)->st_nmodes = rp->nmodes;
+       (*gspp)->st_numobjs = rp->numobjs;
+       (*gspp)->st_nlockers = rp->nlockers;
+       (*gspp)->st_nconflicts = rp->nconflicts;
+       (*gspp)->st_nrequests = rp->nrequests;
+       (*gspp)->st_nreleases = rp->nreleases;
+       (*gspp)->st_ndeadlocks = rp->ndeadlocks;
+       (*gspp)->st_region_nowait = rp->hdr.lock.mutex_set_nowait;
+       (*gspp)->st_region_wait = rp->hdr.lock.mutex_set_wait;
+       (*gspp)->st_refcnt = rp->hdr.refcnt;
+       (*gspp)->st_regsize = rp->hdr.size;
+
+       UNLOCK_LOCKREGION(lt);
+
+       return (0);
+}
+
+static u_int32_t
+__lock_count_locks(lrp)
+       DB_LOCKREGION *lrp;
+{
+       struct __db_lock *newl;
+       u_int32_t count;
+
+       count = 0;
+       for (newl = SH_TAILQ_FIRST(&lrp->free_locks, __db_lock);
+           newl != NULL;
+           newl = SH_TAILQ_NEXT(newl, links, __db_lock))
+               count++;
+
+       return (count);
+}
+
+static u_int32_t
+__lock_count_objs(lrp)
+       DB_LOCKREGION *lrp;
+{
+       DB_LOCKOBJ *obj;
+       u_int32_t count;
+
+       count = 0;
+       for (obj = SH_TAILQ_FIRST(&lrp->free_objs, __db_lockobj);
+           obj != NULL;
+           obj = SH_TAILQ_NEXT(obj, links, __db_lockobj))
+               count++;
+
+       return (count);
+}
+
+#define        LOCK_DUMP_CONF          0x001           /* Conflict matrix. */
+#define        LOCK_DUMP_FREE          0x002           /* Display lock free list. */
+#define        LOCK_DUMP_LOCKERS       0x004           /* Display lockers. */
+#define        LOCK_DUMP_MEM           0x008           /* Display region memory. */
+#define        LOCK_DUMP_OBJECTS       0x010           /* Display objects. */
+#define        LOCK_DUMP_ALL           0x01f           /* Display all. */
+
+/*
+ * __lock_dump_region --
+ *
+ * PUBLIC: void __lock_dump_region __P((DB_LOCKTAB *, char *, FILE *));
+ */
+void
+__lock_dump_region(lt, area, fp)
+       DB_LOCKTAB *lt;
+       char *area;
+       FILE *fp;
+{
+       struct __db_lock *lp;
+       DB_LOCKOBJ *op;
+       DB_LOCKREGION *lrp;
+       u_int32_t flags, i, j;
+       int label;
+
+       /* Make it easy to call from the debugger. */
+       if (fp == NULL)
+               fp = stderr;
+
+       for (flags = 0; *area != '\0'; ++area)
+               switch (*area) {
+               case 'A':
+                       LF_SET(LOCK_DUMP_ALL);
+                       break;
+               case 'c':
+                       LF_SET(LOCK_DUMP_CONF);
+                       break;
+               case 'f':
+                       LF_SET(LOCK_DUMP_FREE);
+                       break;
+               case 'l':
+                       LF_SET(LOCK_DUMP_LOCKERS);
+                       break;
+               case 'm':
+                       LF_SET(LOCK_DUMP_MEM);
+                       break;
+               case 'o':
+                       LF_SET(LOCK_DUMP_OBJECTS);
+                       break;
+               }
+
+       lrp = lt->region;
+
+       fprintf(fp, "%s\nLock region parameters\n", DB_LINE);
+       fprintf(fp, "%s: %lu, %s: %lu, %s: %lu, %s: %lu\n%s: %lu, %s: %lu\n",
+           "table size", (u_long)lrp->table_size,
+           "hash_off", (u_long)lrp->hash_off,
+           "increment", (u_long)lrp->increment,
+           "mem_off", (u_long)lrp->mem_off,
+           "mem_bytes", (u_long)lrp->mem_bytes,
+           "need_dd", (u_long)lrp->need_dd);
+
+       if (LF_ISSET(LOCK_DUMP_CONF)) {
+               fprintf(fp, "\n%s\nConflict matrix\n", DB_LINE);
+               for (i = 0; i < lrp->nmodes; i++) {
+                       for (j = 0; j < lrp->nmodes; j++)
+                               fprintf(fp, "%lu\t",
+                                   (u_long)lt->conflicts[i * lrp->nmodes + j]);
+                       fprintf(fp, "\n");
+               }
+       }
+
+       if (LF_ISSET(LOCK_DUMP_LOCKERS | LOCK_DUMP_OBJECTS)) {
+               fprintf(fp, "%s\nLock hash buckets\n", DB_LINE);
+               for (i = 0; i < lrp->table_size; i++) {
+                       label = 1;
+                       for (op = SH_TAILQ_FIRST(&lt->hashtab[i], __db_lockobj);
+                           op != NULL;
+                           op = SH_TAILQ_NEXT(op, links, __db_lockobj)) {
+                               if (LF_ISSET(LOCK_DUMP_LOCKERS) &&
+                                   op->type == DB_LOCK_LOCKER) {
+                                       if (label) {
+                                               fprintf(fp,
+                                                   "Bucket %lu:\n", (u_long)i);
+                                               label = 0;
+                                       }
+                                       __lock_dump_locker(lt, op, fp);
+                               }
+                               if (LF_ISSET(LOCK_DUMP_OBJECTS) &&
+                                   op->type == DB_LOCK_OBJTYPE) {
+                                       if (label) {
+                                               fprintf(fp,
+                                                   "Bucket %lu:\n", (u_long)i);
+                                               label = 0;
+                                       }
+                                       __lock_dump_object(lt, op, fp);
+                               }
+                       }
+               }
+       }
+
+       if (LF_ISSET(LOCK_DUMP_FREE)) {
+               fprintf(fp, "%s\nLock free list\n", DB_LINE);
+               for (lp = SH_TAILQ_FIRST(&lrp->free_locks, __db_lock);
+                   lp != NULL;
+                   lp = SH_TAILQ_NEXT(lp, links, __db_lock))
+                       fprintf(fp, "0x%x: %lu\t%lu\t%s\t0x%x\n", (u_int)lp,
+                           (u_long)lp->holder, (u_long)lp->mode,
+                           __lock_dump_status(lp->status), (u_int)lp->obj);
+
+               fprintf(fp, "%s\nObject free list\n", DB_LINE);
+               for (op = SH_TAILQ_FIRST(&lrp->free_objs, __db_lockobj);
+                   op != NULL;
+                   op = SH_TAILQ_NEXT(op, links, __db_lockobj))
+                       fprintf(fp, "0x%x\n", (u_int)op);
+       }
+
+       if (LF_ISSET(LOCK_DUMP_MEM))
+               __db_shalloc_dump(lt->mem, fp);
+}
+
+static void
+__lock_dump_locker(lt, op, fp)
+       DB_LOCKTAB *lt;
+       DB_LOCKOBJ *op;
+       FILE *fp;
+{
+       struct __db_lock *lp;
+       u_int32_t locker;
+       void *ptr;
+
+       ptr = SH_DBT_PTR(&op->lockobj);
+       memcpy(&locker, ptr, sizeof(u_int32_t));
+       fprintf(fp, "L %lx", (u_long)locker);
+
+       lp = SH_LIST_FIRST(&op->heldby, __db_lock);
+       if (lp == NULL) {
+               fprintf(fp, "\n");
+               return;
+       }
+       for (; lp != NULL; lp = SH_LIST_NEXT(lp, locker_links, __db_lock))
+               __lock_printlock(lt, lp, 0);
+}
+
+static void
+__lock_dump_object(lt, op, fp)
+       DB_LOCKTAB *lt;
+       DB_LOCKOBJ *op;
+       FILE *fp;
+{
+       struct __db_lock *lp;
+       u_int32_t j;
+       u_int8_t *ptr;
+       u_int ch;
+
+       ptr = SH_DBT_PTR(&op->lockobj);
+       for (j = 0; j < op->lockobj.size; ptr++, j++) {
+               ch = *ptr;
+               fprintf(fp, isprint(ch) ? "%c" : "\\%o", ch);
+       }
+       fprintf(fp, "\n");
+
+       fprintf(fp, "H:");
+       for (lp =
+           SH_TAILQ_FIRST(&op->holders, __db_lock);
+           lp != NULL;
+           lp = SH_TAILQ_NEXT(lp, links, __db_lock))
+               __lock_printlock(lt, lp, 0);
+       lp = SH_TAILQ_FIRST(&op->waiters, __db_lock);
+       if (lp != NULL) {
+               fprintf(fp, "\nW:");
+               for (; lp != NULL; lp = SH_TAILQ_NEXT(lp, links, __db_lock))
+                       __lock_printlock(lt, lp, 0);
+       }
+}
+
+static const char *
+__lock_dump_status(status)
+       db_status_t status;
+{
+       switch (status) {
+       case DB_LSTAT_ABORTED:
+               return ("aborted");
+       case DB_LSTAT_ERR:
+               return ("err");
+       case DB_LSTAT_FREE:
+               return ("free");
+       case DB_LSTAT_HELD:
+               return ("held");
+       case DB_LSTAT_NOGRANT:
+               return ("nogrant");
+       case DB_LSTAT_PENDING:
+               return ("pending");
+       case DB_LSTAT_WAITING:
+               return ("waiting");
+       }
+       return ("unknown status");
+}