17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5346799e8SJonathan W Adams  * Common Development and Distribution License (the "License").
6346799e8SJonathan W Adams  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21c9a6ea2eSBryan Cantrill 
227c478bd9Sstevel@tonic-gate /*
23c9a6ea2eSBryan Cantrill  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
2498144673SJosef 'Jeff' Sipek  * Copyright (c) 2013, Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
25*ab618543SJohn Levon  * Copyright 2018 Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
297c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/regset.h>
337c478bd9Sstevel@tonic-gate #include <sys/stack.h>
347c478bd9Sstevel@tonic-gate #include <sys/thread.h>
35e6fc74c6SGreg Price #include <sys/modctl.h>
36c9a6ea2eSBryan Cantrill #include <assert.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include "findstack.h"
39346799e8SJonathan W Adams #include "thread.h"
40346799e8SJonathan W Adams #include "sobj.h"
41346799e8SJonathan W Adams 
42*ab618543SJohn Levon /*
43*ab618543SJohn Levon  * Parts of this file are shared between targets, but this section is only
44*ab618543SJohn Levon  * used for KVM and KMDB.
45*ab618543SJohn Levon  */
46*ab618543SJohn Levon #ifdef _KERNEL
47*ab618543SJohn Levon 
48c9a6ea2eSBryan Cantrill int findstack_debug_on = 0;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate  * "sp" is a kernel VA.
527c478bd9Sstevel@tonic-gate  */
537c478bd9Sstevel@tonic-gate static int
print_stack(uintptr_t sp,uintptr_t pc,uintptr_t addr,int argc,const mdb_arg_t * argv,int free_state)547c478bd9Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr,
557c478bd9Sstevel@tonic-gate     int argc, const mdb_arg_t *argv, int free_state)
567c478bd9Sstevel@tonic-gate {
577c478bd9Sstevel@tonic-gate 	int showargs = 0, count, err;
58*ab618543SJohn Levon 	char tdesc[128] = "";
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate 	count = mdb_getopts(argc, argv,
617c478bd9Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL);
627c478bd9Sstevel@tonic-gate 	argc -= count;
637c478bd9Sstevel@tonic-gate 	argv += count;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate 	if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING))
667c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
677c478bd9Sstevel@tonic-gate 
68*ab618543SJohn Levon 	(void) thread_getdesc(addr, B_TRUE, tdesc, sizeof (tdesc));
69*ab618543SJohn Levon 
70*ab618543SJohn Levon 	mdb_printf("stack pointer for thread %p%s (%s): %p\n",
71*ab618543SJohn Levon 	    addr, (free_state ? " (TS_FREE)" : ""), tdesc, sp);
727c478bd9Sstevel@tonic-gate 	if (pc != 0)
737c478bd9Sstevel@tonic-gate 		mdb_printf("[ %0?lr %a() ]\n", sp, pc);
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	mdb_inc_indent(2);
767c478bd9Sstevel@tonic-gate 	mdb_set_dot(sp);
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 	if (argc == 1)
797c478bd9Sstevel@tonic-gate 		err = mdb_eval(argv->a_un.a_str);
807c478bd9Sstevel@tonic-gate 	else if (showargs)
817c478bd9Sstevel@tonic-gate 		err = mdb_eval("<.$C");
827c478bd9Sstevel@tonic-gate 	else
837c478bd9Sstevel@tonic-gate 		err = mdb_eval("<.$C0");
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	mdb_dec_indent(2);
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	return ((err == -1) ? DCMD_ABORT : DCMD_OK);
887c478bd9Sstevel@tonic-gate }
897c478bd9Sstevel@tonic-gate 
90346799e8SJonathan W Adams int
findstack(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)91346799e8SJonathan W Adams findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
92346799e8SJonathan W Adams {
93346799e8SJonathan W Adams 	findstack_info_t fsi;
94346799e8SJonathan W Adams 	int retval;
95346799e8SJonathan W Adams 
96346799e8SJonathan W Adams 	if (!(flags & DCMD_ADDRSPEC))
97346799e8SJonathan W Adams 		return (DCMD_USAGE);
98346799e8SJonathan W Adams 
99346799e8SJonathan W Adams 	bzero(&fsi, sizeof (fsi));
100346799e8SJonathan W Adams 
101c9a6ea2eSBryan Cantrill 	if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK ||
102346799e8SJonathan W Adams 	    fsi.fsi_failed)
103346799e8SJonathan W Adams 		return (retval);
104346799e8SJonathan W Adams 
105346799e8SJonathan W Adams 	return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr,
106346799e8SJonathan W Adams 	    argc, argv, fsi.fsi_tstate == TS_FREE));
107346799e8SJonathan W Adams }
108346799e8SJonathan W Adams 
1097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1107c478bd9Sstevel@tonic-gate int
findstack_debug(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * av)1117c478bd9Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av)
1127c478bd9Sstevel@tonic-gate {
1137c478bd9Sstevel@tonic-gate 	findstack_debug_on ^= 1;
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	mdb_printf("findstack: debugging is now %s\n",
1167c478bd9Sstevel@tonic-gate 	    findstack_debug_on ? "on" : "off");
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
121*ab618543SJohn Levon #endif /* _KERNEL */
122*ab618543SJohn Levon 
123346799e8SJonathan W Adams static void
uppercase(char * p)124346799e8SJonathan W Adams uppercase(char *p)
125346799e8SJonathan W Adams {
126346799e8SJonathan W Adams 	for (; *p != '\0'; p++) {
127346799e8SJonathan W Adams 		if (*p >= 'a' && *p <= 'z')
128346799e8SJonathan W Adams 			*p += 'A' - 'a';
129346799e8SJonathan W Adams 	}
130346799e8SJonathan W Adams }
131346799e8SJonathan W Adams 
132346799e8SJonathan W Adams static void
sobj_to_text(uintptr_t addr,char * out,size_t out_sz)133346799e8SJonathan W Adams sobj_to_text(uintptr_t addr, char *out, size_t out_sz)
134346799e8SJonathan W Adams {
135346799e8SJonathan W Adams 	sobj_ops_to_text(addr, out, out_sz);
136346799e8SJonathan W Adams 	uppercase(out);
137346799e8SJonathan W Adams }
138346799e8SJonathan W Adams 
139346799e8SJonathan W Adams #define	SOBJ_ALL	1
140c9a6ea2eSBryan Cantrill 
141346799e8SJonathan W Adams static int
text_to_sobj(const char * text,uintptr_t * out)142346799e8SJonathan W Adams text_to_sobj(const char *text, uintptr_t *out)
143346799e8SJonathan W Adams {
144346799e8SJonathan W Adams 	if (strcasecmp(text, "ALL") == 0) {
145346799e8SJonathan W Adams 		*out = SOBJ_ALL;
146346799e8SJonathan W Adams 		return (0);
147346799e8SJonathan W Adams 	}
148c9a6ea2eSBryan Cantrill 
149346799e8SJonathan W Adams 	return (sobj_text_to_ops(text, out));
150346799e8SJonathan W Adams }
151346799e8SJonathan W Adams 
152346799e8SJonathan W Adams #define	TSTATE_PANIC	-2U
153346799e8SJonathan W Adams static int
text_to_tstate(const char * text,uint_t * out)154346799e8SJonathan W Adams text_to_tstate(const char *text, uint_t *out)
155346799e8SJonathan W Adams {
156346799e8SJonathan W Adams 	if (strcasecmp(text, "panic") == 0)
157346799e8SJonathan W Adams 		*out = TSTATE_PANIC;
158346799e8SJonathan W Adams 	else if (thread_text_to_state(text, out) != 0) {
159346799e8SJonathan W Adams 		mdb_warn("tstate \"%s\" not recognized\n", text);
160346799e8SJonathan W Adams 		return (-1);
161346799e8SJonathan W Adams 	}
162346799e8SJonathan W Adams 	return (0);
163346799e8SJonathan W Adams }
164346799e8SJonathan W Adams 
165346799e8SJonathan W Adams static void
tstate_to_text(uint_t tstate,uint_t paniced,char * out,size_t out_sz)166346799e8SJonathan W Adams tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz)
167346799e8SJonathan W Adams {
168346799e8SJonathan W Adams 	if (paniced)
169346799e8SJonathan W Adams 		mdb_snprintf(out, out_sz, "panic");
170346799e8SJonathan W Adams 	else
171346799e8SJonathan W Adams 		thread_state_to_text(tstate, out, out_sz);
172346799e8SJonathan W Adams 	uppercase(out);
173346799e8SJonathan W Adams }
174346799e8SJonathan W Adams 
175346799e8SJonathan W Adams typedef struct stacks_entry {
176346799e8SJonathan W Adams 	struct stacks_entry	*se_next;
177346799e8SJonathan W Adams 	struct stacks_entry	*se_dup;	/* dups of this stack */
178346799e8SJonathan W Adams 	uintptr_t		se_thread;
179346799e8SJonathan W Adams 	uintptr_t		se_sp;
180346799e8SJonathan W Adams 	uintptr_t		se_sobj_ops;
181346799e8SJonathan W Adams 	uint32_t		se_tstate;
182346799e8SJonathan W Adams 	uint32_t		se_count;	/* # threads w/ this stack */
183346799e8SJonathan W Adams 	uint8_t			se_overflow;
184346799e8SJonathan W Adams 	uint8_t			se_depth;
185346799e8SJonathan W Adams 	uint8_t			se_failed;	/* failure reason; FSI_FAIL_* */
186346799e8SJonathan W Adams 	uint8_t			se_panic;
187346799e8SJonathan W Adams 	uintptr_t		se_stack[1];
188346799e8SJonathan W Adams } stacks_entry_t;
189346799e8SJonathan W Adams #define	STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)])
190346799e8SJonathan W Adams 
191346799e8SJonathan W Adams #define	STACKS_HSIZE 127
192346799e8SJonathan W Adams 
193346799e8SJonathan W Adams /* Maximum stack depth reported in stacks */
194346799e8SJonathan W Adams #define	STACKS_MAX_DEPTH	254
195346799e8SJonathan W Adams 
196346799e8SJonathan W Adams typedef struct stacks_info {
197346799e8SJonathan W Adams 	size_t		si_count;	/* total stacks_entry_ts (incl dups) */
198346799e8SJonathan W Adams 	size_t		si_entries;	/* # entries in hash table */
199346799e8SJonathan W Adams 	stacks_entry_t	**si_hash;	/* hash table */
200346799e8SJonathan W Adams 	findstack_info_t si_fsi;	/* transient callback state */
201346799e8SJonathan W Adams } stacks_info_t;
202346799e8SJonathan W Adams 
203346799e8SJonathan W Adams /* global state cached between invocations */
204346799e8SJonathan W Adams #define	STACKS_STATE_CLEAN	0
205346799e8SJonathan W Adams #define	STACKS_STATE_DIRTY	1
206346799e8SJonathan W Adams #define	STACKS_STATE_DONE	2
207346799e8SJonathan W Adams static uint_t stacks_state = STACKS_STATE_CLEAN;
208346799e8SJonathan W Adams static stacks_entry_t **stacks_hash;
209346799e8SJonathan W Adams static stacks_entry_t **stacks_array;
210346799e8SJonathan W Adams static size_t stacks_array_size;
211346799e8SJonathan W Adams 
212*ab618543SJohn Levon static size_t
stacks_hash_entry(stacks_entry_t * sep)213346799e8SJonathan W Adams stacks_hash_entry(stacks_entry_t *sep)
214346799e8SJonathan W Adams {
215346799e8SJonathan W Adams 	size_t depth = sep->se_depth;
216346799e8SJonathan W Adams 	uintptr_t *stack = sep->se_stack;
217346799e8SJonathan W Adams 
218346799e8SJonathan W Adams 	uint64_t total = depth;
219346799e8SJonathan W Adams 
220346799e8SJonathan W Adams 	while (depth > 0) {
221346799e8SJonathan W Adams 		total += *stack;
222346799e8SJonathan W Adams 		stack++; depth--;
223346799e8SJonathan W Adams 	}
224346799e8SJonathan W Adams 
225346799e8SJonathan W Adams 	return (total % STACKS_HSIZE);
226346799e8SJonathan W Adams }
227346799e8SJonathan W Adams 
228346799e8SJonathan W Adams /*
229346799e8SJonathan W Adams  * This is used to both compare stacks for equality and to sort the final
230346799e8SJonathan W Adams  * list of unique stacks.  forsort specifies the latter behavior, which
231346799e8SJonathan W Adams  * additionally:
232346799e8SJonathan W Adams  *	compares se_count, and
233346799e8SJonathan W Adams  *	sorts the stacks by text function name.
234346799e8SJonathan W Adams  *
235346799e8SJonathan W Adams  * The equality test is independent of se_count, and doesn't care about
236346799e8SJonathan W Adams  * relative ordering, so we don't do the extra work of looking up symbols
237346799e8SJonathan W Adams  * for the stack addresses.
238346799e8SJonathan W Adams  */
239*ab618543SJohn Levon static int
stacks_entry_comp_impl(stacks_entry_t * l,stacks_entry_t * r,uint_t forsort)240346799e8SJonathan W Adams stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r,
241346799e8SJonathan W Adams     uint_t forsort)
2427c478bd9Sstevel@tonic-gate {
243346799e8SJonathan W Adams 	int idx;
244346799e8SJonathan W Adams 
245346799e8SJonathan W Adams 	int depth = MIN(l->se_depth, r->se_depth);
246346799e8SJonathan W Adams 
247346799e8SJonathan W Adams 	/* no matter what, panic stacks come last. */
248346799e8SJonathan W Adams 	if (l->se_panic > r->se_panic)
249346799e8SJonathan W Adams 		return (1);
250346799e8SJonathan W Adams 	if (l->se_panic < r->se_panic)
251346799e8SJonathan W Adams 		return (-1);
252346799e8SJonathan W Adams 
253346799e8SJonathan W Adams 	if (forsort) {
254346799e8SJonathan W Adams 		/* put large counts earlier */
255346799e8SJonathan W Adams 		if (l->se_count > r->se_count)
256346799e8SJonathan W Adams 			return (-1);
257346799e8SJonathan W Adams 		if (l->se_count < r->se_count)
258346799e8SJonathan W Adams 			return (1);
2597c478bd9Sstevel@tonic-gate 	}
2607c478bd9Sstevel@tonic-gate 
261346799e8SJonathan W Adams 	if (l->se_tstate > r->se_tstate)
262346799e8SJonathan W Adams 		return (1);
263346799e8SJonathan W Adams 	if (l->se_tstate < r->se_tstate)
264346799e8SJonathan W Adams 		return (-1);
265346799e8SJonathan W Adams 
266346799e8SJonathan W Adams 	if (l->se_failed > r->se_failed)
267346799e8SJonathan W Adams 		return (1);
268346799e8SJonathan W Adams 	if (l->se_failed < r->se_failed)
269346799e8SJonathan W Adams 		return (-1);
270346799e8SJonathan W Adams 
271346799e8SJonathan W Adams 	for (idx = 0; idx < depth; idx++) {
272346799e8SJonathan W Adams 		char lbuf[MDB_SYM_NAMLEN];
273346799e8SJonathan W Adams 		char rbuf[MDB_SYM_NAMLEN];
274346799e8SJonathan W Adams 
275346799e8SJonathan W Adams 		int rval;
276346799e8SJonathan W Adams 		uintptr_t laddr = l->se_stack[idx];
277346799e8SJonathan W Adams 		uintptr_t raddr = r->se_stack[idx];
278346799e8SJonathan W Adams 
279346799e8SJonathan W Adams 		if (laddr == raddr)
280346799e8SJonathan W Adams 			continue;
281346799e8SJonathan W Adams 
282346799e8SJonathan W Adams 		if (forsort &&
283346799e8SJonathan W Adams 		    mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY,
284346799e8SJonathan W Adams 		    lbuf, sizeof (lbuf), NULL) != -1 &&
285346799e8SJonathan W Adams 		    mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY,
286346799e8SJonathan W Adams 		    rbuf, sizeof (rbuf), NULL) != -1 &&
287346799e8SJonathan W Adams 		    (rval = strcmp(lbuf, rbuf)) != 0)
288346799e8SJonathan W Adams 			return (rval);
289346799e8SJonathan W Adams 
290346799e8SJonathan W Adams 		if (laddr > raddr)
291346799e8SJonathan W Adams 			return (1);
292346799e8SJonathan W Adams 		return (-1);
293346799e8SJonathan W Adams 	}
294346799e8SJonathan W Adams 
295346799e8SJonathan W Adams 	if (l->se_overflow > r->se_overflow)
296346799e8SJonathan W Adams 		return (-1);
297346799e8SJonathan W Adams 	if (l->se_overflow < r->se_overflow)
298346799e8SJonathan W Adams 		return (1);
299346799e8SJonathan W Adams 
300346799e8SJonathan W Adams 	if (l->se_depth > r->se_depth)
301346799e8SJonathan W Adams 		return (1);
302346799e8SJonathan W Adams 	if (l->se_depth < r->se_depth)
303346799e8SJonathan W Adams 		return (-1);
304346799e8SJonathan W Adams 
305346799e8SJonathan W Adams 	if (l->se_sobj_ops > r->se_sobj_ops)
306346799e8SJonathan W Adams 		return (1);
307346799e8SJonathan W Adams 	if (l->se_sobj_ops < r->se_sobj_ops)
308346799e8SJonathan W Adams 		return (-1);
309346799e8SJonathan W Adams 
310346799e8SJonathan W Adams 	return (0);
311346799e8SJonathan W Adams }
312346799e8SJonathan W Adams 
313*ab618543SJohn Levon static int
stacks_entry_comp(const void * l_arg,const void * r_arg)314346799e8SJonathan W Adams stacks_entry_comp(const void *l_arg, const void *r_arg)
315346799e8SJonathan W Adams {
316346799e8SJonathan W Adams 	stacks_entry_t * const *lp = l_arg;
317346799e8SJonathan W Adams 	stacks_entry_t * const *rp = r_arg;
318346799e8SJonathan W Adams 
319346799e8SJonathan W Adams 	return (stacks_entry_comp_impl(*lp, *rp, 1));
320346799e8SJonathan W Adams }
321346799e8SJonathan W Adams 
322346799e8SJonathan W Adams void
stacks_cleanup(int force)323346799e8SJonathan W Adams stacks_cleanup(int force)
324346799e8SJonathan W Adams {
325346799e8SJonathan W Adams 	int idx = 0;
326346799e8SJonathan W Adams 	stacks_entry_t *cur, *next;
327346799e8SJonathan W Adams 
328346799e8SJonathan W Adams 	if (stacks_state == STACKS_STATE_CLEAN)
329346799e8SJonathan W Adams 		return;
330346799e8SJonathan W Adams 
331346799e8SJonathan W Adams 	if (!force && stacks_state == STACKS_STATE_DONE)
332346799e8SJonathan W Adams 		return;
333346799e8SJonathan W Adams 
334346799e8SJonathan W Adams 	/*
335346799e8SJonathan W Adams 	 * Until the array is sorted and stable, stacks_hash will be non-NULL.
336346799e8SJonathan W Adams 	 * This way, we can get at all of the data, even if qsort() was
337346799e8SJonathan W Adams 	 * interrupted while mucking with the array.
338346799e8SJonathan W Adams 	 */
339346799e8SJonathan W Adams 	if (stacks_hash != NULL) {
340346799e8SJonathan W Adams 		for (idx = 0; idx < STACKS_HSIZE; idx++) {
341346799e8SJonathan W Adams 			while ((cur = stacks_hash[idx]) != NULL) {
342346799e8SJonathan W Adams 				while ((next = cur->se_dup) != NULL) {
343346799e8SJonathan W Adams 					cur->se_dup = next->se_dup;
344346799e8SJonathan W Adams 					mdb_free(next,
345346799e8SJonathan W Adams 					    STACKS_ENTRY_SIZE(next->se_depth));
346346799e8SJonathan W Adams 				}
347346799e8SJonathan W Adams 				next = cur->se_next;
348346799e8SJonathan W Adams 				stacks_hash[idx] = next;
349346799e8SJonathan W Adams 				mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
350346799e8SJonathan W Adams 			}
351346799e8SJonathan W Adams 		}
352346799e8SJonathan W Adams 		if (stacks_array != NULL)
353346799e8SJonathan W Adams 			mdb_free(stacks_array,
354346799e8SJonathan W Adams 			    stacks_array_size * sizeof (*stacks_array));
355346799e8SJonathan W Adams 
35698144673SJosef 'Jeff' Sipek 		mdb_free(stacks_hash, STACKS_HSIZE * sizeof (*stacks_hash));
35798144673SJosef 'Jeff' Sipek 
358346799e8SJonathan W Adams 	} else if (stacks_array != NULL) {
359346799e8SJonathan W Adams 		for (idx = 0; idx < stacks_array_size; idx++) {
360346799e8SJonathan W Adams 			if ((cur = stacks_array[idx]) != NULL) {
361346799e8SJonathan W Adams 				while ((next = cur->se_dup) != NULL) {
362346799e8SJonathan W Adams 					cur->se_dup = next->se_dup;
363346799e8SJonathan W Adams 					mdb_free(next,
364346799e8SJonathan W Adams 					    STACKS_ENTRY_SIZE(next->se_depth));
365346799e8SJonathan W Adams 				}
366346799e8SJonathan W Adams 				stacks_array[idx] = NULL;
367346799e8SJonathan W Adams 				mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
368346799e8SJonathan W Adams 			}
369346799e8SJonathan W Adams 		}
370346799e8SJonathan W Adams 		mdb_free(stacks_array,
371346799e8SJonathan W Adams 		    stacks_array_size * sizeof (*stacks_array));
372346799e8SJonathan W Adams 	}
373346799e8SJonathan W Adams 
374c9a6ea2eSBryan Cantrill 	stacks_findstack_cleanup();
375c9a6ea2eSBryan Cantrill 
376346799e8SJonathan W Adams 	stacks_array_size = 0;
377346799e8SJonathan W Adams 	stacks_state = STACKS_STATE_CLEAN;
37898144673SJosef 'Jeff' Sipek 	stacks_hash = NULL;
37998144673SJosef 'Jeff' Sipek 	stacks_array = NULL;
380346799e8SJonathan W Adams }
381346799e8SJonathan W Adams 
382346799e8SJonathan W Adams /*ARGSUSED*/
383*ab618543SJohn Levon static int
stacks_thread_cb(uintptr_t addr,const void * ignored,void * cbarg)384346799e8SJonathan W Adams stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg)
385346799e8SJonathan W Adams {
386346799e8SJonathan W Adams 	stacks_info_t *sip = cbarg;
387346799e8SJonathan W Adams 	findstack_info_t *fsip = &sip->si_fsi;
388346799e8SJonathan W Adams 
389346799e8SJonathan W Adams 	stacks_entry_t **sepp, *nsep, *sep;
390346799e8SJonathan W Adams 	int idx;
391346799e8SJonathan W Adams 	size_t depth;
392346799e8SJonathan W Adams 
393c9a6ea2eSBryan Cantrill 	if (stacks_findstack(addr, fsip, 0) != DCMD_OK &&
394346799e8SJonathan W Adams 	    fsip->fsi_failed == FSI_FAIL_BADTHREAD) {
395346799e8SJonathan W Adams 		mdb_warn("couldn't read thread at %p\n", addr);
396346799e8SJonathan W Adams 		return (WALK_NEXT);
397346799e8SJonathan W Adams 	}
398346799e8SJonathan W Adams 
399346799e8SJonathan W Adams 	sip->si_count++;
400346799e8SJonathan W Adams 
401346799e8SJonathan W Adams 	depth = fsip->fsi_depth;
402346799e8SJonathan W Adams 	nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP);
403346799e8SJonathan W Adams 	nsep->se_thread = addr;
404346799e8SJonathan W Adams 	nsep->se_sp = fsip->fsi_sp;
405346799e8SJonathan W Adams 	nsep->se_sobj_ops = fsip->fsi_sobj_ops;
406346799e8SJonathan W Adams 	nsep->se_tstate = fsip->fsi_tstate;
407346799e8SJonathan W Adams 	nsep->se_count = 1;
408346799e8SJonathan W Adams 	nsep->se_overflow = fsip->fsi_overflow;
409346799e8SJonathan W Adams 	nsep->se_depth = depth;
410346799e8SJonathan W Adams 	nsep->se_failed = fsip->fsi_failed;
411346799e8SJonathan W Adams 	nsep->se_panic = fsip->fsi_panic;
412346799e8SJonathan W Adams 
413346799e8SJonathan W Adams 	for (idx = 0; idx < depth; idx++)
414346799e8SJonathan W Adams 		nsep->se_stack[idx] = fsip->fsi_stack[idx];
415346799e8SJonathan W Adams 
416346799e8SJonathan W Adams 	for (sepp = &sip->si_hash[stacks_hash_entry(nsep)];
417346799e8SJonathan W Adams 	    (sep = *sepp) != NULL;
418346799e8SJonathan W Adams 	    sepp = &sep->se_next) {
419346799e8SJonathan W Adams 
420346799e8SJonathan W Adams 		if (stacks_entry_comp_impl(sep, nsep, 0) != 0)
421346799e8SJonathan W Adams 			continue;
422346799e8SJonathan W Adams 
423346799e8SJonathan W Adams 		nsep->se_dup = sep->se_dup;
424346799e8SJonathan W Adams 		sep->se_dup = nsep;
425346799e8SJonathan W Adams 		sep->se_count++;
426346799e8SJonathan W Adams 		return (WALK_NEXT);
427346799e8SJonathan W Adams 	}
428346799e8SJonathan W Adams 
429346799e8SJonathan W Adams 	nsep->se_next = NULL;
430346799e8SJonathan W Adams 	*sepp = nsep;
431346799e8SJonathan W Adams 	sip->si_entries++;
432346799e8SJonathan W Adams 
433346799e8SJonathan W Adams 	return (WALK_NEXT);
434346799e8SJonathan W Adams }
435346799e8SJonathan W Adams 
436*ab618543SJohn Levon static int
stacks_run_tlist(mdb_pipe_t * tlist,stacks_info_t * si)437e0ad97e3SJonathan Adams stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si)
438e0ad97e3SJonathan Adams {
439e0ad97e3SJonathan Adams 	size_t idx;
440e0ad97e3SJonathan Adams 	size_t found = 0;
441e0ad97e3SJonathan Adams 	int ret;
442e0ad97e3SJonathan Adams 
443e0ad97e3SJonathan Adams 	for (idx = 0; idx < tlist->pipe_len; idx++) {
444e0ad97e3SJonathan Adams 		uintptr_t addr = tlist->pipe_data[idx];
445e0ad97e3SJonathan Adams 
446e0ad97e3SJonathan Adams 		found++;
447e0ad97e3SJonathan Adams 
448c9a6ea2eSBryan Cantrill 		ret = stacks_thread_cb(addr, NULL, si);
449e0ad97e3SJonathan Adams 		if (ret == WALK_DONE)
450e0ad97e3SJonathan Adams 			break;
451e0ad97e3SJonathan Adams 		if (ret != WALK_NEXT)
452e0ad97e3SJonathan Adams 			return (-1);
453e0ad97e3SJonathan Adams 	}
454e0ad97e3SJonathan Adams 
455e0ad97e3SJonathan Adams 	if (found)
456e0ad97e3SJonathan Adams 		return (0);
457e0ad97e3SJonathan Adams 	return (-1);
458e0ad97e3SJonathan Adams }
459e0ad97e3SJonathan Adams 
460*ab618543SJohn Levon static int
stacks_run(int verbose,mdb_pipe_t * tlist)461e0ad97e3SJonathan Adams stacks_run(int verbose, mdb_pipe_t *tlist)
462346799e8SJonathan W Adams {
463346799e8SJonathan W Adams 	stacks_info_t si;
464346799e8SJonathan W Adams 	findstack_info_t *fsip = &si.si_fsi;
465346799e8SJonathan W Adams 	size_t idx;
466346799e8SJonathan W Adams 	stacks_entry_t **cur;
467346799e8SJonathan W Adams 
468346799e8SJonathan W Adams 	bzero(&si, sizeof (si));
469346799e8SJonathan W Adams 
470346799e8SJonathan W Adams 	stacks_state = STACKS_STATE_DIRTY;
471346799e8SJonathan W Adams 
472346799e8SJonathan W Adams 	stacks_hash = si.si_hash =
473346799e8SJonathan W Adams 	    mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP);
474346799e8SJonathan W Adams 	si.si_entries = 0;
475346799e8SJonathan W Adams 	si.si_count = 0;
476346799e8SJonathan W Adams 
477346799e8SJonathan W Adams 	fsip->fsi_max_depth = STACKS_MAX_DEPTH;
478346799e8SJonathan W Adams 	fsip->fsi_stack =
479346799e8SJonathan W Adams 	    mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack),
480346799e8SJonathan W Adams 	    UM_SLEEP | UM_GC);
481346799e8SJonathan W Adams 
482346799e8SJonathan W Adams 	if (verbose)
483346799e8SJonathan W Adams 		mdb_warn("stacks: processing kernel threads\n");
484346799e8SJonathan W Adams 
485e0ad97e3SJonathan Adams 	if (tlist != NULL) {
486e0ad97e3SJonathan Adams 		if (stacks_run_tlist(tlist, &si))
487e0ad97e3SJonathan Adams 			return (DCMD_ERR);
488e0ad97e3SJonathan Adams 	} else {
489e0ad97e3SJonathan Adams 		if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
490e0ad97e3SJonathan Adams 			mdb_warn("cannot walk \"thread\"");
491e0ad97e3SJonathan Adams 			return (DCMD_ERR);
492e0ad97e3SJonathan Adams 		}
493346799e8SJonathan W Adams 	}
494346799e8SJonathan W Adams 
495346799e8SJonathan W Adams 	if (verbose)
496346799e8SJonathan W Adams 		mdb_warn("stacks: %d unique stacks / %d threads\n",
497346799e8SJonathan W Adams 		    si.si_entries, si.si_count);
498346799e8SJonathan W Adams 
499346799e8SJonathan W Adams 	stacks_array_size = si.si_entries;
500346799e8SJonathan W Adams 	stacks_array =
501346799e8SJonathan W Adams 	    mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP);
502346799e8SJonathan W Adams 	cur = stacks_array;
503346799e8SJonathan W Adams 	for (idx = 0; idx < STACKS_HSIZE; idx++) {
504346799e8SJonathan W Adams 		stacks_entry_t *sep;
505346799e8SJonathan W Adams 		for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next)
506346799e8SJonathan W Adams 			*(cur++) = sep;
507346799e8SJonathan W Adams 	}
508346799e8SJonathan W Adams 
509346799e8SJonathan W Adams 	if (cur != stacks_array + si.si_entries) {
510346799e8SJonathan W Adams 		mdb_warn("stacks: miscounted array size (%d != size: %d)\n",
511346799e8SJonathan W Adams 		    (cur - stacks_array), stacks_array_size);
512346799e8SJonathan W Adams 		return (DCMD_ERR);
513346799e8SJonathan W Adams 	}
514346799e8SJonathan W Adams 	qsort(stacks_array, si.si_entries, sizeof (*stacks_array),
515346799e8SJonathan W Adams 	    stacks_entry_comp);
516346799e8SJonathan W Adams 
517346799e8SJonathan W Adams 	/* Now that we're done, free the hash table */
518346799e8SJonathan W Adams 	stacks_hash = NULL;
519346799e8SJonathan W Adams 	mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash));
520346799e8SJonathan W Adams 
521e0ad97e3SJonathan Adams 	if (tlist == NULL)
522e0ad97e3SJonathan Adams 		stacks_state = STACKS_STATE_DONE;
523346799e8SJonathan W Adams 
524346799e8SJonathan W Adams 	if (verbose)
525346799e8SJonathan W Adams 		mdb_warn("stacks: done\n");
526346799e8SJonathan W Adams 
527346799e8SJonathan W Adams 	return (DCMD_OK);
528346799e8SJonathan W Adams }
529346799e8SJonathan W Adams 
530346799e8SJonathan W Adams static int
stacks_has_caller(stacks_entry_t * sep,uintptr_t addr)531346799e8SJonathan W Adams stacks_has_caller(stacks_entry_t *sep, uintptr_t addr)
532346799e8SJonathan W Adams {
533346799e8SJonathan W Adams 	uintptr_t laddr = addr;
534346799e8SJonathan W Adams 	uintptr_t haddr = addr + 1;
535346799e8SJonathan W Adams 	int idx;
536346799e8SJonathan W Adams 	char c[MDB_SYM_NAMLEN];
537346799e8SJonathan W Adams 	GElf_Sym sym;
538346799e8SJonathan W Adams 
539346799e8SJonathan W Adams 	if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY,
540346799e8SJonathan W Adams 	    c, sizeof (c), &sym) != -1 &&
541346799e8SJonathan W Adams 	    addr == (uintptr_t)sym.st_value) {
542346799e8SJonathan W Adams 		laddr = (uintptr_t)sym.st_value;
543346799e8SJonathan W Adams 		haddr = (uintptr_t)sym.st_value + sym.st_size;
544346799e8SJonathan W Adams 	}
545346799e8SJonathan W Adams 
546346799e8SJonathan W Adams 	for (idx = 0; idx < sep->se_depth; idx++)
547346799e8SJonathan W Adams 		if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr)
548346799e8SJonathan W Adams 			return (1);
549346799e8SJonathan W Adams 
550346799e8SJonathan W Adams 	return (0);
551346799e8SJonathan W Adams }
552346799e8SJonathan W Adams 
553c9a6ea2eSBryan Cantrill static int
stacks_has_module(stacks_entry_t * sep,stacks_module_t * mp)554c9a6ea2eSBryan Cantrill stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp)
555e6fc74c6SGreg Price {
556c9a6ea2eSBryan Cantrill 	int idx;
557e6fc74c6SGreg Price 
558c9a6ea2eSBryan Cantrill 	for (idx = 0; idx < sep->se_depth; idx++) {
559c9a6ea2eSBryan Cantrill 		if (sep->se_stack[idx] >= mp->sm_text &&
560c9a6ea2eSBryan Cantrill 		    sep->se_stack[idx] < mp->sm_text + mp->sm_size)
561c9a6ea2eSBryan Cantrill 			return (1);
562e6fc74c6SGreg Price 	}
563e6fc74c6SGreg Price 
564c9a6ea2eSBryan Cantrill 	return (0);
565e6fc74c6SGreg Price }
566e6fc74c6SGreg Price 
567e6fc74c6SGreg Price static int
stacks_module_find(const char * name,stacks_module_t * mp)568c9a6ea2eSBryan Cantrill stacks_module_find(const char *name, stacks_module_t *mp)
569e6fc74c6SGreg Price {
570c9a6ea2eSBryan Cantrill 	(void) strncpy(mp->sm_name, name, sizeof (mp->sm_name));
571e6fc74c6SGreg Price 
572c9a6ea2eSBryan Cantrill 	if (stacks_module(mp) != 0)
573c9a6ea2eSBryan Cantrill 		return (-1);
574c9a6ea2eSBryan Cantrill 
575c9a6ea2eSBryan Cantrill 	if (mp->sm_size == 0) {
576c9a6ea2eSBryan Cantrill 		mdb_warn("stacks: module \"%s\" is unknown\n", name);
577c9a6ea2eSBryan Cantrill 		return (-1);
578c9a6ea2eSBryan Cantrill 	}
579e6fc74c6SGreg Price 
580e6fc74c6SGreg Price 	return (0);
581e6fc74c6SGreg Price }
582e6fc74c6SGreg Price 
583346799e8SJonathan W Adams static int
uintptrcomp(const void * lp,const void * rp)584346799e8SJonathan W Adams uintptrcomp(const void *lp, const void *rp)
585346799e8SJonathan W Adams {
586346799e8SJonathan W Adams 	uintptr_t lhs = *(const uintptr_t *)lp;
587346799e8SJonathan W Adams 	uintptr_t rhs = *(const uintptr_t *)rp;
588346799e8SJonathan W Adams 	if (lhs > rhs)
589346799e8SJonathan W Adams 		return (1);
590346799e8SJonathan W Adams 	if (lhs < rhs)
591346799e8SJonathan W Adams 		return (-1);
592346799e8SJonathan W Adams 	return (0);
593346799e8SJonathan W Adams }
594346799e8SJonathan W Adams 
595346799e8SJonathan W Adams /*ARGSUSED*/
596346799e8SJonathan W Adams int
stacks(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)597346799e8SJonathan W Adams stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
598346799e8SJonathan W Adams {
599346799e8SJonathan W Adams 	size_t idx;
600346799e8SJonathan W Adams 
601346799e8SJonathan W Adams 	char *seen = NULL;
602346799e8SJonathan W Adams 
603346799e8SJonathan W Adams 	const char *caller_str = NULL;
604346799e8SJonathan W Adams 	const char *excl_caller_str = NULL;
605346799e8SJonathan W Adams 	uintptr_t caller = 0, excl_caller = 0;
606e6fc74c6SGreg Price 	const char *module_str = NULL;
607e6fc74c6SGreg Price 	const char *excl_module_str = NULL;
608c9a6ea2eSBryan Cantrill 	stacks_module_t module, excl_module;
609346799e8SJonathan W Adams 	const char *sobj = NULL;
610346799e8SJonathan W Adams 	const char *excl_sobj = NULL;
611346799e8SJonathan W Adams 	uintptr_t sobj_ops = 0, excl_sobj_ops = 0;
612346799e8SJonathan W Adams 	const char *tstate_str = NULL;
613346799e8SJonathan W Adams 	const char *excl_tstate_str = NULL;
614346799e8SJonathan W Adams 	uint_t tstate = -1U;
615346799e8SJonathan W Adams 	uint_t excl_tstate = -1U;
616e0ad97e3SJonathan Adams 	uint_t printed = 0;
617346799e8SJonathan W Adams 
618346799e8SJonathan W Adams 	uint_t all = 0;
619346799e8SJonathan W Adams 	uint_t force = 0;
620346799e8SJonathan W Adams 	uint_t interesting = 0;
621346799e8SJonathan W Adams 	uint_t verbose = 0;
622346799e8SJonathan W Adams 
623346799e8SJonathan W Adams 	/*
624346799e8SJonathan W Adams 	 * We have a slight behavior difference between having piped
625346799e8SJonathan W Adams 	 * input and 'addr::stacks'.  Without a pipe, we assume the
626346799e8SJonathan W Adams 	 * thread pointer given is a representative thread, and so
627346799e8SJonathan W Adams 	 * we include all similar threads in the system in our output.
628346799e8SJonathan W Adams 	 *
629346799e8SJonathan W Adams 	 * With a pipe, we filter down to just the threads in our
630346799e8SJonathan W Adams 	 * input.
631346799e8SJonathan W Adams 	 */
632346799e8SJonathan W Adams 	uint_t addrspec = (flags & DCMD_ADDRSPEC);
633346799e8SJonathan W Adams 	uint_t only_matching = addrspec && (flags & DCMD_PIPE);
634346799e8SJonathan W Adams 
635346799e8SJonathan W Adams 	mdb_pipe_t p;
636346799e8SJonathan W Adams 
637c9a6ea2eSBryan Cantrill 	bzero(&module, sizeof (module));
638c9a6ea2eSBryan Cantrill 	bzero(&excl_module, sizeof (excl_module));
639c9a6ea2eSBryan Cantrill 
640346799e8SJonathan W Adams 	if (mdb_getopts(argc, argv,
641346799e8SJonathan W Adams 	    'a', MDB_OPT_SETBITS, TRUE, &all,
642346799e8SJonathan W Adams 	    'f', MDB_OPT_SETBITS, TRUE, &force,
643346799e8SJonathan W Adams 	    'i', MDB_OPT_SETBITS, TRUE, &interesting,
644346799e8SJonathan W Adams 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
645346799e8SJonathan W Adams 	    'c', MDB_OPT_STR, &caller_str,
646346799e8SJonathan W Adams 	    'C', MDB_OPT_STR, &excl_caller_str,
647e6fc74c6SGreg Price 	    'm', MDB_OPT_STR, &module_str,
648e6fc74c6SGreg Price 	    'M', MDB_OPT_STR, &excl_module_str,
649346799e8SJonathan W Adams 	    's', MDB_OPT_STR, &sobj,
650346799e8SJonathan W Adams 	    'S', MDB_OPT_STR, &excl_sobj,
651346799e8SJonathan W Adams 	    't', MDB_OPT_STR, &tstate_str,
652346799e8SJonathan W Adams 	    'T', MDB_OPT_STR, &excl_tstate_str,
653346799e8SJonathan W Adams 	    NULL) != argc)
654346799e8SJonathan W Adams 		return (DCMD_USAGE);
655346799e8SJonathan W Adams 
656346799e8SJonathan W Adams 	if (interesting) {
657346799e8SJonathan W Adams 		if (sobj != NULL || excl_sobj != NULL ||
658346799e8SJonathan W Adams 		    tstate_str != NULL || excl_tstate_str != NULL) {
659346799e8SJonathan W Adams 			mdb_warn(
660346799e8SJonathan W Adams 			    "stacks: -i is incompatible with -[sStT]\n");
661346799e8SJonathan W Adams 			return (DCMD_USAGE);
662346799e8SJonathan W Adams 		}
663346799e8SJonathan W Adams 		excl_sobj = "CV";
664346799e8SJonathan W Adams 		excl_tstate_str = "FREE";
665346799e8SJonathan W Adams 	}
666346799e8SJonathan W Adams 
667346799e8SJonathan W Adams 	if (caller_str != NULL) {
668346799e8SJonathan W Adams 		mdb_set_dot(0);
669346799e8SJonathan W Adams 		if (mdb_eval(caller_str) != 0) {
670346799e8SJonathan W Adams 			mdb_warn("stacks: evaluation of \"%s\" failed",
671346799e8SJonathan W Adams 			    caller_str);
672346799e8SJonathan W Adams 			return (DCMD_ABORT);
673346799e8SJonathan W Adams 		}
674346799e8SJonathan W Adams 		caller = mdb_get_dot();
675346799e8SJonathan W Adams 	}
676346799e8SJonathan W Adams 
677346799e8SJonathan W Adams 	if (excl_caller_str != NULL) {
678346799e8SJonathan W Adams 		mdb_set_dot(0);
679346799e8SJonathan W Adams 		if (mdb_eval(excl_caller_str) != 0) {
680346799e8SJonathan W Adams 			mdb_warn("stacks: evaluation of \"%s\" failed",
681346799e8SJonathan W Adams 			    excl_caller_str);
682346799e8SJonathan W Adams 			return (DCMD_ABORT);
683346799e8SJonathan W Adams 		}
684346799e8SJonathan W Adams 		excl_caller = mdb_get_dot();
685346799e8SJonathan W Adams 	}
686346799e8SJonathan W Adams 	mdb_set_dot(addr);
687346799e8SJonathan W Adams 
688c9a6ea2eSBryan Cantrill 	if (module_str != NULL && stacks_module_find(module_str, &module) != 0)
689e6fc74c6SGreg Price 		return (DCMD_ABORT);
690e6fc74c6SGreg Price 
691e6fc74c6SGreg Price 	if (excl_module_str != NULL &&
692c9a6ea2eSBryan Cantrill 	    stacks_module_find(excl_module_str, &excl_module) != 0)
693e6fc74c6SGreg Price 		return (DCMD_ABORT);
694e6fc74c6SGreg Price 
695c9a6ea2eSBryan Cantrill 	if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0)
696346799e8SJonathan W Adams 		return (DCMD_USAGE);
697346799e8SJonathan W Adams 
698c9a6ea2eSBryan Cantrill 	if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
699346799e8SJonathan W Adams 		return (DCMD_USAGE);
700346799e8SJonathan W Adams 
701346799e8SJonathan W Adams 	if (sobj_ops != 0 && excl_sobj_ops != 0) {
702346799e8SJonathan W Adams 		mdb_warn("stacks: only one of -s and -S can be specified\n");
703346799e8SJonathan W Adams 		return (DCMD_USAGE);
704346799e8SJonathan W Adams 	}
705346799e8SJonathan W Adams 
706c9a6ea2eSBryan Cantrill 	if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0)
707346799e8SJonathan W Adams 		return (DCMD_USAGE);
708e6fc74c6SGreg Price 
709e6fc74c6SGreg Price 	if (excl_tstate_str != NULL &&
710346799e8SJonathan W Adams 	    text_to_tstate(excl_tstate_str, &excl_tstate) != 0)
711346799e8SJonathan W Adams 		return (DCMD_USAGE);
712346799e8SJonathan W Adams 
713346799e8SJonathan W Adams 	if (tstate != -1U && excl_tstate != -1U) {
714346799e8SJonathan W Adams 		mdb_warn("stacks: only one of -t and -T can be specified\n");
715346799e8SJonathan W Adams 		return (DCMD_USAGE);
716346799e8SJonathan W Adams 	}
717346799e8SJonathan W Adams 
718346799e8SJonathan W Adams 	/*
719346799e8SJonathan W Adams 	 * If there's an address specified, we're going to further filter
720346799e8SJonathan W Adams 	 * to only entries which have an address in the input.  To reduce
721346799e8SJonathan W Adams 	 * overhead (and make the sorted output come out right), we
722346799e8SJonathan W Adams 	 * use mdb_get_pipe() to grab the entire pipeline of input, then
723346799e8SJonathan W Adams 	 * use qsort() and bsearch() to speed up the search.
724346799e8SJonathan W Adams 	 */
725346799e8SJonathan W Adams 	if (addrspec) {
726346799e8SJonathan W Adams 		mdb_get_pipe(&p);
727346799e8SJonathan W Adams 		if (p.pipe_data == NULL || p.pipe_len == 0) {
728346799e8SJonathan W Adams 			p.pipe_data = &addr;
729346799e8SJonathan W Adams 			p.pipe_len = 1;
730346799e8SJonathan W Adams 		}
731346799e8SJonathan W Adams 		qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t),
732346799e8SJonathan W Adams 		    uintptrcomp);
733346799e8SJonathan W Adams 
734346799e8SJonathan W Adams 		/* remove any duplicates in the data */
735346799e8SJonathan W Adams 		idx = 0;
736346799e8SJonathan W Adams 		while (idx < p.pipe_len - 1) {
737346799e8SJonathan W Adams 			uintptr_t *data = &p.pipe_data[idx];
738346799e8SJonathan W Adams 			size_t len = p.pipe_len - idx;
739346799e8SJonathan W Adams 
740346799e8SJonathan W Adams 			if (data[0] == data[1]) {
741346799e8SJonathan W Adams 				memmove(data, data + 1,
742346799e8SJonathan W Adams 				    (len - 1) * sizeof (*data));
743346799e8SJonathan W Adams 				p.pipe_len--;
744346799e8SJonathan W Adams 				continue; /* repeat without incrementing idx */
745346799e8SJonathan W Adams 			}
746346799e8SJonathan W Adams 			idx++;
747346799e8SJonathan W Adams 		}
748346799e8SJonathan W Adams 
749346799e8SJonathan W Adams 		seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC);
750346799e8SJonathan W Adams 	}
751346799e8SJonathan W Adams 
752e0ad97e3SJonathan Adams 	/*
753e0ad97e3SJonathan Adams 	 * Force a cleanup if we're connected to a live system. Never
754e0ad97e3SJonathan Adams 	 * do a cleanup after the first invocation around the loop.
755e0ad97e3SJonathan Adams 	 */
756e0ad97e3SJonathan Adams 	force |= (mdb_get_state() == MDB_STATE_RUNNING);
757e0ad97e3SJonathan Adams 	if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
758e0ad97e3SJonathan Adams 		force = 0;
759e0ad97e3SJonathan Adams 
760e0ad97e3SJonathan Adams 	stacks_cleanup(force);
761e0ad97e3SJonathan Adams 
762e0ad97e3SJonathan Adams 	if (stacks_state == STACKS_STATE_CLEAN) {
763e0ad97e3SJonathan Adams 		int res = stacks_run(verbose, addrspec ? &p : NULL);
764e0ad97e3SJonathan Adams 		if (res != DCMD_OK)
765e0ad97e3SJonathan Adams 			return (res);
766e0ad97e3SJonathan Adams 	}
767e0ad97e3SJonathan Adams 
768346799e8SJonathan W Adams 	for (idx = 0; idx < stacks_array_size; idx++) {
769346799e8SJonathan W Adams 		stacks_entry_t *sep = stacks_array[idx];
770346799e8SJonathan W Adams 		stacks_entry_t *cur = sep;
771346799e8SJonathan W Adams 		int frame;
772346799e8SJonathan W Adams 		size_t count = sep->se_count;
773346799e8SJonathan W Adams 
774346799e8SJonathan W Adams 		if (addrspec) {
775346799e8SJonathan W Adams 			stacks_entry_t *head = NULL, *tail = NULL, *sp;
776346799e8SJonathan W Adams 			size_t foundcount = 0;
777346799e8SJonathan W Adams 			/*
778346799e8SJonathan W Adams 			 * We use the now-unused hash chain field se_next to
779346799e8SJonathan W Adams 			 * link together the dups which match our list.
780346799e8SJonathan W Adams 			 */
781346799e8SJonathan W Adams 			for (sp = sep; sp != NULL; sp = sp->se_dup) {
782346799e8SJonathan W Adams 				uintptr_t *entry = bsearch(&sp->se_thread,
783346799e8SJonathan W Adams 				    p.pipe_data, p.pipe_len, sizeof (uintptr_t),
784346799e8SJonathan W Adams 				    uintptrcomp);
785346799e8SJonathan W Adams 				if (entry != NULL) {
786346799e8SJonathan W Adams 					foundcount++;
787346799e8SJonathan W Adams 					seen[entry - p.pipe_data]++;
788346799e8SJonathan W Adams 					if (head == NULL)
789346799e8SJonathan W Adams 						head = sp;
790346799e8SJonathan W Adams 					else
791346799e8SJonathan W Adams 						tail->se_next = sp;
792346799e8SJonathan W Adams 					tail = sp;
793346799e8SJonathan W Adams 					sp->se_next = NULL;
794346799e8SJonathan W Adams 				}
795346799e8SJonathan W Adams 			}
796346799e8SJonathan W Adams 			if (head == NULL)
797346799e8SJonathan W Adams 				continue;	/* no match, skip entry */
798346799e8SJonathan W Adams 
799346799e8SJonathan W Adams 			if (only_matching) {
800346799e8SJonathan W Adams 				cur = sep = head;
801346799e8SJonathan W Adams 				count = foundcount;
802346799e8SJonathan W Adams 			}
803346799e8SJonathan W Adams 		}
804346799e8SJonathan W Adams 
805346799e8SJonathan W Adams 		if (caller != 0 && !stacks_has_caller(sep, caller))
806346799e8SJonathan W Adams 			continue;
807c9a6ea2eSBryan Cantrill 
808346799e8SJonathan W Adams 		if (excl_caller != 0 && stacks_has_caller(sep, excl_caller))
809346799e8SJonathan W Adams 			continue;
810c9a6ea2eSBryan Cantrill 
811c9a6ea2eSBryan Cantrill 		if (module.sm_size != 0 && !stacks_has_module(sep, &module))
812e6fc74c6SGreg Price 			continue;
813c9a6ea2eSBryan Cantrill 
814c9a6ea2eSBryan Cantrill 		if (excl_module.sm_size != 0 &&
815c9a6ea2eSBryan Cantrill 		    stacks_has_module(sep, &excl_module))
816e6fc74c6SGreg Price 			continue;
817346799e8SJonathan W Adams 
818346799e8SJonathan W Adams 		if (tstate != -1U) {
819346799e8SJonathan W Adams 			if (tstate == TSTATE_PANIC) {
820346799e8SJonathan W Adams 				if (!sep->se_panic)
821346799e8SJonathan W Adams 					continue;
822346799e8SJonathan W Adams 			} else if (sep->se_panic || sep->se_tstate != tstate)
823346799e8SJonathan W Adams 				continue;
824346799e8SJonathan W Adams 		}
825346799e8SJonathan W Adams 		if (excl_tstate != -1U) {
826346799e8SJonathan W Adams 			if (excl_tstate == TSTATE_PANIC) {
827346799e8SJonathan W Adams 				if (sep->se_panic)
828346799e8SJonathan W Adams 					continue;
829346799e8SJonathan W Adams 			} else if (!sep->se_panic &&
830346799e8SJonathan W Adams 			    sep->se_tstate == excl_tstate)
831346799e8SJonathan W Adams 				continue;
832346799e8SJonathan W Adams 		}
833346799e8SJonathan W Adams 
834346799e8SJonathan W Adams 		if (sobj_ops == SOBJ_ALL) {
835346799e8SJonathan W Adams 			if (sep->se_sobj_ops == 0)
836346799e8SJonathan W Adams 				continue;
837346799e8SJonathan W Adams 		} else if (sobj_ops != 0) {
838346799e8SJonathan W Adams 			if (sobj_ops != sep->se_sobj_ops)
839346799e8SJonathan W Adams 				continue;
840346799e8SJonathan W Adams 		}
841346799e8SJonathan W Adams 
842346799e8SJonathan W Adams 		if (!(interesting && sep->se_panic)) {
843346799e8SJonathan W Adams 			if (excl_sobj_ops == SOBJ_ALL) {
844346799e8SJonathan W Adams 				if (sep->se_sobj_ops != 0)
845346799e8SJonathan W Adams 					continue;
846346799e8SJonathan W Adams 			} else if (excl_sobj_ops != 0) {
847346799e8SJonathan W Adams 				if (excl_sobj_ops == sep->se_sobj_ops)
848346799e8SJonathan W Adams 					continue;
849346799e8SJonathan W Adams 			}
850346799e8SJonathan W Adams 		}
851346799e8SJonathan W Adams 
852346799e8SJonathan W Adams 		if (flags & DCMD_PIPE_OUT) {
853346799e8SJonathan W Adams 			while (sep != NULL) {
854346799e8SJonathan W Adams 				mdb_printf("%lr\n", sep->se_thread);
855346799e8SJonathan W Adams 				sep = only_matching ?
856346799e8SJonathan W Adams 				    sep->se_next : sep->se_dup;
857346799e8SJonathan W Adams 			}
858346799e8SJonathan W Adams 			continue;
859346799e8SJonathan W Adams 		}
860346799e8SJonathan W Adams 
861e0ad97e3SJonathan Adams 		if (all || !printed) {
862346799e8SJonathan W Adams 			mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
863e0ad97e3SJonathan Adams 			    "THREAD", "STATE", "SOBJ", "COUNT");
864e0ad97e3SJonathan Adams 			printed = 1;
865346799e8SJonathan W Adams 		}
866346799e8SJonathan W Adams 
867346799e8SJonathan W Adams 		do {
868346799e8SJonathan W Adams 			char state[20];
869346799e8SJonathan W Adams 			char sobj[100];
870346799e8SJonathan W Adams 
871346799e8SJonathan W Adams 			tstate_to_text(cur->se_tstate, cur->se_panic,
872346799e8SJonathan W Adams 			    state, sizeof (state));
873346799e8SJonathan W Adams 			sobj_to_text(cur->se_sobj_ops,
874346799e8SJonathan W Adams 			    sobj, sizeof (sobj));
875346799e8SJonathan W Adams 
876346799e8SJonathan W Adams 			if (cur == sep)
877c9a6ea2eSBryan Cantrill 				mdb_printf("%-?p %-8s %-?s %8d\n",
878346799e8SJonathan W Adams 				    cur->se_thread, state, sobj, count);
879346799e8SJonathan W Adams 			else
880c9a6ea2eSBryan Cantrill 				mdb_printf("%-?p %-8s %-?s %8s\n",
881346799e8SJonathan W Adams 				    cur->se_thread, state, sobj, "-");
882346799e8SJonathan W Adams 
883346799e8SJonathan W Adams 			cur = only_matching ? cur->se_next : cur->se_dup;
884346799e8SJonathan W Adams 		} while (all && cur != NULL);
885346799e8SJonathan W Adams 
886346799e8SJonathan W Adams 		if (sep->se_failed != 0) {
887346799e8SJonathan W Adams 			char *reason;
888346799e8SJonathan W Adams 			switch (sep->se_failed) {
889346799e8SJonathan W Adams 			case FSI_FAIL_NOTINMEMORY:
890346799e8SJonathan W Adams 				reason = "thread not in memory";
891346799e8SJonathan W Adams 				break;
892346799e8SJonathan W Adams 			case FSI_FAIL_THREADCORRUPT:
893346799e8SJonathan W Adams 				reason = "thread structure stack info corrupt";
894346799e8SJonathan W Adams 				break;
895346799e8SJonathan W Adams 			case FSI_FAIL_STACKNOTFOUND:
896346799e8SJonathan W Adams 				reason = "no consistent stack found";
897346799e8SJonathan W Adams 				break;
898346799e8SJonathan W Adams 			default:
899346799e8SJonathan W Adams 				reason = "unknown failure";
900346799e8SJonathan W Adams 				break;
901346799e8SJonathan W Adams 			}
902346799e8SJonathan W Adams 			mdb_printf("%?s <%s>\n", "", reason);
903346799e8SJonathan W Adams 		}
904346799e8SJonathan W Adams 
905346799e8SJonathan W Adams 		for (frame = 0; frame < sep->se_depth; frame++)
906346799e8SJonathan W Adams 			mdb_printf("%?s %a\n", "", sep->se_stack[frame]);
907346799e8SJonathan W Adams 		if (sep->se_overflow)
908346799e8SJonathan W Adams 			mdb_printf("%?s ... truncated ...\n", "");
909346799e8SJonathan W Adams 		mdb_printf("\n");
910346799e8SJonathan W Adams 	}
911346799e8SJonathan W Adams 
912346799e8SJonathan W Adams 	if (flags & DCMD_ADDRSPEC) {
913346799e8SJonathan W Adams 		for (idx = 0; idx < p.pipe_len; idx++)
914346799e8SJonathan W Adams 			if (seen[idx] == 0)
915346799e8SJonathan W Adams 				mdb_warn("stacks: %p not in thread list\n",
916346799e8SJonathan W Adams 				    p.pipe_data[idx]);
917346799e8SJonathan W Adams 	}
9187c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
9197c478bd9Sstevel@tonic-gate }
920