changeset 13937:6b4f289e7094

3465 ::walk ... | ::<dcmd> misinterprets input as symbol names 3466 ::tsd should handle missing/NULL values better 3467 mdb_ctf_vread() could be more useful 3468 mdb enhancements for zfs development 3470 ::whatis does not print callers from KMF_LITE 3473 mdb_get_module() returns wrong module Reviewed by: Adam Leventhal <ahl@delphix.com> Reviewed by: Eric Schrock <eric.schrock@delphix.com> Reviewed by: Dan Kimmel <dan.kimmel@delphix.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Dan McDonald <danmcd@nexenta.com>
author Matthew Ahrens <mahrens@delphix.com>
date Wed, 30 Jan 2013 15:02:58 -0800
parents 60077db1e2cc
children c99b1993453c
files usr/src/cmd/mdb/common/mdb/mdb.c usr/src/cmd/mdb/common/mdb/mdb_cmds.c usr/src/cmd/mdb/common/mdb/mdb_ctf.c usr/src/cmd/mdb/common/mdb/mdb_ctf.h usr/src/cmd/mdb/common/mdb/mdb_frame.h usr/src/cmd/mdb/common/mdb/mdb_modapi.c usr/src/cmd/mdb/common/mdb/mdb_print.c usr/src/cmd/mdb/common/modules/conf/mapfile-extern usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c usr/src/cmd/mdb/common/modules/genunix/kmem.c usr/src/cmd/mdb/common/modules/genunix/tsd.c usr/src/cmd/mdb/common/modules/libc/libc.c usr/src/cmd/mdb/common/modules/zfs/zfs.c usr/src/cmd/mdb/i86xpv/modules/xpv/xpv.c usr/src/cmd/mdb/i86xpv/modules/xpv_psm/xpv_psm.c usr/src/cmd/mdb/i86xpv/modules/xpv_uppc/xpv_uppc.c usr/src/uts/common/fs/zfs/sys/refcount.h
diffstat 17 files changed, 802 insertions(+), 328 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/mdb/mdb.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb.c	Wed Jan 30 15:02:58 2013 -0800
@@ -1391,7 +1391,8 @@
 		return (NULL);
 
 	if (mdb.m_frame->f_wcbs && mdb.m_frame->f_wcbs->w_walker &&
-	    mdb.m_frame->f_wcbs->w_walker->iwlk_modp)
+	    mdb.m_frame->f_wcbs->w_walker->iwlk_modp &&
+	    !mdb.m_frame->f_cbactive)
 		return (mdb.m_frame->f_wcbs->w_walker->iwlk_modp);
 
 	if (mdb.m_frame->f_cp && mdb.m_frame->f_cp->c_dcmd)
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Wed Jan 30 15:02:58 2013 -0800
@@ -2080,7 +2080,7 @@
 static int
 walk_step(uintptr_t addr, const void *data, void *private)
 {
-	mdb_printf("%lr\n", addr);
+	mdb_printf("%#lr\n", addr);
 	return (WALK_NEXT);
 }
 
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c	Wed Jan 30 15:02:58 2013 -0800
@@ -22,6 +22,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
 
 #include <mdb/mdb_ctf.h>
 #include <mdb/mdb_ctf_impl.h>
@@ -740,12 +743,44 @@
 	return (0);
 }
 
+/*
+ * Returns offset in _bits_ in *retp.
+ */
 int
 mdb_ctf_offsetof(mdb_ctf_id_t id, const char *member, ulong_t *retp)
 {
 	return (mdb_ctf_member_info(id, member, retp, NULL));
 }
 
+/*
+ * Returns offset in _bytes_, or -1 on failure.
+ */
+int
+mdb_ctf_offsetof_by_name(const char *type, const char *member)
+{
+	mdb_ctf_id_t id;
+	ulong_t off;
+
+	if (mdb_ctf_lookup_by_name(type, &id) == -1) {
+		mdb_warn("couldn't find type %s", type);
+		return (-1);
+	}
+
+	if (mdb_ctf_offsetof(id, member, &off) == -1) {
+		mdb_warn("couldn't find member %s of type %s", member, type);
+		return (-1);
+	}
+	if (off % 8 != 0) {
+		mdb_warn("member %s of type %s is an unsupported bitfield\n",
+		    member, type);
+		return (-1);
+	}
+	off /= 8;
+
+	return (off);
+}
+
+
 /*ARGSUSED*/
 static int
 num_members_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
@@ -927,6 +962,19 @@
 	return (mbc.mbc_total);
 }
 
+static void
+mdb_ctf_warn(uint_t flags, const char *format, ...)
+{
+	va_list alist;
+
+	if (flags & MDB_CTF_VREAD_QUIET)
+		return;
+
+	va_start(alist, format);
+	vwarn(format, alist);
+	va_end(alist);
+}
+
 /*
  * Check if two types are structurally the same rather than logically
  * the same. That is to say that two types are equal if they have the
@@ -1016,11 +1064,13 @@
 typedef struct member {
 	char		*m_modbuf;
 	char		*m_tgtbuf;
+	const char	*m_tgtname;
 	mdb_ctf_id_t	m_tgtid;
 	uint_t		m_flags;
 } member_t;
 
-static int vread_helper(mdb_ctf_id_t, char *, mdb_ctf_id_t, char *, uint_t);
+static int vread_helper(mdb_ctf_id_t, char *, mdb_ctf_id_t, char *,
+    const char *, uint_t);
 
 static int
 member_cb(const char *name, mdb_ctf_id_t modmid, ulong_t modoff, void *data)
@@ -1030,29 +1080,66 @@
 	mdb_ctf_id_t tgtmid;
 	char *tgtbuf = mp->m_tgtbuf;
 	ulong_t tgtoff;
+	char tgtname[128];
+
+	(void) mdb_snprintf(tgtname, sizeof (tgtname),
+	    "member %s of type %s", name, mp->m_tgtname);
 
 	if (mdb_ctf_member_info(mp->m_tgtid, name, &tgtoff, &tgtmid) != 0) {
-		if (mp->m_flags & MDB_CTF_VREAD_IGNORE_ABSENT)
-			return (0);
-		else
-			return (set_errno(EMDB_CTFNOMEMB));
+		mdb_ctf_warn(mp->m_flags,
+		    "could not find %s\n", tgtname);
+		return (set_errno(EMDB_CTFNOMEMB));
 	}
 
 	return (vread_helper(modmid, modbuf + modoff / NBBY,
-	    tgtmid, tgtbuf + tgtoff / NBBY, mp->m_flags));
+	    tgtmid, tgtbuf + tgtoff / NBBY, tgtname, mp->m_flags));
 }
 
+typedef struct enum_value {
+	int		*ev_modbuf;
+	const char	*ev_name;
+} enum_value_t;
+
+static int
+enum_cb(const char *name, int value, void *data)
+{
+	enum_value_t *ev = data;
+
+	if (strcmp(name, ev->ev_name) == 0) {
+		*ev->ev_modbuf = value;
+		return (1);
+	}
+	return (0);
+}
 
 static int
 vread_helper(mdb_ctf_id_t modid, char *modbuf,
-    mdb_ctf_id_t tgtid, char *tgtbuf, uint_t flags)
+    mdb_ctf_id_t tgtid, char *tgtbuf, const char *tgtname, uint_t flags)
 {
 	size_t modsz, tgtsz;
 	int modkind, tgtkind;
 	member_t mbr;
+	enum_value_t ev;
 	int ret;
 	mdb_ctf_arinfo_t tar, mar;
 	int i;
+	char typename[128];
+	char mdbtypename[128];
+	ctf_encoding_t tgt_encoding, mod_encoding;
+	boolean_t signed_int = B_FALSE;
+
+	if (mdb_ctf_type_name(tgtid, typename, sizeof (typename)) == NULL) {
+		(void) mdb_snprintf(typename, sizeof (typename),
+		    "#%ul", mdb_ctf_type_id(tgtid));
+	}
+	if (mdb_ctf_type_name(modid,
+	    mdbtypename, sizeof (mdbtypename)) == NULL) {
+		(void) mdb_snprintf(mdbtypename, sizeof (mdbtypename),
+		    "#%ul", mdb_ctf_type_id(modid));
+	}
+
+	if (tgtname == NULL)
+		tgtname = "";
 
 	/*
 	 * Resolve the types to their canonical form.
@@ -1060,38 +1147,113 @@
 	(void) mdb_ctf_type_resolve(modid, &modid);
 	(void) mdb_ctf_type_resolve(tgtid, &tgtid);
 
-	if ((modkind = mdb_ctf_type_kind(modid)) == -1)
+	if ((modkind = mdb_ctf_type_kind(modid)) == -1) {
+		mdb_ctf_warn(flags,
+		    "couldn't determine type kind of mdb module type %s\n",
+		    mdbtypename);
 		return (-1); /* errno is set for us */
-	if ((tgtkind = mdb_ctf_type_kind(tgtid)) == -1)
+	}
+	if ((tgtkind = mdb_ctf_type_kind(tgtid)) == -1) {
+		mdb_ctf_warn(flags,
+		    "couldn't determine type kind of %s\n", typename);
 		return (-1); /* errno is set for us */
+	}
 
-	if (tgtkind != modkind)
+	if (tgtkind != modkind) {
+		mdb_ctf_warn(flags, "unexpected kind for type %s (%s)\n",
+		    typename, tgtname);
 		return (set_errno(EMDB_INCOMPAT));
+	}
+
+	if ((modsz = mdb_ctf_type_size(modid)) == -1UL) {
+		mdb_ctf_warn(flags, "couldn't determine type size of "
+		    "mdb module type %s\n", mdbtypename);
+		return (-1); /* errno is set for us */
+	}
+	if ((tgtsz = mdb_ctf_type_size(tgtid)) == -1UL) {
+		mdb_ctf_warn(flags, "couldn't determine size of %s (%s)\n",
+		    typename, tgtname);
+		return (-1); /* errno is set for us */
+	}
 
 	switch (modkind) {
 	case CTF_K_INTEGER:
 	case CTF_K_FLOAT:
-	case CTF_K_POINTER:
-		if ((modsz = mdb_ctf_type_size(modid)) == -1UL)
+		/*
+		 * Must determine if the target and module types have the same
+		 * encoding before we can copy them.
+		 */
+		if (mdb_ctf_type_encoding(tgtid, &tgt_encoding) != 0) {
+			mdb_ctf_warn(flags,
+			    "couldn't determine encoding of type %s (%s)\n",
+			    typename, tgtname);
 			return (-1); /* errno is set for us */
+		}
+		if (mdb_ctf_type_encoding(modid, &mod_encoding) != 0) {
+			mdb_ctf_warn(flags, "couldn't determine encoding of "
+			    "mdb module type %s\n", mdbtypename);
+			return (-1); /* errno is set for us */
+		}
 
-		if ((tgtsz = mdb_ctf_type_size(tgtid)) == -1UL)
-			return (-1); /* errno is set for us */
-
+		if (modkind == CTF_K_INTEGER) {
+			if ((tgt_encoding.cte_format & CTF_INT_SIGNED) !=
+			    (mod_encoding.cte_format & CTF_INT_SIGNED)) {
+				mdb_ctf_warn(flags,
+				    "signedness mismatch between type "
+				    "%s (%s) and mdb module type %s\n",
+				    typename, tgtname, mdbtypename);
+				return (set_errno(EMDB_INCOMPAT));
+			}
+			signed_int =
+			    ((tgt_encoding.cte_format & CTF_INT_SIGNED) != 0);
+		} else if (tgt_encoding.cte_format != mod_encoding.cte_format) {
+			mdb_ctf_warn(flags,
+			    "encoding mismatch (%#x != %#x) between type "
+			    "%s (%s) and mdb module type %s\n",
+			    tgt_encoding.cte_format, mod_encoding.cte_format,
+			    typename, tgtname, mdbtypename);
+			return (set_errno(EMDB_INCOMPAT));
+		}
+		/* FALLTHROUGH */
+	case CTF_K_POINTER:
 		/*
 		 * If the sizes don't match we need to be tricky to make
 		 * sure that the caller gets the correct data.
 		 */
 		if (modsz < tgtsz) {
-			if (!(flags & MDB_CTF_VREAD_IGNORE_GROW))
-				return (set_errno(EMDB_INCOMPAT));
+			mdb_ctf_warn(flags, "size of type %s (%s) is too "
+			    "large for mdb module type %s\n",
+			    typename, tgtname, mdbtypename);
+			return (set_errno(EMDB_INCOMPAT));
+		} else if (modsz > tgtsz) {
+			/* BEGIN CSTYLED */
+			/*
+			 * Fill modbuf with 1's for sign extension if target
+			 * buf is a signed integer and its value is negative.
+			 *
+			 *   S = sign bit (in most-significant byte)
+			 *
+			 *      BIG ENDIAN DATA
+			 *    +--------+--------+--------+--------+
+			 *    |S       |        |        |        |
+			 *    +--------+--------+--------+--------+
+			 *     0        1  ...            sz-1     sz
+			 *
+			 *      LITTLE ENDIAN DATA
+			 *    +--------+--------+--------+--------+
+			 *    |        |        |        |S       |
+			 *    +--------+--------+--------+--------+
+			 *     0        1  ...            sz-1     sz
+			 */
+			/* END CSTYLED */
 #ifdef _BIG_ENDIAN
-			bcopy(tgtbuf + tgtsz - modsz, modbuf, modsz);
+			if (signed_int && (tgtbuf[0] & 0x80) != 0)
 #else
-			bcopy(tgtbuf, modbuf, modsz);
+			if (signed_int && (tgtbuf[tgtsz - 1] & 0x80) != 0)
 #endif
-		} else if (modsz > tgtsz) {
-			bzero(modbuf, modsz);
+				(void) memset(modbuf, 0xFF, modsz);
+			else
+				bzero(modbuf, modsz);
 #ifdef _BIG_ENDIAN
 			bcopy(tgtbuf, modbuf + modsz - tgtsz, tgtsz);
 #else
@@ -1103,11 +1265,53 @@
 
 		return (0);
 
+	case CTF_K_ENUM:
+		if (modsz != tgtsz || modsz != sizeof (int)) {
+			mdb_ctf_warn(flags, "unexpected size of type %s (%s)\n",
+			    typename, tgtname);
+			return (set_errno(EMDB_INCOMPAT));
+		}
+
+		/*
+		 * Default to the same value as in the target.
+		 */
+		bcopy(tgtbuf, modbuf, sizeof (int));
+
+		/* LINTED */
+		i = *(int *)tgtbuf;
+
+		/* LINTED */
+		ev.ev_modbuf = (int *)modbuf;
+		ev.ev_name = mdb_ctf_enum_name(tgtid, i);
+		if (ev.ev_name == NULL) {
+			mdb_ctf_warn(flags,
+			    "unexpected value %u of enum type %s (%s)\n",
+			    i, typename, tgtname);
+			return (set_errno(EMDB_INCOMPAT));
+		}
+
+		ret = mdb_ctf_enum_iter(modid, enum_cb, &ev);
+		if (ret == 0) {
+			/* value not found */
+			mdb_ctf_warn(flags,
+			    "unexpected value %s (%u) of enum type %s (%s)\n",
+			    ev.ev_name, i, typename, tgtname);
+			return (set_errno(EMDB_INCOMPAT));
+		} else if (ret == 1) {
+			/* value found */
+			return (0);
+		} else if (ret == -1) {
+			mdb_ctf_warn(flags, "could not iterate enum %s (%s)\n",
+			    typename, tgtname);
+		}
+		return (ret);
+
 	case CTF_K_STRUCT:
 		mbr.m_modbuf = modbuf;
 		mbr.m_tgtbuf = tgtbuf;
 		mbr.m_tgtid = tgtid;
 		mbr.m_flags = flags;
+		mbr.m_tgtname = typename;
 
 		return (mdb_ctf_member_iter(modid, member_cb, &mbr));
 
@@ -1116,21 +1320,18 @@
 		/*
 		 * Unions are a little tricky. The only time it's truly
 		 * safe to read in a union is if no part of the union or
-		 * any of its component types have changed. We allow the
-		 * consumer to ignore unions. The correct use of this
-		 * feature is to read the containing structure, figure
-		 * out which component of the union is valid, compute
+		 * any of its component types have changed.  The correct
+		 * use of this feature is to read the containing structure,
+		 * figure out which component of the union is valid, compute
 		 * the location of that in the target and then read in
 		 * that part of the structure.
 		 */
-		if (flags & MDB_CTF_VREAD_IGNORE_UNIONS)
-			return (0);
 
-		if (!type_equals(modid, tgtid))
+		if (!type_equals(modid, tgtid)) {
+			mdb_ctf_warn(flags, "inexact match for union %s (%s)\n",
+			    typename, tgtname);
 			return (set_errno(EMDB_INCOMPAT));
-
-		modsz = mdb_ctf_type_size(modid);
-		tgtsz = mdb_ctf_type_size(tgtid);
+		}
 
 		ASSERT(modsz == tgtsz);
 
@@ -1139,23 +1340,42 @@
 		return (0);
 
 	case CTF_K_ARRAY:
-		if (mdb_ctf_array_info(tgtid, &tar) != 0)
+		if (mdb_ctf_array_info(tgtid, &tar) != 0) {
+			mdb_ctf_warn(flags,
+			    "couldn't get array info for %s (%s)\n",
+			    typename, tgtname);
 			return (-1); /* errno is set for us */
-		if (mdb_ctf_array_info(modid, &mar) != 0)
+		}
+		if (mdb_ctf_array_info(modid, &mar) != 0) {
+			mdb_ctf_warn(flags,
+			    "couldn't get array info for mdb module type %s\n",
+			    mdbtypename);
 			return (-1); /* errno is set for us */
+		}
 
-		if (tar.mta_nelems != mar.mta_nelems)
+		if (tar.mta_nelems != mar.mta_nelems) {
+			mdb_ctf_warn(flags,
+			    "unexpected array size (%u) for type %s (%s)\n",
+			    tar.mta_nelems, typename, tgtname);
 			return (set_errno(EMDB_INCOMPAT));
+		}
 
-		if ((modsz = mdb_ctf_type_size(mar.mta_contents)) == -1UL)
+		if ((modsz = mdb_ctf_type_size(mar.mta_contents)) == -1UL) {
+			mdb_ctf_warn(flags, "couldn't determine type size of "
+			    "mdb module type %s\n", mdbtypename);
 			return (-1); /* errno is set for us */
-
-		if ((tgtsz = mdb_ctf_type_size(tar.mta_contents)) == -1UL)
+		}
+		if ((tgtsz = mdb_ctf_type_size(tar.mta_contents)) == -1UL) {
+			mdb_ctf_warn(flags,
+			    "couldn't determine size of %s (%s)\n",
+			    typename, tgtname);
 			return (-1); /* errno is set for us */
+		}
 
 		for (i = 0; i < tar.mta_nelems; i++) {
 			ret = vread_helper(mar.mta_contents, modbuf + i * modsz,
-			    tar.mta_contents, tgtbuf + i * tgtsz, flags);
+			    tar.mta_contents, tgtbuf + i * tgtsz,
+			    tgtname, flags);
 
 			if (ret != 0)
 				return (ret);
@@ -1164,12 +1384,67 @@
 		return (0);
 	}
 
+	mdb_ctf_warn(flags, "unsupported kind %d for type %s (%s)\n",
+	    modkind, typename, tgtname);
 	return (set_errno(EMDB_INCOMPAT));
 }
 
-
+/*
+ * Like mdb_vread(), mdb_ctf_vread() is used to read from the target's
+ * virtual address space.  However, mdb_ctf_vread() can be used to safely
+ * read a complex type (e.g. a struct) from the target, even if MDB was compiled
+ * against a different definition of that type (e.g. when debugging a crash
+ * dump from an older release).
+ *
+ * Callers can achieve this by defining their own type which corresponds to the
+ * type in the target, but contains only the members that the caller requires.
+ * Using the CTF type information embedded in the target, mdb_ctf_vread will
+ * find the required members in the target and fill in the caller's structure.
+ * The members are located by name, and their types are verified to be
+ * compatible.
+ *
+ * By convention, the caller will declare a type with the name "mdb_<type>",
+ * where <type> is the name of the type in the target (e.g. mdb_zio_t).  This
+ * type will contain the members that the caller is interested in.  For example:
+ *
+ * typedef struct mdb_zio {
+ *         enum zio_type io_type;
+ *         void *io_waiter;
+ *         struct {
+ *                 struct {
+ *                         void *list_next;
+ *                 } list_head;
+ *         } io_parent_list;
+ *         int io_error;
+ * } mdb_zio_t;
+ *
+ * mdb_zio_t zio;
+ * error = mdb_ctf_vread(&zio, "zio_t", "mdb_zio_t", zio_target_addr, 0);
+ *
+ * If a given MDB module has different dcmds or walkers that need to read
+ * different members from the same struct, then different "mdb_" types
+ * should be declared for each caller.  By convention, these types should
+ * be named "mdb_<dcmd or walker>_<type>", e.g. mdb_findstack_kthread_t
+ * for ::findstack.  If the MDB module is compiled from several source files,
+ * one must be especially careful to not define different types with the
+ * same name in different source files, because the compiler can not detect
+ * this error.
+ *
+ * Enums will also be translated by name, so the mdb module will receive
+ * the enum value it expects even if the target has renumbered the enum.
+ * Warning: it will therefore only work with enums are only used to store
+ * legitimate enum values (not several values or-ed together).
+ *
+ * By default, if mdb_ctf_vread() can not find any members or enum values,
+ * it will print a descriptive message (with mdb_warn()) and fail.
+ * Passing MDB_CTF_VREAD_QUIET in 'flags' will suppress the warning message.
+ * Additional flags can be used to ignore specific types of translation
+ * failure, but should be used with caution, because they will silently leave
+ * the caller's buffer uninitialized.
+ */
 int
-mdb_ctf_vread(void *modbuf, const char *typename, uintptr_t addr, uint_t flags)
+mdb_ctf_vread(void *modbuf, const char *target_typename,
+    const char *mdb_typename, uintptr_t addr, uint_t flags)
 {
 	ctf_file_t *mfp;
 	ctf_id_t mid;
@@ -1179,44 +1454,67 @@
 	mdb_ctf_id_t modid;
 	mdb_module_t *mod;
 
-	if ((mod = mdb_get_module()) == NULL || (mfp = mod->mod_ctfp) == NULL)
+	if ((mod = mdb_get_module()) == NULL || (mfp = mod->mod_ctfp) == NULL) {
+		mdb_ctf_warn(flags, "no ctf data found for mdb module %s\n",
+		    mod->mod_name);
 		return (set_errno(EMDB_NOCTF));
+	}
 
-	if ((mid = ctf_lookup_by_name(mfp, typename)) == CTF_ERR) {
-		mdb_dprintf(MDB_DBG_CTF, "couldn't find module's ctf data\n");
+	if ((mid = ctf_lookup_by_name(mfp, mdb_typename)) == CTF_ERR) {
+		mdb_ctf_warn(flags, "couldn't find ctf data for "
+		    "type %s in mdb module %s\n",
+		    mdb_typename, mod->mod_name);
 		return (set_errno(ctf_to_errno(ctf_errno(mfp))));
 	}
 
 	set_ctf_id(&modid, mfp, mid);
 
-	if (mdb_ctf_lookup_by_name(typename, &tgtid) != 0) {
-		mdb_dprintf(MDB_DBG_CTF, "couldn't find target's ctf data\n");
+	if (mdb_ctf_lookup_by_name(target_typename, &tgtid) != 0) {
+		mdb_ctf_warn(flags,
+		    "couldn't find type %s in target's ctf data\n",
+		    target_typename);
 		return (set_errno(EMDB_NOCTF));
 	}
 
 	/*
 	 * Read the data out of the target's address space.
 	 */
-	if ((size = mdb_ctf_type_size(tgtid)) == -1UL)
+	if ((size = mdb_ctf_type_size(tgtid)) == -1UL) {
+		mdb_ctf_warn(flags, "couldn't determine size of type %s\n",
+		    target_typename);
 		return (-1); /* errno is set for us */
+	}
 
 	tgtbuf = mdb_alloc(size, UM_SLEEP | UM_GC);
 
-	if (mdb_vread(tgtbuf, size, addr) < 0)
+	if (mdb_vread(tgtbuf, size, addr) < 0) {
+		mdb_ctf_warn(flags, "couldn't read %s from %p\n",
+		    target_typename, addr);
 		return (-1); /* errno is set for us */
+	}
 
-	return (vread_helper(modid, modbuf, tgtid, tgtbuf, flags));
+	return (vread_helper(modid, modbuf, tgtid, tgtbuf, NULL, flags));
 }
 
+/*
+ * Note: mdb_ctf_readsym() doesn't take separate parameters for the name
+ * of the target's type vs the mdb module's type.  Use with complicated
+ * types (e.g. structs) may result in unnecessary failure if a member of
+ * the struct has been changed in the target, but is not actually needed
+ * by the mdb module.  Use mdb_lookup_by_name() + mdb_ctf_vread() to
+ * avoid this problem.
+ */
 int
 mdb_ctf_readsym(void *buf, const char *typename, const char *name, uint_t flags)
 {
 	GElf_Sym sym;
 
-	if (mdb_lookup_by_name(name, &sym) != 0)
+	if (mdb_lookup_by_name(name, &sym) != 0) {
+		mdb_ctf_warn(flags, "couldn't find symbol %s\n", name);
 		return (-1); /* errno is set for us */
+	}
 
-	return (mdb_ctf_vread(buf, typename, sym.st_value, flags));
+	return (mdb_ctf_vread(buf, typename, typename, sym.st_value, flags));
 }
 
 ctf_file_t *
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.h	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h	Wed Jan 30 15:02:58 2013 -0800
@@ -22,6 +22,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
 
 #ifndef	_MDB_CTF_H
 #define	_MDB_CTF_H
@@ -108,17 +111,15 @@
     ulong_t *, mdb_ctf_id_t *);
 extern int mdb_ctf_offsetof(mdb_ctf_id_t, const char *, ulong_t *);
 extern int mdb_ctf_num_members(mdb_ctf_id_t);
+extern int mdb_ctf_offsetof_by_name(const char *, const char *);
 
 extern ssize_t mdb_ctf_offset_to_name(mdb_ctf_id_t, ulong_t, char *, size_t,
     int, mdb_ctf_id_t *, ulong_t *);
 
-#define	MDB_CTF_VREAD_IGNORE_GROW	0x01
-#define	MDB_CTF_VREAD_IGNORE_ABSENT	0x02
-#define	MDB_CTF_VREAD_IGNORE_UNIONS	0x04
+#define	MDB_CTF_VREAD_QUIET		0x100
 
-#define	MDB_CTF_VREAD_IGNORE_ALL	0x07
-
-extern int mdb_ctf_vread(void *, const char *, uintptr_t, uint_t);
+extern int mdb_ctf_vread(void *, const char *, const char *,
+    uintptr_t, uint_t);
 extern int mdb_ctf_readsym(void *, const char *, const char *, uint_t);
 
 #ifdef _MDB
--- a/usr/src/cmd/mdb/common/mdb/mdb_frame.h	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb_frame.h	Wed Jan 30 15:02:58 2013 -0800
@@ -26,8 +26,6 @@
 #ifndef	_MDB_FRAME_H
 #define	_MDB_FRAME_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <mdb/mdb_module.h>
 #include <mdb/mdb_addrvec.h>
 #include <mdb/mdb_list.h>
@@ -67,6 +65,7 @@
 	struct mdb_lex_state *f_lstate;	/* Current lex state */
 	uintmax_t f_dot;		/* Value of '.' */
 	mdb_bool_t pipe;		/* frame has pipe context */
+	uint_t f_cbactive;		/* true iff a callback is active */
 } mdb_frame_t;
 
 #ifdef _MDB
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c	Wed Jan 30 15:02:58 2013 -0800
@@ -510,16 +510,44 @@
 	return (rval);
 }
 
+typedef struct pwalk_step {
+	mdb_walk_cb_t ps_cb;
+	void *ps_private;
+} pwalk_step_t;
+
+static int
+pwalk_step(uintptr_t addr, const void *data, void *private)
+{
+	pwalk_step_t *psp = private;
+	int ret;
+
+	mdb.m_frame->f_cbactive = B_TRUE;
+	ret = psp->ps_cb(addr, data, psp->ps_private);
+	mdb.m_frame->f_cbactive = B_FALSE;
+
+	return (ret);
+}
+
 int
-mdb_pwalk(const char *name, mdb_walk_cb_t func, void *data, uintptr_t addr)
+mdb_pwalk(const char *name, mdb_walk_cb_t func, void *private, uintptr_t addr)
 {
 	mdb_iwalker_t *iwp = mdb_walker_lookup(name);
+	pwalk_step_t p;
 
 	if (func == NULL)
 		return (set_errno(EINVAL));
 
-	if (iwp != NULL)
-		return (walk_common(mdb_wcb_create(iwp, func, data, addr)));
+	p.ps_cb = func;
+	p.ps_private = private;
+
+	if (iwp != NULL) {
+		int ret;
+		int cbactive = mdb.m_frame->f_cbactive;
+		mdb.m_frame->f_cbactive = B_FALSE;
+		ret = walk_common(mdb_wcb_create(iwp, pwalk_step, &p, addr));
+		mdb.m_frame->f_cbactive = cbactive;
+		return (ret);
+	}
 
 	return (-1); /* errno is set for us */
 }
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c	Wed Jan 30 15:02:58 2013 -0800
@@ -696,8 +696,7 @@
 int
 cmd_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
-	mdb_ctf_id_t id;
-	ulong_t offset;
+	int offset;
 	uintptr_t a, tmp;
 	int ret;
 
@@ -730,34 +729,21 @@
 		if (ret != 0)
 			return (ret);
 
-		if (mdb_ctf_lookup_by_name(buf, &id) != 0) {
-			mdb_warn("failed to look up type %s", buf);
+		argv++;
+		argc--;
+
+		member = argv->a_un.a_str;
+		offset = mdb_ctf_offsetof_by_name(buf, member);
+		if (offset == -1)
 			return (DCMD_ABORT);
-		}
 
 		argv++;
 		argc--;
 
-		if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
-			return (DCMD_USAGE);
-
-		member = argv->a_un.a_str;
-
-		argv++;
-		argc--;
-
-		if (mdb_ctf_offsetof(id, member, &offset) != 0) {
-			mdb_warn("failed to find member %s of type %s",
-			    member, buf);
-			return (DCMD_ABORT);
-		}
-
-		if (offset % (sizeof (uintptr_t) * NBBY) != 0) {
+		if (offset % (sizeof (uintptr_t)) != 0) {
 			mdb_warn("%s is not a word-aligned member\n", member);
 			return (DCMD_ABORT);
 		}
-
-		offset /= NBBY;
 	}
 
 	/*
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Wed Jan 30 15:02:58 2013 -0800
@@ -1,5 +1,6 @@
 #
 # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012 by Delphix. All rights reserved.
 #
 # CDDL HEADER START
 #
@@ -60,7 +61,10 @@
 		mdb_ctf_lookup_by_addr		{ FLAGS = EXTERN };
 		mdb_ctf_lookup_by_name		{ FLAGS = EXTERN };
 		mdb_ctf_member_iter		{ FLAGS = EXTERN };
+		mdb_ctf_module_lookup		{ FLAGS = EXTERN };
 		mdb_ctf_offsetof		{ FLAGS = EXTERN };
+		mdb_ctf_offsetof_by_name	{ FLAGS = EXTERN };
+		mdb_ctf_readsym			{ FLAGS = EXTERN };
 		mdb_ctf_type_cmp		{ FLAGS = EXTERN };
 		mdb_ctf_type_invalidate		{ FLAGS = EXTERN };
 		mdb_ctf_type_kind		{ FLAGS = EXTERN };
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c	Wed Jan 30 15:02:58 2013 -0800
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 #include <mdb/mdb_modapi.h>
@@ -126,11 +127,21 @@
 	return (levels);
 }
 
+typedef struct mdb_findstack_kthread {
+	struct _sobj_ops *t_sobj_ops;
+	uint_t	t_state;
+	ushort_t t_flag;
+	ushort_t t_schedflag;
+	caddr_t	t_stk;
+	caddr_t	t_stkbase;
+	label_t	t_pcb;
+} mdb_findstack_kthread_t;
+
 /*ARGSUSED*/
 int
 stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
 {
-	kthread_t thr;
+	mdb_findstack_kthread_t thr;
 	size_t stksz;
 	uintptr_t ubase, utop;
 	uintptr_t kbase, ktop;
@@ -142,11 +153,8 @@
 	fsip->fsi_depth = 0;
 	fsip->fsi_overflow = 0;
 
-	bzero(&thr, sizeof (thr));
-	if (mdb_ctf_vread(&thr, "kthread_t", addr,
-	    MDB_CTF_VREAD_IGNORE_ALL) == -1) {
-		if (print_warnings)
-			mdb_warn("couldn't read thread at %p\n", addr);
+	if (mdb_ctf_vread(&thr, "kthread_t", "mdb_findstack_kthread_t",
+	    addr, print_warnings ? 0 : MDB_CTF_VREAD_QUIET) == -1) {
 		fsip->fsi_failed = FSI_FAIL_BADTHREAD;
 		return (DCMD_ERR);
 	}
--- a/usr/src/cmd/mdb/common/modules/genunix/kmem.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/modules/genunix/kmem.c	Wed Jan 30 15:02:58 2013 -0800
@@ -25,6 +25,7 @@
 
 /*
  * Copyright 2011 Joyent, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 #include <mdb/mdb_param.h>
@@ -2161,8 +2162,10 @@
 	/* 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),
+	if (stat != KMEM_BUFTAG_ALLOC && stat != KMEM_BUFTAG_FREE)
+		return;
+
+	if (mdb_vread(callers, count * sizeof (pc_t),
 	    btaddr + offsetof(kmem_buftag_lite_t, bt_history)) == -1)
 		return;
 
--- a/usr/src/cmd/mdb/common/modules/genunix/tsd.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/modules/genunix/tsd.c	Wed Jan 30 15:02:58 2013 -0800
@@ -23,8 +23,9 @@
  * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
  * All rights reserved.
  */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
 
 #include <sys/thread.h>
 #include "tsd.h"
@@ -84,7 +85,7 @@
 	struct tsd_thread tsdata, *ts = &tsdata;
 	uintptr_t key = NULL;
 	uintptr_t eladdr;
-	void *element;
+	void *element = NULL;
 
 	if (mdb_getopts(argc, argv, 'k', MDB_OPT_UINTPTR, &key, NULL) != argc)
 		return (DCMD_USAGE);
@@ -97,22 +98,16 @@
 		return (DCMD_ERR);
 	}
 
-	if (t->t_tsd == NULL) {
-		if (flags & DCMD_PIPE)
-			return (DCMD_OK);
-		mdb_warn("no tsd on thread\n");
-		return (DCMD_ERR);
-	}
+	if (t->t_tsd == NULL)
+		goto out;
 
 	if (mdb_vread(ts, sizeof (*ts), (uintptr_t)t->t_tsd) == -1) {
 		mdb_warn("failed to read tsd at %p", t->t_tsd);
 		return (DCMD_ERR);
 	}
 
-	if (key > ts->ts_nkeys) {
-		mdb_warn("key out of range\n");
-		return (DCMD_ERR);
-	}
+	if (key > ts->ts_nkeys)
+		goto out;
 
 	eladdr = (uintptr_t)(ts->ts_value + key - 1);
 	if (mdb_vread(&element, sizeof (element), eladdr) == -1) {
@@ -120,6 +115,10 @@
 		return (DCMD_ERR);
 	}
 
+out:
+	if (element == NULL && (flags & DCMD_PIPE))
+		return (DCMD_OK);
+
 	mdb_printf("%p\n", element);
 	return (DCMD_OK);
 }
--- a/usr/src/cmd/mdb/common/modules/libc/libc.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/modules/libc/libc.c	Wed Jan 30 15:02:58 2013 -0800
@@ -21,10 +21,12 @@
 
 /*
  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 #include <sys/mdb_modapi.h>
 #include <mdb/mdb_whatis.h>
+#include <mdb/mdb_ctf.h>
 #include <procfs.h>
 #include <ucontext.h>
 #include <siginfo.h>
@@ -1019,17 +1021,13 @@
 	return (WALK_NEXT);
 }
 
-/*ARGSUSED*/
 static int
-tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+tid2ulwp_impl(uintptr_t tid_addr, uintptr_t *ulwp_addrp)
 {
 	tid2ulwp_walk_t t2u;
 
-	if (argc != 0)
-		return (DCMD_USAGE);
-
 	bzero(&t2u, sizeof (t2u));
-	t2u.t2u_tid = (lwpid_t)addr;
+	t2u.t2u_tid = (lwpid_t)tid_addr;
 
 	if (mdb_walk("ulwp", (mdb_walk_cb_t)tid2ulwp_walk, &t2u) != 0) {
 		mdb_warn("can't walk 'ulwp'");
@@ -1040,9 +1038,78 @@
 		mdb_warn("thread ID %d not found", t2u.t2u_tid);
 		return (DCMD_ERR);
 	}
+	*ulwp_addrp = t2u.t2u_lwp;
+	return (DCMD_OK);
+}
 
-	mdb_printf("%p\n", t2u.t2u_lwp);
+/*ARGSUSED*/
+static int
+tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uintptr_t ulwp_addr;
+	int error;
+
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	error = tid2ulwp_impl(addr, &ulwp_addr);
+	if (error == DCMD_OK)
+		mdb_printf("%p\n", ulwp_addr);
+	return (error);
+}
+
+typedef struct mdb_libc_ulwp {
+	void *ul_ftsd[TSD_NFAST];
+	tsd_t *ul_stsd;
+} mdb_libc_ulwp_t;
+
+/*
+ * Map from thread pointer to tsd for given key
+ */
+static int
+d_tsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_libc_ulwp_t u;
+	uintptr_t ulwp_addr;
+	uintptr_t key = NULL;
+	void *element = NULL;
 
+	if (mdb_getopts(argc, argv, 'k', MDB_OPT_UINTPTR, &key, NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (!(flags & DCMD_ADDRSPEC) || key == NULL)
+		return (DCMD_USAGE);
+
+	if (tid2ulwp_impl(addr, &ulwp_addr) != DCMD_OK)
+		return (DCMD_ERR);
+
+	if (mdb_ctf_vread(&u, "ulwp_t", "mdb_libc_ulwp_t", ulwp_addr, 0) == -1)
+		return (DCMD_ERR);
+
+	if (key < TSD_NFAST) {
+		element = u.ul_ftsd[key];
+	} else if (u.ul_stsd != NULL) {
+		uint_t nalloc;
+		/* tsd_t is a union, so we can't use ctf_vread() on it. */
+		if (mdb_vread(&nalloc, sizeof (nalloc),
+		    (uintptr_t)&u.ul_stsd->tsd_nalloc) == -1) {
+			mdb_warn("failed to read tsd_t at %p", u.ul_stsd);
+			return (DCMD_ERR);
+		}
+		if (key < nalloc) {
+			if (mdb_vread(&element, sizeof (element),
+			    (uintptr_t)&u.ul_stsd->tsd_data[key]) == -1) {
+				mdb_warn("failed to read tsd_t at %p",
+				    u.ul_stsd);
+				return (DCMD_ERR);
+			}
+		}
+	}
+
+	if (element == NULL && (flags & DCMD_PIPE))
+		return (DCMD_OK);
+
+	mdb_printf("%p\n", element);
 	return (DCMD_OK);
 }
 
@@ -1056,6 +1123,7 @@
 	{ "ucontext", ":", "print ucontext_t structure", d_ucontext, NULL },
 	{ "ulwp", ":", "print ulwp_t structure", d_ulwp, NULL },
 	{ "uberdata", ":", "print uberdata_t structure", d_uberdata, NULL },
+	{ "tsd", ":-k key", "print tsd for this thread", d_tsd, NULL },
 	{ NULL }
 };
 
--- a/usr/src/cmd/mdb/common/modules/zfs/zfs.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/common/modules/zfs/zfs.c	Wed Jan 30 15:02:58 2013 -0800
@@ -21,7 +21,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
 /* Portions Copyright 2010 Robert Milkowski */
@@ -50,6 +50,8 @@
 #define	ZFS_OBJ_NAME	"libzpool.so.1"
 #endif
 
+#define	ZFS_STRUCT	"struct " ZFS_OBJ_NAME "`"
+
 #ifndef _KERNEL
 int aok;
 #endif
@@ -94,39 +96,13 @@
 	return (0);
 }
 
-#define	GETMEMB(addr, type, member, dest) \
-	getmember(addr, #type, NULL, #member, sizeof (dest), &(dest))
+#define	GETMEMB(addr, structname, member, dest) \
+	getmember(addr, ZFS_STRUCT structname, NULL, #member, \
+	sizeof (dest), &(dest))
 
 #define	GETMEMBID(addr, ctfid, member, dest) \
 	getmember(addr, NULL, ctfid, #member, sizeof (dest), &(dest))
 
-static int
-getrefcount(uintptr_t addr, mdb_ctf_id_t *id,
-    const char *member, uint64_t *rc)
-{
-	static int gotid;
-	static mdb_ctf_id_t rc_id;
-	ulong_t off;
-
-	if (!gotid) {
-		if (mdb_ctf_lookup_by_name("struct refcount", &rc_id) == -1) {
-			mdb_warn("couldn't find struct refcount");
-			return (DCMD_ERR);
-		}
-		gotid = TRUE;
-	}
-
-	if (mdb_ctf_offsetof(*id, member, &off) == -1) {
-		char name[64];
-		mdb_ctf_type_name(*id, name, sizeof (name));
-		mdb_warn("couldn't find member %s of type %s\n", member, name);
-		return (DCMD_ERR);
-	}
-	off /= 8;
-
-	return (GETMEMBID(addr + off, &rc_id, rc_count, *rc));
-}
-
 static boolean_t
 strisprint(const char *cp)
 {
@@ -188,9 +164,8 @@
 	return (WALK_NEXT);
 }
 
-
 static int
-dataset_name(uintptr_t addr, char *buf)
+mdb_dsl_dir_name(uintptr_t addr, char *buf)
 {
 	static int gotid;
 	static mdb_ctf_id_t dd_id;
@@ -198,7 +173,7 @@
 	char dd_myname[MAXNAMELEN];
 
 	if (!gotid) {
-		if (mdb_ctf_lookup_by_name("struct dsl_dir",
+		if (mdb_ctf_lookup_by_name(ZFS_STRUCT "dsl_dir",
 		    &dd_id) == -1) {
 			mdb_warn("couldn't find struct dsl_dir");
 			return (DCMD_ERR);
@@ -211,7 +186,7 @@
 	}
 
 	if (dd_parent) {
-		if (dataset_name(dd_parent, buf))
+		if (mdb_dsl_dir_name(dd_parent, buf))
 			return (DCMD_ERR);
 		strcat(buf, "/");
 	}
@@ -236,12 +211,12 @@
 	buf[0] = '\0';
 
 	if (!gotid) {
-		if (mdb_ctf_lookup_by_name("struct objset",
+		if (mdb_ctf_lookup_by_name(ZFS_STRUCT "objset",
 		    &os_id) == -1) {
 			mdb_warn("couldn't find struct objset");
 			return (DCMD_ERR);
 		}
-		if (mdb_ctf_lookup_by_name("struct dsl_dataset",
+		if (mdb_ctf_lookup_by_name(ZFS_STRUCT "dsl_dataset",
 		    &ds_id) == -1) {
 			mdb_warn("couldn't find struct dsl_dataset");
 			return (DCMD_ERR);
@@ -263,7 +238,7 @@
 		return (DCMD_ERR);
 	}
 
-	if (ds_dir && dataset_name(ds_dir, buf))
+	if (ds_dir && mdb_dsl_dir_name(ds_dir, buf))
 		return (DCMD_ERR);
 
 	if (ds_snapname[0]) {
@@ -400,58 +375,53 @@
 	return (DCMD_OK);
 }
 
+typedef struct mdb_dmu_buf_impl {
+	struct {
+		uint64_t db_object;
+	} db;
+	void *db_objset;
+	uint64_t db_level;
+	uint64_t db_blkid;
+	struct {
+		uint64_t rc_count;
+	} db_holds;
+} mdb_dmu_buf_impl_t;
+
 /* ARGSUSED */
 static int
 dbuf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
-	mdb_ctf_id_t id;
-	dmu_buf_t db;
-	uintptr_t objset;
-	uint8_t level;
-	uint64_t blkid;
-	uint64_t holds;
+	mdb_dmu_buf_impl_t db;
 	char objectname[32];
 	char blkidname[32];
 	char path[MAXNAMELEN];
 
-	if (DCMD_HDRSPEC(flags)) {
+	if (DCMD_HDRSPEC(flags))
 		mdb_printf("        addr object lvl blkid holds os\n");
-	}
-
-	if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &id) == -1) {
-		mdb_warn("couldn't find struct dmu_buf_impl_t");
+
+	if (mdb_ctf_vread(&db, ZFS_STRUCT "dmu_buf_impl", "mdb_dmu_buf_impl_t",
+	    addr, 0) == -1)
 		return (DCMD_ERR);
-	}
-
-	if (GETMEMBID(addr, &id, db_objset, objset) ||
-	    GETMEMBID(addr, &id, db, db) ||
-	    GETMEMBID(addr, &id, db_level, level) ||
-	    GETMEMBID(addr, &id, db_blkid, blkid)) {
-		return (WALK_ERR);
-	}
-
-	if (getrefcount(addr, &id, "db_holds", &holds)) {
-		return (WALK_ERR);
-	}
-
-	if (db.db_object == DMU_META_DNODE_OBJECT)
+
+	if (db.db.db_object == DMU_META_DNODE_OBJECT)
 		(void) strcpy(objectname, "mdn");
 	else
 		(void) mdb_snprintf(objectname, sizeof (objectname), "%llx",
-		    (u_longlong_t)db.db_object);
-
-	if (blkid == DMU_BONUS_BLKID)
+		    (u_longlong_t)db.db.db_object);
+
+	if (db.db_blkid == DMU_BONUS_BLKID)
 		(void) strcpy(blkidname, "bonus");
 	else
 		(void) mdb_snprintf(blkidname, sizeof (blkidname), "%llx",
-		    (u_longlong_t)blkid);
-
-	if (objset_name(objset, path)) {
-		return (WALK_ERR);
+		    (u_longlong_t)db.db_blkid);
+
+	if (objset_name((uintptr_t)db.db_objset, path)) {
+		return (DCMD_ERR);
 	}
 
-	mdb_printf("%p %8s %1u %9s %2llu %s\n",
-	    addr, objectname, level, blkidname, holds, path);
+	mdb_printf("%p %8s %1u %9s %2llu %s\n", addr,
+	    objectname, (int)db.db_level, blkidname,
+	    db.db_holds.rc_count, path);
 
 	return (DCMD_OK);
 }
@@ -735,7 +705,7 @@
 		}
 	}
 
-	if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &data.id) == -1) {
+	if (mdb_ctf_lookup_by_name(ZFS_STRUCT "dmu_buf_impl", &data.id) == -1) {
 		mdb_warn("couldn't find struct dmu_buf_impl_t");
 		return (DCMD_ERR);
 	}
@@ -801,7 +771,7 @@
 		}
 	}
 
-	if (mdb_ctf_lookup_by_name("struct arc_buf_hdr", &data.id) == -1) {
+	if (mdb_ctf_lookup_by_name(ZFS_STRUCT "arc_buf_hdr", &data.id) == -1) {
 		mdb_warn("couldn't find struct arc_buf_hdr");
 		return (DCMD_ERR);
 	}
@@ -821,6 +791,12 @@
 	return (DCMD_OK);
 }
 
+
+typedef struct dbgmsg_arg {
+	boolean_t da_verbose;
+	boolean_t da_address;
+} dbgmsg_arg_t;
+
 /* ARGSUSED */
 static int
 dbgmsg_cb(uintptr_t addr, const void *unknown, void *arg)
@@ -829,12 +805,13 @@
 	static boolean_t gotid;
 	static ulong_t off;
 
-	int *verbosep = arg;
+	dbgmsg_arg_t *da = arg;
 	time_t timestamp;
 	char buf[1024];
 
 	if (!gotid) {
-		if (mdb_ctf_lookup_by_name("struct zfs_dbgmsg", &id) == -1) {
+		if (mdb_ctf_lookup_by_name(ZFS_STRUCT "zfs_dbgmsg", &id) ==
+		    -1) {
 			mdb_warn("couldn't find struct zfs_dbgmsg");
 			return (WALK_ERR);
 		}
@@ -856,12 +833,14 @@
 		return (DCMD_ERR);
 	}
 
-	if (*verbosep)
+	if (da->da_address)
+		mdb_printf("%p ", addr);
+	if (da->da_verbose)
 		mdb_printf("%Y ", timestamp);
 
 	mdb_printf("%s\n", buf);
 
-	if (*verbosep)
+	if (da->da_verbose)
 		(void) mdb_call_dcmd("whatis", addr, DCMD_ADDRSPEC, 0, NULL);
 
 	return (WALK_NEXT);
@@ -872,10 +851,11 @@
 dbgmsg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
 	GElf_Sym sym;
-	int verbose = FALSE;
+	dbgmsg_arg_t da = { 0 };
 
 	if (mdb_getopts(argc, argv,
-	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
+	    'v', MDB_OPT_SETBITS, B_TRUE, &da.da_verbose,
+	    'a', MDB_OPT_SETBITS, B_TRUE, &da.da_address,
 	    NULL) != argc)
 		return (DCMD_USAGE);
 
@@ -884,7 +864,7 @@
 		return (DCMD_ERR);
 	}
 
-	if (mdb_pwalk("list", dbgmsg_cb, &verbose, sym.st_value) != 0) {
+	if (mdb_pwalk("list", dbgmsg_cb, &da, sym.st_value) != 0) {
 		mdb_warn("can't walk zfs_dbgmsgs");
 		return (DCMD_ERR);
 	}
@@ -1096,6 +1076,10 @@
 	return (DCMD_OK);
 }
 
+typedef struct mdb_spa_config_spa {
+	nvlist_t *spa_config;
+} mdb_spa_config_spa_t;
+
 /*
  * ::spa_config
  *
@@ -1107,15 +1091,14 @@
 static int
 spa_print_config(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
-	spa_t spa;
+	mdb_spa_config_spa_t spa;
 
 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
 		return (DCMD_USAGE);
 
-	if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
-		mdb_warn("failed to read spa_t at %p", addr);
+	if (mdb_ctf_vread(&spa, ZFS_STRUCT "spa", "mdb_spa_config_spa_t",
+	    addr, 0) == -1)
 		return (DCMD_ERR);
-	}
 
 	if (spa.spa_config == NULL) {
 		mdb_printf("(none)\n");
@@ -1369,8 +1352,8 @@
 		ASSERT(mw->mw_nummss == 0);
 
 		vdevp = mw->mw_vdevs[mw->mw_curvdev];
-		if (GETMEMB(vdevp, struct vdev, vdev_ms, mssp) ||
-		    GETMEMB(vdevp, struct vdev, vdev_ms_count, mw->mw_nummss)) {
+		if (GETMEMB(vdevp, "vdev", vdev_ms, mssp) ||
+		    GETMEMB(vdevp, "vdev", vdev_ms_count, mw->mw_nummss)) {
 			return (WALK_ERR);
 		}
 
@@ -1417,9 +1400,9 @@
 
 	mw = mdb_zalloc(sizeof (metaslab_walk_data_t), UM_SLEEP | UM_GC);
 
-	if (GETMEMB(wsp->walk_addr, struct spa, spa_root_vdev, root_vdevp) ||
-	    GETMEMB(root_vdevp, struct vdev, vdev_children, mw->mw_numvdevs) ||
-	    GETMEMB(root_vdevp, struct vdev, vdev_child, childp)) {
+	if (GETMEMB(wsp->walk_addr, "spa", spa_root_vdev, root_vdevp) ||
+	    GETMEMB(root_vdevp, "vdev", vdev_children, mw->mw_numvdevs) ||
+	    GETMEMB(root_vdevp, "vdev", vdev_child, childp)) {
 		return (DCMD_ERR);
 	}
 
@@ -1482,11 +1465,11 @@
 	space_data_t *sd = arg;
 	mdb_metaslab_t ms;
 
-	if (GETMEMB(addr, struct metaslab, ms_allocmap, ms.ms_allocmap) ||
-	    GETMEMB(addr, struct metaslab, ms_freemap, ms.ms_freemap) ||
-	    GETMEMB(addr, struct metaslab, ms_map, ms.ms_map) ||
-	    GETMEMB(addr, struct metaslab, ms_smo, ms.ms_smo) ||
-	    GETMEMB(addr, struct metaslab, ms_smo_syncing, ms.ms_smo_syncing)) {
+	if (GETMEMB(addr, "metaslab", ms_allocmap, ms.ms_allocmap) ||
+	    GETMEMB(addr, "metaslab", ms_freemap, ms.ms_freemap) ||
+	    GETMEMB(addr, "metaslab", ms_map, ms.ms_map) ||
+	    GETMEMB(addr, "metaslab", ms_smo, ms.ms_smo) ||
+	    GETMEMB(addr, "metaslab", ms_smo_syncing, ms.ms_smo_syncing)) {
 		return (WALK_ERR);
 	}
 
@@ -1525,33 +1508,33 @@
 	space_data_t sd;
 	int shift = 20;
 	char *suffix = "M";
-	int bits = FALSE;
-
-	if (mdb_getopts(argc, argv, 'b', MDB_OPT_SETBITS, TRUE, &bits, NULL) !=
+	int bytes = B_FALSE;
+
+	if (mdb_getopts(argc, argv, 'b', MDB_OPT_SETBITS, TRUE, &bytes, NULL) !=
 	    argc)
 		return (DCMD_USAGE);
 	if (!(flags & DCMD_ADDRSPEC))
 		return (DCMD_USAGE);
 
-	if (bits) {
+	if (bytes) {
 		shift = 0;
 		suffix = "";
 	}
 
-	if (GETMEMB(addr, struct spa, spa_dsl_pool, spa.spa_dsl_pool) ||
-	    GETMEMB(addr, struct spa, spa_root_vdev, spa.spa_root_vdev) ||
-	    GETMEMB(spa.spa_root_vdev, struct vdev, vdev_children, children) ||
-	    GETMEMB(spa.spa_root_vdev, struct vdev, vdev_child, childaddr) ||
-	    GETMEMB(spa.spa_dsl_pool, struct dsl_pool,
+	if (GETMEMB(addr, "spa", spa_dsl_pool, spa.spa_dsl_pool) ||
+	    GETMEMB(addr, "spa", spa_root_vdev, spa.spa_root_vdev) ||
+	    GETMEMB(spa.spa_root_vdev, "vdev", vdev_children, children) ||
+	    GETMEMB(spa.spa_root_vdev, "vdev", vdev_child, childaddr) ||
+	    GETMEMB(spa.spa_dsl_pool, "dsl_pool",
 	    dp_root_dir, dp_root_dir) ||
-	    GETMEMB(dp_root_dir, struct dsl_dir, dd_phys, dd.dd_phys) ||
-	    GETMEMB(dp_root_dir, struct dsl_dir,
+	    GETMEMB(dp_root_dir, "dsl_dir", dd_phys, dd.dd_phys) ||
+	    GETMEMB(dp_root_dir, "dsl_dir",
 	    dd_space_towrite, dd.dd_space_towrite) ||
-	    GETMEMB(dd.dd_phys, struct dsl_dir_phys,
+	    GETMEMB(dd.dd_phys, "dsl_dir_phys",
 	    dd_used_bytes, dsp.dd_used_bytes) ||
-	    GETMEMB(dd.dd_phys, struct dsl_dir_phys,
+	    GETMEMB(dd.dd_phys, "dsl_dir_phys",
 	    dd_compressed_bytes, dsp.dd_compressed_bytes) ||
-	    GETMEMB(dd.dd_phys, struct dsl_dir_phys,
+	    GETMEMB(dd.dd_phys, "dsl_dir_phys",
 	    dd_uncompressed_bytes, dsp.dd_uncompressed_bytes)) {
 		return (DCMD_ERR);
 	}
@@ -1749,13 +1732,25 @@
 	uint_t	zpa_flags;
 } zio_print_args_t;
 
+typedef struct mdb_zio {
+	enum zio_type io_type;
+	enum zio_stage io_stage;
+	void *io_waiter;
+	uint64_t io_timestamp;
+	void *io_spa;
+	struct {
+		struct {
+			void *list_next;
+		} list_head;
+	} io_parent_list;
+	int io_error;
+} mdb_zio_t;
+
 static int zio_child_cb(uintptr_t addr, const void *unknown, void *arg);
 
 static int
-zio_print_cb(uintptr_t addr, const void *data, void *priv)
+zio_print_cb(uintptr_t addr, const mdb_zio_t *zio, zio_print_args_t *zpa)
 {
-	const zio_t *zio = data;
-	zio_print_args_t *zpa = priv;
 	mdb_ctf_id_t type_enum, stage_enum;
 	int indent = zpa->zpa_current_depth;
 	const char *type, *stage;
@@ -1775,10 +1770,15 @@
 	else
 		type = "?";
 
-	if ((stage = mdb_ctf_enum_name(stage_enum, zio->io_stage)) != NULL)
-		stage += sizeof ("ZIO_STAGE_") - 1;
-	else
-		stage = "?";
+	if (zio->io_error == 0) {
+		stage = mdb_ctf_enum_name(stage_enum, zio->io_stage);
+		if (stage != NULL)
+			stage += sizeof ("ZIO_STAGE_") - 1;
+		else
+			stage = "?";
+	} else {
+		stage = "FAILED";
+	}
 
 	if (zpa->zpa_current_depth >= zpa->zpa_min_depth) {
 		if (zpa->zpa_flags & DCMD_PIPE_OUT) {
@@ -1797,9 +1797,11 @@
 		return (WALK_NEXT);
 
 	if (zpa->zpa_type == ZIO_WALK_PARENT)
-		laddr = addr + OFFSETOF(zio_t, io_parent_list);
+		laddr = addr + mdb_ctf_offsetof_by_name(ZFS_STRUCT "zio",
+		    "io_parent_list");
 	else
-		laddr = addr + OFFSETOF(zio_t, io_child_list);
+		laddr = addr + mdb_ctf_offsetof_by_name(ZFS_STRUCT "zio",
+		    "io_child_list");
 
 	zpa->zpa_current_depth++;
 	if (mdb_pwalk("list", zio_child_cb, zpa, laddr) != 0) {
@@ -1816,7 +1818,7 @@
 zio_child_cb(uintptr_t addr, const void *unknown, void *arg)
 {
 	zio_link_t zl;
-	zio_t zio;
+	mdb_zio_t zio;
 	uintptr_t ziop;
 	zio_print_args_t *zpa = arg;
 
@@ -1830,10 +1832,8 @@
 	else
 		ziop = (uintptr_t)zl.zl_child;
 
-	if (mdb_vread(&zio, sizeof (zio_t), ziop) == -1) {
-		mdb_warn("failed to read zio_t at %p", ziop);
+	if (mdb_ctf_vread(&zio, ZFS_STRUCT "zio", "mdb_zio_t", ziop, 0) == -1)
 		return (WALK_ERR);
-	}
 
 	return (zio_print_cb(ziop, &zio, arg));
 }
@@ -1842,7 +1842,7 @@
 static int
 zio_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
-	zio_t zio;
+	mdb_zio_t zio;
 	zio_print_args_t zpa = { 0 };
 
 	if (!(flags & DCMD_ADDRSPEC))
@@ -1864,10 +1864,8 @@
 		zpa.zpa_max_depth = 1;
 	}
 
-	if (mdb_vread(&zio, sizeof (zio_t), addr) == -1) {
-		mdb_warn("failed to read zio_t at %p", addr);
+	if (mdb_ctf_vread(&zio, ZFS_STRUCT "zio", "mdb_zio_t", addr, 0) == -1)
 		return (DCMD_ERR);
-	}
 
 	if (!(flags & DCMD_PIPE_OUT) && DCMD_HDRSPEC(flags))
 		mdb_printf("%<u>%-*s %-5s %-16s %-?s%</u>\n", ZIO_MAXWIDTH,
@@ -2063,12 +2061,11 @@
 static int
 zio_walk_step(mdb_walk_state_t *wsp)
 {
-	zio_t zio;
-
-	if (mdb_vread(&zio, sizeof (zio), wsp->walk_addr) == -1) {
-		mdb_warn("failed to read zio_t at %p", wsp->walk_addr);
+	mdb_zio_t zio;
+
+	if (mdb_ctf_vread(&zio, ZFS_STRUCT "zio", "mdb_zio_t",
+	    wsp->walk_addr, 0) == -1)
 		return (WALK_ERR);
-	}
 
 	if (wsp->walk_data != NULL && wsp->walk_data != zio.io_spa)
 		return (WALK_NEXT);
@@ -2084,19 +2081,20 @@
 static int
 zio_walk_root_step(mdb_walk_state_t *wsp)
 {
-	zio_t zio;
-
-	if (mdb_vread(&zio, sizeof (zio), wsp->walk_addr) == -1) {
-		mdb_warn("failed to read zio_t at %p", wsp->walk_addr);
+	mdb_zio_t zio;
+
+	if (mdb_ctf_vread(&zio, ZFS_STRUCT "zio", "mdb_zio_t",
+	    wsp->walk_addr, 0) == -1)
 		return (WALK_ERR);
-	}
 
 	if (wsp->walk_data != NULL && wsp->walk_data != zio.io_spa)
 		return (WALK_NEXT);
 
 	/* If the parent list is not empty, ignore */
-	if (zio.io_parent_list.list_head.list_next !=
-	    &((zio_t *)wsp->walk_addr)->io_parent_list.list_head)
+	if ((uintptr_t)zio.io_parent_list.list_head.list_next !=
+	    wsp->walk_addr +
+	    mdb_ctf_offsetof_by_name(ZFS_STRUCT "zio", "io_parent_list") +
+	    mdb_ctf_offsetof_by_name("struct list", "list_head"))
 		return (WALK_NEXT);
 
 	return (wsp->walk_callback(wsp->walk_addr, &zio, wsp->walk_cbdata));
@@ -2179,8 +2177,8 @@
 	if (!(flags & DCMD_ADDRSPEC))
 		return (DCMD_USAGE);
 
-	if (GETMEMB(addr, struct spa, spa_dsl_pool, addr) ||
-	    GETMEMB(addr, struct dsl_pool, dp_blkstats, addr) ||
+	if (GETMEMB(addr, "spa", spa_dsl_pool, addr) ||
+	    GETMEMB(addr, "dsl_pool", dp_blkstats, addr) ||
 	    mdb_vread(&stats, sizeof (zfs_all_blkstats_t), addr) == -1) {
 		mdb_warn("failed to read data at %p;", addr);
 		mdb_printf("maybe no stats? run \"zpool scrub\" first.");
@@ -2334,10 +2332,16 @@
 	static int gotid;
 	static mdb_ctf_id_t rc_id;
 	ulong_t off;
+	boolean_t released = B_FALSE;
 
 	if (!(flags & DCMD_ADDRSPEC))
 		return (DCMD_USAGE);
 
+	if (mdb_getopts(argc, argv,
+	    'r', MDB_OPT_SETBITS, B_TRUE, &released,
+	    NULL) != argc)
+		return (DCMD_USAGE);
+
 	if (!gotid) {
 		/*
 		 * The refcount structure is different when compiled debug
@@ -2345,7 +2349,7 @@
 		 * refcount definition from the ZFS module, in case it has
 		 * been compiled debug but genunix is nondebug.
 		 */
-		if (mdb_ctf_lookup_by_name("struct " ZFS_OBJ_NAME "`refcount",
+		if (mdb_ctf_lookup_by_name(ZFS_STRUCT "refcount",
 		    &rc_id) == -1) {
 			mdb_warn("couldn't find struct refcount");
 			return (DCMD_ERR);
@@ -2353,10 +2357,15 @@
 		gotid = TRUE;
 	}
 
-	if (GETMEMBID(addr, &rc_id, rc_count, rc_count) ||
-	    GETMEMBID(addr, &rc_id, rc_removed_count, rc_removed_count))
+	if (GETMEMBID(addr, &rc_id, rc_count, rc_count))
 		return (DCMD_ERR);
 
+	if (GETMEMBID(addr, &rc_id, rc_removed_count, rc_removed_count)) {
+		mdb_printf("refcount_t at %p has %llu current holds\n",
+		    addr, (longlong_t)rc_count);
+		return (DCMD_OK);
+	}
+
 	mdb_printf("refcount_t at %p has %llu current holds, "
 	    "%llu recently released holds\n",
 	    addr, (longlong_t)rc_count, (longlong_t)rc_removed_count);
@@ -2368,12 +2377,14 @@
 	rc_list = addr + off/NBBY;
 	mdb_pwalk("list", reference_cb, (void*)B_FALSE, rc_list);
 
-	if (rc_removed_count > 0)
-		mdb_printf("released holds:\n");
-	if (mdb_ctf_offsetof(rc_id, "rc_removed", &off) == -1)
-		return (DCMD_ERR);
-	rc_removed = addr + off/NBBY;
-	mdb_pwalk("list", reference_cb, (void*)B_TRUE, rc_removed);
+	if (released) {
+		if (rc_removed_count > 0)
+			mdb_printf("released holds:\n");
+		if (mdb_ctf_offsetof(rc_id, "rc_removed", &off) == -1)
+			return (DCMD_ERR);
+		rc_removed = addr + off/NBBY;
+		mdb_pwalk("list", reference_cb, (void*)B_TRUE, rc_removed);
+	}
 
 	return (DCMD_OK);
 }
@@ -2419,7 +2430,7 @@
 {
 	uintptr_t idx_table;
 
-	if (GETMEMB(addr, struct sa_idx_tab, sa_idx_tab, idx_table)) {
+	if (GETMEMB(addr, "sa_idx_tab", sa_idx_tab, idx_table)) {
 		mdb_printf("can't find offset table in sa_idx_tab\n");
 		return (-1);
 	}
@@ -2457,22 +2468,22 @@
 	else
 		return (DCMD_USAGE);
 
-	if (GETMEMB(addr, struct sa_handle, sa_bonus_tab, bonus_tab) ||
-	    GETMEMB(addr, struct sa_handle, sa_spill_tab, spill_tab) ||
-	    GETMEMB(addr, struct sa_handle, sa_os, os) ||
-	    GETMEMB(addr, struct sa_handle, sa_bonus, db_bonus) ||
-	    GETMEMB(addr, struct sa_handle, sa_spill, db_spill)) {
+	if (GETMEMB(addr, "sa_handle", sa_bonus_tab, bonus_tab) ||
+	    GETMEMB(addr, "sa_handle", sa_spill_tab, spill_tab) ||
+	    GETMEMB(addr, "sa_handle", sa_os, os) ||
+	    GETMEMB(addr, "sa_handle", sa_bonus, db_bonus) ||
+	    GETMEMB(addr, "sa_handle", sa_spill, db_spill)) {
 		mdb_printf("Can't find necessary information in sa_handle "
 		    "in sa_handle\n");
 		return (DCMD_ERR);
 	}
 
-	if (GETMEMB(os, struct objset, os_sa, os_sa)) {
+	if (GETMEMB(os, "objset", os_sa, os_sa)) {
 		mdb_printf("Can't find os_sa in objset\n");
 		return (DCMD_ERR);
 	}
 
-	if (GETMEMB(os_sa, struct sa_os, sa_num_attrs, attr_count)) {
+	if (GETMEMB(os_sa, "sa_os", sa_num_attrs, attr_count)) {
 		mdb_printf("Can't find sa_num_attrs\n");
 		return (DCMD_ERR);
 	}
@@ -2488,7 +2499,7 @@
 			return (DCMD_ERR);
 		}
 
-		if (GETMEMB(db_bonus, struct dmu_buf, db_data, db_data)) {
+		if (GETMEMB(db_bonus, "dmu_buf", db_data, db_data)) {
 			mdb_printf("can't find db_data in bonus dbuf\n");
 			return (DCMD_ERR);
 		}
@@ -2503,7 +2514,7 @@
 		    attr_count) == -1) {
 			return (DCMD_ERR);
 		}
-		if (GETMEMB(db_spill, struct dmu_buf, db_data, db_data)) {
+		if (GETMEMB(db_spill, "dmu_buf", db_data, db_data)) {
 			mdb_printf("can't find db_data in spill dbuf\n");
 			return (DCMD_ERR);
 		}
@@ -2814,7 +2825,8 @@
 		return (WALK_ERR);
 	}
 
-	wsp->walk_addr += OFFSETOF(zfs_acl_t, z_acl);
+	wsp->walk_addr +=
+	    mdb_ctf_offsetof_by_name(ZFS_STRUCT "zfs_acl", "z_acl");
 
 	if (mdb_layered_walk("list", wsp) == -1) {
 		mdb_warn("failed to walk 'list'\n");
@@ -2974,6 +2986,58 @@
 	return (status);
 }
 
+typedef struct mdb_zfs_rrwlock {
+	kthread_t	*rr_writer;
+	boolean_t	rr_writer_wanted;
+} mdb_zfs_rrwlock_t;
+
+static uint_t rrw_key;
+
+/* ARGSUSED */
+static int
+rrwlock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_zfs_rrwlock_t rrw;
+
+	if (rrw_key == 0) {
+		if (mdb_ctf_readsym(&rrw_key, "uint_t", "rrw_tsd_key", 0) == -1)
+			return (DCMD_ERR);
+	}
+
+	if (mdb_ctf_vread(&rrw, "rrwlock_t", "mdb_zfs_rrwlock_t", addr,
+	    0) == -1)
+		return (DCMD_ERR);
+
+	if (rrw.rr_writer != NULL) {
+		mdb_printf("write lock held by thread %p\n", rrw.rr_writer);
+		return (DCMD_OK);
+	}
+
+	if (rrw.rr_writer_wanted) {
+		mdb_printf("writer wanted\n");
+	}
+
+	mdb_printf("anonymous references:\n");
+	(void) mdb_call_dcmd("refcount", addr +
+	    mdb_ctf_offsetof_by_name(ZFS_STRUCT "rrwlock", "rr_anon_rcount"),
+	    DCMD_ADDRSPEC, 0, NULL);
+
+	mdb_printf("linked references:\n");
+	(void) mdb_call_dcmd("refcount", addr +
+	    mdb_ctf_offsetof_by_name(ZFS_STRUCT "rrwlock", "rr_linked_rcount"),
+	    DCMD_ADDRSPEC, 0, NULL);
+
+	/*
+	 * XXX This should find references from
+	 * "::walk thread | ::tsd -v <rrw_key>", but there is no support
+	 * for programmatic consumption of dcmds, so this would be
+	 * difficult, potentially requiring reimplementing ::tsd (both
+	 * user and kernel versions) in this MDB module.
+	 */
+
+	return (DCMD_OK);
+}
+
 /*
  * MDB module linkage information:
  *
@@ -3014,7 +3078,9 @@
 	    "given a spa_t, print block type stats from last scrub",
 	    zfs_blkstats },
 	{ "zfs_params", "", "print zfs tunable parameters", zfs_params },
-	{ "refcount", "", "print refcount_t holders", refcount },
+	{ "refcount", ":[-r]\n"
+	    "\t-r display recently removed references",
+	    "print refcount_t holders", refcount },
 	{ "zap_leaf", "", "print zap_leaf_phys_t", zap_leaf },
 	{ "zfs_aces", ":[-v]", "print all ACEs from a zfs_acl_t",
 	    zfs_acl_dump },
@@ -3024,33 +3090,35 @@
 	    sa_attr_table},
 	{ "sa_attr", ": attr_id",
 	    "print SA attribute address when given sa_handle_t", sa_attr_print},
-	{ "zfs_dbgmsg", ":[-v]",
+	{ "zfs_dbgmsg", ":[-va]",
 	    "print zfs debug log", dbgmsg},
+	{ "rrwlock", ":",
+	    "print rrwlock_t, including readers", rrwlock},
 	{ NULL }
 };
 
 static const mdb_walker_t walkers[] = {
 	{ "zms_freelist", "walk ZFS metaslab freelist",
-		freelist_walk_init, freelist_walk_step, NULL },
+	    freelist_walk_init, freelist_walk_step, NULL },
 	{ "txg_list", "given any txg_list_t *, walk all entries in all txgs",
-		txg_list_walk_init, txg_list_walk_step, NULL },
+	    txg_list_walk_init, txg_list_walk_step, NULL },
 	{ "txg_list0", "given any txg_list_t *, walk all entries in txg 0",
-		txg_list0_walk_init, txg_list_walk_step, NULL },
+	    txg_list0_walk_init, txg_list_walk_step, NULL },
 	{ "txg_list1", "given any txg_list_t *, walk all entries in txg 1",
-		txg_list1_walk_init, txg_list_walk_step, NULL },
+	    txg_list1_walk_init, txg_list_walk_step, NULL },
 	{ "txg_list2", "given any txg_list_t *, walk all entries in txg 2",
-		txg_list2_walk_init, txg_list_walk_step, NULL },
+	    txg_list2_walk_init, txg_list_walk_step, NULL },
 	{ "txg_list3", "given any txg_list_t *, walk all entries in txg 3",
-		txg_list3_walk_init, txg_list_walk_step, NULL },
+	    txg_list3_walk_init, txg_list_walk_step, NULL },
 	{ "zio", "walk all zio structures, optionally for a particular spa_t",
-		zio_walk_init, zio_walk_step, NULL },
-	{ "zio_root", "walk all root zio_t structures, optionally for a "
-	    "particular spa_t",
-		zio_walk_init, zio_walk_root_step, NULL },
+	    zio_walk_init, zio_walk_step, NULL },
+	{ "zio_root",
+	    "walk all root zio_t structures, optionally for a particular spa_t",
+	    zio_walk_init, zio_walk_root_step, NULL },
 	{ "spa", "walk all spa_t entries in the namespace",
-		spa_walk_init, spa_walk_step, NULL },
+	    spa_walk_init, spa_walk_step, NULL },
 	{ "metaslab", "given a spa_t *, walk all metaslab_t structures",
-		metaslab_walk_init, metaslab_walk_step, NULL },
+	    metaslab_walk_init, metaslab_walk_step, NULL },
 	{ "zfs_acl_node", "given a zfs_acl_t, walk all zfs_acl_nodes",
 	    zfs_acl_node_walk_init, zfs_acl_node_walk_step, NULL },
 	{ "zfs_acl_node_aces", "given a zfs_acl_node_t, walk all ACEs",
--- a/usr/src/cmd/mdb/i86xpv/modules/xpv/xpv.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/i86xpv/modules/xpv/xpv.c	Wed Jan 30 15:02:58 2013 -0800
@@ -22,8 +22,9 @@
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
 
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_ks.h>
@@ -51,7 +52,7 @@
  * interesting bits from the binary, and stash them in the structure
  * defined below.
  */
-typedef struct domain {
+typedef struct mdb_xpv_domain {
 	short		domain_id;
 	int		tot_pages;
 	int		max_pages;
@@ -59,9 +60,9 @@
 	ulong_t		domain_flags;
 	char		is_hvm;
 	struct vcpu	*vcpu[MAX_VIRT_CPUS];
-	struct evtchn   *evtchn[NR_EVTCHN_BUCKETS];
-	struct domain 	*next_in_list;
-} domain_t;
+	struct evtchn	*evtchn[NR_EVTCHN_BUCKETS];
+	struct domain	*next_in_list;
+} mdb_xpv_domain_t;
 
 static uintptr_t
 get_dom0_addr()
@@ -110,7 +111,7 @@
 domain_walk_step(mdb_walk_state_t *wsp)
 {
 	domain_walk_t *dwp = (domain_walk_t *)wsp->walk_data;
-	struct domain dom;
+	mdb_xpv_domain_t dom;
 	int status;
 
 	if (wsp->walk_addr == NULL)
@@ -119,11 +120,9 @@
 	status = wsp->walk_callback(wsp->walk_addr, (void *)wsp->walk_addr,
 	    wsp->walk_cbdata);
 
-	if (mdb_ctf_vread(&dom, "struct domain", wsp->walk_addr,
-	    MDB_CTF_VREAD_IGNORE_ABSENT) != 0) {
-		mdb_warn("can't find next domain");
+	if (mdb_ctf_vread(&dom, "struct domain", "mdb_xpv_domain_t",
+	    wsp->walk_addr, 0) != 0)
 		return (WALK_ERR);
-	}
 	wsp->walk_addr = (uintptr_t)dom.next_in_list;
 
 	dwp->dw_step = TRUE;
@@ -199,7 +198,7 @@
 int
 domain(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
-	domain_t dom;
+	mdb_xpv_domain_t dom;
 	uintptr_t off, vcpu_addr, evtchn_addr;
 
 	if (!mdb_ctf_type_valid(domain_type)) {
@@ -220,11 +219,9 @@
 		    "ADDR", "ID", "TPAGES", "MPAGES", "FLAGS", "HVM",
 		    "VCPU", "EVTCHN");
 
-	if (mdb_ctf_vread(&dom, "struct domain", addr,
-	    MDB_CTF_VREAD_IGNORE_ABSENT) != 0) {
-		mdb_warn("can't read domain information");
+	if (mdb_ctf_vread(&dom, "struct domain", "mdb_xpv_domain_t", addr,
+	    0) != 0)
 		return (DCMD_ERR);
-	}
 
 	if (mdb_ctf_offsetof(domain_type, "vcpu", &off)) {
 		mdb_warn("can't find per-domain vcpu information");
@@ -258,24 +255,23 @@
 
 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
 
+typedef struct mdb_xpv_panic_info {
+	int pi_version;
+} mdb_xpv_panic_info_t;
+
 const mdb_modinfo_t *
 _mdb_init(void)
 {
-	GElf_Sym sym;
 	uintptr_t pip;
-	struct panic_info pi;
+	mdb_xpv_panic_info_t pi;
 
-	if (mdb_lookup_by_name("xpv_panic_info", &sym) < 0)
-		return (NULL);
-
-	if (mdb_ctf_vread(&pip, "uintptr_t", sym.st_value, 0) == -1) {
+	if (mdb_readsym(&pip, sizeof (pip), "xpv_panic_info") == -1) {
 		mdb_warn("failed to read xpv panic_info pointer");
 		return (NULL);
 	}
-	if (mdb_ctf_vread(&pi, "struct panic_info", pip, 0) == -1) {
-		mdb_warn("failed to read xpv panic_info");
+	if (mdb_ctf_vread(&pi, "struct panic_info", "mdb_xpv_panic_info_t",
+	    pip, 0) == -1)
 		return (NULL);
-	}
 
 	if (pi.pi_version != PANIC_INFO_VERSION) {
 		mdb_warn("unrecognized hypervisor panic format");
--- a/usr/src/cmd/mdb/i86xpv/modules/xpv_psm/xpv_psm.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/i86xpv/modules/xpv_psm/xpv_psm.c	Wed Jan 30 15:02:58 2013 -0800
@@ -22,6 +22,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
 
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_ks.h>
@@ -32,7 +35,12 @@
 
 #include "intr_common.h"
 
-static shared_info_t shared_info;
+typedef struct mdb_shared_info {
+	unsigned long evtchn_pending[sizeof (unsigned long) * NBBY];
+	unsigned long evtchn_mask[sizeof (unsigned long) * NBBY];
+} mdb_shared_info_t;
+
+static mdb_shared_info_t shared_info;
 static int have_shared_info;
 static uintptr_t evtchn_cpus_addr;
 static struct av_head avec_tbl[NR_IRQS];
@@ -109,8 +117,8 @@
 	/*
 	 * It's normal for this to fail with a domain dump.
 	 */
-	if (mdb_ctf_vread(&shared_info, "shared_info_t",
-	    shared_info_addr, 0) != -1)
+	if (mdb_ctf_vread(&shared_info, "shared_info_t", "mdb_shared_info_t",
+	    shared_info_addr, MDB_CTF_VREAD_QUIET) != -1)
 		have_shared_info = 1;
 
 	return (1);
@@ -255,18 +263,20 @@
 	return (!!TEST_EVTCHN_BIT(i, &shared_info.evtchn_pending[0]));
 }
 
+typedef struct mdb_xpv_psm_autovec {
+	dev_info_t *av_dip;
+} mdb_xpv_psm_autovec_t;
+
 static void
 print_bus(int irq)
 {
 	char parent[7];
 	uintptr_t dip_addr;
 	struct dev_info	dev_info;
-	struct autovec avhp;
+	mdb_xpv_psm_autovec_t avhp;
 
-	bzero(&avhp, sizeof (avhp));
-
-	if (mdb_ctf_vread(&avhp, "struct autovec",
-	    (uintptr_t)avec_tbl[irq].avh_link, 0) == -1)
+	if (mdb_ctf_vread(&avhp, "struct autovec", "mdb_xpv_psm_autovec_t",
+	    (uintptr_t)avec_tbl[irq].avh_link, MDB_CTF_VREAD_QUIET) == -1)
 		goto fail;
 
 	dip_addr = (uintptr_t)avhp.av_dip;
--- a/usr/src/cmd/mdb/i86xpv/modules/xpv_uppc/xpv_uppc.c	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/cmd/mdb/i86xpv/modules/xpv_uppc/xpv_uppc.c	Wed Jan 30 15:02:58 2013 -0800
@@ -22,8 +22,9 @@
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
 
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_ks.h>
@@ -32,7 +33,12 @@
 
 #include "intr_common.h"
 
-static shared_info_t	shared_info;
+typedef struct mdb_shared_info {
+	unsigned long evtchn_pending[sizeof (unsigned long) * NBBY];
+	unsigned long evtchn_mask[sizeof (unsigned long) * NBBY];
+} mdb_shared_info_t;
+
+static mdb_shared_info_t	shared_info;
 static struct av_head	avec_tbl[NR_IRQS];
 static uint16_t		shared_tbl[MAX_ISA_IRQ + 1];
 static irq_info_t	irq_tbl[NR_IRQS];
@@ -74,11 +80,9 @@
 		return (0);
 	}
 
-	if (mdb_ctf_vread(&shared_info, "shared_info_t",
-	    shared_info_addr, 0) == -1) {
-		mdb_warn("failed to read shared_info");
+	if (mdb_ctf_vread(&shared_info, "shared_info_t", "mdb_shared_info_t",
+	    shared_info_addr, 0) == -1)
 		return (0);
-	}
 
 	return (1);
 }
--- a/usr/src/uts/common/fs/zfs/sys/refcount.h	Wed Jan 30 23:35:38 2013 +0100
+++ b/usr/src/uts/common/fs/zfs/sys/refcount.h	Wed Jan 30 15:02:58 2013 -0800
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 #ifndef	_SYS_REFCOUNT_H
@@ -52,8 +53,8 @@
 	kmutex_t rc_mtx;
 	list_t rc_list;
 	list_t rc_removed;
-	int64_t rc_count;
-	int64_t rc_removed_count;
+	uint64_t rc_count;
+	uint64_t rc_removed_count;
 } refcount_t;
 
 /* Note: refcount_t must be initialized with refcount_create() */