6881824 ::whatis needs to be faster, smarter, and modular
authorJonathan Adams <Jonathan.Adams@Sun.COM>
Tue, 22 Sep 2009 13:42:17 -0700
changeset 10610 218c21980cfd
parent 10609 f1cdb59c41b4
child 10611 5facf6222c62
6881824 ::whatis needs to be faster, smarter, and modular
usr/src/cmd/mdb/Makefile.kmdb
usr/src/cmd/mdb/Makefile.kmdb.files
usr/src/cmd/mdb/Makefile.mdb
usr/src/cmd/mdb/common/mdb/mdb_cmds.c
usr/src/cmd/mdb/common/mdb/mdb_module.c
usr/src/cmd/mdb/common/mdb/mdb_whatis.c
usr/src/cmd/mdb/common/mdb/mdb_whatis.h
usr/src/cmd/mdb/common/mdb/mdb_whatis_impl.h
usr/src/cmd/mdb/common/modules/conf/mapfile-extern
usr/src/cmd/mdb/common/modules/genunix/genunix.c
usr/src/cmd/mdb/common/modules/genunix/kmem.c
usr/src/cmd/mdb/common/modules/genunix/kmem.h
usr/src/cmd/mdb/common/modules/libc/libc.c
usr/src/cmd/mdb/common/modules/libumem/libumem.c
usr/src/cmd/mdb/common/modules/libumem/umem.c
usr/src/cmd/mdb/intel/Makefile.kmdb
usr/src/cmd/mdb/sparc/Makefile.kmdb
usr/src/cmd/mdb/sun4u/Makefile.kmdb
usr/src/cmd/mdb/sun4u/modules/unix/unix.c
usr/src/cmd/mdb/sun4v/Makefile.kmdb
--- a/usr/src/cmd/mdb/Makefile.kmdb	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/Makefile.kmdb	Tue Sep 22 13:42:17 2009 -0700
@@ -101,6 +101,16 @@
 MAPFILE = mapfile
 MAPFILE_INTERMEDIATE = $(MAPFILE).i
 MAPFILE_TEMPLATE = ../../../common/kmdb/mapfile_skel
+MAPFILE_SOURCES_COMMON = \
+	../../../common/kmdb/kmdb_dpi.h \
+	../../../common/kmdb/kmdb_kctl.h \
+	../../../common/kmdb/kmdb_kdi.h \
+	../../../common/kmdb/kmdb_wr.h \
+	../../../common/mdb/mdb_ctf.h \
+	../../../common/mdb/mdb_ks.h \
+	../../../common/mdb/mdb_modapi.h \
+	../../../common/mdb/mdb_param.h \
+	../../../common/mdb/mdb_whatis.h
 
 mdb_lex.o mdb_grammar.o := CCVERBOSE =
 
--- a/usr/src/cmd/mdb/Makefile.kmdb.files	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/Makefile.kmdb.files	Tue Sep 22 13:42:17 2009 -0700
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 KMDBSRCS += \
 	ffs.c \
@@ -85,6 +83,7 @@
 	mdb_value.c \
 	mdb_vcb.c \
 	mdb_wcb.c \
+	mdb_whatis.c \
 	kmdb_wr.c
 
 KMDBML +=
--- a/usr/src/cmd/mdb/Makefile.mdb	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/Makefile.mdb	Tue Sep 22 13:42:17 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -81,7 +81,8 @@
 	mdb_umem.c \
 	mdb_value.c \
 	mdb_vcb.c \
-	mdb_wcb.c
+	mdb_wcb.c \
+	mdb_whatis.c
 
 $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
 CPPFLAGS += -D_MDB -I. -I../.. -I../../../common
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Tue Sep 22 13:42:17 2009 -0700
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/elf.h>
 #include <sys/elf_SPARC.h>
 
@@ -60,6 +58,8 @@
 #include <mdb/mdb_set.h>
 #include <mdb/mdb_demangle.h>
 #include <mdb/mdb_ctf.h>
+#include <mdb/mdb_whatis.h>
+#include <mdb/mdb_whatis_impl.h>
 #include <mdb/mdb_macalias.h>
 #ifdef _KMDB
 #include <kmdb/kmdb_kdi.h>
@@ -1406,23 +1406,33 @@
 	return (DCMD_OK);
 }
 
+static const char *
+map_name(const mdb_map_t *map, const char *name)
+{
+	if (map->map_flags & MDB_TGT_MAP_HEAP)
+		return ("[ heap ]");
+	if (name != NULL && name[0] != 0)
+		return (name);
+
+	if (map->map_flags & MDB_TGT_MAP_SHMEM)
+		return ("[ shmem ]");
+	if (map->map_flags & MDB_TGT_MAP_STACK)
+		return ("[ stack ]");
+	if (map->map_flags & MDB_TGT_MAP_ANON)
+		return ("[ anon ]");
+	if (map->map_name != NULL)
+		return (map->map_name);
+	return ("[ unknown ]");
+}
+
 /*ARGSUSED*/
 static int
 print_map(void *ignored, const mdb_map_t *map, const char *name)
 {
-	if (name == NULL || *name == '\0') {
-		if (map->map_flags & MDB_TGT_MAP_SHMEM)
-			name = "[ shmem ]";
-		else if (map->map_flags & MDB_TGT_MAP_STACK)
-			name = "[ stack ]";
-		else if (map->map_flags & MDB_TGT_MAP_HEAP)
-			name = "[ heap ]";
-		else if (map->map_flags & MDB_TGT_MAP_ANON)
-			name = "[ anon ]";
-	}
-
-	mdb_printf("%?p %?p %?lx %s\n", map->map_base, map->map_base +
-	    map->map_size, map->map_size, name ? name : map->map_name);
+	name = map_name(map, name);
+
+	mdb_printf("%?p %?p %?lx %s\n", map->map_base,
+	    map->map_base + map->map_size, map->map_size, name);
 	return (0);
 }
 
@@ -1460,6 +1470,29 @@
 	return (DCMD_OK);
 }
 
+static int
+whatis_map_callback(void *wp, const mdb_map_t *map, const char *name)
+{
+	mdb_whatis_t *w = wp;
+	uintptr_t cur;
+
+	name = map_name(map, name);
+
+	while (mdb_whatis_match(w, map->map_base, map->map_size, &cur))
+		mdb_whatis_report_address(w, cur, "in %s [%p,%p)\n",
+		    name, map->map_base, map->map_base + map->map_size);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+whatis_run_mappings(mdb_whatis_t *w, void *ignored)
+{
+	(void) mdb_tgt_mapping_iter(mdb.m_target, whatis_map_callback, w);
+	return (0);
+}
+
 /*ARGSUSED*/
 static int
 objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
@@ -2922,6 +2955,8 @@
 	    cmd_vtop },
 	{ "walk", "?name [variable]", "walk data structure", cmd_walk },
 	{ "walkers", NULL, "list available walkers", cmd_walkers },
+	{ "whatis", ":[-aikqv]", "given an address, return information",
+	    cmd_whatis, whatis_help },
 	{ "whence", "[-v] name ...", "show source of walk or dcmd", cmd_which },
 	{ "which", "[-v] name ...", "show source of walk or dcmd", cmd_which },
 	{ "xdata", NULL, "print list of external data buffers", cmd_xdata },
--- a/usr/src/cmd/mdb/common/mdb/mdb_module.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module.c	Tue Sep 22 13:42:17 2009 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/param.h>
 #include <unistd.h>
 #include <strings.h>
@@ -42,6 +39,7 @@
 #include <mdb/mdb_err.h>
 #include <mdb/mdb_io.h>
 #include <mdb/mdb_frame.h>
+#include <mdb/mdb_whatis_impl.h>
 #include <mdb/mdb.h>
 
 /*
@@ -268,6 +266,8 @@
 	return (0);
 
 err:
+	mdb_whatis_unregister_module(mod);
+
 	if (mod->mod_ctfp != NULL)
 		ctf_close(mod->mod_ctfp);
 
@@ -316,6 +316,8 @@
 		mod->mod_fini();
 	}
 
+	mdb_whatis_unregister_module(mod);
+
 	if (mod->mod_ctfp != NULL)
 		ctf_close(mod->mod_ctfp);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/mdb/mdb_whatis.c	Tue Sep 22 13:42:17 2009 -0700
@@ -0,0 +1,653 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/mdb_modapi.h>
+#include <mdb/mdb.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_whatis.h>
+#include <mdb/mdb_whatis_impl.h>
+#include <limits.h>
+
+static int whatis_debug = 0;
+
+/* for bsearch;  r is an array of {base, size}, e points into w->w_addrs */
+static int
+find_range(const void *r, const void *e)
+{
+	const uintptr_t *range = r;
+	uintptr_t el = *(const uintptr_t *)e;
+
+	if (el < range[0])
+		return (1);
+
+	if ((el - range[0]) >= range[1])
+		return (-1);
+
+	return (0);
+}
+
+/* for qsort; simple uintptr comparator */
+static int
+uintptr_cmp(const void *l, const void *r)
+{
+	uintptr_t lhs = *(const uintptr_t *)l;
+	uintptr_t rhs = *(const uintptr_t *)r;
+
+	if (lhs < rhs)
+		return (-1);
+	if (lhs > rhs)
+		return (1);
+	return (0);
+}
+
+static const uintptr_t *
+mdb_whatis_search(mdb_whatis_t *w, uintptr_t base, size_t size)
+{
+	uintptr_t range[2];
+
+	range[0] = base;
+	range[1] = size;
+
+	return (bsearch(range, w->w_addrs, w->w_naddrs, sizeof (*w->w_addrs),
+	    find_range));
+}
+
+/*
+ * Returns non-zero if and only if there is at least one address of interest
+ * in the range [base, base+size).
+ */
+int
+mdb_whatis_overlaps(mdb_whatis_t *w, uintptr_t base, size_t size)
+{
+	const uintptr_t *f;
+	uint_t offset, cur;
+
+	if (whatis_debug && w->w_magic != WHATIS_MAGIC) {
+		mdb_warn(
+		    "mdb_whatis_overlaps(): bogus mdb_whatis_t pointer\n");
+		return (0);
+	}
+
+	if (w->w_done || size == 0)
+		return (0);
+
+	if (base + size - 1 < base) {
+		mdb_warn("mdb_whatis_overlaps(): [%p, %p+%p) overflows\n",
+		    base, base, size);
+		return (0);
+	}
+
+	f = mdb_whatis_search(w, base, size);
+	if (f == NULL)
+		return (0);
+
+	cur = offset = f - w->w_addrs;
+
+	/*
+	 * We only return success if there's an address we'll actually
+	 * match in the range.  We can quickly check for the ALL flag
+	 * or a non-found address at our match point.
+	 */
+	if ((w->w_flags & WHATIS_ALL) || !w->w_addrfound[cur])
+		return (1);
+
+	/* Search backwards then forwards for a non-found address */
+	while (cur > 0) {
+		cur--;
+
+		if (w->w_addrs[cur] < base)
+			break;
+
+		if (!w->w_addrfound[cur])
+			return (1);
+	}
+
+	for (cur = offset + 1; cur < w->w_naddrs; cur++) {
+		if ((w->w_addrs[cur] - base) >= size)
+			break;
+
+		if (!w->w_addrfound[cur])
+			return (1);
+	}
+
+	return (0);			/* everything has already been seen */
+}
+
+/*
+ * Iteratively search our list of addresses for matches in [base, base+size).
+ */
+int
+mdb_whatis_match(mdb_whatis_t *w, uintptr_t base, size_t size, uintptr_t *out)
+{
+	size_t offset;
+
+	if (whatis_debug) {
+		if (w->w_magic != WHATIS_MAGIC) {
+			mdb_warn(
+			    "mdb_whatis_match(): bogus mdb_whatis_t pointer\n");
+			goto done;
+		}
+	}
+
+	if (w->w_done || size == 0)
+		goto done;
+
+	if (base + size - 1 < base) {
+		mdb_warn("mdb_whatis_match(): [%p, %p+%x) overflows\n",
+		    base, base, size);
+		return (0);
+	}
+
+	if ((offset = w->w_match_next) != 0 &&
+	    (base != w->w_match_base || size != w->w_match_size)) {
+		mdb_warn("mdb_whatis_match(): new range [%p, %p+%p) "
+		    "while still searching [%p, %p+%p)\n",
+		    base, base, size,
+		    w->w_match_base, w->w_match_base, w->w_match_size);
+		offset = 0;
+	}
+
+	if (offset == 0) {
+		const uintptr_t *f = mdb_whatis_search(w, base, size);
+
+		if (f == NULL)
+			goto done;
+
+		offset = (f - w->w_addrs);
+
+		/* Walk backwards until we reach the first match */
+		while (offset > 0 && w->w_addrs[offset - 1] >= base)
+			offset--;
+
+		w->w_match_base = base;
+		w->w_match_size = size;
+	}
+
+	for (; offset < w->w_naddrs && ((w->w_addrs[offset] - base) < size);
+	    offset++) {
+
+		*out = w->w_addrs[offset];
+		w->w_match_next = offset + 1;
+
+		if (w->w_addrfound[offset]) {
+			/* if we're not seeing everything, skip it */
+			if (!(w->w_flags & WHATIS_ALL))
+				continue;
+
+			return (1);
+		}
+
+		/* We haven't seen this address yet. */
+		w->w_found++;
+		w->w_addrfound[offset] = 1;
+
+		/* If we've found them all, we're done */
+		if (w->w_found == w->w_naddrs && !(w->w_flags & WHATIS_ALL))
+			w->w_done = 1;
+
+		return (1);
+	}
+
+done:
+	w->w_match_next = 0;
+	w->w_match_base = 0;
+	w->w_match_size = 0;
+	return (0);
+}
+
+/*
+ * Report a pointer (addr) in an object beginning at (base) in standard
+ * whatis-style.  (format, ...) are mdb_printf() arguments, to be printed
+ * after the address information.  The caller is responsible for printing
+ * a newline (either in format or after the call returns)
+ */
+/*ARGSUSED*/
+void
+mdb_whatis_report_object(mdb_whatis_t *w,
+    uintptr_t addr, uintptr_t base, const char *format, ...)
+{
+	va_list alist;
+
+	if (whatis_debug) {
+		if (mdb_whatis_search(w, addr, 1) == NULL)
+			mdb_warn("mdb_whatis_report_object(): addr "
+			    "%p is not a pointer of interest.\n", addr);
+	}
+
+	if (addr < base)
+		mdb_warn("whatis: addr (%p) is less than base (%p)\n",
+		    addr, base);
+
+	if (addr == base)
+		mdb_printf("%p is ", addr);
+	else
+		mdb_printf("%p is %p+%p, ", addr, base, addr - base);
+
+	if (format == NULL)
+		return;
+
+	va_start(alist, format);
+	mdb_iob_vprintf(mdb.m_out, format, alist);
+	va_end(alist);
+}
+
+/*
+ * Report an address (addr), with symbolic information if available, in
+ * standard whatis-style.  (format, ...) are mdb_printf() arguments, to be
+ * printed after the address information.  The caller is responsible for
+ * printing a newline (either in format or after the call returns)
+ */
+/*ARGSUSED*/
+void
+mdb_whatis_report_address(mdb_whatis_t *w, uintptr_t addr,
+    const char *format, ...)
+{
+	GElf_Sym sym;
+	va_list alist;
+
+	if (whatis_debug) {
+		if (mdb_whatis_search(w, addr, 1) == NULL)
+			mdb_warn("mdb_whatis_report_adddress(): addr "
+			    "%p is not a pointer of interest.\n", addr);
+	}
+
+	mdb_printf("%p is ", addr);
+
+	if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, NULL, 0, &sym) != -1 &&
+	    (addr - (uintptr_t)sym.st_value) < sym.st_size) {
+		mdb_printf("%a, ", addr);
+	}
+
+	va_start(alist, format);
+	mdb_iob_vprintf(mdb.m_out, format, alist);
+	va_end(alist);
+}
+
+uint_t
+mdb_whatis_flags(mdb_whatis_t *w)
+{
+	/* Mask out the internal-only flags */
+	return (w->w_flags & WHATIS_PUBLIC);
+}
+
+uint_t
+mdb_whatis_done(mdb_whatis_t *w)
+{
+	return (w->w_done);
+}
+
+/*
+ * Whatis callback list management
+ */
+typedef struct whatis_callback {
+	uint64_t	wcb_index;
+	mdb_module_t	*wcb_module;
+	const char	*wcb_modname;
+	char		*wcb_name;
+	mdb_whatis_cb_f	*wcb_func;
+	void		*wcb_arg;
+	uint_t		wcb_prio;
+	uint_t		wcb_flags;
+} whatis_callback_t;
+
+static whatis_callback_t builtin_whatis[] = {
+	{ 0, NULL, "mdb", "mappings", whatis_run_mappings, NULL,
+	    WHATIS_PRIO_MIN, WHATIS_REG_NO_ID }
+};
+#define	NBUILTINS	(sizeof (builtin_whatis) / sizeof (*builtin_whatis))
+
+static whatis_callback_t *whatis_cb_start[NBUILTINS];
+static whatis_callback_t **whatis_cb = NULL;	/* callback array */
+static size_t whatis_cb_count;			/* count of callbacks */
+static size_t whatis_cb_size;			/* size of whatis_cb array */
+static uint64_t whatis_cb_index;		/* global count */
+
+#define	WHATIS_CB_SIZE_MIN	8	/* initial allocation size */
+
+static int
+whatis_cbcmp(const void *lhs, const void *rhs)
+{
+	whatis_callback_t *l = *(whatis_callback_t * const *)lhs;
+	whatis_callback_t *r = *(whatis_callback_t * const *)rhs;
+	int ret;
+
+	/* First, handle NULLs; we want them at the end */
+	if (l == NULL && r == NULL)
+		return (0);
+	if (l == NULL)
+		return (1);
+	if (r == NULL)
+		return (-1);
+
+	/* Next, compare priorities */
+	if (l->wcb_prio < r->wcb_prio)
+		return (-1);
+	if (l->wcb_prio > r->wcb_prio)
+		return (1);
+
+	/* then module name */
+	if ((ret = strcmp(l->wcb_modname, r->wcb_modname)) != 0)
+		return (ret);
+
+	/* and finally insertion order */
+	if (l->wcb_index < r->wcb_index)
+		return (-1);
+	if (l->wcb_index > r->wcb_index)
+		return (1);
+
+	mdb_warn("whatis_cbcmp(): can't happen: duplicate indices\n");
+	return (0);
+}
+
+static void
+whatis_init(void)
+{
+	int idx;
+
+	for (idx = 0; idx < NBUILTINS; idx++) {
+		whatis_cb_start[idx] = &builtin_whatis[idx];
+		whatis_cb_start[idx]->wcb_index = idx;
+	}
+	whatis_cb_index = idx;
+
+	whatis_cb = whatis_cb_start;
+	whatis_cb_count = whatis_cb_size = NBUILTINS;
+
+	qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), whatis_cbcmp);
+}
+
+void
+mdb_whatis_register(const char *name, mdb_whatis_cb_f *func, void *arg,
+    uint_t prio, uint_t flags)
+{
+	whatis_callback_t *wcp;
+
+	if (mdb.m_lmod == NULL) {
+		mdb_warn("mdb_whatis_register(): can only be called during "
+		    "module load\n");
+		return;
+	}
+
+	if (strbadid(name)) {
+		mdb_warn("mdb_whatis_register(): whatis name '%s' contains "
+		    "illegal characters\n");
+		return;
+	}
+
+	if ((flags & ~(WHATIS_REG_NO_ID|WHATIS_REG_ID_ONLY)) != 0) {
+		mdb_warn("mdb_whatis_register(): flags (%x) contain unknown "
+		    "flags\n", flags);
+		return;
+	}
+	if ((flags & WHATIS_REG_NO_ID) && (flags & WHATIS_REG_ID_ONLY)) {
+		mdb_warn("mdb_whatis_register(): flags (%x) contains both "
+		    "NO_ID and ID_ONLY.\n", flags);
+		return;
+	}
+
+	if (prio > WHATIS_PRIO_MIN)
+		prio = WHATIS_PRIO_MIN;
+
+	if (whatis_cb == NULL)
+		whatis_init();
+
+	wcp = mdb_zalloc(sizeof (*wcp), UM_SLEEP);
+
+	wcp->wcb_index = whatis_cb_index++;
+	wcp->wcb_prio = prio;
+	wcp->wcb_module = mdb.m_lmod;
+	wcp->wcb_modname = mdb.m_lmod->mod_name;
+	wcp->wcb_name = strdup(name);
+	wcp->wcb_func = func;
+	wcp->wcb_arg = arg;
+	wcp->wcb_flags = flags;
+
+	/*
+	 * See if we need to grow the array;  note that at initialization
+	 * time, whatis_cb_count is greater than whatis_cb_size; this clues
+	 * us in to the fact that the array doesn't need to be freed.
+	 */
+	if (whatis_cb_count == whatis_cb_size) {
+		size_t nsize = MAX(2 * whatis_cb_size, WHATIS_CB_SIZE_MIN);
+
+		size_t obytes = sizeof (*whatis_cb) * whatis_cb_size;
+		size_t nbytes = sizeof (*whatis_cb) * nsize;
+
+		whatis_callback_t **narray = mdb_zalloc(nbytes, UM_SLEEP);
+
+		bcopy(whatis_cb, narray, obytes);
+
+		if (whatis_cb != whatis_cb_start)
+			mdb_free(whatis_cb, obytes);
+		whatis_cb = narray;
+		whatis_cb_size = nsize;
+	}
+
+	/* add it into the table and re-sort */
+	whatis_cb[whatis_cb_count++] = wcp;
+	qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), whatis_cbcmp);
+}
+
+void
+mdb_whatis_unregister_module(mdb_module_t *mod)
+{
+	int found = 0;
+	int idx;
+
+	if (mod == NULL)
+		return;
+
+	for (idx = 0; idx < whatis_cb_count; idx++) {
+		whatis_callback_t *cur = whatis_cb[idx];
+
+		if (cur->wcb_module == mod) {
+			found++;
+			whatis_cb[idx] = NULL;
+
+			strfree(cur->wcb_name);
+			mdb_free(cur, sizeof (*cur));
+		}
+	}
+	/* If any were removed, compact the array */
+	if (found != 0) {
+		qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb),
+		    whatis_cbcmp);
+		whatis_cb_count -= found;
+	}
+}
+
+int
+cmd_whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_whatis_t w;
+	size_t idx;
+	int ret;
+	int keep = 0;
+	int list = 0;
+
+	if (flags & DCMD_PIPE_OUT) {
+		mdb_warn("whatis: cannot be output into a pipe\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb.m_lmod != NULL) {
+		mdb_warn("whatis: cannot be called during module load\n");
+		return (DCMD_ERR);
+	}
+
+	if (whatis_cb == NULL)
+		whatis_init();
+
+	bzero(&w, sizeof (w));
+	w.w_magic = WHATIS_MAGIC;
+
+	whatis_debug = 0;
+
+	if (mdb_getopts(argc, argv,
+	    'D', MDB_OPT_SETBITS, TRUE, &whatis_debug,		/* hidden */
+	    'b', MDB_OPT_SETBITS, WHATIS_BUFCTL, &w.w_flags,	/* hidden */
+	    'l', MDB_OPT_SETBITS, TRUE, &list,			/* hidden */
+	    'a', MDB_OPT_SETBITS, WHATIS_ALL, &w.w_flags,
+	    'i', MDB_OPT_SETBITS, WHATIS_IDSPACE, &w.w_flags,
+	    'k', MDB_OPT_SETBITS, TRUE, &keep,
+	    'q', MDB_OPT_SETBITS, WHATIS_QUIET, &w.w_flags,
+	    'v', MDB_OPT_SETBITS, WHATIS_VERBOSE, &w.w_flags,
+	    NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (list) {
+		mdb_printf("%<u>%-16s %-12s %4s %?s %?s %8s%</u>",
+		    "NAME", "MODULE", "PRIO", "FUNC", "ARG", "FLAGS");
+
+		for (idx = 0; idx < whatis_cb_count; idx++) {
+			whatis_callback_t *cur = whatis_cb[idx];
+
+			const char *curfl =
+			    (cur->wcb_flags & WHATIS_REG_NO_ID) ? "NO_ID" :
+			    (cur->wcb_flags & WHATIS_REG_ID_ONLY) ? "ID_ONLY" :
+			    "none";
+
+			mdb_printf("%-16s %-12s %4d %-?p %-?p %8s\n",
+			    cur->wcb_name, cur->wcb_modname, cur->wcb_prio,
+			    cur->wcb_func, cur->wcb_arg, curfl);
+		}
+		return (DCMD_OK);
+	}
+
+	if (!(flags & DCMD_ADDRSPEC))
+		return (DCMD_USAGE);
+
+	w.w_addrs = &addr;
+	w.w_naddrs = 1;
+
+	/* If our input is a pipe, try to slurp it all up. */
+	if (!keep && (flags & DCMD_PIPE)) {
+		mdb_pipe_t p;
+		mdb_get_pipe(&p);
+
+		if (p.pipe_len != 0) {
+			w.w_addrs = p.pipe_data;
+			w.w_naddrs = p.pipe_len;
+
+			/* sort the address list */
+			qsort(w.w_addrs, w.w_naddrs, sizeof (*w.w_addrs),
+			    uintptr_cmp);
+		}
+	}
+	w.w_addrfound = mdb_zalloc(w.w_naddrs * sizeof (*w.w_addrfound),
+	    UM_SLEEP | UM_GC);
+
+	if (whatis_debug) {
+		mdb_printf("Searching for:\n");
+		for (idx = 0; idx < w.w_naddrs; idx++)
+			mdb_printf("    %p", w.w_addrs[idx]);
+	}
+
+	ret = 0;
+
+	/* call in to the registered handlers */
+	for (idx = 0; idx < whatis_cb_count; idx++) {
+		whatis_callback_t *cur = whatis_cb[idx];
+
+		/* Honor the ident flags */
+		if (w.w_flags & WHATIS_IDSPACE) {
+			if (cur->wcb_flags & WHATIS_REG_NO_ID)
+				continue;
+		} else {
+			if (cur->wcb_flags & WHATIS_REG_ID_ONLY)
+				continue;
+		}
+
+		if (w.w_flags & WHATIS_VERBOSE)
+			mdb_printf("Searching %s`%s...\n",
+			    cur->wcb_modname, cur->wcb_name);
+
+		if (cur->wcb_func(&w, cur->wcb_arg) != 0)
+			ret = 1;
+
+		/* reset the match state for the next callback */
+		w.w_match_next = 0;
+		w.w_match_base = 0;
+		w.w_match_size = 0;
+
+		if (w.w_done)
+			break;
+	}
+
+	/* Report any unexplained pointers */
+	for (idx = 0; idx < w.w_naddrs; idx++) {
+		uintptr_t addr = w.w_addrs[idx];
+
+		if (w.w_addrfound[idx])
+			continue;
+
+		mdb_whatis_report_object(&w, addr, addr, "unknown\n");
+	}
+
+	return ((ret != 0) ? DCMD_ERR : DCMD_OK);
+}
+
+void
+whatis_help(void)
+{
+	int idx;
+
+	mdb_printf("%s\n",
+"Given a virtual address (with -i, an identifier), report where it came\n"
+"from.\n"
+"\n"
+"When fed from a pipeline, ::whatis will not maintain the order the input\n"
+"comes in; addresses will be reported as it finds them. (-k prevents this;\n"
+"the output will be in the same order as the input)\n");
+	(void) mdb_dec_indent(2);
+	mdb_printf("%<b>OPTIONS%</b>\n");
+	(void) mdb_inc_indent(2);
+	mdb_printf("%s",
+"  -a  Report all information about each address/identifier.  The default\n"
+"      behavior is to report only the first (most specific) source for each\n"
+"      address/identifier.\n"
+"  -i  addr is an identifier, not a virtual address.\n"
+"  -k  Do not re-order the input. (may be slower)\n"
+"  -q  Quiet; don't print multi-line reports. (stack traces, etc.)\n"
+"  -v  Verbose output; display information about the progress of the search\n");
+
+	if (mdb.m_lmod != NULL)
+		return;
+
+	(void) mdb_dec_indent(2);
+	mdb_printf("\n%<b>SOURCES%</b>\n\n");
+	(void) mdb_inc_indent(2);
+	mdb_printf("The following information sources will be used:\n\n");
+
+	(void) mdb_inc_indent(2);
+	for (idx = 0; idx < whatis_cb_count; idx++) {
+		whatis_callback_t *cur = whatis_cb[idx];
+
+		mdb_printf("%s`%s\n", cur->wcb_modname, cur->wcb_name);
+	}
+	(void) mdb_dec_indent(2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/mdb/mdb_whatis.h	Tue Sep 22 13:42:17 2009 -0700
@@ -0,0 +1,158 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_WHATIS_H
+#define	_MDB_WHATIS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct mdb_whatis;
+typedef struct mdb_whatis mdb_whatis_t;
+
+/*
+ * int mdb_whatis_overlaps(mdb_whatis_t *w, uintptr_t base, size_t size):
+ *
+ * Returns non-zero if and only if a call to
+ *
+ *	mdb_whatis_match(w, base, size, ...)
+ *
+ * will succeed;  that is, there is an address of interest in the
+ * range [base, base+size).
+ */
+extern int mdb_whatis_overlaps(mdb_whatis_t *, uintptr_t, size_t);
+
+/*
+ * int mdb_whatis_match(mdb_whatis_t *w, uintptr_t base, size_t size,
+ *	uintptr_t *out)
+ *
+ * Perform an iterative search for an address of interest in [base, base+size).
+ * Each call returning a non-zero value returns the next interesting address
+ * in the range.  This must be called repeatedly until it returns a zero
+ * value, indicating that the search is complete.
+ *
+ * For example:
+ *	uintptr_t cur;
+ *
+ *	while (mdb_whatis_match(w, base, size, &cur))
+ *		mdb_whatis_report_object(w, cur, base, "allocated from ...");
+ */
+extern int mdb_whatis_match(mdb_whatis_t *, uintptr_t, size_t, uintptr_t *);
+
+/*
+ * void mdb_whatis_report_address(mdb_whatis_t *w, uintptr_t addr,
+ *	uintptr_t base, const char *format, ...)
+ *
+ * Reports addr (an address from mdb_whatis_match()).  If addr is inside
+ * a symbol, that will be reported. (format, ...) is an mdb_printf()
+ * format string and associated arguments, and will follow a string like
+ * "addr is ".  For example, it could be "in libfoo's text segment\n":
+ *
+ *	addr is in libfoo's text segment
+ *
+ * The caller should make sure to output a newline, either in format or in a
+ * separate mdb_printf() call.
+ */
+extern void mdb_whatis_report_address(mdb_whatis_t *, uintptr_t,
+    const char *, ...);
+
+/*
+ * void mdb_whatis_report_object(mdb_whatis_t *w, uintptr_t addr,
+ *	uintptr_t base, const char *format, ...)
+ *
+ * Reports addr (an address from mdb_whatis_match()) as being part of an
+ * object beginning at base.  (format, ...) is an mdb_printf() format
+ * string and associated arguments, and will follow a string like
+ * "addr is base+offset, ".  For example, it could be "allocated from foo\n":
+ *
+ *	addr is base+offset, allocated from foo
+ *
+ * The caller should make sure to output a newline, either in format or in a
+ * separate mdb_printf() call.
+ */
+extern void mdb_whatis_report_object(mdb_whatis_t *, uintptr_t, uintptr_t,
+    const char *, ...);
+
+/*
+ * uint_t mdb_whatis_flags(mdb_whatis_t *w)
+ *
+ * Reports which flags were passed to ::whatis.  See the flag definitions
+ * for more details.
+ */
+extern uint_t mdb_whatis_flags(mdb_whatis_t *);
+
+#define	WHATIS_BUFCTL	0x1	/* -b, the caller requested bufctls */
+#define	WHATIS_IDSPACE 	0x2	/* -i, only search identifiers */
+#define	WHATIS_QUIET	0x4	/* -q, single-line reports only */
+#define	WHATIS_VERBOSE	0x8	/* -v, report information about the search */
+
+/*
+ * uint_t mdb_whatis_done(mdb_whatis_t *w)
+ *
+ * Returns non-zero if and only if all addresses have been reported, and it
+ * is time to get out of the callback as quickly as possible.
+ */
+extern uint_t mdb_whatis_done(mdb_whatis_t *);
+
+/* Macro for returning from a walker callback */
+#define	WHATIS_WALKRET(w)	(mdb_whatis_done(w) ? WALK_DONE : WALK_NEXT)
+
+typedef int mdb_whatis_cb_f(mdb_whatis_t *, void *);
+
+/*
+ * void mdb_whatis_register(const char *name, mdb_whatis_cb_f *cb, void *arg,
+ *	uint_t prio, uint_t flags)
+ *
+ * May only be called from _mdb_init() for a module.
+ *
+ * Registers a whatis callback named "name" (which must be an MDB identifier),
+ * with a callback function cb and argument arg.  prio determines when the
+ * callback will be invoked, compared to other registered ones, and flags
+ * determines when the callback will be invoked (see below).
+ *
+ * Callbacks with the same priority registered by the same module will be
+ * executed in the order they were added.  The callbacks will be invoked as:
+ *
+ *	int ret = (*cb)(w, arg)
+ *
+ * Where w is an opaque mdb_whatis_t pointer which is to be passed to the API
+ * routines, above.  The function should return 0 unless an error occurs.
+ */
+extern void mdb_whatis_register(const char *,
+    mdb_whatis_cb_f *, void *, uint_t, uint_t);
+
+#define	WHATIS_PRIO_EARLY	10	/* execute before allocator callbacks */
+#define	WHATIS_PRIO_ALLOCATOR	20
+#define	WHATIS_PRIO_LATE	30	/* execute after allocator callbacks */
+
+#define	WHATIS_REG_ID_ONLY	0x1	/* only invoke for '-i' */
+#define	WHATIS_REG_NO_ID	0x2	/* don't invoke for '-i' */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_WHATIS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/mdb/mdb_whatis_impl.h	Tue Sep 22 13:42:17 2009 -0700
@@ -0,0 +1,75 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_WHATIS_IMPL_H
+#define	_MDB_WHATIS_IMPL_H
+
+#include <mdb/mdb_module.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	WHATIS_MS(c, s) (((uint64_t)(c)) << (s))
+
+#define	WHATIS_MAGIC 	/* whatis 0x2009 */ \
+	(WHATIS_MS('w', 56) | WHATIS_MS('h', 48) | WHATIS_MS('a', 40) | \
+	    WHATIS_MS('t', 32) | WHATIS_MS('i', 24) | WHATIS_MS('s', 16) | \
+	    WHATIS_MS(0x2009, 0))
+
+struct mdb_whatis {
+	uint64_t w_magic;	/* just for sanity */
+	uintptr_t *w_addrs;	/* w_naddr sorted addresses */
+	char *w_addrfound;	/* array of w_naddr "found" flags */
+	size_t w_naddrs;
+	size_t w_match_next;	/* next match offset, or 0 if no active match */
+	uintptr_t w_match_base;	/* base of current match */
+	size_t w_match_size;	/* size of current match */
+	size_t w_found;		/* count of set entries in w_addrfound */
+	uint_t w_flags;		/* see WHATIS_* for details */
+	uint8_t w_done;		/* set when no more processing is needed */
+};
+
+#define	WHATIS_PUBLIC		0x0ffff
+
+/* flags which aren't part of the public interface */
+#define	WHATIS_ALL		0x10000	/* -a, report all matches */
+
+#define	WHATIS_PRIO_MIN		99
+
+extern int cmd_whatis(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void whatis_help(void);
+
+/* built-in callbacks */
+extern int whatis_run_mappings(struct mdb_whatis *, void *);
+
+/* callback at module unload time */
+extern void mdb_whatis_unregister_module(mdb_module_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_WHATIS_IMPL_H */
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Tue Sep 22 13:42:17 2009 -0700
@@ -144,6 +144,13 @@
 		mdb_walk =			EXTERN;
 		mdb_walk_dcmd =			EXTERN;
 		mdb_warn =			EXTERN;
+		mdb_whatis_done =		EXTERN;
+		mdb_whatis_flags =		EXTERN;
+		mdb_whatis_match =		EXTERN;
+		mdb_whatis_overlaps =		EXTERN;
+		mdb_whatis_register =		EXTERN;
+		mdb_whatis_report_address =	EXTERN;
+		mdb_whatis_report_object =	EXTERN;
 		mdb_writevar =			EXTERN;
 		mdb_writestr =			EXTERN;
 		mdb_writesym =			EXTERN;
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Tue Sep 22 13:42:17 2009 -0700
@@ -4437,8 +4437,6 @@
 	{ "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
 		"[-m minsize] [-M maxsize] [-t thread] [-T type]",
 		"print or filter a vmem_seg", vmem_seg, vmem_seg_help },
-	{ "whatis", ":[-abiqv]", "given an address, return information", whatis,
-		whatis_help },
 	{ "whatthread", ":[-v]", "print threads whose stack contains the "
 		"given address", whatthread },
 
--- a/usr/src/cmd/mdb/common/modules/genunix/kmem.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/kmem.c	Tue Sep 22 13:42:17 2009 -0700
@@ -26,6 +26,7 @@
 #include <mdb/mdb_param.h>
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_ctf.h>
+#include <mdb/mdb_whatis.h>
 #include <sys/cpuvar.h>
 #include <sys/kmem_impl.h>
 #include <sys/vmem_impl.h>
@@ -2089,33 +2090,19 @@
 	return (" (below sp)");
 }
 
-typedef struct whatis {
-	uintptr_t w_addr;
-	const kmem_cache_t *w_cache;
-	const vmem_t *w_vmem;
-	size_t w_slab_align;
-	int w_slab_found;
-	int w_found;
-	int w_kmem_lite_count;
-	uint_t w_all;
-	uint_t w_bufctl;
-	uint_t w_freemem;
-	uint_t w_idspace;
-	uint_t w_quiet;
-	uint_t w_verbose;
-} whatis_t;
-
-/* nicely report pointers as offsets from a base */
-static void
-whatis_report_pointer(uintptr_t addr, uintptr_t base, const char *description)
-{
-	if (addr == base)
-		mdb_printf("%p is %s",
-		    addr, description);
-	else
-		mdb_printf("%p is %p+%p, %s",
-		    addr, base, addr - base, description);
-}
+/*
+ * Additional state for the kmem and vmem ::whatis handlers
+ */
+typedef struct whatis_info {
+	mdb_whatis_t *wi_w;
+	const kmem_cache_t *wi_cache;
+	const vmem_t *wi_vmem;
+	vmem_t *wi_msb_arena;
+	size_t wi_slab_size;
+	uint_t wi_slab_found;
+	uint_t wi_kmem_lite_count;
+	uint_t wi_freemem;
+} whatis_info_t;
 
 /* call one of our dcmd functions with "-v" and the provided address */
 static void
@@ -2125,206 +2112,220 @@
 	a.a_type = MDB_TYPE_STRING;
 	a.a_un.a_str = "-v";
 
+	mdb_printf(":\n");
 	(void) (*dcmd)(addr, DCMD_ADDRSPEC, 1, &a);
 }
 
 static void
-whatis_print_kmem(uintptr_t addr, uintptr_t baddr, whatis_t *w)
+whatis_print_kmf_lite(uintptr_t btaddr, size_t count)
 {
-	const kmem_cache_t *cp = w->w_cache;
+#define	KMEM_LITE_MAX	16
+	pc_t callers[KMEM_LITE_MAX];
+	pc_t uninit = (pc_t)KMEM_UNINITIALIZED_PATTERN;
+
+	kmem_buftag_t bt;
+	intptr_t stat;
+	const char *plural = "";
+	int i;
+
+	/* validate our arguments and read in the buftag */
+	if (count == 0 || count > KMEM_LITE_MAX ||
+	    mdb_vread(&bt, sizeof (bt), btaddr) == -1)
+		return;
+
+	/* validate the buffer state and read in the callers */
+	stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat;
+
+	if (stat != KMEM_BUFTAG_ALLOC || stat != KMEM_BUFTAG_FREE ||
+	    mdb_vread(callers, count * sizeof (pc_t),
+	    btaddr + offsetof(kmem_buftag_lite_t, bt_history)) == -1)
+		return;
+
+	/* If there aren't any filled in callers, bail */
+	if (callers[0] == uninit)
+		return;
+
+	plural = (callers[1] == uninit) ? "" : "s";
+
+	/* Everything's done and checked; print them out */
+	mdb_printf(":\n");
+
+	mdb_inc_indent(8);
+	mdb_printf("recent caller%s: %a", plural, callers[0]);
+	for (i = 1; i < count; i++) {
+		if (callers[i] == uninit)
+			break;
+		mdb_printf(", %a", callers[i]);
+	}
+	mdb_dec_indent(8);
+}
+
+static void
+whatis_print_kmem(whatis_info_t *wi, uintptr_t maddr, uintptr_t addr,
+    uintptr_t baddr)
+{
+	mdb_whatis_t *w = wi->wi_w;
+
+	const kmem_cache_t *cp = wi->wi_cache;
 	/* LINTED pointer cast may result in improper alignment */
 	uintptr_t btaddr = (uintptr_t)KMEM_BUFTAG(cp, addr);
-	intptr_t stat;
-	int call_printer;
-	int count = 0;
-	int i;
-	pc_t callers[16];
-
-	if (cp->cache_flags & KMF_REDZONE) {
-		kmem_buftag_t bt;
-
-		if (mdb_vread(&bt, sizeof (bt), btaddr) == -1)
-			goto done;
-
-		stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat;
-
-		if (stat != KMEM_BUFTAG_ALLOC && stat != KMEM_BUFTAG_FREE)
-			goto done;
-
-		/*
-		 * provide the bufctl ptr if it has useful information
-		 */
-		if (baddr == 0 && (cp->cache_flags & KMF_AUDIT))
-			baddr = (uintptr_t)bt.bt_bufctl;
-
-		if (cp->cache_flags & KMF_LITE) {
-			count = w->w_kmem_lite_count;
-
-			if (count * sizeof (pc_t) > sizeof (callers))
-				count = 0;
-
-			if (count > 0 &&
-			    mdb_vread(callers, count * sizeof (pc_t),
-			    btaddr +
-			    offsetof(kmem_buftag_lite_t, bt_history)) == -1)
-				count = 0;
-
-			/*
-			 * skip unused callers
-			 */
-			while (count > 0 && callers[count - 1] ==
-			    (pc_t)KMEM_UNINITIALIZED_PATTERN)
-				count--;
-		}
-	}
-
-done:
-	call_printer =
-	    (!w->w_quiet && baddr != 0 && (cp->cache_flags & KMF_AUDIT));
-
-	whatis_report_pointer(w->w_addr, addr, "");
+	int quiet = (mdb_whatis_flags(w) & WHATIS_QUIET);
+	int call_printer = (!quiet && (cp->cache_flags & KMF_AUDIT));
+
+	mdb_whatis_report_object(w, maddr, addr, "");
 
 	if (baddr != 0 && !call_printer)
 		mdb_printf("bufctl %p ", baddr);
 
-	mdb_printf("%s from %s%s\n",
-	    (w->w_freemem == FALSE) ? "allocated" : "freed", cp->cache_name,
-	    (call_printer || (!w->w_quiet && count > 0)) ? ":" : "");
-
-	if (call_printer)
+	mdb_printf("%s from %s",
+	    (wi->wi_freemem == FALSE) ? "allocated" : "freed", cp->cache_name);
+
+	if (baddr != 0 && call_printer) {
 		whatis_call_printer(bufctl, baddr);
-
-	if (!w->w_quiet && count > 0) {
-		mdb_inc_indent(8);
-		mdb_printf("recent caller%s: %a%s", (count != 1)? "s":"",
-		    callers[0], (count != 1)? ", ":"\n");
-		for (i = 1; i < count; i++)
-			mdb_printf("%a%s", callers[i],
-			    (i + 1 < count)? ", ":"\n");
-		mdb_dec_indent(8);
+		return;
 	}
+
+	/* for KMF_LITE caches, try to print out the previous callers */
+	if (!quiet && (cp->cache_flags & KMF_LITE))
+		whatis_print_kmf_lite(btaddr, wi->wi_kmem_lite_count);
+
+	mdb_printf("\n");
+}
+
+/*ARGSUSED*/
+static int
+whatis_walk_kmem(uintptr_t addr, void *ignored, whatis_info_t *wi)
+{
+	mdb_whatis_t *w = wi->wi_w;
+
+	uintptr_t cur;
+	size_t size = wi->wi_cache->cache_bufsize;
+
+	while (mdb_whatis_match(w, addr, size, &cur))
+		whatis_print_kmem(wi, cur, addr, NULL);
+
+	return (WHATIS_WALKRET(w));
 }
 
 /*ARGSUSED*/
 static int
-whatis_walk_kmem(uintptr_t addr, void *ignored, whatis_t *w)
+whatis_walk_bufctl(uintptr_t baddr, const kmem_bufctl_t *bcp, whatis_info_t *wi)
 {
-	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
-		return (WALK_NEXT);
-
-	whatis_print_kmem(addr, 0, w);
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	mdb_whatis_t *w = wi->wi_w;
+
+	uintptr_t cur;
+	uintptr_t addr = (uintptr_t)bcp->bc_addr;
+	size_t size = wi->wi_cache->cache_bufsize;
+
+	while (mdb_whatis_match(w, addr, size, &cur))
+		whatis_print_kmem(wi, cur, addr, baddr);
+
+	return (WHATIS_WALKRET(w));
 }
 
 static int
-whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w)
+whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_info_t *wi)
 {
-	if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end)
+	mdb_whatis_t *w = wi->wi_w;
+
+	size_t size = vs->vs_end - vs->vs_start;
+	uintptr_t cur;
+
+	/* We're not interested in anything but alloc and free segments */
+	if (vs->vs_type != VMEM_ALLOC && vs->vs_type != VMEM_FREE)
 		return (WALK_NEXT);
 
-	whatis_report_pointer(w->w_addr, vs->vs_start, "");
-
-	/*
-	 * If we're not printing it seperately, provide the vmem_seg
-	 * pointer if it has a stack trace.
-	 */
-	if (w->w_quiet && (w->w_bufctl == TRUE ||
-	    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0))) {
-		mdb_printf("vmem_seg %p ", addr);
+	while (mdb_whatis_match(w, vs->vs_start, size, &cur)) {
+		mdb_whatis_report_object(w, cur, vs->vs_start, "");
+
+		/*
+		 * If we're not printing it seperately, provide the vmem_seg
+		 * pointer if it has a stack trace.
+		 */
+		if ((mdb_whatis_flags(w) & WHATIS_QUIET) &&
+		    (!(mdb_whatis_flags(w) & WHATIS_BUFCTL) ||
+		    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0))) {
+			mdb_printf("vmem_seg %p ", addr);
+		}
+
+		mdb_printf("%s from the %s vmem arena",
+		    (vs->vs_type == VMEM_ALLOC) ? "allocated" : "freed",
+		    wi->wi_vmem->vm_name);
+
+		if (!(mdb_whatis_flags(w) & WHATIS_QUIET))
+			whatis_call_printer(vmem_seg, addr);
+		else
+			mdb_printf("\n");
 	}
 
-	mdb_printf("%s from %s vmem arena%s\n",
-	    (w->w_freemem == FALSE) ? "allocated" : "freed", w->w_vmem->vm_name,
-	    !w->w_quiet ? ":" : "");
-
-	if (!w->w_quiet)
-		whatis_call_printer(vmem_seg, addr);
-
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	return (WHATIS_WALKRET(w));
 }
 
 static int
-whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w)
+whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_info_t *wi)
 {
+	mdb_whatis_t *w = wi->wi_w;
 	const char *nm = vmem->vm_name;
-	w->w_vmem = vmem;
-	w->w_freemem = FALSE;
-
-	if (((vmem->vm_cflags & VMC_IDENTIFIER) != 0) ^ w->w_idspace)
+
+	int identifier = ((vmem->vm_cflags & VMC_IDENTIFIER) != 0);
+	int idspace = ((mdb_whatis_flags(w) & WHATIS_IDSPACE) != 0);
+
+	if (identifier != idspace)
 		return (WALK_NEXT);
 
-	if (w->w_verbose)
+	wi->wi_vmem = vmem;
+
+	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
 		mdb_printf("Searching vmem arena %s...\n", nm);
 
-	if (mdb_pwalk("vmem_alloc",
-	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
-		mdb_warn("can't walk vmem seg for %p", addr);
+	if (mdb_pwalk("vmem_seg",
+	    (mdb_walk_cb_t)whatis_walk_seg, wi, addr) == -1) {
+		mdb_warn("can't walk vmem_seg for %p", addr);
 		return (WALK_NEXT);
 	}
 
-	if (w->w_found && w->w_all == FALSE)
-		return (WALK_DONE);
-
-	if (w->w_verbose)
-		mdb_printf("Searching vmem arena %s for free virtual...\n", nm);
-
-	w->w_freemem = TRUE;
-
-	if (mdb_pwalk("vmem_free",
-	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
-		mdb_warn("can't walk vmem seg for %p", addr);
-		return (WALK_NEXT);
-	}
-
-	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
+	return (WHATIS_WALKRET(w));
 }
 
 /*ARGSUSED*/
 static int
-whatis_walk_bufctl(uintptr_t baddr, const kmem_bufctl_t *bcp, whatis_t *w)
+whatis_walk_slab(uintptr_t saddr, const kmem_slab_t *sp, whatis_info_t *wi)
 {
-	uintptr_t addr;
-
-	if (bcp == NULL)
-		return (WALK_NEXT);
-
-	addr = (uintptr_t)bcp->bc_addr;
-
-	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
-		return (WALK_NEXT);
-
-	whatis_print_kmem(addr, baddr, w);
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
-}
-
-/*ARGSUSED*/
-static int
-whatis_walk_slab(uintptr_t saddr, const kmem_slab_t *sp, whatis_t *w)
-{
-	uintptr_t base = P2ALIGN((uintptr_t)sp->slab_base, w->w_slab_align);
-
-	if ((w->w_addr - base) >= w->w_cache->cache_slabsize)
-		return (WALK_NEXT);
-
-	w->w_slab_found++;
-	return (WALK_DONE);
+	mdb_whatis_t *w = wi->wi_w;
+
+	/* It must overlap with the slab data, or it's not interesting */
+	if (mdb_whatis_overlaps(w,
+	    (uintptr_t)sp->slab_base, wi->wi_slab_size)) {
+		wi->wi_slab_found++;
+		return (WALK_DONE);
+	}
+	return (WALK_NEXT);
 }
 
 static int
-whatis_walk_cache(uintptr_t addr, const kmem_cache_t *c, whatis_t *w)
+whatis_walk_cache(uintptr_t addr, const kmem_cache_t *c, whatis_info_t *wi)
 {
+	mdb_whatis_t *w = wi->wi_w;
+
 	char *walk, *freewalk;
 	mdb_walk_cb_t func;
-	vmem_t *vmp = c->cache_arena;
-
-	if (((c->cache_flags & KMC_IDENTIFIER) != 0) ^ w->w_idspace)
+	int do_bufctl;
+
+	int identifier = ((c->cache_flags & KMC_IDENTIFIER) != 0);
+	int idspace = ((mdb_whatis_flags(w) & WHATIS_IDSPACE) != 0);
+
+	if (identifier != idspace)
 		return (WALK_NEXT);
 
-	/* For caches with auditing info, we always walk the bufctls */
-	if (w->w_bufctl || (c->cache_flags & KMF_AUDIT)) {
+	/* Override the '-b' flag as necessary */
+	if (!(c->cache_flags & KMF_HASH))
+		do_bufctl = FALSE;	/* no bufctls to walk */
+	else if (c->cache_flags & KMF_AUDIT)
+		do_bufctl = TRUE;	/* we always want debugging info */
+	else
+		do_bufctl = ((mdb_whatis_flags(w) & WHATIS_BUFCTL) != 0);
+
+	if (do_bufctl) {
 		walk = "bufctl";
 		freewalk = "freectl";
 		func = (mdb_walk_cb_t)whatis_walk_bufctl;
@@ -2334,130 +2335,142 @@
 		func = (mdb_walk_cb_t)whatis_walk_kmem;
 	}
 
-	w->w_cache = c;
-
-	if (w->w_verbose)
-		mdb_printf("Searching %s's slabs...\n", c->cache_name);
+	wi->wi_cache = c;
+
+	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
+		mdb_printf("Searching %s...\n", c->cache_name);
 
 	/*
-	 * Verify that the address is in one of the cache's slabs.  If not,
-	 * we can skip the more expensive walkers.  (this is purely a
-	 * heuristic -- as long as there are no false-negatives, we'll be fine)
-	 *
-	 * We try to get the cache's arena's quantum, since to accurately
-	 * get the base of a slab, you have to align it to the quantum.  If
-	 * it doesn't look sensible, we fall back to not aligning.
+	 * If more then two buffers live on each slab, figure out if we're
+	 * interested in anything in any slab before doing the more expensive
+	 * kmem/freemem (bufctl/freectl) walkers.
 	 */
-	if (mdb_vread(&w->w_slab_align, sizeof (w->w_slab_align),
-	    (uintptr_t)&vmp->vm_quantum) == -1) {
-		mdb_warn("unable to read %p->cache_arena->vm_quantum", c);
-		w->w_slab_align = 1;
-	}
-
-	if ((c->cache_slabsize < w->w_slab_align) || w->w_slab_align == 0 ||
-	    (w->w_slab_align & (w->w_slab_align - 1))) {
-		mdb_warn("%p's arena has invalid quantum (0x%p)\n", c,
-		    w->w_slab_align);
-		w->w_slab_align = 1;
+	wi->wi_slab_size = c->cache_slabsize - c->cache_maxcolor;
+	if (!(c->cache_flags & KMF_HASH))
+		wi->wi_slab_size -= sizeof (kmem_slab_t);
+
+	if ((wi->wi_slab_size / c->cache_chunksize) > 2) {
+		wi->wi_slab_found = 0;
+		if (mdb_pwalk("kmem_slab", (mdb_walk_cb_t)whatis_walk_slab, wi,
+		    addr) == -1) {
+			mdb_warn("can't find kmem_slab walker");
+			return (WALK_DONE);
+		}
+		if (wi->wi_slab_found == 0)
+			return (WALK_NEXT);
 	}
 
-	w->w_slab_found = 0;
-	if (mdb_pwalk("kmem_slab", (mdb_walk_cb_t)whatis_walk_slab, w,
-	    addr) == -1) {
-		mdb_warn("can't find kmem_slab walker");
-		return (WALK_DONE);
-	}
-	if (w->w_slab_found == 0)
-		return (WALK_NEXT);
-
-	if (c->cache_flags & KMF_LITE) {
-		if (mdb_readvar(&w->w_kmem_lite_count,
-		    "kmem_lite_count") == -1 || w->w_kmem_lite_count > 16)
-			w->w_kmem_lite_count = 0;
-	}
-
-	if (w->w_verbose)
-		mdb_printf("Searching %s...\n", c->cache_name);
-
-	w->w_freemem = FALSE;
-
-	if (mdb_pwalk(walk, func, w, addr) == -1) {
+	wi->wi_freemem = FALSE;
+	if (mdb_pwalk(walk, func, wi, addr) == -1) {
 		mdb_warn("can't find %s walker", walk);
 		return (WALK_DONE);
 	}
 
-	if (w->w_found && w->w_all == FALSE)
+	if (mdb_whatis_done(w))
 		return (WALK_DONE);
 
 	/*
 	 * We have searched for allocated memory; now search for freed memory.
 	 */
-	if (w->w_verbose)
+	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
 		mdb_printf("Searching %s for free memory...\n", c->cache_name);
 
-	w->w_freemem = TRUE;
-
-	if (mdb_pwalk(freewalk, func, w, addr) == -1) {
+	wi->wi_freemem = TRUE;
+	if (mdb_pwalk(freewalk, func, wi, addr) == -1) {
 		mdb_warn("can't find %s walker", freewalk);
 		return (WALK_DONE);
 	}
 
-	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
+	return (WHATIS_WALKRET(w));
+}
+
+static int
+whatis_walk_touch(uintptr_t addr, const kmem_cache_t *c, whatis_info_t *wi)
+{
+	if (c->cache_arena == wi->wi_msb_arena ||
+	    (c->cache_cflags & KMC_NOTOUCH))
+		return (WALK_NEXT);
+
+	return (whatis_walk_cache(addr, c, wi));
 }
 
 static int
-whatis_walk_touch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w)
+whatis_walk_metadata(uintptr_t addr, const kmem_cache_t *c, whatis_info_t *wi)
 {
-	if (c->cache_cflags & KMC_NOTOUCH)
+	if (c->cache_arena != wi->wi_msb_arena)
 		return (WALK_NEXT);
 
-	return (whatis_walk_cache(addr, c, w));
+	return (whatis_walk_cache(addr, c, wi));
 }
 
 static int
-whatis_walk_notouch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w)
+whatis_walk_notouch(uintptr_t addr, const kmem_cache_t *c, whatis_info_t *wi)
 {
-	if (!(c->cache_cflags & KMC_NOTOUCH))
+	if (c->cache_arena == wi->wi_msb_arena ||
+	    !(c->cache_cflags & KMC_NOTOUCH))
 		return (WALK_NEXT);
 
-	return (whatis_walk_cache(addr, c, w));
+	return (whatis_walk_cache(addr, c, wi));
 }
 
 static int
-whatis_walk_thread(uintptr_t addr, const kthread_t *t, whatis_t *w)
+whatis_walk_thread(uintptr_t addr, const kthread_t *t, mdb_whatis_t *w)
 {
+	uintptr_t cur;
+	uintptr_t saddr;
+	size_t size;
+
 	/*
 	 * Often, one calls ::whatis on an address from a thread structure.
 	 * We use this opportunity to short circuit this case...
 	 */
-	if (w->w_addr >= addr && w->w_addr < addr + sizeof (kthread_t)) {
-		whatis_report_pointer(w->w_addr, addr,
+	while (mdb_whatis_match(w, addr, sizeof (kthread_t), &cur))
+		mdb_whatis_report_object(w, cur, addr,
 		    "allocated as a thread structure\n");
-		w->w_found++;
-		return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
-	}
-
-	if (w->w_addr < (uintptr_t)t->t_stkbase ||
-	    w->w_addr > (uintptr_t)t->t_stk)
-		return (WALK_NEXT);
-
+
+	/*
+	 * Now check the stack
+	 */
 	if (t->t_stkbase == NULL)
 		return (WALK_NEXT);
 
-	mdb_printf("%p is in thread %p's stack%s\n", w->w_addr, addr,
-	    stack_active(t, w->w_addr));
-
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	/*
+	 * This assumes that t_stk is the end of the stack, but it's really
+	 * only the initial stack pointer for the thread.  Arguments to the
+	 * initial procedure, SA(MINFRAME), etc. are all after t_stk.  So
+	 * that 't->t_stk::whatis' reports "part of t's stack", we include
+	 * t_stk in the range (the "+ 1", below), but the kernel should
+	 * really include the full stack bounds where we can find it.
+	 */
+	saddr = (uintptr_t)t->t_stkbase;
+	size = (uintptr_t)t->t_stk - saddr + 1;
+	while (mdb_whatis_match(w, saddr, size, &cur))
+		mdb_whatis_report_object(w, cur, cur,
+		    "in thread %p's stack%s\n", addr, stack_active(t, cur));
+
+	return (WHATIS_WALKRET(w));
+}
+
+static void
+whatis_modctl_match(mdb_whatis_t *w, const char *name,
+    uintptr_t base, size_t size, const char *where)
+{
+	uintptr_t cur;
+
+	/*
+	 * Since we're searching for addresses inside a module, we report
+	 * them as symbols.
+	 */
+	while (mdb_whatis_match(w, base, size, &cur))
+		mdb_whatis_report_address(w, cur, "in %s's %s\n", name, where);
 }
 
 static int
-whatis_walk_modctl(uintptr_t addr, const struct modctl *m, whatis_t *w)
+whatis_walk_modctl(uintptr_t addr, const struct modctl *m, mdb_whatis_t *w)
 {
+	char name[MODMAXNAMELEN];
 	struct module mod;
-	char name[MODMAXNAMELEN], *where;
 	Shdr shdr;
-	GElf_Sym sym;
 
 	if (m->mod_mp == NULL)
 		return (WALK_NEXT);
@@ -2467,207 +2480,138 @@
 		return (WALK_NEXT);
 	}
 
-	if (w->w_addr >= (uintptr_t)mod.text &&
-	    w->w_addr < (uintptr_t)mod.text + mod.text_size) {
-		where = "text segment";
-		goto found;
-	}
-
-	if (w->w_addr >= (uintptr_t)mod.data &&
-	    w->w_addr < (uintptr_t)mod.data + mod.data_size) {
-		where = "data segment";
-		goto found;
-	}
-
-	if (w->w_addr >= (uintptr_t)mod.bss &&
-	    w->w_addr < (uintptr_t)mod.bss + mod.bss_size) {
-		where = "bss";
-		goto found;
-	}
+	if (mdb_readstr(name, sizeof (name), (uintptr_t)m->mod_modname) == -1)
+		(void) mdb_snprintf(name, sizeof (name), "0x%p", addr);
+
+	whatis_modctl_match(w, name,
+	    (uintptr_t)mod.text, mod.text_size, "text segment");
+	whatis_modctl_match(w, name,
+	    (uintptr_t)mod.data, mod.data_size, "data segment");
+	whatis_modctl_match(w, name,
+	    (uintptr_t)mod.bss, mod.bss_size, "bss segment");
 
 	if (mdb_vread(&shdr, sizeof (shdr), (uintptr_t)mod.symhdr) == -1) {
 		mdb_warn("couldn't read symbol header for %p's module", addr);
 		return (WALK_NEXT);
 	}
 
-	if (w->w_addr >= (uintptr_t)mod.symtbl && w->w_addr <
-	    (uintptr_t)mod.symtbl + (uintptr_t)mod.nsyms * shdr.sh_entsize) {
-		where = "symtab";
-		goto found;
-	}
-
-	if (w->w_addr >= (uintptr_t)mod.symspace &&
-	    w->w_addr < (uintptr_t)mod.symspace + (uintptr_t)mod.symsize) {
-		where = "symspace";
-		goto found;
+	whatis_modctl_match(w, name,
+	    (uintptr_t)mod.symtbl, mod.nsyms * shdr.sh_entsize, "symtab");
+	whatis_modctl_match(w, name,
+	    (uintptr_t)mod.symspace, mod.symsize, "symtab");
+
+	return (WHATIS_WALKRET(w));
+}
+
+/*ARGSUSED*/
+static int
+whatis_walk_memseg(uintptr_t addr, const struct memseg *seg, mdb_whatis_t *w)
+{
+	uintptr_t cur;
+
+	uintptr_t base = (uintptr_t)seg->pages;
+	size_t size = (uintptr_t)seg->epages - base;
+
+	while (mdb_whatis_match(w, base, size, &cur)) {
+		/* round our found pointer down to the page_t base. */
+		size_t offset = (cur - base) % sizeof (page_t);
+
+		mdb_whatis_report_object(w, cur, cur - offset,
+		    "allocated as a page structure\n");
 	}
 
-	return (WALK_NEXT);
-
-found:
-	if (mdb_readstr(name, sizeof (name), (uintptr_t)m->mod_modname) == -1)
-		(void) mdb_snprintf(name, sizeof (name), "0x%p", addr);
-
-	mdb_printf("%p is ", w->w_addr);
-
+	return (WHATIS_WALKRET(w));
+}
+
+/*ARGSUSED*/
+static int
+whatis_run_modules(mdb_whatis_t *w, void *arg)
+{
+	if (mdb_walk("modctl", (mdb_walk_cb_t)whatis_walk_modctl, w) == -1) {
+		mdb_warn("couldn't find modctl walker");
+		return (1);
+	}
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+whatis_run_threads(mdb_whatis_t *w, void *ignored)
+{
 	/*
-	 * If we found this address in a module, then there's a chance that
-	 * it's actually a named symbol.  Try the symbol lookup.
+	 * Now search all thread stacks.  Yes, this is a little weak; we
+	 * can save a lot of work by first checking to see if the
+	 * address is in segkp vs. segkmem.  But hey, computers are
+	 * fast.
 	 */
-	if (mdb_lookup_by_addr(w->w_addr, MDB_SYM_FUZZY, NULL, 0, &sym) != -1 &&
-	    (w->w_addr - (uintptr_t)sym.st_value) < sym.st_size) {
-		mdb_printf("%a, ", w->w_addr);
+	if (mdb_walk("thread", (mdb_walk_cb_t)whatis_walk_thread, w) == -1) {
+		mdb_warn("couldn't find thread walker");
+		return (1);
 	}
-
-	mdb_printf("in %s's %s\n", name, where);
-
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	return (0);
 }
 
 /*ARGSUSED*/
 static int
-whatis_walk_page(uintptr_t addr, const void *ignored, whatis_t *w)
+whatis_run_pages(mdb_whatis_t *w, void *ignored)
 {
-	static int machsize = 0;
-	mdb_ctf_id_t id;
-
-	if (machsize == 0) {
-		if (mdb_ctf_lookup_by_name("unix`page_t", &id) == 0)
-			machsize = mdb_ctf_type_size(id);
-		else {
-			mdb_warn("could not get size of page_t");
-			machsize = sizeof (page_t);
-		}
+	if (mdb_walk("memseg", (mdb_walk_cb_t)whatis_walk_memseg, w) == -1) {
+		mdb_warn("couldn't find memseg walker");
+		return (1);
 	}
-
-	if (w->w_addr < addr || w->w_addr >= addr + machsize)
-		return (WALK_NEXT);
-
-	whatis_report_pointer(w->w_addr, addr,
-	    "allocated as a page structure\n");
-
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	return (0);
 }
 
-int
-whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+/*ARGSUSED*/
+static int
+whatis_run_kmem(mdb_whatis_t *w, void *ignored)
 {
-	whatis_t w;
-
-	if (!(flags & DCMD_ADDRSPEC))
-		return (DCMD_USAGE);
-
-	w.w_all = FALSE;
-	w.w_bufctl = FALSE;
-	w.w_idspace = FALSE;
-	w.w_quiet = FALSE;
-	w.w_verbose = FALSE;
-
-	if (mdb_getopts(argc, argv,
-	    'a', MDB_OPT_SETBITS, TRUE, &w.w_all,
-	    'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl,
-	    'i', MDB_OPT_SETBITS, TRUE, &w.w_idspace,
-	    'q', MDB_OPT_SETBITS, TRUE, &w.w_quiet,
-	    'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose,
-	    NULL) != argc)
-		return (DCMD_USAGE);
-
-	w.w_addr = addr;
-	w.w_found = 0;
-
-	if (w.w_verbose)
-		mdb_printf("Searching modules...\n");
-
-	if (!w.w_idspace) {
-		if (mdb_walk("modctl", (mdb_walk_cb_t)whatis_walk_modctl, &w)
-		    == -1) {
-			mdb_warn("couldn't find modctl walker");
-			return (DCMD_ERR);
-		}
-
-		if (w.w_found && w.w_all == FALSE)
-			return (DCMD_OK);
-
-		/*
-		 * Now search all thread stacks.  Yes, this is a little weak; we
-		 * can save a lot of work by first checking to see if the
-		 * address is in segkp vs. segkmem.  But hey, computers are
-		 * fast.
-		 */
-		if (w.w_verbose)
-			mdb_printf("Searching threads...\n");
-
-		if (mdb_walk("thread", (mdb_walk_cb_t)whatis_walk_thread, &w)
-		    == -1) {
-			mdb_warn("couldn't find thread walker");
-			return (DCMD_ERR);
-		}
-
-		if (w.w_found && w.w_all == FALSE)
-			return (DCMD_OK);
-
-		if (w.w_verbose)
-			mdb_printf("Searching page structures...\n");
-
-		if (mdb_walk("page", (mdb_walk_cb_t)whatis_walk_page, &w)
-		    == -1) {
-			mdb_warn("couldn't find page walker");
-			return (DCMD_ERR);
-		}
-
-		if (w.w_found && w.w_all == FALSE)
-			return (DCMD_OK);
+	whatis_info_t wi;
+
+	bzero(&wi, sizeof (wi));
+	wi.wi_w = w;
+
+	if (mdb_readvar(&wi.wi_msb_arena, "kmem_msb_arena") == -1)
+		mdb_warn("unable to readvar \"kmem_msb_arena\"");
+
+	if (mdb_readvar(&wi.wi_kmem_lite_count,
+	    "kmem_lite_count") == -1 || wi.wi_kmem_lite_count > 16)
+		wi.wi_kmem_lite_count = 0;
+
+	/*
+	 * We process kmem caches in the following order:
+	 *
+	 *	non-KMC_NOTOUCH, non-metadata	(typically the most interesting)
+	 *	metadata			(can be huge with KMF_AUDIT)
+	 *	KMC_NOTOUCH, non-metadata	(see kmem_walk_all())
+	 */
+	if (mdb_walk("kmem_cache", (mdb_walk_cb_t)whatis_walk_touch,
+	    &wi) == -1 ||
+	    mdb_walk("kmem_cache", (mdb_walk_cb_t)whatis_walk_metadata,
+	    &wi) == -1 ||
+	    mdb_walk("kmem_cache", (mdb_walk_cb_t)whatis_walk_notouch,
+	    &wi) == -1) {
+		mdb_warn("couldn't find kmem_cache walker");
+		return (1);
 	}
-
-	if (mdb_walk("kmem_cache",
-	    (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) {
-		mdb_warn("couldn't find kmem_cache walker");
-		return (DCMD_ERR);
-	}
-
-	if (w.w_found && w.w_all == FALSE)
-		return (DCMD_OK);
-
-	if (mdb_walk("kmem_cache",
-	    (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) {
-		mdb_warn("couldn't find kmem_cache walker");
-		return (DCMD_ERR);
-	}
-
-	if (w.w_found && w.w_all == FALSE)
-		return (DCMD_OK);
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+whatis_run_vmem(mdb_whatis_t *w, void *ignored)
+{
+	whatis_info_t wi;
+
+	bzero(&wi, sizeof (wi));
+	wi.wi_w = w;
 
 	if (mdb_walk("vmem_postfix",
-	    (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) {
+	    (mdb_walk_cb_t)whatis_walk_vmem, &wi) == -1) {
 		mdb_warn("couldn't find vmem_postfix walker");
-		return (DCMD_ERR);
+		return (1);
 	}
-
-	if (w.w_found == 0)
-		mdb_printf("%p is unknown\n", addr);
-
-	return (DCMD_OK);
-}
-
-void
-whatis_help(void)
-{
-	mdb_printf(
-	    "Given a virtual address, attempt to determine where it came\n"
-	    "from.\n"
-	    "\n"
-	    "\t-a\tFind all possible sources.  Default behavior is to stop at\n"
-	    "\t\tthe first (most specific) source.\n"
-	    "\t-b\tReport bufctls and vmem_segs for matches in kmem and vmem,\n"
-	    "\t\trespectively.  Warning: if the buffer exists, but does not\n"
-	    "\t\thave a bufctl, it will not be reported.\n"
-	    "\t-i\tSearch only identifier arenas and caches.  By default\n"
-	    "\t\tthese are ignored.\n"
-	    "\t-q\tDon't print multi-line reports (stack traces, etc.)\n"
-	    "\t-v\tVerbose output; display caches/arenas/etc as they are\n"
-	    "\t\tsearched\n");
+	return (0);
 }
 
 typedef struct kmem_log_cpu {
@@ -4322,6 +4266,18 @@
 	}
 
 	kmem_statechange();
+
+	/* register our ::whatis handlers */
+	mdb_whatis_register("modules", whatis_run_modules, NULL,
+	    WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID);
+	mdb_whatis_register("threads", whatis_run_threads, NULL,
+	    WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID);
+	mdb_whatis_register("pages", whatis_run_pages, NULL,
+	    WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID);
+	mdb_whatis_register("kmem", whatis_run_kmem, NULL,
+	    WHATIS_PRIO_ALLOCATOR, 0);
+	mdb_whatis_register("vmem", whatis_run_vmem, NULL,
+	    WHATIS_PRIO_ALLOCATOR, 0);
 }
 
 typedef struct whatthread {
--- a/usr/src/cmd/mdb/common/modules/genunix/kmem.h	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/kmem.h	Tue Sep 22 13:42:17 2009 -0700
@@ -88,7 +88,6 @@
 extern int kmem_slabs(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int allocdby(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int freedby(uintptr_t, uint_t, int, const mdb_arg_t *);
-extern int whatis(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int kmem_log(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int kmem_debug(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int bufctl(uintptr_t, uint_t, int, const mdb_arg_t *);
@@ -101,7 +100,6 @@
 extern int kmausers(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern void kmem_cache_help(void);
 extern void kmem_slabs_help(void);
-extern void whatis_help(void);
 extern void bufctl_help(void);
 extern void vmem_seg_help(void);
 extern void kmausers_help(void);
--- a/usr/src/cmd/mdb/common/modules/libc/libc.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/libc/libc.c	Tue Sep 22 13:42:17 2009 -0700
@@ -25,6 +25,7 @@
  */
 
 #include <sys/mdb_modapi.h>
+#include <mdb/mdb_whatis.h>
 #include <procfs.h>
 #include <ucontext.h>
 #include <siginfo.h>
@@ -914,6 +915,61 @@
 	return (wsp->walk_callback(addr, &ulwp, wsp->walk_cbdata));
 }
 
+/* Avoid classifying NULL pointers as part of the main stack on x86 */
+#define	MIN_STACK_ADDR		(0x10000ul)
+
+static int
+whatis_walk_ulwp(uintptr_t addr, const ulwp_t *ulwp, mdb_whatis_t *w)
+{
+	uintptr_t cur;
+	lwpid_t id = ulwp->ul_lwpid;
+	uintptr_t top, base, size;
+
+	while (mdb_whatis_match(w, addr, sizeof (ulwp_t), &cur))
+		mdb_whatis_report_object(w, cur, addr,
+		    "allocated as thread %#r's ulwp_t\n", id);
+
+	top = (uintptr_t)ulwp->ul_stktop;
+	size = ulwp->ul_stksiz;
+
+	/*
+	 * The main stack ends up being a little weird, especially if
+	 * the stack ulimit is unlimited.  This tries to take that into
+	 * account.
+	 */
+	if (size > top)
+		size = top;
+	if (top > MIN_STACK_ADDR && top - size < MIN_STACK_ADDR)
+		size = top - MIN_STACK_ADDR;
+
+	base = top - size;
+
+	while (mdb_whatis_match(w, base, size, &cur))
+		mdb_whatis_report_address(w, cur, "in [ stack tid=%#r ]\n", id);
+
+	if (ulwp->ul_ustack.ss_flags & SS_ONSTACK) {
+		base = (uintptr_t)ulwp->ul_ustack.ss_sp;
+		size = ulwp->ul_ustack.ss_size;
+
+		while (mdb_whatis_match(w, base, size, &cur))
+			mdb_whatis_report_address(w, cur,
+			    "in [ altstack tid=%#r ]\n", id);
+	}
+
+	return (WHATIS_WALKRET(w));
+}
+
+/*ARGSUSED*/
+static int
+whatis_run_ulwps(mdb_whatis_t *w, void *arg)
+{
+	if (mdb_walk("ulwps", (mdb_walk_cb_t)whatis_walk_ulwp, w) == -1) {
+		mdb_warn("couldn't find ulwps walker");
+		return (1);
+	}
+	return (0);
+}
+
 /*
  * =======================================================
  * End of thread (previously libthread) interfaces.
@@ -945,5 +1001,8 @@
 const mdb_modinfo_t *
 _mdb_init(void)
 {
+	mdb_whatis_register("threads", whatis_run_ulwps, NULL,
+	    WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID);
+
 	return (&modinfo);
 }
--- a/usr/src/cmd/mdb/common/modules/libumem/libumem.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/libumem/libumem.c	Tue Sep 22 13:42:17 2009 -0700
@@ -352,8 +352,6 @@
 	{ "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
 		"[-m minsize] [-M maxsize] [-t thread] [-T type]",
 		"print or filter a vmem_seg", vmem_seg, vmem_seg_help },
-	{ "whatis", ":[-abqv]", "given an address, return information",
-		whatis },
 
 #ifndef _KMDB
 	/* from ../genunix/kgrep.c + libumem.c */
--- a/usr/src/cmd/mdb/common/modules/libumem/umem.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/libumem/umem.c	Tue Sep 22 13:42:17 2009 -0700
@@ -30,6 +30,7 @@
 
 #include <alloca.h>
 #include <limits.h>
+#include <mdb/mdb_whatis.h>
 
 #include "misc.h"
 #include "leaky.h"
@@ -145,29 +146,6 @@
 }
 
 int
-umem_init(void)
-{
-	mdb_walker_t w = {
-		"umem_cache", "walk list of umem caches", umem_cache_walk_init,
-		umem_cache_walk_step, umem_cache_walk_fini
-	};
-
-	if (mdb_add_walker(&w) == -1) {
-		mdb_warn("failed to add umem_cache walker");
-		return (-1);
-	}
-
-	if (umem_update_variables() == -1)
-		return (-1);
-
-	/* install a callback so that our variables are always up-to-date */
-	(void) mdb_callback_add(MDB_CALLBACK_STCHG, umem_statechange_cb, NULL);
-	umem_statechange_cb(NULL);
-
-	return (0);
-}
-
-int
 umem_abort_messages(void)
 {
 	char *umem_error_buffer;
@@ -1845,29 +1823,15 @@
 	return (allocdby_common(addr, flags, "freedby"));
 }
 
-typedef struct whatis {
-	uintptr_t w_addr;
-	const umem_cache_t *w_cache;
-	const vmem_t *w_vmem;
-	int w_found;
-	uint_t w_all;
-	uint_t w_bufctl;
-	uint_t w_freemem;
-	uint_t w_quiet;
-	uint_t w_verbose;
-} whatis_t;
-
-/* nicely report pointers as offsets from a base */
-static void
-whatis_report_pointer(uintptr_t addr, uintptr_t base, const char *description)
-{
-	if (addr == base)
-		mdb_printf("%p is %s",
-		    addr, description);
-	else
-		mdb_printf("%p is %p+%p, %s",
-		    addr, base, addr - base, description);
-}
+typedef struct whatis_info {
+	mdb_whatis_t *wi_w;
+	const umem_cache_t *wi_cache;
+	const vmem_t *wi_vmem;
+	vmem_t *wi_msb_arena;
+	size_t wi_slab_size;
+	int wi_slab_found;
+	uint_t wi_freemem;
+} whatis_info_t;
 
 /* call one of our dcmd functions with "-v" and the provided address */
 static void
@@ -1877,153 +1841,156 @@
 	a.a_type = MDB_TYPE_STRING;
 	a.a_un.a_str = "-v";
 
+	mdb_printf(":\n");
 	(void) (*dcmd)(addr, DCMD_ADDRSPEC, 1, &a);
 }
 
 static void
-whatis_print_umem(uintptr_t addr, uintptr_t baddr, whatis_t *w)
+whatis_print_umem(whatis_info_t *wi, uintptr_t maddr, uintptr_t addr,
+    uintptr_t baddr)
 {
-	const umem_cache_t *cp = w->w_cache;
-	/* LINTED pointer cast may result in improper alignment */
-	uintptr_t btaddr = (uintptr_t)UMEM_BUFTAG(cp, addr);
-	intptr_t stat;
-	int call_printer;
-
-	if (cp->cache_flags & UMF_REDZONE) {
-		umem_buftag_t bt;
-
-		if (mdb_vread(&bt, sizeof (bt), btaddr) == -1)
-			goto done;
-
-		stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat;
-
-		if (stat != UMEM_BUFTAG_ALLOC && stat != UMEM_BUFTAG_FREE)
-			goto done;
-
-		/*
-		 * provide the bufctl ptr if it has useful information
-		 */
-		if (baddr == 0 && (cp->cache_flags & UMF_AUDIT))
-			baddr = (uintptr_t)bt.bt_bufctl;
-	}
-
-done:
-	call_printer =
-	    (!w->w_quiet && baddr != 0 && (cp->cache_flags & UMF_AUDIT));
-
-	whatis_report_pointer(w->w_addr, addr, "");
+	mdb_whatis_t *w = wi->wi_w;
+	const umem_cache_t *cp = wi->wi_cache;
+	int quiet = (mdb_whatis_flags(w) & WHATIS_QUIET);
+
+	int call_printer = (!quiet && (cp->cache_flags & UMF_AUDIT));
+
+	mdb_whatis_report_object(w, maddr, addr, "");
 
 	if (baddr != 0 && !call_printer)
 		mdb_printf("bufctl %p ", baddr);
 
-	mdb_printf("%s from %s%s\n",
-	    (w->w_freemem == FALSE) ? "allocated" : "freed", cp->cache_name,
-	    call_printer ? ":" : "");
-
-	if (call_printer)
+	mdb_printf("%s from %s",
+	    (wi->wi_freemem == FALSE) ? "allocated" : "freed", cp->cache_name);
+
+	if (call_printer && baddr != 0) {
 		whatis_call_printer(bufctl, baddr);
+		return;
+	}
+	mdb_printf("\n");
+}
+
+/*ARGSUSED*/
+static int
+whatis_walk_umem(uintptr_t addr, void *ignored, whatis_info_t *wi)
+{
+	mdb_whatis_t *w = wi->wi_w;
+
+	uintptr_t cur;
+	size_t size = wi->wi_cache->cache_bufsize;
+
+	while (mdb_whatis_match(w, addr, size, &cur))
+		whatis_print_umem(wi, cur, addr, NULL);
+
+	return (WHATIS_WALKRET(w));
 }
 
 /*ARGSUSED*/
 static int
-whatis_walk_umem(uintptr_t addr, void *ignored, whatis_t *w)
+whatis_walk_bufctl(uintptr_t baddr, const umem_bufctl_t *bcp, whatis_info_t *wi)
 {
-	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
+	mdb_whatis_t *w = wi->wi_w;
+
+	uintptr_t cur;
+	uintptr_t addr = (uintptr_t)bcp->bc_addr;
+	size_t size = wi->wi_cache->cache_bufsize;
+
+	while (mdb_whatis_match(w, addr, size, &cur))
+		whatis_print_umem(wi, cur, addr, baddr);
+
+	return (WHATIS_WALKRET(w));
+}
+
+
+static int
+whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_info_t *wi)
+{
+	mdb_whatis_t *w = wi->wi_w;
+
+	size_t size = vs->vs_end - vs->vs_start;
+	uintptr_t cur;
+
+	/* We're not interested in anything but alloc and free segments */
+	if (vs->vs_type != VMEM_ALLOC && vs->vs_type != VMEM_FREE)
 		return (WALK_NEXT);
 
-	whatis_print_umem(addr, 0, w);
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	while (mdb_whatis_match(w, vs->vs_start, size, &cur)) {
+		mdb_whatis_report_object(w, cur, vs->vs_start, "");
+
+		/*
+		 * If we're not printing it seperately, provide the vmem_seg
+		 * pointer if it has a stack trace.
+		 */
+		if ((mdb_whatis_flags(w) & WHATIS_QUIET) &&
+		    ((mdb_whatis_flags(w) & WHATIS_BUFCTL) != 0 ||
+		    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0))) {
+			mdb_printf("vmem_seg %p ", addr);
+		}
+
+		mdb_printf("%s from %s vmem arena",
+		    (vs->vs_type == VMEM_ALLOC) ? "allocated" : "freed",
+		    wi->wi_vmem->vm_name);
+
+		if (!mdb_whatis_flags(w) & WHATIS_QUIET)
+			whatis_call_printer(vmem_seg, addr);
+		else
+			mdb_printf("\n");
+	}
+
+	return (WHATIS_WALKRET(w));
 }
 
 static int
-whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w)
+whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_info_t *wi)
 {
-	if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end)
-		return (WALK_NEXT);
-
-	whatis_report_pointer(w->w_addr, vs->vs_start, "");
-
-	/*
-	 * If we're not going to print it anyway, provide the vmem_seg pointer
-	 * if it has a stack trace.
-	 */
-	if (w->w_quiet && (w->w_bufctl ||
-	    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0))) {
-		mdb_printf("vmem_seg %p ", addr);
-	}
-
-	mdb_printf("%s from %s vmem arena%s\n",
-	    (w->w_freemem == FALSE) ? "allocated" : "freed",
-	    w->w_vmem->vm_name, !w->w_quiet ? ":" : "");
-
-	if (!w->w_quiet)
-		whatis_call_printer(vmem_seg, addr);
-
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
-}
-
-static int
-whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w)
-{
+	mdb_whatis_t *w = wi->wi_w;
 	const char *nm = vmem->vm_name;
-	w->w_vmem = vmem;
-	w->w_freemem = FALSE;
-
-	if (w->w_verbose)
+	wi->wi_vmem = vmem;
+
+	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
 		mdb_printf("Searching vmem arena %s...\n", nm);
 
-	if (mdb_pwalk("vmem_alloc",
-	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
+	if (mdb_pwalk("vmem_seg",
+	    (mdb_walk_cb_t)whatis_walk_seg, wi, addr) == -1) {
 		mdb_warn("can't walk vmem seg for %p", addr);
 		return (WALK_NEXT);
 	}
 
-	if (w->w_found && w->w_all == FALSE)
-		return (WALK_DONE);
-
-	if (w->w_verbose)
-		mdb_printf("Searching vmem arena %s for free virtual...\n", nm);
-
-	w->w_freemem = TRUE;
-
-	if (mdb_pwalk("vmem_free",
-	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
-		mdb_warn("can't walk vmem seg for %p", addr);
-		return (WALK_NEXT);
-	}
-
-	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
+	return (WHATIS_WALKRET(w));
 }
 
 /*ARGSUSED*/
 static int
-whatis_walk_bufctl(uintptr_t baddr, const umem_bufctl_t *bcp, whatis_t *w)
+whatis_walk_slab(uintptr_t saddr, const umem_slab_t *sp, whatis_info_t *wi)
 {
-	uintptr_t addr;
-
-	if (bcp == NULL)
-		return (WALK_NEXT);
-
-	addr = (uintptr_t)bcp->bc_addr;
-
-	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
-		return (WALK_NEXT);
-
-	whatis_print_umem(addr, baddr, w);
-	w->w_found++;
-	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
+	mdb_whatis_t *w = wi->wi_w;
+
+	/* It must overlap with the slab data, or it's not interesting */
+	if (mdb_whatis_overlaps(w,
+	    (uintptr_t)sp->slab_base, wi->wi_slab_size)) {
+		wi->wi_slab_found++;
+		return (WALK_DONE);
+	}
+	return (WALK_NEXT);
 }
 
 static int
-whatis_walk_cache(uintptr_t addr, const umem_cache_t *c, whatis_t *w)
+whatis_walk_cache(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
 {
+	mdb_whatis_t *w = wi->wi_w;
 	char *walk, *freewalk;
 	mdb_walk_cb_t func;
-
-	/* For caches with auditing info, we always walk the bufctls */
-	if (w->w_bufctl || (c->cache_flags & UMF_AUDIT)) {
+	int do_bufctl;
+
+	/* Override the '-b' flag as necessary */
+	if (!(c->cache_flags & UMF_HASH))
+		do_bufctl = FALSE;	/* no bufctls to walk */
+	else if (c->cache_flags & UMF_AUDIT)
+		do_bufctl = TRUE;	/* we always want debugging info */
+	else
+		do_bufctl = ((mdb_whatis_flags(w) & WHATIS_BUFCTL) != 0);
+
+	if (do_bufctl) {
 		walk = "bufctl";
 		freewalk = "freectl";
 		func = (mdb_walk_cb_t)whatis_walk_bufctl;
@@ -2033,109 +2000,163 @@
 		func = (mdb_walk_cb_t)whatis_walk_umem;
 	}
 
-	if (w->w_verbose)
+	wi->wi_cache = c;
+
+	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
 		mdb_printf("Searching %s...\n", c->cache_name);
 
-	w->w_cache = c;
-	w->w_freemem = FALSE;
-
-	if (mdb_pwalk(walk, func, w, addr) == -1) {
+	/*
+	 * If more then two buffers live on each slab, figure out if we're
+	 * interested in anything in any slab before doing the more expensive
+	 * umem/freemem (bufctl/freectl) walkers.
+	 */
+	wi->wi_slab_size = c->cache_slabsize - c->cache_maxcolor;
+	if (!(c->cache_flags & UMF_HASH))
+		wi->wi_slab_size -= sizeof (umem_slab_t);
+
+	if ((wi->wi_slab_size / c->cache_chunksize) > 2) {
+		wi->wi_slab_found = 0;
+		if (mdb_pwalk("umem_slab", (mdb_walk_cb_t)whatis_walk_slab, wi,
+		    addr) == -1) {
+			mdb_warn("can't find umem_slab walker");
+			return (WALK_DONE);
+		}
+		if (wi->wi_slab_found == 0)
+			return (WALK_NEXT);
+	}
+
+	wi->wi_freemem = FALSE;
+	if (mdb_pwalk(walk, func, wi, addr) == -1) {
 		mdb_warn("can't find %s walker", walk);
 		return (WALK_DONE);
 	}
 
-	if (w->w_found && w->w_all == FALSE)
+	if (mdb_whatis_done(w))
 		return (WALK_DONE);
 
 	/*
 	 * We have searched for allocated memory; now search for freed memory.
 	 */
-	if (w->w_verbose)
+	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
 		mdb_printf("Searching %s for free memory...\n", c->cache_name);
 
-	w->w_freemem = TRUE;
-
-	if (mdb_pwalk(freewalk, func, w, addr) == -1) {
+	wi->wi_freemem = TRUE;
+
+	if (mdb_pwalk(freewalk, func, wi, addr) == -1) {
 		mdb_warn("can't find %s walker", freewalk);
 		return (WALK_DONE);
 	}
 
-	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
+	return (WHATIS_WALKRET(w));
+}
+
+static int
+whatis_walk_touch(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
+{
+	if (c->cache_arena == wi->wi_msb_arena ||
+	    (c->cache_cflags & UMC_NOTOUCH))
+		return (WALK_NEXT);
+
+	return (whatis_walk_cache(addr, c, wi));
+}
+
+static int
+whatis_walk_metadata(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
+{
+	if (c->cache_arena != wi->wi_msb_arena)
+		return (WALK_NEXT);
+
+	return (whatis_walk_cache(addr, c, wi));
 }
 
 static int
-whatis_walk_touch(uintptr_t addr, const umem_cache_t *c, whatis_t *w)
+whatis_walk_notouch(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
 {
-	if (c->cache_cflags & UMC_NOTOUCH)
+	if (c->cache_arena == wi->wi_msb_arena ||
+	    !(c->cache_cflags & UMC_NOTOUCH))
 		return (WALK_NEXT);
 
-	return (whatis_walk_cache(addr, c, w));
+	return (whatis_walk_cache(addr, c, wi));
 }
 
+/*ARGSUSED*/
 static int
-whatis_walk_notouch(uintptr_t addr, const umem_cache_t *c, whatis_t *w)
+whatis_run_umem(mdb_whatis_t *w, void *ignored)
 {
-	if (!(c->cache_cflags & UMC_NOTOUCH))
-		return (WALK_NEXT);
-
-	return (whatis_walk_cache(addr, c, w));
+	whatis_info_t wi;
+
+	bzero(&wi, sizeof (wi));
+	wi.wi_w = w;
+
+	/* umem's metadata is allocated from the umem_internal_arena */
+	if (mdb_readvar(&wi.wi_msb_arena, "umem_internal_arena") == -1)
+		mdb_warn("unable to readvar \"umem_internal_arena\"");
+
+	/*
+	 * We process umem caches in the following order:
+	 *
+	 *	non-UMC_NOTOUCH, non-metadata	(typically the most interesting)
+	 *	metadata			(can be huge with UMF_AUDIT)
+	 *	UMC_NOTOUCH, non-metadata	(see umem_walk_all())
+	 */
+	if (mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_touch,
+	    &wi) == -1 ||
+	    mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_metadata,
+	    &wi) == -1 ||
+	    mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_notouch,
+	    &wi) == -1) {
+		mdb_warn("couldn't find umem_cache walker");
+		return (1);
+	}
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+whatis_run_vmem(mdb_whatis_t *w, void *ignored)
+{
+	whatis_info_t wi;
+
+	bzero(&wi, sizeof (wi));
+	wi.wi_w = w;
+
+	if (mdb_walk("vmem_postfix",
+	    (mdb_walk_cb_t)whatis_walk_vmem, &wi) == -1) {
+		mdb_warn("couldn't find vmem_postfix walker");
+		return (1);
+	}
+	return (0);
 }
 
 int
-whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+umem_init(void)
 {
-	whatis_t w;
-
-	if (!(flags & DCMD_ADDRSPEC))
-		return (DCMD_USAGE);
-
-	w.w_all = FALSE;
-	w.w_bufctl = FALSE;
-	w.w_quiet = FALSE;
-	w.w_verbose = FALSE;
-
-	if (mdb_getopts(argc, argv,
-	    'a', MDB_OPT_SETBITS, TRUE, &w.w_all,
-	    'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl,
-	    'q', MDB_OPT_SETBITS, TRUE, &w.w_quiet,
-	    'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose,
-	    NULL) != argc)
-		return (DCMD_USAGE);
-
-	w.w_addr = addr;
-	w.w_found = 0;
+	mdb_walker_t w = {
+		"umem_cache", "walk list of umem caches", umem_cache_walk_init,
+		umem_cache_walk_step, umem_cache_walk_fini
+	};
+
+	if (mdb_add_walker(&w) == -1) {
+		mdb_warn("failed to add umem_cache walker");
+		return (-1);
+	}
+
+	if (umem_update_variables() == -1)
+		return (-1);
+
+	/* install a callback so that our variables are always up-to-date */
+	(void) mdb_callback_add(MDB_CALLBACK_STCHG, umem_statechange_cb, NULL);
+	umem_statechange_cb(NULL);
 
 	/*
-	 * Mappings and threads should eventually be added here.
+	 * Register our ::whatis callbacks.
 	 */
-	if (mdb_walk("umem_cache",
-	    (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) {
-		mdb_warn("couldn't find umem_cache walker");
-		return (DCMD_ERR);
-	}
-
-	if (w.w_found && w.w_all == FALSE)
-		return (DCMD_OK);
-
-	if (mdb_walk("umem_cache",
-	    (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) {
-		mdb_warn("couldn't find umem_cache walker");
-		return (DCMD_ERR);
-	}
-
-	if (w.w_found && w.w_all == FALSE)
-		return (DCMD_OK);
-
-	if (mdb_walk("vmem_postfix",
-	    (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) {
-		mdb_warn("couldn't find vmem_postfix walker");
-		return (DCMD_ERR);
-	}
-
-	if (w.w_found == 0)
-		mdb_printf("%p is unknown\n", addr);
-
-	return (DCMD_OK);
+	mdb_whatis_register("umem", whatis_run_umem, NULL,
+	    WHATIS_PRIO_ALLOCATOR, WHATIS_REG_NO_ID);
+	mdb_whatis_register("vmem", whatis_run_vmem, NULL,
+	    WHATIS_PRIO_ALLOCATOR, WHATIS_REG_NO_ID);
+
+	return (0);
 }
 
 typedef struct umem_log_cpu {
--- a/usr/src/cmd/mdb/intel/Makefile.kmdb	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/intel/Makefile.kmdb	Tue Sep 22 13:42:17 2009 -0700
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 PROMSRCS += \
 	prom_env.c \
@@ -66,14 +64,7 @@
 KMDBLIBS = $(STANDLIBS) ../mdb_ks/kmod/mdb_ks
 
 MAPFILE_SOURCES = \
-	../../../common/mdb/mdb_ctf.h \
-	../../../common/kmdb/kmdb_dpi.h \
-	../../../common/kmdb/kmdb_kctl.h \
-	../../../common/kmdb/kmdb_kdi.h \
-	../../../common/mdb/mdb_ks.h \
-	../../../common/mdb/mdb_modapi.h \
-	../../../common/mdb/mdb_param.h \
-	../../../common/kmdb/kmdb_wr.h \
+	$(MAPFILE_SOURCES_COMMON) \
 	../../kmdb/kmdb_dpi_isadep.h \
 	$(MAPFILE_SOURCES_$(MACH))
 
--- a/usr/src/cmd/mdb/sparc/Makefile.kmdb	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/sparc/Makefile.kmdb	Tue Sep 22 13:42:17 2009 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,21 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 SACPPFLAGS = -D__sparc
 
 MAPFILE_SOURCES = \
-	../../../common/mdb/mdb_ctf.h \
-	../../../common/kmdb/kmdb_dpi.h \
-	../../../common/kmdb/kmdb_kctl.h \
-	../../../common/kmdb/kmdb_kdi.h \
-	../../../common/mdb/mdb_ks.h \
-	../../../common/mdb/mdb_modapi.h \
-	../../../common/mdb/mdb_param.h \
-	../../../common/kmdb/kmdb_wr.h \
+	$(MAPFILE_SOURCES_COMMON) \
 	../../../sparc/kmdb/kmdb_dpi_isadep.h \
 	$(MAPFILE_SOURCES_$(MACH))
--- a/usr/src/cmd/mdb/sun4u/Makefile.kmdb	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/sun4u/Makefile.kmdb	Tue Sep 22 13:42:17 2009 -0700
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 PROMSRCS += \
 	prom_2path.c \
@@ -80,14 +79,7 @@
 KMDBLIBS = $(STANDLIBS) ../../../sparc/v9/mdb_ks/kmod/mdb_ks
 
 MAPFILE_SOURCES = \
-	../../../common/mdb/mdb_ctf.h \
-	../../../common/kmdb/kmdb_dpi.h \
-	../../../common/kmdb/kmdb_kctl.h \
-	../../../common/kmdb/kmdb_kdi.h \
-	../../../common/mdb/mdb_ks.h \
-	../../../common/mdb/mdb_modapi.h \
-	../../../common/mdb/mdb_param.h \
-	../../../common/kmdb/kmdb_wr.h \
+	$(MAPFILE_SOURCES_COMMON) \
 	../../../sparc/kmdb/kmdb_dpi_isadep.h \
 	$(MAPFILE_SOURCES_$(MACH))
 
--- a/usr/src/cmd/mdb/sun4u/modules/unix/unix.c	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/sun4u/modules/unix/unix.c	Tue Sep 22 13:42:17 2009 -0700
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 
 #ifndef DEBUG
@@ -45,6 +43,7 @@
 
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_ctf.h>
+#include <mdb/mdb_whatis.h>
 #include "sfmmu.h"
 
 #ifndef SYSTRAP_TT
@@ -882,7 +881,7 @@
 
 		htraptrace_buf_inuse = 1;
 		mdb_vread(&hdr, sizeof (htrap_trace_hdr_t),
-			(uintptr_t)ctl->d.hvaddr_base);
+		    (uintptr_t)ctl->d.hvaddr_base);
 		mdb_printf("htrap_trace_ctl[%d] = {\n", i);
 		mdb_printf("  vaddr_base = 0x%lx\n", (long)ctl->d.hvaddr_base);
 		mdb_printf("  last_offset = 0x%lx\n", hdr.last_offset);
@@ -917,8 +916,8 @@
 	ttstr = ttp->tt_tt < ttndescr ? ttdescr[ttp->tt_tt] : "?";
 
 	mdb_printf("%016llx %02x  %04hx %04hx %-16s %02x  %02x  %0?p %A\n",
-		ttp->tt_tick, ttp->tt_ty, ttp->tt_tag, ttp->tt_tt, ttstr,
-		ttp->tt_tl, ttp->tt_gl, ttp->tt_tpc, ttp->tt_tpc);
+	    ttp->tt_tick, ttp->tt_ty, ttp->tt_tag, ttp->tt_tt, ttstr,
+	    ttp->tt_tl, ttp->tt_gl, ttp->tt_tpc, ttp->tt_tpc);
 
 	return (WALK_NEXT);
 }
@@ -935,10 +934,10 @@
 		return (WALK_NEXT);
 
 	mdb_printf("%016llx %016llx %02x  %02x  %04hx %04hx %02x  %02x  %0?p "
-		"[%p,%p,%p,%p]\n",
-		ttp->tt_tick, ttp->tt_tstate, ttp->tt_hpstate, ttp->tt_ty,
-		ttp->tt_tag, ttp->tt_tt, ttp->tt_tl, ttp->tt_gl, ttp->tt_tpc,
-		ttp->tt_f1, ttp->tt_f2, ttp->tt_f3, ttp->tt_f4);
+	    "[%p,%p,%p,%p]\n",
+	    ttp->tt_tick, ttp->tt_tstate, ttp->tt_hpstate, ttp->tt_ty,
+	    ttp->tt_tag, ttp->tt_tt, ttp->tt_tl, ttp->tt_gl, ttp->tt_tpc,
+	    ttp->tt_f1, ttp->tt_f2, ttp->tt_f3, ttp->tt_f4);
 
 	return (WALK_NEXT);
 }
@@ -1006,9 +1005,9 @@
 		} else {
 			hdr = (htrap_trace_hdr_t *)buf;
 			tc->tc_rec = (struct htrap_trace_record *)
-				((uintptr_t)buf + (uintptr_t)hdr->last_offset);
+			    ((uintptr_t)buf + (uintptr_t)hdr->last_offset);
 			tc->tc_stop = (struct htrap_trace_record *)
-				((uintptr_t)buf + (uintptr_t)hdr->offset);
+			    ((uintptr_t)buf + (uintptr_t)hdr->offset);
 		}
 	}
 	if (!htraptrace_buf_inuse) {
@@ -1114,13 +1113,13 @@
 
 	if (opt_x) {
 		mdb_printf("%-16s %-16s %-3s %-3s %-4s %-4s %-3s %-3s %-?s "
-			"F1-4\n", "%tick", "%tstate", "%hp", "%ty", "%tag",
-			"%tt", "%tl", "%gl", "%tpc");
+		    "F1-4\n", "%tick", "%tstate", "%hp", "%ty", "%tag",
+		    "%tt", "%tl", "%gl", "%tpc");
 		ttprint = (mdb_walk_cb_t)httprint_long;
 	} else {
 		mdb_printf("%-16s %-3s %-4s %-4s %-16s %-3s %-3s %s\n",
-			"%tick", "%ty", "%tag", "%tt", "", "%tl", "%gl",
-			"%tpc");
+		    "%tick", "%ty", "%tag", "%tt", "", "%tl", "%gl",
+		    "%tpc");
 		ttprint = (mdb_walk_cb_t)httprint_short;
 	}
 
@@ -1520,38 +1519,32 @@
 }
 
 static int
-whatis_walk_tt(uintptr_t taddr, const trap_trace_fullrec_t *ttf, uintptr_t *ap)
+whatis_walk_tt(uintptr_t taddr, const trap_trace_fullrec_t *ttf,
+    mdb_whatis_t *w)
 {
-	uintptr_t addr = *ap;
+	uintptr_t cur = 0;
 
-	if (addr < taddr || addr > taddr + sizeof (struct trap_trace_record))
-		return (WALK_NEXT);
+	while (mdb_whatis_match(w, taddr, sizeof (struct trap_trace_record),
+	    &cur))
+		mdb_whatis_report_object(w, cur, taddr,
+		    "trap trace record for cpu %d\n", ttf->ttf_cpu);
 
-	mdb_printf("%p is %p+%p, trap trace record for cpu %d\n",
-	    addr, taddr, addr - taddr, ttf->ttf_cpu);
-
-	mdb_set_dot(1);
-	return (WALK_DONE);
+	return (WHATIS_WALKRET(w));
 }
 
 /*ARGSUSED*/
-int
-whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+static int
+whatis_run_traptrace(mdb_whatis_t *w, void *ignored)
 {
 	GElf_Sym sym;
 
-	if (!(flags & DCMD_ADDRSPEC))
-		return (DCMD_USAGE);
+	if (mdb_lookup_by_name("trap_trace_ctl", &sym) == -1)
+		return (0);
 
-	if (mdb_lookup_by_name("trap_trace_ctl", &sym) == -1)
-		return (DCMD_NEXT);
-
-	mdb_set_dot(0);
-
-	if (mdb_walk("ttrace", (mdb_walk_cb_t)whatis_walk_tt, &addr) == -1)
+	if (mdb_walk("ttrace", (mdb_walk_cb_t)whatis_walk_tt, w) == -1)
 		mdb_warn("failed to walk 'ttrace'");
 
-	return (DCMD_NEXT);
+	return (0);
 }
 
 /*ARGSUSED*/
@@ -1598,7 +1591,6 @@
 	    vecint_dcmd },
 	{ "softint", NULL, "display a registered software interrupt",
 	    softint_dcmd },
-	{ "whatis", ":[-abv]", "given an address, return information", whatis },
 	{ "sfmmu_vtop", ":[[-v] -a as]", "print virtual to physical mapping",
 	    sfmmu_vtop },
 	{ "page_num2pp", ":", "page frame number to page structure",
@@ -1639,5 +1631,8 @@
 		return (NULL);
 	}
 
+	mdb_whatis_register("traptrace", whatis_run_traptrace, NULL,
+	    WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID);
+
 	return (&modinfo);
 }
--- a/usr/src/cmd/mdb/sun4v/Makefile.kmdb	Tue Sep 22 13:42:10 2009 -0700
+++ b/usr/src/cmd/mdb/sun4v/Makefile.kmdb	Tue Sep 22 13:42:17 2009 -0700
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 PROMSRCS += \
 	prom_2path.c \
@@ -89,14 +88,7 @@
 KMDBLIBS = $(STANDLIBS) ../../../sparc/v9/mdb_ks/kmod/mdb_ks
 
 MAPFILE_SOURCES = \
-	../../../common/mdb/mdb_ctf.h \
-	../../../common/kmdb/kmdb_dpi.h \
-	../../../common/kmdb/kmdb_kctl.h \
-	../../../common/kmdb/kmdb_kdi.h \
-	../../../common/mdb/mdb_ks.h \
-	../../../common/mdb/mdb_modapi.h \
-	../../../common/mdb/mdb_param.h \
-	../../../common/kmdb/kmdb_wr.h \
+	$(MAPFILE_SOURCES_COMMON) \
 	../../../sparc/kmdb/kmdb_dpi_isadep.h \
 	$(MAPFILE_SOURCES_$(MACH))