changeset 12914:3bb859a7330c

6679140 asymmetric alloc/dealloc activity can induce dynamic variable drops 6679193 dtrace_dynvar walker produces flood of dtrace_dynhash_sink 6935550 would like ::stacks for user-land 6970441 allocation failure can induce crashes in libdtrace 6971885 dtrace_program_strcompile() fails spuriously on UTF-8 input
author Bryan Cantrill <Bryan.Cantrill@Sun.COM>
date Fri, 23 Jul 2010 17:34:02 -0700
parents 90a66b2c063f
children 2794a0c9cce1
files usr/src/cmd/mdb/common/mdb/mdb_modapi.c usr/src/cmd/mdb/common/mdb/mdb_modapi.h usr/src/cmd/mdb/common/mdb/mdb_module_load.c usr/src/cmd/mdb/common/modules/conf/mapfile-extern usr/src/cmd/mdb/common/modules/dtrace/dtrace.c usr/src/cmd/mdb/common/modules/genunix/Makefile.files usr/src/cmd/mdb/common/modules/genunix/findstack.c usr/src/cmd/mdb/common/modules/genunix/findstack.h usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c usr/src/cmd/mdb/common/modules/libc/findstack_subr.c usr/src/cmd/mdb/common/modules/libc/libc.c usr/src/cmd/mdb/intel/amd64/libc/Makefile usr/src/cmd/mdb/intel/ia32/libc/Makefile usr/src/cmd/mdb/sparc/v7/libc/Makefile usr/src/cmd/mdb/sparc/v9/libc/Makefile usr/src/lib/libdtrace/Makefile.com usr/src/lib/libdtrace/common/dt_cc.c usr/src/lib/libdtrace/common/dt_dof.c usr/src/lib/libdtrace/common/dt_lex.l usr/src/lib/libdtrace/common/dt_module.c usr/src/lib/libdtrace/common/dt_printf.c usr/src/lib/libdtrace/common/dt_program.c usr/src/lib/libdtrace/common/dt_string.c usr/src/uts/common/dtrace/dtrace.c
diffstat 24 files changed, 1306 insertions(+), 548 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c	Fri Jul 23 17:34:02 2010 -0700
@@ -18,13 +18,11 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_module.h>
 #include <mdb/mdb_string.h>
@@ -189,6 +187,12 @@
 	    buf, nbytes, sym, NULL));
 }
 
+int
+mdb_getareg(mdb_tid_t tid, const char *rname, mdb_reg_t *rp)
+{
+	return (mdb_tgt_getareg(mdb.m_target, tid, rname, rp));
+}
+
 u_longlong_t
 mdb_strtoull(const char *s)
 {
@@ -744,6 +748,51 @@
 }
 
 /*
+ * Private callback structure for implementing mdb_object_iter, below.
+ */
+typedef struct {
+	mdb_object_cb_t oi_cb;
+	void *oi_arg;
+	int oi_rval;
+} object_iter_arg_t;
+
+/*ARGSUSED*/
+static int
+mdb_object_cb(void *data, const mdb_map_t *map, const char *fullname)
+{
+	object_iter_arg_t *arg = data;
+	mdb_object_t obj;
+
+	if (arg->oi_rval != 0)
+		return (0);
+
+	bzero(&obj, sizeof (obj));
+	obj.obj_base = map->map_base;
+	obj.obj_name = strbasename(map->map_name);
+	obj.obj_size = map->map_size;
+	obj.obj_fullname = fullname;
+
+	arg->oi_rval = arg->oi_cb(&obj, arg->oi_arg);
+
+	return (0);
+}
+
+int
+mdb_object_iter(mdb_object_cb_t cb, void *data)
+{
+	object_iter_arg_t arg;
+
+	arg.oi_cb = cb;
+	arg.oi_arg = data;
+	arg.oi_rval = 0;
+
+	if (mdb_tgt_object_iter(mdb.m_target, mdb_object_cb, &arg) != 0)
+		return (-1);
+
+	return (arg.oi_rval);
+}
+
+/*
  * Private structure and function for implementing mdb_dumpptr on top
  * of mdb_dump_internal
  */
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h	Fri Jul 23 17:34:02 2010 -0700
@@ -18,16 +18,14 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_MDB_MODAPI_H
 #define	_MDB_MODAPI_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * MDB Module API
  *
@@ -164,6 +162,13 @@
 	size_t pipe_len;		/* Array length */
 } mdb_pipe_t;
 
+typedef struct mdb_object {
+	const char *obj_name;		/* name of object */
+	const char *obj_fullname;	/* full name of object */
+	uintptr_t obj_base;		/* base address of object */
+	uintptr_t obj_size;		/* in memory size of object in bytes */
+} mdb_object_t;
+
 extern int mdb_pwalk(const char *, mdb_walk_cb_t, void *, uintptr_t);
 extern int mdb_walk(const char *, mdb_walk_cb_t, void *);
 
@@ -211,6 +216,11 @@
 extern int mdb_lookup_by_obj(const char *, const char *, GElf_Sym *);
 extern int mdb_lookup_by_addr(uintptr_t, uint_t, char *, size_t, GElf_Sym *);
 
+typedef uintptr_t mdb_tid_t;
+typedef uint64_t mdb_reg_t;
+
+extern int mdb_getareg(mdb_tid_t, const char *, mdb_reg_t *);
+
 #define	MDB_OPT_SETBITS	1			/* Set specified flag bits */
 #define	MDB_OPT_CLRBITS	2			/* Clear specified flag bits */
 #define	MDB_OPT_STR	3			/* const char * argument */
@@ -272,6 +282,9 @@
 
 extern ssize_t mdb_get_xdata(const char *, void *, size_t);
 
+typedef int (*mdb_object_cb_t)(mdb_object_t *, void *);
+extern int mdb_object_iter(mdb_object_cb_t, void *);
+
 #define	MDB_STATE_IDLE		0	/* Target is idle (not running yet) */
 #define	MDB_STATE_RUNNING	1	/* Target is currently executing */
 #define	MDB_STATE_STOPPED	2	/* Target is stopped */
--- a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c	Fri Jul 23 17:34:02 2010 -0700
@@ -18,9 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/param.h>
@@ -148,10 +148,10 @@
 
 /*ARGSUSED*/
 static int
-module_load(void *fp, const mdb_map_t *map, const char *name)
+module_load(void *fp, const mdb_map_t *map, const char *fullname)
 {
 	mdb_modload_data_t *mld = fp;
-	name = strbasename(name);
+	const char *name = strbasename(fullname);
 
 	if (mdb_module_load(name, mld->mld_mode) == 0 && mdb.m_term != NULL) {
 		if (mld->mld_first == TRUE) {
@@ -162,6 +162,23 @@
 		mdb_iob_flush(mdb.m_out);
 	}
 
+	if (strstr(fullname, "/libc/") != NULL) {
+		/*
+		 * A bit of a kludge:  because we manage alternately capable
+		 * libc instances by mounting the appropriately capable
+		 * instance over /lib/libc.so.1, we may find that our object
+		 * list does not contain libc.so.1, but rather one of its
+		 * hwcap variants.  Unfortunately, there is not a way of
+		 * getting from this shared object to the object that it is
+		 * effectively interposing on -- which means that without
+		 * special processing, we will not load any libc.so dmod.  So
+		 * if we see that we have a shared object coming out of the
+		 * "libc" directory, we assume that we have a "libc-like"
+		 * object, and explicitly load the "libc.so" dmod.
+		 */
+		return (module_load(fp, map, "libc.so.1"));
+	}
+
 	return (0);
 }
 
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Fri Jul 23 17:34:02 2010 -0700
@@ -88,6 +88,7 @@
 		mdb_gelf_destroy		{ FLAGS = EXTERN };
 		mdb_gelf_sect_by_name		{ FLAGS = EXTERN };
 		mdb_gelf_sect_load		{ FLAGS = EXTERN };
+		mdb_getareg			{ FLAGS = EXTERN };
 		mdb_get_dot			{ FLAGS = EXTERN };
 		mdb_get_lbolt			{ FLAGS = EXTERN };
 		mdb_get_pipe			{ FLAGS = EXTERN };
@@ -113,6 +114,7 @@
 		mdb_memio_create		{ FLAGS = EXTERN };
 		mdb_name_to_major		{ FLAGS = EXTERN };
 		mdb_nhconvert			{ FLAGS = EXTERN };
+		mdb_object_iter			{ FLAGS = EXTERN };
 		mdb_one_bit			{ FLAGS = EXTERN };
 		mdb_page2pfn			{ FLAGS = EXTERN };
 		mdb_page_lookup			{ FLAGS = EXTERN };
--- a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c	Fri Jul 23 17:34:02 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -435,10 +434,12 @@
 }
 
 /*
- * This is essentially identical to its cousin in the kernel.
+ * This is essentially identical to its cousin in the kernel -- with the
+ * notable exception that we automatically set DTRACEOPT_GRABANON if this
+ * state is an anonymous enabling.
  */
 static dof_hdr_t *
-dtracemdb_dof_create(dtrace_state_t *state)
+dtracemdb_dof_create(dtrace_state_t *state, int isanon)
 {
 	dof_hdr_t *dof;
 	dof_sec_t *sec;
@@ -490,6 +491,9 @@
 		opt[i].dofo_value = state->dts_options[i];
 	}
 
+	if (isanon)
+		opt[DTRACEOPT_GRABANON].dofo_value = 1;
+
 	return (dof);
 }
 
@@ -621,6 +625,7 @@
 	char *dtmd_symstr;
 	char *dtmd_modstr;
 	uintptr_t dtmd_addr;
+	int dtmd_isanon;
 } dtracemdb_data_t;
 
 static int
@@ -645,7 +650,7 @@
 	case DTRACEIOC_DOFGET: {
 		dof_hdr_t *hdr = arg, *dof;
 
-		dof = dtracemdb_dof_create(state);
+		dof = dtracemdb_dof_create(state, data->dtmd_isanon);
 		bcopy(dof, hdr, MIN(hdr->dofh_loadsz, dof->dofh_loadsz));
 		mdb_free(dof, dof->dofh_loadsz);
 
@@ -974,6 +979,7 @@
 	dtrace_optval_t val;
 	dtracemdb_data_t md;
 	int rval = DCMD_ERR;
+	dtrace_anon_t anon;
 
 	if (!(flags & DCMD_ADDRSPEC))
 		return (DCMD_USAGE);
@@ -991,6 +997,15 @@
 		return (DCMD_ERR);
 	}
 
+	if (state.dts_anon != NULL) {
+		addr = (uintptr_t)state.dts_anon;
+
+		if (mdb_vread(&state, sizeof (state), addr) == -1) {
+			mdb_warn("couldn't read anonymous state at %p", addr);
+			return (DCMD_ERR);
+		}
+	}
+
 	bzero(&md, sizeof (md));
 	md.dtmd_state = &state;
 
@@ -1001,6 +1016,17 @@
 		return (DCMD_ERR);
 	}
 
+	/*
+	 * If this is the anonymous enabling, we need to set a bit indicating
+	 * that DTRACEOPT_GRABANON should be set.
+	 */
+	if (mdb_readvar(&anon, "dtrace_anon") == -1) {
+		mdb_warn("failed to read 'dtrace_anon'");
+		return (DCMD_ERR);
+	}
+
+	md.dtmd_isanon = ((uintptr_t)anon.dta_state == addr);
+
 	if (dtrace_go(dtp) != 0) {
 		mdb_warn("failed to initialize dtrace: %s\n",
 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
@@ -1750,6 +1776,7 @@
 	uintptr_t dtdvd_hashsize;
 	uintptr_t dtdvd_next;
 	uintptr_t dtdvd_ndx;
+	uintptr_t dtdvd_sink;
 } dtrace_dynvar_data_t;
 
 int
@@ -1759,6 +1786,7 @@
 	dtrace_dstate_t dstate;
 	dtrace_dynvar_data_t *data;
 	size_t hsize;
+	GElf_Sym sym;
 
 	if ((addr = wsp->walk_addr) == NULL) {
 		mdb_warn("dtrace_dynvar walk needs dtrace_dstate_t\n");
@@ -1770,11 +1798,17 @@
 		return (WALK_ERR);
 	}
 
+	if (mdb_lookup_by_name("dtrace_dynhash_sink", &sym) == -1) {
+		mdb_warn("couldn't find 'dtrace_dynhash_sink'");
+		return (WALK_ERR);
+	}
+
 	data = mdb_zalloc(sizeof (dtrace_dynvar_data_t), UM_SLEEP);
 
 	data->dtdvd_hashsize = dstate.dtds_hashsize;
 	hsize = dstate.dtds_hashsize * sizeof (dtrace_dynhash_t);
 	data->dtdvd_hash = mdb_alloc(hsize, UM_SLEEP);
+	data->dtdvd_sink = (uintptr_t)sym.st_value;
 
 	if (mdb_vread(data->dtdvd_hash, hsize,
 	    (uintptr_t)dstate.dtds_hash) == -1) {
@@ -1785,6 +1819,8 @@
 		return (WALK_ERR);
 	}
 
+	data->dtdvd_next = (uintptr_t)data->dtdvd_hash[0].dtdh_chain;
+
 	wsp->walk_data = data;
 	return (WALK_NEXT);
 }
@@ -1798,7 +1834,7 @@
 	uintptr_t addr;
 	int nkeys;
 
-	while ((addr = data->dtdvd_next) == NULL) {
+	while ((addr = data->dtdvd_next) == data->dtdvd_sink) {
 		if (data->dtdvd_ndx == data->dtdvd_hashsize)
 			return (WALK_DONE);
 
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Fri Jul 23 17:34:02 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 #
@@ -41,6 +40,7 @@
 	devinfo.c	\
 	dist.c		\
 	findstack.c	\
+	findstack_subr.c \
 	fm.c		\
 	genunix.c	\
 	group.c		\
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.c	Fri Jul 23 17:34:02 2010 -0700
@@ -18,9 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <mdb/mdb_modapi.h>
@@ -31,130 +31,13 @@
 #include <sys/stack.h>
 #include <sys/thread.h>
 #include <sys/modctl.h>
+#include <assert.h>
 
 #include "findstack.h"
 #include "thread.h"
 #include "sobj.h"
 
-typedef struct findstack_info {
-	uintptr_t	*fsi_stack;	/* place to record frames */
-
-	uintptr_t	fsi_sp;		/* stack pointer */
-	uintptr_t	fsi_pc;		/* pc */
-	uintptr_t	fsi_sobj_ops;	/* sobj_ops */
-
-	uint_t		fsi_tstate;	/* t_state */
-
-	uchar_t		fsi_depth;	/* stack depth */
-	uchar_t		fsi_failed;	/* search failed */
-	uchar_t		fsi_overflow;	/* stack was deeper than max_depth */
-	uchar_t		fsi_panic;	/* thread called panic() */
-
-	uchar_t		fsi_max_depth;	/* stack frames available */
-} findstack_info_t;
-#define	FSI_FAIL_BADTHREAD	1
-#define	FSI_FAIL_NOTINMEMORY	2
-#define	FSI_FAIL_THREADCORRUPT	3
-#define	FSI_FAIL_STACKNOTFOUND	4
-
-#ifndef STACK_BIAS
-#define	STACK_BIAS	0
-#endif
-
-#define	fs_dprintf(x)					\
-	if (findstack_debug_on) {			\
-		mdb_printf("findstack debug: ");	\
-		/*CSTYLED*/				\
-		mdb_printf x ;				\
-	}
-
-static int findstack_debug_on = 0;
-
-#if defined(__i386) || defined(__amd64)
-struct rwindow {
-	uintptr_t rw_fp;
-	uintptr_t rw_rtn;
-};
-#endif
-
-#define	TOO_BIG_FOR_A_STACK (1024 * 1024)
-
-#define	KTOU(p) ((p) - kbase + ubase)
-#define	UTOK(p) ((p) - ubase + kbase)
-
-#define	CRAWL_FOUNDALL	(-1)
-
-/*
- * Given a stack pointer, try to crawl down it to the bottom.
- * "frame" is a VA in MDB's address space.
- *
- * Returns the number of frames successfully crawled down, or
- * CRAWL_FOUNDALL if it got to the bottom of the stack.
- */
-static int
-crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase,
-    int kill_fp, findstack_info_t *fsip)
-{
-	int levels = 0;
-
-	fsip->fsi_depth = 0;
-	fsip->fsi_overflow = 0;
-
-	fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n",
-	    frame, kbase, ktop, ubase));
-	for (;;) {
-		uintptr_t fp;
-		long *fpp = (long *)&((struct rwindow *)frame)->rw_fp;
-
-		fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame));
-
-		if ((frame & (STACK_ALIGN - 1)) != 0)
-			break;
-
-		fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS;
-		if (fsip->fsi_depth < fsip->fsi_max_depth)
-			fsip->fsi_stack[fsip->fsi_depth++] =
-			    ((struct rwindow *)frame)->rw_rtn;
-		else
-			fsip->fsi_overflow = 1;
-
-		fs_dprintf(("<2> fp = %p\n", fp));
-
-		if (fp == ktop)
-			return (CRAWL_FOUNDALL);
-		fs_dprintf(("<3> not at base\n"));
-
-#if defined(__i386) || defined(__amd64)
-		if (ktop - fp == sizeof (struct rwindow)) {
-			fs_dprintf(("<4> found base\n"));
-			return (CRAWL_FOUNDALL);
-		}
-#endif
-
-		fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n",
-		    fp, kbase, ktop - sizeof (struct rwindow)));
-
-		if (fp < kbase || fp >= (ktop - sizeof (struct rwindow)))
-			break;
-
-		frame = KTOU(fp);
-		fs_dprintf(("<6> frame = %p\n", frame));
-
-		/*
-		 * NULL out the old %fp so we don't go down this stack
-		 * more than once.
-		 */
-		if (kill_fp) {
-			fs_dprintf(("<7> fpp = %p\n", fpp));
-			*fpp = NULL;
-		}
-
-		fs_dprintf(("<8> levels = %d\n", levels));
-		levels++;
-	}
-
-	return (levels);
-}
+int findstack_debug_on = 0;
 
 /*
  * "sp" is a kernel VA.
@@ -193,164 +76,6 @@
 	return ((err == -1) ? DCMD_ABORT : DCMD_OK);
 }
 
-/*ARGSUSED*/
-static int
-do_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
-{
-	kthread_t thr;
-	size_t stksz;
-	uintptr_t ubase, utop;
-	uintptr_t kbase, ktop;
-	uintptr_t win, sp;
-
-	fsip->fsi_failed = 0;
-	fsip->fsi_pc = 0;
-	fsip->fsi_sp = 0;
-	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);
-		fsip->fsi_failed = FSI_FAIL_BADTHREAD;
-		return (DCMD_ERR);
-	}
-
-	fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops;
-	fsip->fsi_tstate = thr.t_state;
-	fsip->fsi_panic = !!(thr.t_flag & T_PANIC);
-
-	if ((thr.t_schedflag & TS_LOAD) == 0) {
-		if (print_warnings)
-			mdb_warn("thread %p isn't in memory\n", addr);
-		fsip->fsi_failed = FSI_FAIL_NOTINMEMORY;
-		return (DCMD_ERR);
-	}
-
-	if (thr.t_stk < thr.t_stkbase) {
-		if (print_warnings)
-			mdb_warn(
-			    "stack base or stack top corrupt for thread %p\n",
-			    addr);
-		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
-		return (DCMD_ERR);
-	}
-
-	kbase = (uintptr_t)thr.t_stkbase;
-	ktop = (uintptr_t)thr.t_stk;
-	stksz = ktop - kbase;
-
-#ifdef __amd64
-	/*
-	 * The stack on amd64 is intentionally misaligned, so ignore the top
-	 * half-frame.  See thread_stk_init().  When handling traps, the frame
-	 * is automatically aligned by the hardware, so we only alter ktop if
-	 * needed.
-	 */
-	if ((ktop & (STACK_ALIGN - 1)) != 0)
-		ktop -= STACK_ENTRY_ALIGN;
-#endif
-
-	/*
-	 * If the stack size is larger than a meg, assume that it's bogus.
-	 */
-	if (stksz > TOO_BIG_FOR_A_STACK) {
-		if (print_warnings)
-			mdb_warn("stack size for thread %p is too big to be "
-			    "reasonable\n", addr);
-		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
-		return (DCMD_ERR);
-	}
-
-	/*
-	 * This could be (and was) a UM_GC allocation.  Unfortunately,
-	 * stksz tends to be very large.  As currently implemented, dcmds
-	 * invoked as part of pipelines don't have their UM_GC-allocated
-	 * memory freed until the pipeline completes.  With stksz in the
-	 * neighborhood of 20k, the popular ::walk thread |::findstack
-	 * pipeline can easily run memory-constrained debuggers (kmdb) out
-	 * of memory.  This can be changed back to a gc-able allocation when
-	 * the debugger is changed to free UM_GC memory more promptly.
-	 */
-	ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP);
-	utop = ubase + stksz;
-	if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) {
-		mdb_free((void *)ubase, stksz);
-		if (print_warnings)
-			mdb_warn("couldn't read entire stack for thread %p\n",
-			    addr);
-		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
-		return (DCMD_ERR);
-	}
-
-	/*
-	 * Try the saved %sp first, if it looks reasonable.
-	 */
-	sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS);
-	if (sp >= ubase && sp <= utop) {
-		if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) {
-			fsip->fsi_sp = (uintptr_t)thr.t_sp;
-#if !defined(__i386)
-			fsip->fsi_pc = (uintptr_t)thr.t_pc;
-#endif
-			goto found;
-		}
-	}
-
-	/*
-	 * Now walk through the whole stack, starting at the base,
-	 * trying every possible "window".
-	 */
-	for (win = ubase;
-	    win + sizeof (struct rwindow) <= utop;
-	    win += sizeof (struct rwindow *)) {
-		if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) {
-			fsip->fsi_sp = UTOK(win) - STACK_BIAS;
-			goto found;
-		}
-	}
-
-	/*
-	 * We didn't conclusively find the stack.  So we'll take another lap,
-	 * and print out anything that looks possible.
-	 */
-	if (print_warnings)
-		mdb_printf("Possible stack pointers for thread %p:\n", addr);
-	(void) mdb_vread((caddr_t)ubase, stksz, kbase);
-
-	for (win = ubase;
-	    win + sizeof (struct rwindow) <= utop;
-	    win += sizeof (struct rwindow *)) {
-		uintptr_t fp = ((struct rwindow *)win)->rw_fp;
-		int levels;
-
-		if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) {
-			if (print_warnings)
-				mdb_printf("  %p (%d)\n", fp, levels);
-		} else if (levels == CRAWL_FOUNDALL) {
-			/*
-			 * If this is a live system, the stack could change
-			 * between the two mdb_vread(ubase, utop, kbase)'s,
-			 * and we could have a fully valid stack here.
-			 */
-			fsip->fsi_sp = UTOK(win) - STACK_BIAS;
-			goto found;
-		}
-	}
-
-	fsip->fsi_depth = 0;
-	fsip->fsi_overflow = 0;
-	fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND;
-
-	mdb_free((void *)ubase, stksz);
-	return (DCMD_ERR);
-found:
-	mdb_free((void *)ubase, stksz);
-	return (DCMD_OK);
-}
-
 int
 findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
@@ -362,7 +87,7 @@
 
 	bzero(&fsi, sizeof (fsi));
 
-	if ((retval = do_findstack(addr, &fsi, 1)) != DCMD_OK ||
+	if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK ||
 	    fsi.fsi_failed)
 		return (retval);
 
@@ -399,6 +124,7 @@
 }
 
 #define	SOBJ_ALL	1
+
 static int
 text_to_sobj(const char *text, uintptr_t *out)
 {
@@ -406,6 +132,7 @@
 		*out = SOBJ_ALL;
 		return (0);
 	}
+
 	return (sobj_text_to_ops(text, out));
 }
 
@@ -457,11 +184,9 @@
 	size_t		si_count;	/* total stacks_entry_ts (incl dups) */
 	size_t		si_entries;	/* # entries in hash table */
 	stacks_entry_t	**si_hash;	/* hash table */
-
 	findstack_info_t si_fsi;	/* transient callback state */
 } stacks_info_t;
 
-
 /* global state cached between invocations */
 #define	STACKS_STATE_CLEAN	0
 #define	STACKS_STATE_DIRTY	1
@@ -631,6 +356,8 @@
 		    stacks_array_size * sizeof (*stacks_array));
 	}
 
+	stacks_findstack_cleanup();
+
 	stacks_array_size = 0;
 	stacks_state = STACKS_STATE_CLEAN;
 }
@@ -646,7 +373,7 @@
 	int idx;
 	size_t depth;
 
-	if (do_findstack(addr, fsip, 0) != DCMD_OK &&
+	if (stacks_findstack(addr, fsip, 0) != DCMD_OK &&
 	    fsip->fsi_failed == FSI_FAIL_BADTHREAD) {
 		mdb_warn("couldn't read thread at %p\n", addr);
 		return (WALK_NEXT);
@@ -694,19 +421,14 @@
 {
 	size_t idx;
 	size_t found = 0;
-	kthread_t kt;
 	int ret;
 
 	for (idx = 0; idx < tlist->pipe_len; idx++) {
 		uintptr_t addr = tlist->pipe_data[idx];
 
-		if (mdb_vread(&kt, sizeof (kt), addr) == -1) {
-			mdb_warn("unable to read kthread_t at %p", addr);
-			continue;
-		}
 		found++;
 
-		ret = stacks_thread_cb(addr, &kt, si);
+		ret = stacks_thread_cb(addr, NULL, si);
 		if (ret == WALK_DONE)
 			break;
 		if (ret != WALK_NEXT)
@@ -811,66 +533,36 @@
 	return (0);
 }
 
-typedef struct find_module_struct {
-	struct modctl *mcp;
-	const char *name;
-} find_module_struct_t;
-
-/*ARGSUSED*/
-int
-find_module_cb(uintptr_t addr, const void *modctl_arg, void *cbarg)
+static int
+stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp)
 {
-	find_module_struct_t *sp = cbarg;
-	char mod_modname[MODMAXNAMELEN + 1];
-	const struct modctl *mp = modctl_arg;
+	int idx;
 
-	if (!mp->mod_modname)
-		return (WALK_NEXT);
-
-	if (mdb_readstr(mod_modname, sizeof (mod_modname),
-	    (uintptr_t)mp->mod_modname) == -1) {
-		mdb_warn("failed to read mod_modname in \"modctl\" walk");
-		return (WALK_ERR);
+	for (idx = 0; idx < sep->se_depth; idx++) {
+		if (sep->se_stack[idx] >= mp->sm_text &&
+		    sep->se_stack[idx] < mp->sm_text + mp->sm_size)
+			return (1);
 	}
 
-	if (strcmp(sp->name, mod_modname))
-		return (WALK_NEXT);
-
-	sp->mcp = mdb_alloc(sizeof (*sp->mcp), UM_SLEEP | UM_GC);
-	bcopy(mp, sp->mcp, sizeof (*sp->mcp));
-	return (WALK_DONE);
-}
-
-static struct modctl *
-find_module(const char *name)
-{
-	find_module_struct_t mptr;
-
-	mptr.name = name;
-	mptr.mcp = NULL;
-
-	if (mdb_walk("modctl", find_module_cb, &mptr) != 0)
-		mdb_warn("cannot walk \"modctl\"");
-	return (mptr.mcp);
+	return (0);
 }
 
 static int
-stacks_has_module(stacks_entry_t *sep, struct modctl *mp)
+stacks_module_find(const char *name, stacks_module_t *mp)
 {
-	int idx;
+	(void) strncpy(mp->sm_name, name, sizeof (mp->sm_name));
 
-	if (mp == NULL)
-		return (0);
+	if (stacks_module(mp) != 0)
+		return (-1);
 
-	for (idx = 0; idx < sep->se_depth; idx++)
-		if (sep->se_stack[idx] >= (uintptr_t)mp->mod_text &&
-		    sep->se_stack[idx] <
-		    ((uintptr_t)mp->mod_text + mp->mod_text_size))
-			return (1);
+	if (mp->sm_size == 0) {
+		mdb_warn("stacks: module \"%s\" is unknown\n", name);
+		return (-1);
+	}
+
 	return (0);
 }
 
-
 static int
 uintptrcomp(const void *lp, const void *rp)
 {
@@ -884,96 +576,6 @@
 }
 
 /*ARGSUSED*/
-static void
-print_sobj_help(int type, const char *name, const char *ops_name, void *ign)
-{
-	mdb_printf(" %s", name);
-}
-
-/*ARGSUSED*/
-static void
-print_tstate_help(uint_t state, const char *name, void *ignored)
-{
-	mdb_printf(" %s", name);
-}
-
-void
-stacks_help(void)
-{
-	mdb_printf(
-"::stacks processes all of the thread stacks on the system, grouping\n"
-"together threads which have the same:\n"
-"\n"
-"  * Thread state,\n"
-"  * Sync object type, and\n"
-"  * PCs in their stack trace.\n"
-"\n"
-"The default output (no address or options) is just a dump of the thread\n"
-"groups in the system.  For a view of active threads, use \"::stacks -i\",\n"
-"which filters out FREE threads (interrupt threads which are currently\n"
-"inactive) and threads sleeping on a CV. (Note that those threads may still\n"
-"be noteworthy; this is just for a first glance.)  More general filtering\n"
-"options are described below, in the \"FILTERS\" section.\n"
-"\n"
-"::stacks can be used in a pipeline.  The input to ::stacks is one or more\n"
-"thread pointers.  For example, to get a summary of threads in a process,\n"
-"you can do:\n"
-"\n"
-"  %<b>procp%</b>::walk thread | ::stacks\n"
-"\n"
-"When output into a pipe, ::stacks prints all of the threads input,\n"
-"filtered by the given filtering options.  This means that multiple\n"
-"::stacks invocations can be piped together to achieve more complicated\n"
-"filters.  For example, to get threads which have both 'fop_read' and\n"
-"'cv_wait_sig_swap' in their stack trace, you could do:\n"
-"\n"
-"  ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n"
-"\n"
-"To get the full list of threads in each group, use the '-a' flag:\n"
-"\n"
-"  ::stacks -a\n"
-"\n");
-	mdb_dec_indent(2);
-	mdb_printf("%<b>OPTIONS%</b>\n");
-	mdb_inc_indent(2);
-	mdb_printf("%s",
-"  -a    Print all of the grouped threads, instead of just a count.\n"
-"  -f    Force a re-run of the thread stack gathering.\n"
-"  -v    Be verbose about thread stack gathering.\n"
-"\n");
-	mdb_dec_indent(2);
-	mdb_printf("%<b>FILTERS%</b>\n");
-	mdb_inc_indent(2);
-	mdb_printf("%s",
-"  -i    Show active threads; equivalent to '-S CV -T FREE'.\n"
-"  -c func[+offset]\n"
-"        Only print threads whose stacks contain func/func+offset.\n"
-"  -C func[+offset]\n"
-"        Only print threads whose stacks do not contain func/func+offset.\n"
-"  -m module\n"
-"        Only print threads whose stacks contain functions from module.\n"
-"  -M module\n"
-"        Only print threads whose stacks do not contain functions from\n"
-"        module.\n"
-"  -s {type | ALL}\n"
-"        Only print threads which are on a 'type' synchronization object\n"
-"        (SOBJ).\n"
-"  -S {type | ALL}\n"
-"        Only print threads which are not on a 'type' SOBJ.\n"
-"  -t tstate\n"
-"        Only print threads which are in thread state 'tstate'.\n"
-"  -T tstate\n"
-"        Only print threads which are not in thread state 'tstate'.\n"
-"\n");
-	mdb_printf("   SOBJ types:");
-	sobj_type_walk(print_sobj_help, NULL);
-	mdb_printf("\n");
-	mdb_printf("Thread states:");
-	thread_walk_states(print_tstate_help, NULL);
-	mdb_printf(" panic\n");
-}
-
-/*ARGSUSED*/
 int
 stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
@@ -986,7 +588,7 @@
 	uintptr_t caller = 0, excl_caller = 0;
 	const char *module_str = NULL;
 	const char *excl_module_str = NULL;
-	struct modctl *module = NULL, *excl_module = NULL;
+	stacks_module_t module, excl_module;
 	const char *sobj = NULL;
 	const char *excl_sobj = NULL;
 	uintptr_t sobj_ops = 0, excl_sobj_ops = 0;
@@ -1015,6 +617,9 @@
 
 	mdb_pipe_t p;
 
+	bzero(&module, sizeof (module));
+	bzero(&excl_module, sizeof (excl_module));
+
 	if (mdb_getopts(argc, argv,
 	    'a', MDB_OPT_SETBITS, TRUE, &all,
 	    'f', MDB_OPT_SETBITS, TRUE, &force,
@@ -1063,24 +668,17 @@
 	}
 	mdb_set_dot(addr);
 
-	if (module_str != NULL &&
-	    (module = find_module(module_str)) == NULL) {
-		mdb_warn("stacks: module \"%s\" is unknown", module_str);
+	if (module_str != NULL && stacks_module_find(module_str, &module) != 0)
 		return (DCMD_ABORT);
-	}
 
 	if (excl_module_str != NULL &&
-	    (excl_module = find_module(excl_module_str)) == NULL) {
-		mdb_warn("stacks: module \"%s\" is unknown", excl_module_str);
+	    stacks_module_find(excl_module_str, &excl_module) != 0)
 		return (DCMD_ABORT);
-	}
 
-	if (sobj != NULL &&
-	    text_to_sobj(sobj, &sobj_ops) != 0)
+	if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0)
 		return (DCMD_USAGE);
 
-	if (excl_sobj != NULL &&
-	    text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
+	if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
 		return (DCMD_USAGE);
 
 	if (sobj_ops != 0 && excl_sobj_ops != 0) {
@@ -1088,8 +686,7 @@
 		return (DCMD_USAGE);
 	}
 
-	if (tstate_str != NULL &&
-	    text_to_tstate(tstate_str, &tstate) != 0)
+	if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0)
 		return (DCMD_USAGE);
 
 	if (excl_tstate_str != NULL &&
@@ -1190,11 +787,15 @@
 
 		if (caller != 0 && !stacks_has_caller(sep, caller))
 			continue;
+
 		if (excl_caller != 0 && stacks_has_caller(sep, excl_caller))
 			continue;
-		if (module != 0 && !stacks_has_module(sep, module))
+
+		if (module.sm_size != 0 && !stacks_has_module(sep, &module))
 			continue;
-		if (excl_module != 0 && stacks_has_module(sep, excl_module))
+
+		if (excl_module.sm_size != 0 &&
+		    stacks_has_module(sep, &excl_module))
 			continue;
 
 		if (tstate != -1U) {
@@ -1256,10 +857,10 @@
 			    sobj, sizeof (sobj));
 
 			if (cur == sep)
-				mdb_printf("%?p %-8s %-?s %8d\n",
+				mdb_printf("%-?p %-8s %-?s %8d\n",
 				    cur->se_thread, state, sobj, count);
 			else
-				mdb_printf("%?p %-8s %-?s %8s\n",
+				mdb_printf("%-?p %-8s %-?s %8s\n",
 				    cur->se_thread, state, sobj, "-");
 
 			cur = only_matching ? cur->se_next : cur->se_dup;
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack.h	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.h	Fri Jul 23 17:34:02 2010 -0700
@@ -18,26 +18,73 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_MDB_FINDSTACK_H
 #define	_MDB_FINDSTACK_H
 
 #include <mdb/mdb_modapi.h>
+#include <sys/param.h>
 
 #ifdef	__cplusplus
 extern "C" {
 #endif
 
+typedef struct findstack_info {
+	uintptr_t	*fsi_stack;	/* place to record frames */
+	uintptr_t	fsi_sp;		/* stack pointer */
+	uintptr_t	fsi_pc;		/* pc */
+	uintptr_t	fsi_sobj_ops;	/* sobj_ops */
+	uint_t		fsi_tstate;	/* t_state */
+	uchar_t		fsi_depth;	/* stack depth */
+	uchar_t		fsi_failed;	/* search failed */
+	uchar_t		fsi_overflow;	/* stack was deeper than max_depth */
+	uchar_t		fsi_panic;	/* thread called panic() */
+	uchar_t		fsi_max_depth;	/* stack frames available */
+} findstack_info_t;
+
+#define	FSI_FAIL_BADTHREAD	1
+#define	FSI_FAIL_NOTINMEMORY	2
+#define	FSI_FAIL_THREADCORRUPT	3
+#define	FSI_FAIL_STACKNOTFOUND	4
+
+typedef struct stacks_module {
+	char		sm_name[MAXPATHLEN]; /* name of module */
+	uintptr_t	sm_text;	/* base address of text in module */
+	size_t		sm_size;	/* size of text in module */
+} stacks_module_t;
+
 extern int findstack(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int findstack_debug(uintptr_t, uint_t, int, const mdb_arg_t *);
 
+/*
+ * The following routines are implemented in findstack.c, shared across both
+ * genunix and libc.
+ */
 extern int stacks(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void stacks_cleanup(int);
+
+/*
+ * The following routines are specific to their context (kernel vs. user-land)
+ * and are therefore implemented in findstack_subr.c (of which each of genunix
+ * and libc have their own copy).
+ */
 extern void stacks_help(void);
-extern void stacks_cleanup(int);
+extern int stacks_findstack(uintptr_t, findstack_info_t *, uint_t);
+extern void stacks_findstack_cleanup();
+extern int stacks_module(stacks_module_t *);
+
+extern int findstack_debug_on;
+
+#define	fs_dprintf(x)					\
+	if (findstack_debug_on) {			\
+		mdb_printf("findstack debug: ");	\
+		/*CSTYLED*/				\
+		mdb_printf x ;				\
+	}
 
 #ifdef	__cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c	Fri Jul 23 17:34:02 2010 -0700
@@ -0,0 +1,414 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ctf.h>
+
+#include <sys/types.h>
+#include <sys/regset.h>
+#include <sys/stack.h>
+#include <sys/thread.h>
+#include <sys/modctl.h>
+
+#include "findstack.h"
+#include "thread.h"
+#include "sobj.h"
+
+#define	TOO_BIG_FOR_A_STACK (1024 * 1024)
+
+#define	KTOU(p) ((p) - kbase + ubase)
+#define	UTOK(p) ((p) - ubase + kbase)
+
+#define	CRAWL_FOUNDALL	(-1)
+
+#if defined(__i386) || defined(__amd64)
+struct rwindow {
+	uintptr_t rw_fp;
+	uintptr_t rw_rtn;
+};
+#endif
+
+#ifndef STACK_BIAS
+#define	STACK_BIAS	0
+#endif
+
+/*
+ * Given a stack pointer, try to crawl down it to the bottom.
+ * "frame" is a VA in MDB's address space.
+ *
+ * Returns the number of frames successfully crawled down, or
+ * CRAWL_FOUNDALL if it got to the bottom of the stack.
+ */
+static int
+crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase,
+    int kill_fp, findstack_info_t *fsip)
+{
+	int levels = 0;
+
+	fsip->fsi_depth = 0;
+	fsip->fsi_overflow = 0;
+
+	fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n",
+	    frame, kbase, ktop, ubase));
+	for (;;) {
+		uintptr_t fp;
+		long *fpp = (long *)&((struct rwindow *)frame)->rw_fp;
+
+		fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame));
+
+		if ((frame & (STACK_ALIGN - 1)) != 0)
+			break;
+
+		fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS;
+		if (fsip->fsi_depth < fsip->fsi_max_depth)
+			fsip->fsi_stack[fsip->fsi_depth++] =
+			    ((struct rwindow *)frame)->rw_rtn;
+		else
+			fsip->fsi_overflow = 1;
+
+		fs_dprintf(("<2> fp = %p\n", fp));
+
+		if (fp == ktop)
+			return (CRAWL_FOUNDALL);
+		fs_dprintf(("<3> not at base\n"));
+
+#if defined(__i386) || defined(__amd64)
+		if (ktop - fp == sizeof (struct rwindow)) {
+			fs_dprintf(("<4> found base\n"));
+			return (CRAWL_FOUNDALL);
+		}
+#endif
+
+		fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n",
+		    fp, kbase, ktop - sizeof (struct rwindow)));
+
+		if (fp < kbase || fp >= (ktop - sizeof (struct rwindow)))
+			break;
+
+		frame = KTOU(fp);
+		fs_dprintf(("<6> frame = %p\n", frame));
+
+		/*
+		 * NULL out the old %fp so we don't go down this stack
+		 * more than once.
+		 */
+		if (kill_fp) {
+			fs_dprintf(("<7> fpp = %p\n", fpp));
+			*fpp = NULL;
+		}
+
+		fs_dprintf(("<8> levels = %d\n", levels));
+		levels++;
+	}
+
+	return (levels);
+}
+
+/*ARGSUSED*/
+int
+stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
+{
+	kthread_t thr;
+	size_t stksz;
+	uintptr_t ubase, utop;
+	uintptr_t kbase, ktop;
+	uintptr_t win, sp;
+
+	fsip->fsi_failed = 0;
+	fsip->fsi_pc = 0;
+	fsip->fsi_sp = 0;
+	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);
+		fsip->fsi_failed = FSI_FAIL_BADTHREAD;
+		return (DCMD_ERR);
+	}
+
+	fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops;
+	fsip->fsi_tstate = thr.t_state;
+	fsip->fsi_panic = !!(thr.t_flag & T_PANIC);
+
+	if ((thr.t_schedflag & TS_LOAD) == 0) {
+		if (print_warnings)
+			mdb_warn("thread %p isn't in memory\n", addr);
+		fsip->fsi_failed = FSI_FAIL_NOTINMEMORY;
+		return (DCMD_ERR);
+	}
+
+	if (thr.t_stk < thr.t_stkbase) {
+		if (print_warnings)
+			mdb_warn(
+			    "stack base or stack top corrupt for thread %p\n",
+			    addr);
+		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
+		return (DCMD_ERR);
+	}
+
+	kbase = (uintptr_t)thr.t_stkbase;
+	ktop = (uintptr_t)thr.t_stk;
+	stksz = ktop - kbase;
+
+#ifdef __amd64
+	/*
+	 * The stack on amd64 is intentionally misaligned, so ignore the top
+	 * half-frame.  See thread_stk_init().  When handling traps, the frame
+	 * is automatically aligned by the hardware, so we only alter ktop if
+	 * needed.
+	 */
+	if ((ktop & (STACK_ALIGN - 1)) != 0)
+		ktop -= STACK_ENTRY_ALIGN;
+#endif
+
+	/*
+	 * If the stack size is larger than a meg, assume that it's bogus.
+	 */
+	if (stksz > TOO_BIG_FOR_A_STACK) {
+		if (print_warnings)
+			mdb_warn("stack size for thread %p is too big to be "
+			    "reasonable\n", addr);
+		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
+		return (DCMD_ERR);
+	}
+
+	/*
+	 * This could be (and was) a UM_GC allocation.  Unfortunately,
+	 * stksz tends to be very large.  As currently implemented, dcmds
+	 * invoked as part of pipelines don't have their UM_GC-allocated
+	 * memory freed until the pipeline completes.  With stksz in the
+	 * neighborhood of 20k, the popular ::walk thread |::findstack
+	 * pipeline can easily run memory-constrained debuggers (kmdb) out
+	 * of memory.  This can be changed back to a gc-able allocation when
+	 * the debugger is changed to free UM_GC memory more promptly.
+	 */
+	ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP);
+	utop = ubase + stksz;
+	if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) {
+		mdb_free((void *)ubase, stksz);
+		if (print_warnings)
+			mdb_warn("couldn't read entire stack for thread %p\n",
+			    addr);
+		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
+		return (DCMD_ERR);
+	}
+
+	/*
+	 * Try the saved %sp first, if it looks reasonable.
+	 */
+	sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS);
+	if (sp >= ubase && sp <= utop) {
+		if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) {
+			fsip->fsi_sp = (uintptr_t)thr.t_sp;
+#if !defined(__i386)
+			fsip->fsi_pc = (uintptr_t)thr.t_pc;
+#endif
+			goto found;
+		}
+	}
+
+	/*
+	 * Now walk through the whole stack, starting at the base,
+	 * trying every possible "window".
+	 */
+	for (win = ubase;
+	    win + sizeof (struct rwindow) <= utop;
+	    win += sizeof (struct rwindow *)) {
+		if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) {
+			fsip->fsi_sp = UTOK(win) - STACK_BIAS;
+			goto found;
+		}
+	}
+
+	/*
+	 * We didn't conclusively find the stack.  So we'll take another lap,
+	 * and print out anything that looks possible.
+	 */
+	if (print_warnings)
+		mdb_printf("Possible stack pointers for thread %p:\n", addr);
+	(void) mdb_vread((caddr_t)ubase, stksz, kbase);
+
+	for (win = ubase;
+	    win + sizeof (struct rwindow) <= utop;
+	    win += sizeof (struct rwindow *)) {
+		uintptr_t fp = ((struct rwindow *)win)->rw_fp;
+		int levels;
+
+		if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) {
+			if (print_warnings)
+				mdb_printf("  %p (%d)\n", fp, levels);
+		} else if (levels == CRAWL_FOUNDALL) {
+			/*
+			 * If this is a live system, the stack could change
+			 * between the two mdb_vread(ubase, utop, kbase)'s,
+			 * and we could have a fully valid stack here.
+			 */
+			fsip->fsi_sp = UTOK(win) - STACK_BIAS;
+			goto found;
+		}
+	}
+
+	fsip->fsi_depth = 0;
+	fsip->fsi_overflow = 0;
+	fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND;
+
+	mdb_free((void *)ubase, stksz);
+	return (DCMD_ERR);
+found:
+	mdb_free((void *)ubase, stksz);
+	return (DCMD_OK);
+}
+
+void
+stacks_findstack_cleanup()
+{}
+
+/*ARGSUSED*/
+int
+stacks_module_cb(uintptr_t addr, const modctl_t *mp, stacks_module_t *smp)
+{
+	char mod_modname[MODMAXNAMELEN + 1];
+
+	if (!mp->mod_modname)
+		return (WALK_NEXT);
+
+	if (mdb_readstr(mod_modname, sizeof (mod_modname),
+	    (uintptr_t)mp->mod_modname) == -1) {
+		mdb_warn("failed to read mod_modname in \"modctl\" walk");
+		return (WALK_ERR);
+	}
+
+	if (strcmp(smp->sm_name, mod_modname))
+		return (WALK_NEXT);
+
+	smp->sm_text = (uintptr_t)mp->mod_text;
+	smp->sm_size = mp->mod_text_size;
+
+	return (WALK_DONE);
+}
+
+int
+stacks_module(stacks_module_t *smp)
+{
+	if (mdb_walk("modctl", (mdb_walk_cb_t)stacks_module_cb, smp) != 0) {
+		mdb_warn("cannot walk \"modctl\"");
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+print_sobj_help(int type, const char *name, const char *ops_name, void *ign)
+{
+	mdb_printf(" %s", name);
+}
+
+/*ARGSUSED*/
+static void
+print_tstate_help(uint_t state, const char *name, void *ignored)
+{
+	mdb_printf(" %s", name);
+}
+
+void
+stacks_help(void)
+{
+	mdb_printf(
+"::stacks processes all of the thread stacks on the system, grouping\n"
+"together threads which have the same:\n"
+"\n"
+"  * Thread state,\n"
+"  * Sync object type, and\n"
+"  * PCs in their stack trace.\n"
+"\n"
+"The default output (no address or options) is just a dump of the thread\n"
+"groups in the system.  For a view of active threads, use \"::stacks -i\",\n"
+"which filters out FREE threads (interrupt threads which are currently\n"
+"inactive) and threads sleeping on a CV. (Note that those threads may still\n"
+"be noteworthy; this is just for a first glance.)  More general filtering\n"
+"options are described below, in the \"FILTERS\" section.\n"
+"\n"
+"::stacks can be used in a pipeline.  The input to ::stacks is one or more\n"
+"thread pointers.  For example, to get a summary of threads in a process,\n"
+"you can do:\n"
+"\n"
+"  %<b>procp%</b>::walk thread | ::stacks\n"
+"\n"
+"When output into a pipe, ::stacks prints all of the threads input,\n"
+"filtered by the given filtering options.  This means that multiple\n"
+"::stacks invocations can be piped together to achieve more complicated\n"
+"filters.  For example, to get threads which have both 'fop_read' and\n"
+"'cv_wait_sig_swap' in their stack trace, you could do:\n"
+"\n"
+"  ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n"
+"\n"
+"To get the full list of threads in each group, use the '-a' flag:\n"
+"\n"
+"  ::stacks -a\n"
+"\n");
+	mdb_dec_indent(2);
+	mdb_printf("%<b>OPTIONS%</b>\n");
+	mdb_inc_indent(2);
+	mdb_printf("%s",
+"  -a    Print all of the grouped threads, instead of just a count.\n"
+"  -f    Force a re-run of the thread stack gathering.\n"
+"  -v    Be verbose about thread stack gathering.\n"
+"\n");
+	mdb_dec_indent(2);
+	mdb_printf("%<b>FILTERS%</b>\n");
+	mdb_inc_indent(2);
+	mdb_printf("%s",
+"  -i    Show active threads; equivalent to '-S CV -T FREE'.\n"
+"  -c func[+offset]\n"
+"        Only print threads whose stacks contain func/func+offset.\n"
+"  -C func[+offset]\n"
+"        Only print threads whose stacks do not contain func/func+offset.\n"
+"  -m module\n"
+"        Only print threads whose stacks contain functions from module.\n"
+"  -M module\n"
+"        Only print threads whose stacks do not contain functions from\n"
+"        module.\n"
+"  -s {type | ALL}\n"
+"        Only print threads which are on a 'type' synchronization object\n"
+"        (SOBJ).\n"
+"  -S {type | ALL}\n"
+"        Only print threads which are not on a 'type' SOBJ.\n"
+"  -t tstate\n"
+"        Only print threads which are in thread state 'tstate'.\n"
+"  -T tstate\n"
+"        Only print threads which are not in thread state 'tstate'.\n"
+"\n");
+	mdb_printf("   SOBJ types:");
+	sobj_type_walk(print_sobj_help, NULL);
+	mdb_printf("\n");
+	mdb_printf("Thread states:");
+	thread_walk_states(print_tstate_help, NULL);
+	mdb_printf(" panic\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c	Fri Jul 23 17:34:02 2010 -0700
@@ -0,0 +1,382 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/avl.h>
+#include <sys/lwp.h>
+#include <thr_uberdata.h>
+#include <stddef.h>
+#include "findstack.h"
+
+#if defined(__i386) || defined(__amd64)
+struct rwindow {
+	uintptr_t rw_fp;
+	uintptr_t rw_rtn;
+};
+#endif
+
+#ifndef STACK_BIAS
+#define	STACK_BIAS	0
+#endif
+
+#ifdef __amd64
+#define	STACKS_REGS_FP	"rbp"
+#define	STACKS_REGS_RC	"rip"
+#else
+#ifdef __i386
+#define	STACKS_REGS_FP	"ebp"
+#define	STACKS_REGS_RC	"eip"
+#else
+#define	STACKS_REGS_FP	"fp"
+#define	STACKS_REGS_RC	"pc"
+#endif
+#endif
+
+#define	STACKS_SOBJ_MX	(uintptr_t)"MX"
+#define	STACKS_SOBJ_CV	(uintptr_t)"CV"
+
+int
+thread_text_to_state(const char *state, uint_t *out)
+{
+	if (strcmp(state, "PARKED") == 0) {
+		*out = B_TRUE;
+	} else if (strcmp(state, "UNPARKED") == 0) {
+		*out = B_FALSE;
+	} else if (strcmp(state, "FREE") == 0) {
+		/*
+		 * When run with "-i", ::stacks filters out "FREE" threads.
+		 * We therefore need to recognize "FREE", and set it to a
+		 * value that will never match fsi_tstate.
+		 */
+		*out = UINT_MAX;
+	} else {
+		return (-1);
+	}
+
+	return (0);
+}
+
+void
+thread_state_to_text(uint_t state, char *out, size_t out_sz)
+{
+	(void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED");
+}
+
+int
+sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
+{
+	if (strcmp(name, "MX") == 0) {
+		*sobj_ops_out = STACKS_SOBJ_MX;
+	} else if (strcmp(name, "CV") == 0) {
+		*sobj_ops_out = STACKS_SOBJ_CV;
+	} else {
+		mdb_warn("sobj \"%s\" not recognized\n", name);
+		return (-1);
+	}
+
+	return (0);
+}
+
+void
+sobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
+{
+	(void) snprintf(out, sz, "%s", addr == NULL ? "<none>" : (char *)addr);
+}
+
+static int
+stacks_module_callback(mdb_object_t *obj, void *arg)
+{
+	stacks_module_t *smp = arg;
+	boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0);
+	char *suffix = ".so";
+	const char *s, *next;
+	size_t len;
+
+	if (smp->sm_size != 0)
+		return (0);
+
+	/*
+	 * It doesn't match the name, but -- for convenience -- we want to
+	 * allow matches before ".so.[suffix]".  An aside:  why doesn't
+	 * strrstr() exist?  (Don't google that.  I'm serious, don't do it.
+	 * If you do, and you read the thread of "why doesn't strrstr() exist?"
+	 * circa 2005 you will see things that you will NEVER be able to unsee!)
+	 */
+	if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) {
+		while ((next = strstr(s + 1, suffix)) != NULL) {
+			s = next;
+			continue;
+		}
+
+		len = s - obj->obj_name;
+
+		match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 &&
+		    smp->sm_name[len] == '\0');
+	}
+
+	/*
+	 * If we have a library that has the libc directory in the path, we
+	 * want to match against anything that would match libc.so.1.  (This
+	 * is necessary to be able to easily deal with libc implementations
+	 * that have alternate hardware capabilities.)
+	 */
+	if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) {
+		mdb_object_t libc = *obj;
+
+		libc.obj_name = "libc.so.1";
+		libc.obj_fullname = "";
+
+		return (stacks_module_callback(&libc, arg));
+	}
+
+	if (match) {
+		smp->sm_text = obj->obj_base;
+		smp->sm_size = obj->obj_size;
+	}
+
+	return (0);
+}
+
+int
+stacks_module(stacks_module_t *smp)
+{
+	if (mdb_object_iter(stacks_module_callback, smp) != 0)
+		return (-1);
+
+	return (0);
+}
+
+typedef struct stacks_ulwp {
+	avl_node_t sulwp_node;
+	lwpid_t sulwp_id;
+	uintptr_t sulwp_addr;
+} stacks_ulwp_t;
+
+boolean_t stacks_ulwp_initialized;
+avl_tree_t stacks_ulwp_byid;
+
+/*ARGSUSED*/
+int
+stacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored)
+{
+	stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP);
+
+	sulwp->sulwp_id = ulwp->ul_lwpid;
+	sulwp->sulwp_addr = addr;
+
+	if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) {
+		mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid);
+		return (WALK_ERR);
+	}
+
+	avl_add(&stacks_ulwp_byid, sulwp);
+
+	return (WALK_NEXT);
+}
+
+static int
+stacks_ulwp_compare(const void *l, const void *r)
+{
+	const stacks_ulwp_t *lhs = l;
+	const stacks_ulwp_t *rhs = r;
+
+	if (lhs->sulwp_id > rhs->sulwp_id)
+		return (1);
+
+	if (lhs->sulwp_id < rhs->sulwp_id)
+		return (-1);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
+{
+	mdb_reg_t reg;
+	uintptr_t fp;
+	struct rwindow frame;
+	avl_tree_t *tree = &stacks_ulwp_byid;
+	stacks_ulwp_t *sulwp, cmp;
+	ulwp_t ulwp;
+
+	fsip->fsi_failed = 0;
+	fsip->fsi_pc = 0;
+	fsip->fsi_sp = 0;
+	fsip->fsi_depth = 0;
+	fsip->fsi_overflow = 0;
+
+	if (!stacks_ulwp_initialized) {
+		avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t),
+		    offsetof(stacks_ulwp_t, sulwp_node));
+
+		if (mdb_walk("ulwp",
+		    (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) {
+			mdb_warn("couldn't walk 'ulwp'");
+			return (-1);
+		}
+
+		stacks_ulwp_initialized = B_TRUE;
+	}
+
+	bzero(&cmp, sizeof (cmp));
+	cmp.sulwp_id = (lwpid_t)addr;
+
+	if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) {
+		mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id);
+		return (-1);
+	}
+
+	if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) {
+		mdb_warn("couldn't read ulwp_t for tid %d at %p",
+		    cmp.sulwp_id, sulwp->sulwp_addr);
+		return (-1);
+	}
+
+	fsip->fsi_tstate = ulwp.ul_sleepq != NULL;
+	fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ? NULL :
+	    (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV));
+
+	if (mdb_getareg(addr, STACKS_REGS_FP, &reg) != 0) {
+		mdb_warn("couldn't read frame pointer for thread 0x%p", addr);
+		return (-1);
+	}
+
+	fsip->fsi_sp = fp = (uintptr_t)reg;
+
+#if !defined(__i386)
+	if (mdb_getareg(addr, STACKS_REGS_RC, &reg) != 0) {
+		mdb_warn("couldn't read program counter for thread 0x%p", addr);
+		return (-1);
+	}
+
+	fsip->fsi_pc = (uintptr_t)reg;
+#endif
+
+	while (fp != NULL) {
+		if (mdb_vread(&frame, sizeof (frame), fp) == -1) {
+			mdb_warn("couldn't read frame for thread 0x%p at %p",
+			    addr, fp);
+			return (-1);
+		}
+
+		if (frame.rw_rtn == NULL)
+			break;
+
+		if (fsip->fsi_depth < fsip->fsi_max_depth) {
+			fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn;
+		} else {
+			fsip->fsi_overflow = 1;
+			break;
+		}
+
+		fp = frame.rw_fp + STACK_BIAS;
+	}
+
+	return (0);
+}
+
+void
+stacks_findstack_cleanup()
+{
+	avl_tree_t *tree = &stacks_ulwp_byid;
+	void *cookie = NULL;
+	stacks_ulwp_t *sulwp;
+
+	if (!stacks_ulwp_initialized)
+		return;
+
+	while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL)
+		mdb_free(sulwp, sizeof (stacks_ulwp_t));
+
+	bzero(tree, sizeof (*tree));
+	stacks_ulwp_initialized = B_FALSE;
+}
+
+void
+stacks_help(void)
+{
+	mdb_printf(
+"::stacks processes all of the thread stacks in the process, grouping\n"
+"together threads which have the same:\n"
+"\n"
+"  * Thread state,\n"
+"  * Sync object type, and\n"
+"  * PCs in their stack trace.\n"
+"\n"
+"The default output (no address or options) is just a dump of the thread\n"
+"groups in the process.  For a view of active threads, use \"::stacks -i\",\n"
+"which filters out threads sleeping on a CV.  More general filtering options\n"
+"are described below, in the \"FILTERS\" section.\n"
+"\n"
+"::stacks can be used in a pipeline.  The input to ::stacks is one or more\n"
+"thread IDs.  When output into a pipe, ::stacks prints all of the threads \n"
+"input, filtered by the given filtering options.  This means that multiple\n"
+"::stacks invocations can be piped together to achieve more complicated\n"
+"filters.  For example, to get threads which have both '__door_return' and\n"
+"'mutex_lock' in their stack trace, you could do:\n"
+"\n"
+"  ::stacks -c __door_return | ::stacks -c mutex_lock\n"
+"\n"
+"To get the full list of threads in each group, use the '-a' flag:\n"
+"\n"
+"  ::stacks -a\n"
+"\n");
+	mdb_dec_indent(2);
+	mdb_printf("%<b>OPTIONS%</b>\n");
+	mdb_inc_indent(2);
+	mdb_printf("%s",
+"  -a    Print all of the grouped threads, instead of just a count.\n"
+"  -f    Force a re-run of the thread stack gathering.\n"
+"  -v    Be verbose about thread stack gathering.\n"
+"\n");
+	mdb_dec_indent(2);
+	mdb_printf("%<b>FILTERS%</b>\n");
+	mdb_inc_indent(2);
+	mdb_printf("%s",
+"  -i    Show active threads; equivalent to '-S CV'.\n"
+"  -c func[+offset]\n"
+"        Only print threads whose stacks contain func/func+offset.\n"
+"  -C func[+offset]\n"
+"        Only print threads whose stacks do not contain func/func+offset.\n"
+"  -m module\n"
+"        Only print threads whose stacks contain functions from module.\n"
+"  -M module\n"
+"        Only print threads whose stacks do not contain functions from\n"
+"        module.\n"
+"  -s {type | ALL}\n"
+"        Only print threads which are on a 'type' synchronization object\n"
+"        (SOBJ).\n"
+"  -S {type | ALL}\n"
+"        Only print threads which are not on a 'type' SOBJ.\n"
+"  -t tstate\n"
+"        Only print threads which are in thread state 'tstate'.\n"
+"  -T tstate\n"
+"        Only print threads which are not in thread state 'tstate'.\n"
+"\n");
+}
--- a/usr/src/cmd/mdb/common/modules/libc/libc.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/libc/libc.c	Fri Jul 23 17:34:02 2010 -0700
@@ -32,6 +32,7 @@
 #include <setjmp.h>
 #include <string.h>
 #include <thr_uberdata.h>
+#include "findstack.h"
 
 static const char *
 stack_flags(const stack_t *sp)
@@ -981,10 +982,76 @@
  * ==================== threads ==========================
  */
 
+int
+stacks_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	int rval = stacks(addr, flags, argc, argv);
+
+	/*
+	 * For the user-level variant of ::stacks, we don't bother caching
+	 * state, as even a very large program is unlikely to compare to the
+	 * kernel in terms of number of threads.  (And if you find yourself
+	 * here in anger, frustrated about how long ::stacks is running on
+	 * your galactically complicated zillion-thread program, hopefully
+	 * you will find some solace in the irony.  Okay, probably not...)
+	 */
+	stacks_cleanup(B_TRUE);
+	return (rval);
+}
+
+typedef struct tid2ulwp_walk {
+	lwpid_t t2u_tid;
+	uintptr_t t2u_lwp;
+	boolean_t t2u_found;
+} tid2ulwp_walk_t;
+
+/*ARGSUSED*/
+static int
+tid2ulwp_walk(uintptr_t addr, ulwp_t *ulwp, tid2ulwp_walk_t *t2u)
+{
+	if (ulwp->ul_lwpid == t2u->t2u_tid) {
+		t2u->t2u_lwp = addr;
+		t2u->t2u_found = B_TRUE;
+		return (WALK_DONE);
+	}
+
+	return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	tid2ulwp_walk_t t2u;
+
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	bzero(&t2u, sizeof (t2u));
+	t2u.t2u_tid = (lwpid_t)addr;
+
+	if (mdb_walk("ulwp", (mdb_walk_cb_t)tid2ulwp_walk, &t2u) != 0) {
+		mdb_warn("can't walk 'ulwp'");
+		return (DCMD_ERR);
+	}
+
+	if (!t2u.t2u_found) {
+		mdb_warn("thread ID %d not found", t2u.t2u_tid);
+		return (DCMD_ERR);
+	}
+
+	mdb_printf("%p\n", t2u.t2u_lwp);
+
+	return (DCMD_OK);
+}
+
 static const mdb_dcmd_t dcmds[] = {
 	{ "jmp_buf", ":", "print jmp_buf contents", d_jmp_buf, NULL },
 	{ "sigjmp_buf", ":", "print sigjmp_buf contents", d_sigjmp_buf, NULL },
 	{ "siginfo", ":", "print siginfo_t structure", d_siginfo, NULL },
+	{ "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] ",
+		"print unique thread stacks", stacks_dcmd, stacks_help },
+	{ "tid2ulwp", "?", "convert TID to ulwp_t address", tid2ulwp },
 	{ "ucontext", ":", "print ucontext_t structure", d_ucontext, NULL },
 	{ "ulwp", ":", "print ulwp_t structure", d_ulwp, NULL },
 	{ "uberdata", ":", "print uberdata_t structure", d_uberdata, NULL },
@@ -998,6 +1065,8 @@
 		oldc_walk_init, oldc_walk_step, oldc_walk_fini, NULL },
 	{ "ulwps", "walk list of ulwp_t pointers",
 		ulwp_walk_init, ulwp_walk_step, NULL, NULL },
+	{ "ulwp", "walk list of ulwp_t pointers",
+		ulwp_walk_init, ulwp_walk_step, NULL, NULL },
 	{ NULL }
 };
 
--- a/usr/src/cmd/mdb/intel/amd64/libc/Makefile	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/intel/amd64/libc/Makefile	Fri Jul 23 17:34:02 2010 -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,27 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 MODULE = libc.so
 MDBTGT = proc
 
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+	libc.c \
+	findstack.c \
+	findstack_subr.c
 
 include ../../../../Makefile.cmd
 include ../../../../Makefile.cmd.64
 
 CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
 
 include ../../Makefile.amd64
 include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
--- a/usr/src/cmd/mdb/intel/ia32/libc/Makefile	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/intel/ia32/libc/Makefile	Fri Jul 23 17:34:02 2010 -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,20 +19,26 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2000-2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 MODULE = libc.so
 MDBTGT = proc
 
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+	libc.c \
+	findstack.c \
+	findstack_subr.c
 
 include ../../../../Makefile.cmd
 
 CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
 
 include ../../Makefile.ia32
 include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
--- a/usr/src/cmd/mdb/sparc/v7/libc/Makefile	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/sparc/v7/libc/Makefile	Fri Jul 23 17:34:02 2010 -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,20 +19,26 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2000-2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 MODULE = libc.so
 MDBTGT = proc
 
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+	libc.c \
+	findstack.c \
+	findstack_subr.c
 
 include ../../../../Makefile.cmd
 
 CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
 
 include ../../Makefile.sparcv7
 include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
--- a/usr/src/cmd/mdb/sparc/v9/libc/Makefile	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/cmd/mdb/sparc/v9/libc/Makefile	Fri Jul 23 17:34:02 2010 -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,27 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2000-2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 MODULE = libc.so
 MDBTGT = proc
 
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+	libc.c \
+	findstack.c \
+	findstack_subr.c
 
 include ../../../../Makefile.cmd
 
 CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
 
 include ../../../../Makefile.cmd.64
 include ../../Makefile.sparcv9
 include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
--- a/usr/src/lib/libdtrace/Makefile.com	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/Makefile.com	Fri Jul 23 17:34:02 2010 -0700
@@ -114,7 +114,7 @@
 CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS)
 CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS)
 YYCFLAGS =
-LDLIBS += -lgen -lproc -lrtld_db -lctf -lelf -lc
+LDLIBS += -lgen -lproc -lrtld_db -lnsl -lsocket -lctf -lelf -lc
 DRTILDLIBS = $(LDLIBS.lib) -lc
 
 yydebug := YYCFLAGS += -DYYDEBUG
--- a/usr/src/lib/libdtrace/common/dt_cc.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_cc.c	Fri Jul 23 17:34:02 2010 -0700
@@ -20,12 +20,9 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * DTrace D Language Compiler
  *
@@ -2069,11 +2066,11 @@
 	if (dt_list_next(&dtp->dt_lib_path) != NULL && dt_load_libs(dtp) != 0)
 		return (NULL); /* errno is set for us */
 
-	(void) ctf_discard(dtp->dt_cdefs->dm_ctfp);
-	(void) ctf_discard(dtp->dt_ddefs->dm_ctfp);
+	if (dtp->dt_globals->dh_nelems != 0)
+		(void) dt_idhash_iter(dtp->dt_globals, dt_idreset, NULL);
 
-	(void) dt_idhash_iter(dtp->dt_globals, dt_idreset, NULL);
-	(void) dt_idhash_iter(dtp->dt_tls, dt_idreset, NULL);
+	if (dtp->dt_tls->dh_nelems != 0)
+		(void) dt_idhash_iter(dtp->dt_tls, dt_idreset, NULL);
 
 	if (fp && (cflags & DTRACE_C_CPP) && (fp = dt_preproc(dtp, fp)) == NULL)
 		return (NULL); /* errno is set for us */
--- a/usr/src/lib/libdtrace/common/dt_dof.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_dof.c	Fri Jul 23 17:34:02 2010 -0700
@@ -20,12 +20,9 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/sysmacros.h>
 
@@ -834,7 +831,6 @@
 	 */
 	h.dofh_secnum = ddo->ddo_nsecs;
 	ssize = sizeof (h) + dt_buf_len(&ddo->ddo_secs);
-	assert(ssize == sizeof (h) + sizeof (dof_sec_t) * ddo->ddo_nsecs);
 
 	h.dofh_loadsz = ssize +
 	    dt_buf_len(&ddo->ddo_ldata) +
@@ -860,6 +856,7 @@
 
 	sp = dt_buf_ptr(&ddo->ddo_secs);
 	assert(sp[ddo->ddo_strsec].dofs_type == DOF_SECT_STRTAB);
+	assert(ssize == sizeof (h) + sizeof (dof_sec_t) * ddo->ddo_nsecs);
 
 	sp[ddo->ddo_strsec].dofs_offset = ssize + dt_buf_len(&ddo->ddo_ldata);
 	sp[ddo->ddo_strsec].dofs_size = dt_buf_len(&ddo->ddo_strs);
--- a/usr/src/lib/libdtrace/common/dt_lex.l	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_lex.l	Fri Jul 23 17:34:02 2010 -0700
@@ -18,12 +18,11 @@
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
- *
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 
 #include <string.h>
 #include <stdlib.h>
@@ -803,7 +802,7 @@
 	else if (yypcb->pcb_fileptr != NULL)
 		c = fgetc(yypcb->pcb_fileptr);
 	else if (yypcb->pcb_strptr < yypcb->pcb_string + yypcb->pcb_strlen)
-		c = *yypcb->pcb_strptr++;
+		c = *(unsigned char *)(yypcb->pcb_strptr++);
 	else
 		c = EOF;
 
--- a/usr/src/lib/libdtrace/common/dt_module.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_module.c	Fri Jul 23 17:34:02 2010 -0700
@@ -18,9 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -717,10 +717,25 @@
 void
 dt_module_destroy(dtrace_hdl_t *dtp, dt_module_t *dmp)
 {
+	uint_t h = dt_strtab_hash(dmp->dm_name, NULL) % dtp->dt_modbuckets;
+	dt_module_t **dmpp = &dtp->dt_mods[h];
+
 	dt_list_delete(&dtp->dt_modlist, dmp);
 	assert(dtp->dt_nmods != 0);
 	dtp->dt_nmods--;
 
+	/*
+	 * Now remove this module from its hash chain.  We expect to always
+	 * find the module on its hash chain, so in this loop we assert that
+	 * we don't run off the end of the list.
+	 */
+	while (*dmpp != dmp) {
+		dmpp = &((*dmpp)->dm_next);
+		assert(*dmpp != NULL);
+	}
+
+	*dmpp = dmp->dm_next;
+
 	dt_module_unload(dtp, dmp);
 	free(dmp);
 }
--- a/usr/src/lib/libdtrace/common/dt_printf.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_printf.c	Fri Jul 23 17:34:02 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/sysmacros.h>
@@ -32,6 +31,11 @@
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
 
 #include <dt_printf.h>
 #include <dt_string.h>
@@ -492,6 +496,49 @@
 
 /*ARGSUSED*/
 static int
+pfprint_port(dtrace_hdl_t *dtp, FILE *fp, const char *format,
+    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+{
+	uint16_t port = htons(*((uint16_t *)addr));
+	char buf[256];
+	struct servent *sv, res;
+
+	if ((sv = getservbyport_r(port, NULL, &res, buf, sizeof (buf))) != NULL)
+		return (dt_printf(dtp, fp, format, sv->s_name));
+
+	(void) snprintf(buf, sizeof (buf), "%d", *((uint16_t *)addr));
+	return (dt_printf(dtp, fp, format, buf));
+}
+
+/*ARGSUSED*/
+static int
+pfprint_inetaddr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
+    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+{
+	char *s = alloca(size + 1);
+	struct hostent *host, res;
+	char inetaddr[NS_IN6ADDRSZ];
+	char buf[1024];
+	int e;
+
+	bcopy(addr, s, size);
+	s[size] = '\0';
+
+	if (strchr(s, ':') == NULL && inet_pton(AF_INET, s, inetaddr) != -1) {
+		if ((host = gethostbyaddr_r(inetaddr, NS_INADDRSZ,
+		    AF_INET, &res, buf, sizeof (buf), &e)) != NULL)
+			return (dt_printf(dtp, fp, format, host->h_name));
+	} else if (inet_pton(AF_INET6, s, inetaddr) != -1) {
+		if ((host = getipnodebyaddr(inetaddr, NS_IN6ADDRSZ,
+		    AF_INET6, &e)) != NULL)
+			return (dt_printf(dtp, fp, format, host->h_name));
+	}
+
+	return (dt_printf(dtp, fp, format, s));
+}
+
+/*ARGSUSED*/
+static int
 pfprint_cstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
 {
@@ -595,6 +642,7 @@
 { "hx", "x", "short", pfcheck_xshort, pfprint_uint },
 { "hX", "X", "short", pfcheck_xshort, pfprint_uint },
 { "i", "i", pfproto_xint, pfcheck_dint, pfprint_dint },
+{ "I", "s", pfproto_cstr, pfcheck_str, pfprint_inetaddr },
 { "k", "s", "stack", pfcheck_stack, pfprint_stack },
 { "lc", "lc", "int", pfcheck_type, pfprint_sint }, /* a.k.a. wint_t */
 { "ld",	"d", "long", pfcheck_type, pfprint_sint },
@@ -617,6 +665,7 @@
 { "LG",	"G", "long double", pfcheck_type, pfprint_fp },
 { "o", "o", pfproto_xint, pfcheck_xint, pfprint_uint },
 { "p", "x", pfproto_addr, pfcheck_addr, pfprint_uint },
+{ "P", "s", "uint16_t", pfcheck_type, pfprint_port },
 { "s", "s", "char [] or string (or use stringof)", pfcheck_str, pfprint_cstr },
 { "S", "s", pfproto_cstr, pfcheck_str, pfprint_estr },
 { "T", "s", "int64_t", pfcheck_time, pfprint_time822 },
--- a/usr/src/lib/libdtrace/common/dt_program.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_program.c	Fri Jul 23 17:34:02 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <unistd.h>
@@ -42,10 +41,12 @@
 {
 	dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
 
-	if (pgp != NULL)
+	if (pgp != NULL) {
 		dt_list_append(&dtp->dt_programs, pgp);
-	else
+	} else {
 		(void) dt_set_errno(dtp, EDT_NOMEM);
+		return (NULL);
+	}
 
 	/*
 	 * By default, programs start with DOF version 1 so that output files
--- a/usr/src/lib/libdtrace/common/dt_string.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/lib/libdtrace/common/dt_string.c	Fri Jul 23 17:34:02 2010 -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.
@@ -19,19 +18,18 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <strings.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <ctype.h>
 
 #include <dt_string.h>
+#include <dt_impl.h>
 
 /*
  * Create a copy of string s, but only duplicate the first n bytes.
@@ -41,6 +39,9 @@
 {
 	char *s2 = malloc(n + 1);
 
+	if (s2 == NULL)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+
 	(void) strncpy(s2, s, n);
 	s2[n] = '\0';
 	return (s2);
--- a/usr/src/uts/common/dtrace/dtrace.c	Fri Jul 23 16:35:08 2010 -0700
+++ b/usr/src/uts/common/dtrace/dtrace.c	Fri Jul 23 17:34:02 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -186,6 +185,7 @@
 static dtrace_enabling_t *dtrace_retained;	/* list of retained enablings */
 static dtrace_genid_t	dtrace_retained_gen;	/* current retained enab gen */
 static dtrace_dynvar_t	dtrace_dynhash_sink;	/* end of dynamic hash chains */
+static int		dtrace_dynvar_failclean; /* dynvars failed to clean */
 
 /*
  * DTrace Locking
@@ -1188,12 +1188,12 @@
 {
 	dtrace_dynvar_t *dirty;
 	dtrace_dstate_percpu_t *dcpu;
-	int i, work = 0;
+	dtrace_dynvar_t **rinsep;
+	int i, j, work = 0;
 
 	for (i = 0; i < NCPU; i++) {
 		dcpu = &dstate->dtds_percpu[i];
-
-		ASSERT(dcpu->dtdsc_rinsing == NULL);
+		rinsep = &dcpu->dtdsc_rinsing;
 
 		/*
 		 * If the dirty list is NULL, there is no dirty work to do.
@@ -1201,14 +1201,62 @@
 		if (dcpu->dtdsc_dirty == NULL)
 			continue;
 
-		/*
-		 * If the clean list is non-NULL, then we're not going to do
-		 * any work for this CPU -- it means that there has not been
-		 * a dtrace_dynvar() allocation on this CPU (or from this CPU)
-		 * since the last time we cleaned house.
-		 */
-		if (dcpu->dtdsc_clean != NULL)
+		if (dcpu->dtdsc_rinsing != NULL) {
+			/*
+			 * If the rinsing list is non-NULL, then it is because
+			 * this CPU was selected to accept another CPU's
+			 * dirty list -- and since that time, dirty buffers
+			 * have accumulated.  This is a highly unlikely
+			 * condition, but we choose to ignore the dirty
+			 * buffers -- they'll be picked up a future cleanse.
+			 */
 			continue;
+		}
+
+		if (dcpu->dtdsc_clean != NULL) {
+			/*
+			 * If the clean list is non-NULL, then we're in a
+			 * situation where a CPU has done deallocations (we
+			 * have a non-NULL dirty list) but no allocations (we
+			 * also have a non-NULL clean list).  We can't simply
+			 * move the dirty list into the clean list on this
+			 * CPU, yet we also don't want to allow this condition
+			 * to persist, lest a short clean list prevent a
+			 * massive dirty list from being cleaned (which in
+			 * turn could lead to otherwise avoidable dynamic
+			 * drops).  To deal with this, we look for some CPU
+			 * with a NULL clean list, NULL dirty list, and NULL
+			 * rinsing list -- and then we borrow this CPU to
+			 * rinse our dirty list.
+			 */
+			for (j = 0; j < NCPU; j++) {
+				dtrace_dstate_percpu_t *rinser;
+
+				rinser = &dstate->dtds_percpu[j];
+
+				if (rinser->dtdsc_rinsing != NULL)
+					continue;
+
+				if (rinser->dtdsc_dirty != NULL)
+					continue;
+
+				if (rinser->dtdsc_clean != NULL)
+					continue;
+
+				rinsep = &rinser->dtdsc_rinsing;
+				break;
+			}
+
+			if (j == NCPU) {
+				/*
+				 * We were unable to find another CPU that
+				 * could accept this dirty list -- we are
+				 * therefore unable to clean it now.
+				 */
+				dtrace_dynvar_failclean++;
+				continue;
+			}
+		}
 
 		work = 1;
 
@@ -1225,7 +1273,7 @@
 			 * on a hash chain, either the dirty list or the
 			 * rinsing list for some CPU must be non-NULL.)
 			 */
-			dcpu->dtdsc_rinsing = dirty;
+			*rinsep = dirty;
 			dtrace_membar_producer();
 		} while (dtrace_casptr(&dcpu->dtdsc_dirty,
 		    dirty, NULL) != dirty);
@@ -1656,7 +1704,7 @@
 			ASSERT(clean->dtdv_hashval == DTRACE_DYNHASH_FREE);
 
 			/*
-			 * Now we'll move the clean list to the free list.
+			 * Now we'll move the clean list to our free list.
 			 * It's impossible for this to fail:  the only way
 			 * the free list can be updated is through this
 			 * code path, and only one CPU can own the clean list.
@@ -1669,6 +1717,7 @@
 			 * owners of the clean lists out before resetting
 			 * the clean lists.
 			 */
+			dcpu = &dstate->dtds_percpu[me];
 			rval = dtrace_casptr(&dcpu->dtdsc_free, NULL, clean);
 			ASSERT(rval == NULL);
 			goto retry;
@@ -3606,7 +3655,7 @@
 		int64_t index = (int64_t)tupregs[1].dttk_value;
 		int64_t remaining = (int64_t)tupregs[2].dttk_value;
 		size_t len = dtrace_strlen((char *)s, size);
-		int64_t i = 0;
+		int64_t i;
 
 		if (!dtrace_canload(s, len + 1, mstate, vstate)) {
 			regs[rd] = NULL;