xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/findstack.c (revision 12902:3bb859a7330c)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
58721SJonathan.Adams@Sun.COM  * Common Development and Distribution License (the "License").
68721SJonathan.Adams@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*12902SBryan.Cantrill@Sun.COM 
220Sstevel@tonic-gate /*
23*12902SBryan.Cantrill@Sun.COM  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
270Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/regset.h>
310Sstevel@tonic-gate #include <sys/stack.h>
320Sstevel@tonic-gate #include <sys/thread.h>
338742Sgap@sun.com #include <sys/modctl.h>
34*12902SBryan.Cantrill@Sun.COM #include <assert.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include "findstack.h"
378721SJonathan.Adams@Sun.COM #include "thread.h"
388721SJonathan.Adams@Sun.COM #include "sobj.h"
398721SJonathan.Adams@Sun.COM 
40*12902SBryan.Cantrill@Sun.COM int findstack_debug_on = 0;
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * "sp" is a kernel VA.
440Sstevel@tonic-gate  */
450Sstevel@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)460Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr,
470Sstevel@tonic-gate     int argc, const mdb_arg_t *argv, int free_state)
480Sstevel@tonic-gate {
490Sstevel@tonic-gate 	int showargs = 0, count, err;
500Sstevel@tonic-gate 
510Sstevel@tonic-gate 	count = mdb_getopts(argc, argv,
520Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL);
530Sstevel@tonic-gate 	argc -= count;
540Sstevel@tonic-gate 	argv += count;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate 	if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING))
570Sstevel@tonic-gate 		return (DCMD_USAGE);
580Sstevel@tonic-gate 
590Sstevel@tonic-gate 	mdb_printf("stack pointer for thread %p%s: %p\n",
600Sstevel@tonic-gate 	    addr, (free_state ? " (TS_FREE)" : ""), sp);
610Sstevel@tonic-gate 	if (pc != 0)
620Sstevel@tonic-gate 		mdb_printf("[ %0?lr %a() ]\n", sp, pc);
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	mdb_inc_indent(2);
650Sstevel@tonic-gate 	mdb_set_dot(sp);
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	if (argc == 1)
680Sstevel@tonic-gate 		err = mdb_eval(argv->a_un.a_str);
690Sstevel@tonic-gate 	else if (showargs)
700Sstevel@tonic-gate 		err = mdb_eval("<.$C");
710Sstevel@tonic-gate 	else
720Sstevel@tonic-gate 		err = mdb_eval("<.$C0");
730Sstevel@tonic-gate 
740Sstevel@tonic-gate 	mdb_dec_indent(2);
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 	return ((err == -1) ? DCMD_ABORT : DCMD_OK);
770Sstevel@tonic-gate }
780Sstevel@tonic-gate 
798721SJonathan.Adams@Sun.COM int
findstack(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)808721SJonathan.Adams@Sun.COM findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
818721SJonathan.Adams@Sun.COM {
828721SJonathan.Adams@Sun.COM 	findstack_info_t fsi;
838721SJonathan.Adams@Sun.COM 	int retval;
848721SJonathan.Adams@Sun.COM 
858721SJonathan.Adams@Sun.COM 	if (!(flags & DCMD_ADDRSPEC))
868721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
878721SJonathan.Adams@Sun.COM 
888721SJonathan.Adams@Sun.COM 	bzero(&fsi, sizeof (fsi));
898721SJonathan.Adams@Sun.COM 
90*12902SBryan.Cantrill@Sun.COM 	if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK ||
918721SJonathan.Adams@Sun.COM 	    fsi.fsi_failed)
928721SJonathan.Adams@Sun.COM 		return (retval);
938721SJonathan.Adams@Sun.COM 
948721SJonathan.Adams@Sun.COM 	return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr,
958721SJonathan.Adams@Sun.COM 	    argc, argv, fsi.fsi_tstate == TS_FREE));
968721SJonathan.Adams@Sun.COM }
978721SJonathan.Adams@Sun.COM 
980Sstevel@tonic-gate /*ARGSUSED*/
990Sstevel@tonic-gate int
findstack_debug(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * av)1000Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av)
1010Sstevel@tonic-gate {
1020Sstevel@tonic-gate 	findstack_debug_on ^= 1;
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 	mdb_printf("findstack: debugging is now %s\n",
1050Sstevel@tonic-gate 	    findstack_debug_on ? "on" : "off");
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	return (DCMD_OK);
1080Sstevel@tonic-gate }
1090Sstevel@tonic-gate 
1108721SJonathan.Adams@Sun.COM static void
uppercase(char * p)1118721SJonathan.Adams@Sun.COM uppercase(char *p)
1128721SJonathan.Adams@Sun.COM {
1138721SJonathan.Adams@Sun.COM 	for (; *p != '\0'; p++) {
1148721SJonathan.Adams@Sun.COM 		if (*p >= 'a' && *p <= 'z')
1158721SJonathan.Adams@Sun.COM 			*p += 'A' - 'a';
1168721SJonathan.Adams@Sun.COM 	}
1178721SJonathan.Adams@Sun.COM }
1188721SJonathan.Adams@Sun.COM 
1198721SJonathan.Adams@Sun.COM static void
sobj_to_text(uintptr_t addr,char * out,size_t out_sz)1208721SJonathan.Adams@Sun.COM sobj_to_text(uintptr_t addr, char *out, size_t out_sz)
1218721SJonathan.Adams@Sun.COM {
1228721SJonathan.Adams@Sun.COM 	sobj_ops_to_text(addr, out, out_sz);
1238721SJonathan.Adams@Sun.COM 	uppercase(out);
1248721SJonathan.Adams@Sun.COM }
1258721SJonathan.Adams@Sun.COM 
1268721SJonathan.Adams@Sun.COM #define	SOBJ_ALL	1
127*12902SBryan.Cantrill@Sun.COM 
1288721SJonathan.Adams@Sun.COM static int
text_to_sobj(const char * text,uintptr_t * out)1298721SJonathan.Adams@Sun.COM text_to_sobj(const char *text, uintptr_t *out)
1308721SJonathan.Adams@Sun.COM {
1318721SJonathan.Adams@Sun.COM 	if (strcasecmp(text, "ALL") == 0) {
1328721SJonathan.Adams@Sun.COM 		*out = SOBJ_ALL;
1338721SJonathan.Adams@Sun.COM 		return (0);
1348721SJonathan.Adams@Sun.COM 	}
135*12902SBryan.Cantrill@Sun.COM 
1368721SJonathan.Adams@Sun.COM 	return (sobj_text_to_ops(text, out));
1378721SJonathan.Adams@Sun.COM }
1388721SJonathan.Adams@Sun.COM 
1398721SJonathan.Adams@Sun.COM #define	TSTATE_PANIC	-2U
1408721SJonathan.Adams@Sun.COM static int
text_to_tstate(const char * text,uint_t * out)1418721SJonathan.Adams@Sun.COM text_to_tstate(const char *text, uint_t *out)
1428721SJonathan.Adams@Sun.COM {
1438721SJonathan.Adams@Sun.COM 	if (strcasecmp(text, "panic") == 0)
1448721SJonathan.Adams@Sun.COM 		*out = TSTATE_PANIC;
1458721SJonathan.Adams@Sun.COM 	else if (thread_text_to_state(text, out) != 0) {
1468721SJonathan.Adams@Sun.COM 		mdb_warn("tstate \"%s\" not recognized\n", text);
1478721SJonathan.Adams@Sun.COM 		return (-1);
1488721SJonathan.Adams@Sun.COM 	}
1498721SJonathan.Adams@Sun.COM 	return (0);
1508721SJonathan.Adams@Sun.COM }
1518721SJonathan.Adams@Sun.COM 
1528721SJonathan.Adams@Sun.COM static void
tstate_to_text(uint_t tstate,uint_t paniced,char * out,size_t out_sz)1538721SJonathan.Adams@Sun.COM tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz)
1548721SJonathan.Adams@Sun.COM {
1558721SJonathan.Adams@Sun.COM 	if (paniced)
1568721SJonathan.Adams@Sun.COM 		mdb_snprintf(out, out_sz, "panic");
1578721SJonathan.Adams@Sun.COM 	else
1588721SJonathan.Adams@Sun.COM 		thread_state_to_text(tstate, out, out_sz);
1598721SJonathan.Adams@Sun.COM 	uppercase(out);
1608721SJonathan.Adams@Sun.COM }
1618721SJonathan.Adams@Sun.COM 
1628721SJonathan.Adams@Sun.COM typedef struct stacks_entry {
1638721SJonathan.Adams@Sun.COM 	struct stacks_entry	*se_next;
1648721SJonathan.Adams@Sun.COM 	struct stacks_entry	*se_dup;	/* dups of this stack */
1658721SJonathan.Adams@Sun.COM 	uintptr_t		se_thread;
1668721SJonathan.Adams@Sun.COM 	uintptr_t		se_sp;
1678721SJonathan.Adams@Sun.COM 	uintptr_t		se_sobj_ops;
1688721SJonathan.Adams@Sun.COM 	uint32_t		se_tstate;
1698721SJonathan.Adams@Sun.COM 	uint32_t		se_count;	/* # threads w/ this stack */
1708721SJonathan.Adams@Sun.COM 	uint8_t			se_overflow;
1718721SJonathan.Adams@Sun.COM 	uint8_t			se_depth;
1728721SJonathan.Adams@Sun.COM 	uint8_t			se_failed;	/* failure reason; FSI_FAIL_* */
1738721SJonathan.Adams@Sun.COM 	uint8_t			se_panic;
1748721SJonathan.Adams@Sun.COM 	uintptr_t		se_stack[1];
1758721SJonathan.Adams@Sun.COM } stacks_entry_t;
1768721SJonathan.Adams@Sun.COM #define	STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)])
1778721SJonathan.Adams@Sun.COM 
1788721SJonathan.Adams@Sun.COM #define	STACKS_HSIZE 127
1798721SJonathan.Adams@Sun.COM 
1808721SJonathan.Adams@Sun.COM /* Maximum stack depth reported in stacks */
1818721SJonathan.Adams@Sun.COM #define	STACKS_MAX_DEPTH	254
1828721SJonathan.Adams@Sun.COM 
1838721SJonathan.Adams@Sun.COM typedef struct stacks_info {
1848721SJonathan.Adams@Sun.COM 	size_t		si_count;	/* total stacks_entry_ts (incl dups) */
1858721SJonathan.Adams@Sun.COM 	size_t		si_entries;	/* # entries in hash table */
1868721SJonathan.Adams@Sun.COM 	stacks_entry_t	**si_hash;	/* hash table */
1878721SJonathan.Adams@Sun.COM 	findstack_info_t si_fsi;	/* transient callback state */
1888721SJonathan.Adams@Sun.COM } stacks_info_t;
1898721SJonathan.Adams@Sun.COM 
1908721SJonathan.Adams@Sun.COM /* global state cached between invocations */
1918721SJonathan.Adams@Sun.COM #define	STACKS_STATE_CLEAN	0
1928721SJonathan.Adams@Sun.COM #define	STACKS_STATE_DIRTY	1
1938721SJonathan.Adams@Sun.COM #define	STACKS_STATE_DONE	2
1948721SJonathan.Adams@Sun.COM static uint_t stacks_state = STACKS_STATE_CLEAN;
1958721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_hash;
1968721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_array;
1978721SJonathan.Adams@Sun.COM static size_t stacks_array_size;
1988721SJonathan.Adams@Sun.COM 
1998721SJonathan.Adams@Sun.COM size_t
stacks_hash_entry(stacks_entry_t * sep)2008721SJonathan.Adams@Sun.COM stacks_hash_entry(stacks_entry_t *sep)
2018721SJonathan.Adams@Sun.COM {
2028721SJonathan.Adams@Sun.COM 	size_t depth = sep->se_depth;
2038721SJonathan.Adams@Sun.COM 	uintptr_t *stack = sep->se_stack;
2048721SJonathan.Adams@Sun.COM 
2058721SJonathan.Adams@Sun.COM 	uint64_t total = depth;
2068721SJonathan.Adams@Sun.COM 
2078721SJonathan.Adams@Sun.COM 	while (depth > 0) {
2088721SJonathan.Adams@Sun.COM 		total += *stack;
2098721SJonathan.Adams@Sun.COM 		stack++; depth--;
2108721SJonathan.Adams@Sun.COM 	}
2118721SJonathan.Adams@Sun.COM 
2128721SJonathan.Adams@Sun.COM 	return (total % STACKS_HSIZE);
2138721SJonathan.Adams@Sun.COM }
2148721SJonathan.Adams@Sun.COM 
2158721SJonathan.Adams@Sun.COM /*
2168721SJonathan.Adams@Sun.COM  * This is used to both compare stacks for equality and to sort the final
2178721SJonathan.Adams@Sun.COM  * list of unique stacks.  forsort specifies the latter behavior, which
2188721SJonathan.Adams@Sun.COM  * additionally:
2198721SJonathan.Adams@Sun.COM  *	compares se_count, and
2208721SJonathan.Adams@Sun.COM  *	sorts the stacks by text function name.
2218721SJonathan.Adams@Sun.COM  *
2228721SJonathan.Adams@Sun.COM  * The equality test is independent of se_count, and doesn't care about
2238721SJonathan.Adams@Sun.COM  * relative ordering, so we don't do the extra work of looking up symbols
2248721SJonathan.Adams@Sun.COM  * for the stack addresses.
2258721SJonathan.Adams@Sun.COM  */
2260Sstevel@tonic-gate int
stacks_entry_comp_impl(stacks_entry_t * l,stacks_entry_t * r,uint_t forsort)2278721SJonathan.Adams@Sun.COM stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r,
2288721SJonathan.Adams@Sun.COM     uint_t forsort)
2290Sstevel@tonic-gate {
2308721SJonathan.Adams@Sun.COM 	int idx;
2318721SJonathan.Adams@Sun.COM 
2328721SJonathan.Adams@Sun.COM 	int depth = MIN(l->se_depth, r->se_depth);
2338721SJonathan.Adams@Sun.COM 
2348721SJonathan.Adams@Sun.COM 	/* no matter what, panic stacks come last. */
2358721SJonathan.Adams@Sun.COM 	if (l->se_panic > r->se_panic)
2368721SJonathan.Adams@Sun.COM 		return (1);
2378721SJonathan.Adams@Sun.COM 	if (l->se_panic < r->se_panic)
2388721SJonathan.Adams@Sun.COM 		return (-1);
2398721SJonathan.Adams@Sun.COM 
2408721SJonathan.Adams@Sun.COM 	if (forsort) {
2418721SJonathan.Adams@Sun.COM 		/* put large counts earlier */
2428721SJonathan.Adams@Sun.COM 		if (l->se_count > r->se_count)
2438721SJonathan.Adams@Sun.COM 			return (-1);
2448721SJonathan.Adams@Sun.COM 		if (l->se_count < r->se_count)
2458721SJonathan.Adams@Sun.COM 			return (1);
2468721SJonathan.Adams@Sun.COM 	}
2478721SJonathan.Adams@Sun.COM 
2488721SJonathan.Adams@Sun.COM 	if (l->se_tstate > r->se_tstate)
2498721SJonathan.Adams@Sun.COM 		return (1);
2508721SJonathan.Adams@Sun.COM 	if (l->se_tstate < r->se_tstate)
2518721SJonathan.Adams@Sun.COM 		return (-1);
2528721SJonathan.Adams@Sun.COM 
2538721SJonathan.Adams@Sun.COM 	if (l->se_failed > r->se_failed)
2548721SJonathan.Adams@Sun.COM 		return (1);
2558721SJonathan.Adams@Sun.COM 	if (l->se_failed < r->se_failed)
2568721SJonathan.Adams@Sun.COM 		return (-1);
2578721SJonathan.Adams@Sun.COM 
2588721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < depth; idx++) {
2598721SJonathan.Adams@Sun.COM 		char lbuf[MDB_SYM_NAMLEN];
2608721SJonathan.Adams@Sun.COM 		char rbuf[MDB_SYM_NAMLEN];
2618721SJonathan.Adams@Sun.COM 
2628721SJonathan.Adams@Sun.COM 		int rval;
2638721SJonathan.Adams@Sun.COM 		uintptr_t laddr = l->se_stack[idx];
2648721SJonathan.Adams@Sun.COM 		uintptr_t raddr = r->se_stack[idx];
2658721SJonathan.Adams@Sun.COM 
2668721SJonathan.Adams@Sun.COM 		if (laddr == raddr)
2678721SJonathan.Adams@Sun.COM 			continue;
2688721SJonathan.Adams@Sun.COM 
2698721SJonathan.Adams@Sun.COM 		if (forsort &&
2708721SJonathan.Adams@Sun.COM 		    mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY,
2718721SJonathan.Adams@Sun.COM 		    lbuf, sizeof (lbuf), NULL) != -1 &&
2728721SJonathan.Adams@Sun.COM 		    mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY,
2738721SJonathan.Adams@Sun.COM 		    rbuf, sizeof (rbuf), NULL) != -1 &&
2748721SJonathan.Adams@Sun.COM 		    (rval = strcmp(lbuf, rbuf)) != 0)
2758721SJonathan.Adams@Sun.COM 			return (rval);
2768721SJonathan.Adams@Sun.COM 
2778721SJonathan.Adams@Sun.COM 		if (laddr > raddr)
2788721SJonathan.Adams@Sun.COM 			return (1);
2798721SJonathan.Adams@Sun.COM 		return (-1);
2800Sstevel@tonic-gate 	}
2818721SJonathan.Adams@Sun.COM 
2828721SJonathan.Adams@Sun.COM 	if (l->se_overflow > r->se_overflow)
2838721SJonathan.Adams@Sun.COM 		return (-1);
2848721SJonathan.Adams@Sun.COM 	if (l->se_overflow < r->se_overflow)
2858721SJonathan.Adams@Sun.COM 		return (1);
2868721SJonathan.Adams@Sun.COM 
2878721SJonathan.Adams@Sun.COM 	if (l->se_depth > r->se_depth)
2888721SJonathan.Adams@Sun.COM 		return (1);
2898721SJonathan.Adams@Sun.COM 	if (l->se_depth < r->se_depth)
2908721SJonathan.Adams@Sun.COM 		return (-1);
2918721SJonathan.Adams@Sun.COM 
2928721SJonathan.Adams@Sun.COM 	if (l->se_sobj_ops > r->se_sobj_ops)
2938721SJonathan.Adams@Sun.COM 		return (1);
2948721SJonathan.Adams@Sun.COM 	if (l->se_sobj_ops < r->se_sobj_ops)
2958721SJonathan.Adams@Sun.COM 		return (-1);
2968721SJonathan.Adams@Sun.COM 
2978721SJonathan.Adams@Sun.COM 	return (0);
2988721SJonathan.Adams@Sun.COM }
2998721SJonathan.Adams@Sun.COM 
3008721SJonathan.Adams@Sun.COM int
stacks_entry_comp(const void * l_arg,const void * r_arg)3018721SJonathan.Adams@Sun.COM stacks_entry_comp(const void *l_arg, const void *r_arg)
3028721SJonathan.Adams@Sun.COM {
3038721SJonathan.Adams@Sun.COM 	stacks_entry_t * const *lp = l_arg;
3048721SJonathan.Adams@Sun.COM 	stacks_entry_t * const *rp = r_arg;
3058721SJonathan.Adams@Sun.COM 
3068721SJonathan.Adams@Sun.COM 	return (stacks_entry_comp_impl(*lp, *rp, 1));
3078721SJonathan.Adams@Sun.COM }
3088721SJonathan.Adams@Sun.COM 
3098721SJonathan.Adams@Sun.COM void
stacks_cleanup(int force)3108721SJonathan.Adams@Sun.COM stacks_cleanup(int force)
3118721SJonathan.Adams@Sun.COM {
3128721SJonathan.Adams@Sun.COM 	int idx = 0;
3138721SJonathan.Adams@Sun.COM 	stacks_entry_t *cur, *next;
3148721SJonathan.Adams@Sun.COM 
3158721SJonathan.Adams@Sun.COM 	if (stacks_state == STACKS_STATE_CLEAN)
3168721SJonathan.Adams@Sun.COM 		return;
3178721SJonathan.Adams@Sun.COM 
3188721SJonathan.Adams@Sun.COM 	if (!force && stacks_state == STACKS_STATE_DONE)
3198721SJonathan.Adams@Sun.COM 		return;
3208721SJonathan.Adams@Sun.COM 
3218721SJonathan.Adams@Sun.COM 	/*
3228721SJonathan.Adams@Sun.COM 	 * Until the array is sorted and stable, stacks_hash will be non-NULL.
3238721SJonathan.Adams@Sun.COM 	 * This way, we can get at all of the data, even if qsort() was
3248721SJonathan.Adams@Sun.COM 	 * interrupted while mucking with the array.
3258721SJonathan.Adams@Sun.COM 	 */
3268721SJonathan.Adams@Sun.COM 	if (stacks_hash != NULL) {
3278721SJonathan.Adams@Sun.COM 		for (idx = 0; idx < STACKS_HSIZE; idx++) {
3288721SJonathan.Adams@Sun.COM 			while ((cur = stacks_hash[idx]) != NULL) {
3298721SJonathan.Adams@Sun.COM 				while ((next = cur->se_dup) != NULL) {
3308721SJonathan.Adams@Sun.COM 					cur->se_dup = next->se_dup;
3318721SJonathan.Adams@Sun.COM 					mdb_free(next,
3328721SJonathan.Adams@Sun.COM 					    STACKS_ENTRY_SIZE(next->se_depth));
3338721SJonathan.Adams@Sun.COM 				}
3348721SJonathan.Adams@Sun.COM 				next = cur->se_next;
3358721SJonathan.Adams@Sun.COM 				stacks_hash[idx] = next;
3368721SJonathan.Adams@Sun.COM 				mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
3378721SJonathan.Adams@Sun.COM 			}
3388721SJonathan.Adams@Sun.COM 		}
3398721SJonathan.Adams@Sun.COM 		if (stacks_array != NULL)
3408721SJonathan.Adams@Sun.COM 			mdb_free(stacks_array,
3418721SJonathan.Adams@Sun.COM 			    stacks_array_size * sizeof (*stacks_array));
3428721SJonathan.Adams@Sun.COM 
3438721SJonathan.Adams@Sun.COM 	} else if (stacks_array != NULL) {
3448721SJonathan.Adams@Sun.COM 		for (idx = 0; idx < stacks_array_size; idx++) {
3458721SJonathan.Adams@Sun.COM 			if ((cur = stacks_array[idx]) != NULL) {
3468721SJonathan.Adams@Sun.COM 				while ((next = cur->se_dup) != NULL) {
3478721SJonathan.Adams@Sun.COM 					cur->se_dup = next->se_dup;
3488721SJonathan.Adams@Sun.COM 					mdb_free(next,
3498721SJonathan.Adams@Sun.COM 					    STACKS_ENTRY_SIZE(next->se_depth));
3508721SJonathan.Adams@Sun.COM 				}
3518721SJonathan.Adams@Sun.COM 				stacks_array[idx] = NULL;
3528721SJonathan.Adams@Sun.COM 				mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
3538721SJonathan.Adams@Sun.COM 			}
3548721SJonathan.Adams@Sun.COM 		}
3558721SJonathan.Adams@Sun.COM 		mdb_free(stacks_array,
3568721SJonathan.Adams@Sun.COM 		    stacks_array_size * sizeof (*stacks_array));
3578721SJonathan.Adams@Sun.COM 	}
3588721SJonathan.Adams@Sun.COM 
359*12902SBryan.Cantrill@Sun.COM 	stacks_findstack_cleanup();
360*12902SBryan.Cantrill@Sun.COM 
3618721SJonathan.Adams@Sun.COM 	stacks_array_size = 0;
3628721SJonathan.Adams@Sun.COM 	stacks_state = STACKS_STATE_CLEAN;
3638721SJonathan.Adams@Sun.COM }
3648721SJonathan.Adams@Sun.COM 
3658721SJonathan.Adams@Sun.COM /*ARGSUSED*/
3668721SJonathan.Adams@Sun.COM int
stacks_thread_cb(uintptr_t addr,const void * ignored,void * cbarg)3678721SJonathan.Adams@Sun.COM stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg)
3688721SJonathan.Adams@Sun.COM {
3698721SJonathan.Adams@Sun.COM 	stacks_info_t *sip = cbarg;
3708721SJonathan.Adams@Sun.COM 	findstack_info_t *fsip = &sip->si_fsi;
3718721SJonathan.Adams@Sun.COM 
3728721SJonathan.Adams@Sun.COM 	stacks_entry_t **sepp, *nsep, *sep;
3738721SJonathan.Adams@Sun.COM 	int idx;
3748721SJonathan.Adams@Sun.COM 	size_t depth;
3758721SJonathan.Adams@Sun.COM 
376*12902SBryan.Cantrill@Sun.COM 	if (stacks_findstack(addr, fsip, 0) != DCMD_OK &&
3778721SJonathan.Adams@Sun.COM 	    fsip->fsi_failed == FSI_FAIL_BADTHREAD) {
3788721SJonathan.Adams@Sun.COM 		mdb_warn("couldn't read thread at %p\n", addr);
3798721SJonathan.Adams@Sun.COM 		return (WALK_NEXT);
3808721SJonathan.Adams@Sun.COM 	}
3818721SJonathan.Adams@Sun.COM 
3828721SJonathan.Adams@Sun.COM 	sip->si_count++;
3838721SJonathan.Adams@Sun.COM 
3848721SJonathan.Adams@Sun.COM 	depth = fsip->fsi_depth;
3858721SJonathan.Adams@Sun.COM 	nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP);
3868721SJonathan.Adams@Sun.COM 	nsep->se_thread = addr;
3878721SJonathan.Adams@Sun.COM 	nsep->se_sp = fsip->fsi_sp;
3888721SJonathan.Adams@Sun.COM 	nsep->se_sobj_ops = fsip->fsi_sobj_ops;
3898721SJonathan.Adams@Sun.COM 	nsep->se_tstate = fsip->fsi_tstate;
3908721SJonathan.Adams@Sun.COM 	nsep->se_count = 1;
3918721SJonathan.Adams@Sun.COM 	nsep->se_overflow = fsip->fsi_overflow;
3928721SJonathan.Adams@Sun.COM 	nsep->se_depth = depth;
3938721SJonathan.Adams@Sun.COM 	nsep->se_failed = fsip->fsi_failed;
3948721SJonathan.Adams@Sun.COM 	nsep->se_panic = fsip->fsi_panic;
3958721SJonathan.Adams@Sun.COM 
3968721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < depth; idx++)
3978721SJonathan.Adams@Sun.COM 		nsep->se_stack[idx] = fsip->fsi_stack[idx];
3988721SJonathan.Adams@Sun.COM 
3998721SJonathan.Adams@Sun.COM 	for (sepp = &sip->si_hash[stacks_hash_entry(nsep)];
4008721SJonathan.Adams@Sun.COM 	    (sep = *sepp) != NULL;
4018721SJonathan.Adams@Sun.COM 	    sepp = &sep->se_next) {
4028721SJonathan.Adams@Sun.COM 
4038721SJonathan.Adams@Sun.COM 		if (stacks_entry_comp_impl(sep, nsep, 0) != 0)
4048721SJonathan.Adams@Sun.COM 			continue;
4058721SJonathan.Adams@Sun.COM 
4068721SJonathan.Adams@Sun.COM 		nsep->se_dup = sep->se_dup;
4078721SJonathan.Adams@Sun.COM 		sep->se_dup = nsep;
4088721SJonathan.Adams@Sun.COM 		sep->se_count++;
4098721SJonathan.Adams@Sun.COM 		return (WALK_NEXT);
4108721SJonathan.Adams@Sun.COM 	}
4118721SJonathan.Adams@Sun.COM 
4128721SJonathan.Adams@Sun.COM 	nsep->se_next = NULL;
4138721SJonathan.Adams@Sun.COM 	*sepp = nsep;
4148721SJonathan.Adams@Sun.COM 	sip->si_entries++;
4158721SJonathan.Adams@Sun.COM 
4168721SJonathan.Adams@Sun.COM 	return (WALK_NEXT);
4178721SJonathan.Adams@Sun.COM }
4188721SJonathan.Adams@Sun.COM 
4198721SJonathan.Adams@Sun.COM int
stacks_run_tlist(mdb_pipe_t * tlist,stacks_info_t * si)42010889SJonathan.Adams@Sun.COM stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si)
42110889SJonathan.Adams@Sun.COM {
42210889SJonathan.Adams@Sun.COM 	size_t idx;
42310889SJonathan.Adams@Sun.COM 	size_t found = 0;
42410889SJonathan.Adams@Sun.COM 	int ret;
42510889SJonathan.Adams@Sun.COM 
42610889SJonathan.Adams@Sun.COM 	for (idx = 0; idx < tlist->pipe_len; idx++) {
42710889SJonathan.Adams@Sun.COM 		uintptr_t addr = tlist->pipe_data[idx];
42810889SJonathan.Adams@Sun.COM 
42910889SJonathan.Adams@Sun.COM 		found++;
43010889SJonathan.Adams@Sun.COM 
431*12902SBryan.Cantrill@Sun.COM 		ret = stacks_thread_cb(addr, NULL, si);
43210889SJonathan.Adams@Sun.COM 		if (ret == WALK_DONE)
43310889SJonathan.Adams@Sun.COM 			break;
43410889SJonathan.Adams@Sun.COM 		if (ret != WALK_NEXT)
43510889SJonathan.Adams@Sun.COM 			return (-1);
43610889SJonathan.Adams@Sun.COM 	}
43710889SJonathan.Adams@Sun.COM 
43810889SJonathan.Adams@Sun.COM 	if (found)
43910889SJonathan.Adams@Sun.COM 		return (0);
44010889SJonathan.Adams@Sun.COM 	return (-1);
44110889SJonathan.Adams@Sun.COM }
44210889SJonathan.Adams@Sun.COM 
44310889SJonathan.Adams@Sun.COM int
stacks_run(int verbose,mdb_pipe_t * tlist)44410889SJonathan.Adams@Sun.COM stacks_run(int verbose, mdb_pipe_t *tlist)
4458721SJonathan.Adams@Sun.COM {
4468721SJonathan.Adams@Sun.COM 	stacks_info_t si;
4478721SJonathan.Adams@Sun.COM 	findstack_info_t *fsip = &si.si_fsi;
4488721SJonathan.Adams@Sun.COM 	size_t idx;
4498721SJonathan.Adams@Sun.COM 	stacks_entry_t **cur;
4508721SJonathan.Adams@Sun.COM 
4518721SJonathan.Adams@Sun.COM 	bzero(&si, sizeof (si));
4528721SJonathan.Adams@Sun.COM 
4538721SJonathan.Adams@Sun.COM 	stacks_state = STACKS_STATE_DIRTY;
4548721SJonathan.Adams@Sun.COM 
4558721SJonathan.Adams@Sun.COM 	stacks_hash = si.si_hash =
4568721SJonathan.Adams@Sun.COM 	    mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP);
4578721SJonathan.Adams@Sun.COM 	si.si_entries = 0;
4588721SJonathan.Adams@Sun.COM 	si.si_count = 0;
4598721SJonathan.Adams@Sun.COM 
4608721SJonathan.Adams@Sun.COM 	fsip->fsi_max_depth = STACKS_MAX_DEPTH;
4618721SJonathan.Adams@Sun.COM 	fsip->fsi_stack =
4628721SJonathan.Adams@Sun.COM 	    mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack),
4638721SJonathan.Adams@Sun.COM 	    UM_SLEEP | UM_GC);
4648721SJonathan.Adams@Sun.COM 
4658721SJonathan.Adams@Sun.COM 	if (verbose)
4668721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: processing kernel threads\n");
4678721SJonathan.Adams@Sun.COM 
46810889SJonathan.Adams@Sun.COM 	if (tlist != NULL) {
46910889SJonathan.Adams@Sun.COM 		if (stacks_run_tlist(tlist, &si))
47010889SJonathan.Adams@Sun.COM 			return (DCMD_ERR);
47110889SJonathan.Adams@Sun.COM 	} else {
47210889SJonathan.Adams@Sun.COM 		if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
47310889SJonathan.Adams@Sun.COM 			mdb_warn("cannot walk \"thread\"");
47410889SJonathan.Adams@Sun.COM 			return (DCMD_ERR);
47510889SJonathan.Adams@Sun.COM 		}
4768721SJonathan.Adams@Sun.COM 	}
4778721SJonathan.Adams@Sun.COM 
4788721SJonathan.Adams@Sun.COM 	if (verbose)
4798721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: %d unique stacks / %d threads\n",
4808721SJonathan.Adams@Sun.COM 		    si.si_entries, si.si_count);
4818721SJonathan.Adams@Sun.COM 
4828721SJonathan.Adams@Sun.COM 	stacks_array_size = si.si_entries;
4838721SJonathan.Adams@Sun.COM 	stacks_array =
4848721SJonathan.Adams@Sun.COM 	    mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP);
4858721SJonathan.Adams@Sun.COM 	cur = stacks_array;
4868721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < STACKS_HSIZE; idx++) {
4878721SJonathan.Adams@Sun.COM 		stacks_entry_t *sep;
4888721SJonathan.Adams@Sun.COM 		for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next)
4898721SJonathan.Adams@Sun.COM 			*(cur++) = sep;
4908721SJonathan.Adams@Sun.COM 	}
4918721SJonathan.Adams@Sun.COM 
4928721SJonathan.Adams@Sun.COM 	if (cur != stacks_array + si.si_entries) {
4938721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: miscounted array size (%d != size: %d)\n",
4948721SJonathan.Adams@Sun.COM 		    (cur - stacks_array), stacks_array_size);
4958721SJonathan.Adams@Sun.COM 		return (DCMD_ERR);
4968721SJonathan.Adams@Sun.COM 	}
4978721SJonathan.Adams@Sun.COM 	qsort(stacks_array, si.si_entries, sizeof (*stacks_array),
4988721SJonathan.Adams@Sun.COM 	    stacks_entry_comp);
4998721SJonathan.Adams@Sun.COM 
5008721SJonathan.Adams@Sun.COM 	/* Now that we're done, free the hash table */
5018721SJonathan.Adams@Sun.COM 	stacks_hash = NULL;
5028721SJonathan.Adams@Sun.COM 	mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash));
5038721SJonathan.Adams@Sun.COM 
50410889SJonathan.Adams@Sun.COM 	if (tlist == NULL)
50510889SJonathan.Adams@Sun.COM 		stacks_state = STACKS_STATE_DONE;
5068721SJonathan.Adams@Sun.COM 
5078721SJonathan.Adams@Sun.COM 	if (verbose)
5088721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: done\n");
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	return (DCMD_OK);
5110Sstevel@tonic-gate }
5128721SJonathan.Adams@Sun.COM 
5138721SJonathan.Adams@Sun.COM static int
stacks_has_caller(stacks_entry_t * sep,uintptr_t addr)5148721SJonathan.Adams@Sun.COM stacks_has_caller(stacks_entry_t *sep, uintptr_t addr)
5158721SJonathan.Adams@Sun.COM {
5168721SJonathan.Adams@Sun.COM 	uintptr_t laddr = addr;
5178721SJonathan.Adams@Sun.COM 	uintptr_t haddr = addr + 1;
5188721SJonathan.Adams@Sun.COM 	int idx;
5198721SJonathan.Adams@Sun.COM 	char c[MDB_SYM_NAMLEN];
5208721SJonathan.Adams@Sun.COM 	GElf_Sym sym;
5218721SJonathan.Adams@Sun.COM 
5228721SJonathan.Adams@Sun.COM 	if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY,
5238721SJonathan.Adams@Sun.COM 	    c, sizeof (c), &sym) != -1 &&
5248721SJonathan.Adams@Sun.COM 	    addr == (uintptr_t)sym.st_value) {
5258721SJonathan.Adams@Sun.COM 		laddr = (uintptr_t)sym.st_value;
5268721SJonathan.Adams@Sun.COM 		haddr = (uintptr_t)sym.st_value + sym.st_size;
5278721SJonathan.Adams@Sun.COM 	}
5288721SJonathan.Adams@Sun.COM 
5298721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < sep->se_depth; idx++)
5308721SJonathan.Adams@Sun.COM 		if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr)
5318721SJonathan.Adams@Sun.COM 			return (1);
5328721SJonathan.Adams@Sun.COM 
5338721SJonathan.Adams@Sun.COM 	return (0);
5348721SJonathan.Adams@Sun.COM }
5358721SJonathan.Adams@Sun.COM 
536*12902SBryan.Cantrill@Sun.COM static int
stacks_has_module(stacks_entry_t * sep,stacks_module_t * mp)537*12902SBryan.Cantrill@Sun.COM stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp)
5388742Sgap@sun.com {
539*12902SBryan.Cantrill@Sun.COM 	int idx;
5408742Sgap@sun.com 
541*12902SBryan.Cantrill@Sun.COM 	for (idx = 0; idx < sep->se_depth; idx++) {
542*12902SBryan.Cantrill@Sun.COM 		if (sep->se_stack[idx] >= mp->sm_text &&
543*12902SBryan.Cantrill@Sun.COM 		    sep->se_stack[idx] < mp->sm_text + mp->sm_size)
544*12902SBryan.Cantrill@Sun.COM 			return (1);
5458742Sgap@sun.com 	}
5468742Sgap@sun.com 
547*12902SBryan.Cantrill@Sun.COM 	return (0);
5488742Sgap@sun.com }
5498742Sgap@sun.com 
5508742Sgap@sun.com static int
stacks_module_find(const char * name,stacks_module_t * mp)551*12902SBryan.Cantrill@Sun.COM stacks_module_find(const char *name, stacks_module_t *mp)
5528742Sgap@sun.com {
553*12902SBryan.Cantrill@Sun.COM 	(void) strncpy(mp->sm_name, name, sizeof (mp->sm_name));
5548742Sgap@sun.com 
555*12902SBryan.Cantrill@Sun.COM 	if (stacks_module(mp) != 0)
556*12902SBryan.Cantrill@Sun.COM 		return (-1);
5578742Sgap@sun.com 
558*12902SBryan.Cantrill@Sun.COM 	if (mp->sm_size == 0) {
559*12902SBryan.Cantrill@Sun.COM 		mdb_warn("stacks: module \"%s\" is unknown\n", name);
560*12902SBryan.Cantrill@Sun.COM 		return (-1);
561*12902SBryan.Cantrill@Sun.COM 	}
562*12902SBryan.Cantrill@Sun.COM 
5638742Sgap@sun.com 	return (0);
5648742Sgap@sun.com }
5658742Sgap@sun.com 
5668721SJonathan.Adams@Sun.COM static int
uintptrcomp(const void * lp,const void * rp)5678721SJonathan.Adams@Sun.COM uintptrcomp(const void *lp, const void *rp)
5688721SJonathan.Adams@Sun.COM {
5698721SJonathan.Adams@Sun.COM 	uintptr_t lhs = *(const uintptr_t *)lp;
5708721SJonathan.Adams@Sun.COM 	uintptr_t rhs = *(const uintptr_t *)rp;
5718721SJonathan.Adams@Sun.COM 	if (lhs > rhs)
5728721SJonathan.Adams@Sun.COM 		return (1);
5738721SJonathan.Adams@Sun.COM 	if (lhs < rhs)
5748721SJonathan.Adams@Sun.COM 		return (-1);
5758721SJonathan.Adams@Sun.COM 	return (0);
5768721SJonathan.Adams@Sun.COM }
5778721SJonathan.Adams@Sun.COM 
5788721SJonathan.Adams@Sun.COM /*ARGSUSED*/
5798721SJonathan.Adams@Sun.COM int
stacks(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)5808721SJonathan.Adams@Sun.COM stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5818721SJonathan.Adams@Sun.COM {
5828721SJonathan.Adams@Sun.COM 	size_t idx;
5838721SJonathan.Adams@Sun.COM 
5848721SJonathan.Adams@Sun.COM 	char *seen = NULL;
5858721SJonathan.Adams@Sun.COM 
5868721SJonathan.Adams@Sun.COM 	const char *caller_str = NULL;
5878721SJonathan.Adams@Sun.COM 	const char *excl_caller_str = NULL;
5888721SJonathan.Adams@Sun.COM 	uintptr_t caller = 0, excl_caller = 0;
5898742Sgap@sun.com 	const char *module_str = NULL;
5908742Sgap@sun.com 	const char *excl_module_str = NULL;
591*12902SBryan.Cantrill@Sun.COM 	stacks_module_t module, excl_module;
5928721SJonathan.Adams@Sun.COM 	const char *sobj = NULL;
5938721SJonathan.Adams@Sun.COM 	const char *excl_sobj = NULL;
5948721SJonathan.Adams@Sun.COM 	uintptr_t sobj_ops = 0, excl_sobj_ops = 0;
5958721SJonathan.Adams@Sun.COM 	const char *tstate_str = NULL;
5968721SJonathan.Adams@Sun.COM 	const char *excl_tstate_str = NULL;
5978721SJonathan.Adams@Sun.COM 	uint_t tstate = -1U;
5988721SJonathan.Adams@Sun.COM 	uint_t excl_tstate = -1U;
59910889SJonathan.Adams@Sun.COM 	uint_t printed = 0;
6008721SJonathan.Adams@Sun.COM 
6018721SJonathan.Adams@Sun.COM 	uint_t all = 0;
6028721SJonathan.Adams@Sun.COM 	uint_t force = 0;
6038721SJonathan.Adams@Sun.COM 	uint_t interesting = 0;
6048721SJonathan.Adams@Sun.COM 	uint_t verbose = 0;
6058721SJonathan.Adams@Sun.COM 
6068721SJonathan.Adams@Sun.COM 	/*
6078721SJonathan.Adams@Sun.COM 	 * We have a slight behavior difference between having piped
6088721SJonathan.Adams@Sun.COM 	 * input and 'addr::stacks'.  Without a pipe, we assume the
6098721SJonathan.Adams@Sun.COM 	 * thread pointer given is a representative thread, and so
6108721SJonathan.Adams@Sun.COM 	 * we include all similar threads in the system in our output.
6118721SJonathan.Adams@Sun.COM 	 *
6128721SJonathan.Adams@Sun.COM 	 * With a pipe, we filter down to just the threads in our
6138721SJonathan.Adams@Sun.COM 	 * input.
6148721SJonathan.Adams@Sun.COM 	 */
6158721SJonathan.Adams@Sun.COM 	uint_t addrspec = (flags & DCMD_ADDRSPEC);
6168721SJonathan.Adams@Sun.COM 	uint_t only_matching = addrspec && (flags & DCMD_PIPE);
6178721SJonathan.Adams@Sun.COM 
6188721SJonathan.Adams@Sun.COM 	mdb_pipe_t p;
6198721SJonathan.Adams@Sun.COM 
620*12902SBryan.Cantrill@Sun.COM 	bzero(&module, sizeof (module));
621*12902SBryan.Cantrill@Sun.COM 	bzero(&excl_module, sizeof (excl_module));
622*12902SBryan.Cantrill@Sun.COM 
6238721SJonathan.Adams@Sun.COM 	if (mdb_getopts(argc, argv,
6248721SJonathan.Adams@Sun.COM 	    'a', MDB_OPT_SETBITS, TRUE, &all,
6258721SJonathan.Adams@Sun.COM 	    'f', MDB_OPT_SETBITS, TRUE, &force,
6268721SJonathan.Adams@Sun.COM 	    'i', MDB_OPT_SETBITS, TRUE, &interesting,
6278721SJonathan.Adams@Sun.COM 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
6288721SJonathan.Adams@Sun.COM 	    'c', MDB_OPT_STR, &caller_str,
6298721SJonathan.Adams@Sun.COM 	    'C', MDB_OPT_STR, &excl_caller_str,
6308742Sgap@sun.com 	    'm', MDB_OPT_STR, &module_str,
6318742Sgap@sun.com 	    'M', MDB_OPT_STR, &excl_module_str,
6328721SJonathan.Adams@Sun.COM 	    's', MDB_OPT_STR, &sobj,
6338721SJonathan.Adams@Sun.COM 	    'S', MDB_OPT_STR, &excl_sobj,
6348721SJonathan.Adams@Sun.COM 	    't', MDB_OPT_STR, &tstate_str,
6358721SJonathan.Adams@Sun.COM 	    'T', MDB_OPT_STR, &excl_tstate_str,
6368721SJonathan.Adams@Sun.COM 	    NULL) != argc)
6378721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6388721SJonathan.Adams@Sun.COM 
6398721SJonathan.Adams@Sun.COM 	if (interesting) {
6408721SJonathan.Adams@Sun.COM 		if (sobj != NULL || excl_sobj != NULL ||
6418721SJonathan.Adams@Sun.COM 		    tstate_str != NULL || excl_tstate_str != NULL) {
6428721SJonathan.Adams@Sun.COM 			mdb_warn(
6438721SJonathan.Adams@Sun.COM 			    "stacks: -i is incompatible with -[sStT]\n");
6448721SJonathan.Adams@Sun.COM 			return (DCMD_USAGE);
6458721SJonathan.Adams@Sun.COM 		}
6468721SJonathan.Adams@Sun.COM 		excl_sobj = "CV";
6478721SJonathan.Adams@Sun.COM 		excl_tstate_str = "FREE";
6488721SJonathan.Adams@Sun.COM 	}
6498721SJonathan.Adams@Sun.COM 
6508721SJonathan.Adams@Sun.COM 	if (caller_str != NULL) {
6518721SJonathan.Adams@Sun.COM 		mdb_set_dot(0);
6528721SJonathan.Adams@Sun.COM 		if (mdb_eval(caller_str) != 0) {
6538721SJonathan.Adams@Sun.COM 			mdb_warn("stacks: evaluation of \"%s\" failed",
6548721SJonathan.Adams@Sun.COM 			    caller_str);
6558721SJonathan.Adams@Sun.COM 			return (DCMD_ABORT);
6568721SJonathan.Adams@Sun.COM 		}
6578721SJonathan.Adams@Sun.COM 		caller = mdb_get_dot();
6588721SJonathan.Adams@Sun.COM 	}
6598721SJonathan.Adams@Sun.COM 
6608721SJonathan.Adams@Sun.COM 	if (excl_caller_str != NULL) {
6618721SJonathan.Adams@Sun.COM 		mdb_set_dot(0);
6628721SJonathan.Adams@Sun.COM 		if (mdb_eval(excl_caller_str) != 0) {
6638721SJonathan.Adams@Sun.COM 			mdb_warn("stacks: evaluation of \"%s\" failed",
6648721SJonathan.Adams@Sun.COM 			    excl_caller_str);
6658721SJonathan.Adams@Sun.COM 			return (DCMD_ABORT);
6668721SJonathan.Adams@Sun.COM 		}
6678721SJonathan.Adams@Sun.COM 		excl_caller = mdb_get_dot();
6688721SJonathan.Adams@Sun.COM 	}
6698721SJonathan.Adams@Sun.COM 	mdb_set_dot(addr);
6708721SJonathan.Adams@Sun.COM 
671*12902SBryan.Cantrill@Sun.COM 	if (module_str != NULL && stacks_module_find(module_str, &module) != 0)
6728742Sgap@sun.com 		return (DCMD_ABORT);
6738742Sgap@sun.com 
6748742Sgap@sun.com 	if (excl_module_str != NULL &&
675*12902SBryan.Cantrill@Sun.COM 	    stacks_module_find(excl_module_str, &excl_module) != 0)
6768742Sgap@sun.com 		return (DCMD_ABORT);
6778742Sgap@sun.com 
678*12902SBryan.Cantrill@Sun.COM 	if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0)
6798721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6808721SJonathan.Adams@Sun.COM 
681*12902SBryan.Cantrill@Sun.COM 	if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
6828721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6838721SJonathan.Adams@Sun.COM 
6848721SJonathan.Adams@Sun.COM 	if (sobj_ops != 0 && excl_sobj_ops != 0) {
6858721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: only one of -s and -S can be specified\n");
6868721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6878721SJonathan.Adams@Sun.COM 	}
6888721SJonathan.Adams@Sun.COM 
689*12902SBryan.Cantrill@Sun.COM 	if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0)
6908721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6918742Sgap@sun.com 
6928742Sgap@sun.com 	if (excl_tstate_str != NULL &&
6938721SJonathan.Adams@Sun.COM 	    text_to_tstate(excl_tstate_str, &excl_tstate) != 0)
6948721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6958721SJonathan.Adams@Sun.COM 
6968721SJonathan.Adams@Sun.COM 	if (tstate != -1U && excl_tstate != -1U) {
6978721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: only one of -t and -T can be specified\n");
6988721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
6998721SJonathan.Adams@Sun.COM 	}
7008721SJonathan.Adams@Sun.COM 
7018721SJonathan.Adams@Sun.COM 	/*
7028721SJonathan.Adams@Sun.COM 	 * If there's an address specified, we're going to further filter
7038721SJonathan.Adams@Sun.COM 	 * to only entries which have an address in the input.  To reduce
7048721SJonathan.Adams@Sun.COM 	 * overhead (and make the sorted output come out right), we
7058721SJonathan.Adams@Sun.COM 	 * use mdb_get_pipe() to grab the entire pipeline of input, then
7068721SJonathan.Adams@Sun.COM 	 * use qsort() and bsearch() to speed up the search.
7078721SJonathan.Adams@Sun.COM 	 */
7088721SJonathan.Adams@Sun.COM 	if (addrspec) {
7098721SJonathan.Adams@Sun.COM 		mdb_get_pipe(&p);
7108721SJonathan.Adams@Sun.COM 		if (p.pipe_data == NULL || p.pipe_len == 0) {
7118721SJonathan.Adams@Sun.COM 			p.pipe_data = &addr;
7128721SJonathan.Adams@Sun.COM 			p.pipe_len = 1;
7138721SJonathan.Adams@Sun.COM 		}
7148721SJonathan.Adams@Sun.COM 		qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t),
7158721SJonathan.Adams@Sun.COM 		    uintptrcomp);
7168721SJonathan.Adams@Sun.COM 
7178721SJonathan.Adams@Sun.COM 		/* remove any duplicates in the data */
7188721SJonathan.Adams@Sun.COM 		idx = 0;
7198721SJonathan.Adams@Sun.COM 		while (idx < p.pipe_len - 1) {
7208721SJonathan.Adams@Sun.COM 			uintptr_t *data = &p.pipe_data[idx];
7218721SJonathan.Adams@Sun.COM 			size_t len = p.pipe_len - idx;
7228721SJonathan.Adams@Sun.COM 
7238721SJonathan.Adams@Sun.COM 			if (data[0] == data[1]) {
7248721SJonathan.Adams@Sun.COM 				memmove(data, data + 1,
7258721SJonathan.Adams@Sun.COM 				    (len - 1) * sizeof (*data));
7268721SJonathan.Adams@Sun.COM 				p.pipe_len--;
7278721SJonathan.Adams@Sun.COM 				continue; /* repeat without incrementing idx */
7288721SJonathan.Adams@Sun.COM 			}
7298721SJonathan.Adams@Sun.COM 			idx++;
7308721SJonathan.Adams@Sun.COM 		}
7318721SJonathan.Adams@Sun.COM 
7328721SJonathan.Adams@Sun.COM 		seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC);
7338721SJonathan.Adams@Sun.COM 	}
7348721SJonathan.Adams@Sun.COM 
73510889SJonathan.Adams@Sun.COM 	/*
73610889SJonathan.Adams@Sun.COM 	 * Force a cleanup if we're connected to a live system. Never
73710889SJonathan.Adams@Sun.COM 	 * do a cleanup after the first invocation around the loop.
73810889SJonathan.Adams@Sun.COM 	 */
73910889SJonathan.Adams@Sun.COM 	force |= (mdb_get_state() == MDB_STATE_RUNNING);
74010889SJonathan.Adams@Sun.COM 	if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
74110889SJonathan.Adams@Sun.COM 		force = 0;
74210889SJonathan.Adams@Sun.COM 
74310889SJonathan.Adams@Sun.COM 	stacks_cleanup(force);
74410889SJonathan.Adams@Sun.COM 
74510889SJonathan.Adams@Sun.COM 	if (stacks_state == STACKS_STATE_CLEAN) {
74610889SJonathan.Adams@Sun.COM 		int res = stacks_run(verbose, addrspec ? &p : NULL);
74710889SJonathan.Adams@Sun.COM 		if (res != DCMD_OK)
74810889SJonathan.Adams@Sun.COM 			return (res);
74910889SJonathan.Adams@Sun.COM 	}
75010889SJonathan.Adams@Sun.COM 
7518721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < stacks_array_size; idx++) {
7528721SJonathan.Adams@Sun.COM 		stacks_entry_t *sep = stacks_array[idx];
7538721SJonathan.Adams@Sun.COM 		stacks_entry_t *cur = sep;
7548721SJonathan.Adams@Sun.COM 		int frame;
7558721SJonathan.Adams@Sun.COM 		size_t count = sep->se_count;
7568721SJonathan.Adams@Sun.COM 
7578721SJonathan.Adams@Sun.COM 		if (addrspec) {
7588721SJonathan.Adams@Sun.COM 			stacks_entry_t *head = NULL, *tail = NULL, *sp;
7598721SJonathan.Adams@Sun.COM 			size_t foundcount = 0;
7608721SJonathan.Adams@Sun.COM 			/*
7618721SJonathan.Adams@Sun.COM 			 * We use the now-unused hash chain field se_next to
7628721SJonathan.Adams@Sun.COM 			 * link together the dups which match our list.
7638721SJonathan.Adams@Sun.COM 			 */
7648721SJonathan.Adams@Sun.COM 			for (sp = sep; sp != NULL; sp = sp->se_dup) {
7658721SJonathan.Adams@Sun.COM 				uintptr_t *entry = bsearch(&sp->se_thread,
7668721SJonathan.Adams@Sun.COM 				    p.pipe_data, p.pipe_len, sizeof (uintptr_t),
7678721SJonathan.Adams@Sun.COM 				    uintptrcomp);
7688721SJonathan.Adams@Sun.COM 				if (entry != NULL) {
7698721SJonathan.Adams@Sun.COM 					foundcount++;
7708721SJonathan.Adams@Sun.COM 					seen[entry - p.pipe_data]++;
7718721SJonathan.Adams@Sun.COM 					if (head == NULL)
7728721SJonathan.Adams@Sun.COM 						head = sp;
7738721SJonathan.Adams@Sun.COM 					else
7748721SJonathan.Adams@Sun.COM 						tail->se_next = sp;
7758721SJonathan.Adams@Sun.COM 					tail = sp;
7768721SJonathan.Adams@Sun.COM 					sp->se_next = NULL;
7778721SJonathan.Adams@Sun.COM 				}
7788721SJonathan.Adams@Sun.COM 			}
7798721SJonathan.Adams@Sun.COM 			if (head == NULL)
7808721SJonathan.Adams@Sun.COM 				continue;	/* no match, skip entry */
7818721SJonathan.Adams@Sun.COM 
7828721SJonathan.Adams@Sun.COM 			if (only_matching) {
7838721SJonathan.Adams@Sun.COM 				cur = sep = head;
7848721SJonathan.Adams@Sun.COM 				count = foundcount;
7858721SJonathan.Adams@Sun.COM 			}
7868721SJonathan.Adams@Sun.COM 		}
7878721SJonathan.Adams@Sun.COM 
7888721SJonathan.Adams@Sun.COM 		if (caller != 0 && !stacks_has_caller(sep, caller))
7898721SJonathan.Adams@Sun.COM 			continue;
790*12902SBryan.Cantrill@Sun.COM 
7918721SJonathan.Adams@Sun.COM 		if (excl_caller != 0 && stacks_has_caller(sep, excl_caller))
7928721SJonathan.Adams@Sun.COM 			continue;
793*12902SBryan.Cantrill@Sun.COM 
794*12902SBryan.Cantrill@Sun.COM 		if (module.sm_size != 0 && !stacks_has_module(sep, &module))
7958742Sgap@sun.com 			continue;
796*12902SBryan.Cantrill@Sun.COM 
797*12902SBryan.Cantrill@Sun.COM 		if (excl_module.sm_size != 0 &&
798*12902SBryan.Cantrill@Sun.COM 		    stacks_has_module(sep, &excl_module))
7998742Sgap@sun.com 			continue;
8008721SJonathan.Adams@Sun.COM 
8018721SJonathan.Adams@Sun.COM 		if (tstate != -1U) {
8028721SJonathan.Adams@Sun.COM 			if (tstate == TSTATE_PANIC) {
8038721SJonathan.Adams@Sun.COM 				if (!sep->se_panic)
8048721SJonathan.Adams@Sun.COM 					continue;
8058721SJonathan.Adams@Sun.COM 			} else if (sep->se_panic || sep->se_tstate != tstate)
8068721SJonathan.Adams@Sun.COM 				continue;
8078721SJonathan.Adams@Sun.COM 		}
8088721SJonathan.Adams@Sun.COM 		if (excl_tstate != -1U) {
8098721SJonathan.Adams@Sun.COM 			if (excl_tstate == TSTATE_PANIC) {
8108721SJonathan.Adams@Sun.COM 				if (sep->se_panic)
8118721SJonathan.Adams@Sun.COM 					continue;
8128721SJonathan.Adams@Sun.COM 			} else if (!sep->se_panic &&
8138721SJonathan.Adams@Sun.COM 			    sep->se_tstate == excl_tstate)
8148721SJonathan.Adams@Sun.COM 				continue;
8158721SJonathan.Adams@Sun.COM 		}
8168721SJonathan.Adams@Sun.COM 
8178721SJonathan.Adams@Sun.COM 		if (sobj_ops == SOBJ_ALL) {
8188721SJonathan.Adams@Sun.COM 			if (sep->se_sobj_ops == 0)
8198721SJonathan.Adams@Sun.COM 				continue;
8208721SJonathan.Adams@Sun.COM 		} else if (sobj_ops != 0) {
8218721SJonathan.Adams@Sun.COM 			if (sobj_ops != sep->se_sobj_ops)
8228721SJonathan.Adams@Sun.COM 				continue;
8238721SJonathan.Adams@Sun.COM 		}
8248721SJonathan.Adams@Sun.COM 
8258721SJonathan.Adams@Sun.COM 		if (!(interesting && sep->se_panic)) {
8268721SJonathan.Adams@Sun.COM 			if (excl_sobj_ops == SOBJ_ALL) {
8278721SJonathan.Adams@Sun.COM 				if (sep->se_sobj_ops != 0)
8288721SJonathan.Adams@Sun.COM 					continue;
8298721SJonathan.Adams@Sun.COM 			} else if (excl_sobj_ops != 0) {
8308721SJonathan.Adams@Sun.COM 				if (excl_sobj_ops == sep->se_sobj_ops)
8318721SJonathan.Adams@Sun.COM 					continue;
8328721SJonathan.Adams@Sun.COM 			}
8338721SJonathan.Adams@Sun.COM 		}
8348721SJonathan.Adams@Sun.COM 
8358721SJonathan.Adams@Sun.COM 		if (flags & DCMD_PIPE_OUT) {
8368721SJonathan.Adams@Sun.COM 			while (sep != NULL) {
8378721SJonathan.Adams@Sun.COM 				mdb_printf("%lr\n", sep->se_thread);
8388721SJonathan.Adams@Sun.COM 				sep = only_matching ?
8398721SJonathan.Adams@Sun.COM 				    sep->se_next : sep->se_dup;
8408721SJonathan.Adams@Sun.COM 			}
8418721SJonathan.Adams@Sun.COM 			continue;
8428721SJonathan.Adams@Sun.COM 		}
8438721SJonathan.Adams@Sun.COM 
84410889SJonathan.Adams@Sun.COM 		if (all || !printed) {
8458721SJonathan.Adams@Sun.COM 			mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
84610889SJonathan.Adams@Sun.COM 			    "THREAD", "STATE", "SOBJ", "COUNT");
84710889SJonathan.Adams@Sun.COM 			printed = 1;
8488721SJonathan.Adams@Sun.COM 		}
8498721SJonathan.Adams@Sun.COM 
8508721SJonathan.Adams@Sun.COM 		do {
8518721SJonathan.Adams@Sun.COM 			char state[20];
8528721SJonathan.Adams@Sun.COM 			char sobj[100];
8538721SJonathan.Adams@Sun.COM 
8548721SJonathan.Adams@Sun.COM 			tstate_to_text(cur->se_tstate, cur->se_panic,
8558721SJonathan.Adams@Sun.COM 			    state, sizeof (state));
8568721SJonathan.Adams@Sun.COM 			sobj_to_text(cur->se_sobj_ops,
8578721SJonathan.Adams@Sun.COM 			    sobj, sizeof (sobj));
8588721SJonathan.Adams@Sun.COM 
8598721SJonathan.Adams@Sun.COM 			if (cur == sep)
860*12902SBryan.Cantrill@Sun.COM 				mdb_printf("%-?p %-8s %-?s %8d\n",
8618721SJonathan.Adams@Sun.COM 				    cur->se_thread, state, sobj, count);
8628721SJonathan.Adams@Sun.COM 			else
863*12902SBryan.Cantrill@Sun.COM 				mdb_printf("%-?p %-8s %-?s %8s\n",
8648721SJonathan.Adams@Sun.COM 				    cur->se_thread, state, sobj, "-");
8658721SJonathan.Adams@Sun.COM 
8668721SJonathan.Adams@Sun.COM 			cur = only_matching ? cur->se_next : cur->se_dup;
8678721SJonathan.Adams@Sun.COM 		} while (all && cur != NULL);
8688721SJonathan.Adams@Sun.COM 
8698721SJonathan.Adams@Sun.COM 		if (sep->se_failed != 0) {
8708721SJonathan.Adams@Sun.COM 			char *reason;
8718721SJonathan.Adams@Sun.COM 			switch (sep->se_failed) {
8728721SJonathan.Adams@Sun.COM 			case FSI_FAIL_NOTINMEMORY:
8738721SJonathan.Adams@Sun.COM 				reason = "thread not in memory";
8748721SJonathan.Adams@Sun.COM 				break;
8758721SJonathan.Adams@Sun.COM 			case FSI_FAIL_THREADCORRUPT:
8768721SJonathan.Adams@Sun.COM 				reason = "thread structure stack info corrupt";
8778721SJonathan.Adams@Sun.COM 				break;
8788721SJonathan.Adams@Sun.COM 			case FSI_FAIL_STACKNOTFOUND:
8798721SJonathan.Adams@Sun.COM 				reason = "no consistent stack found";
8808721SJonathan.Adams@Sun.COM 				break;
8818721SJonathan.Adams@Sun.COM 			default:
8828721SJonathan.Adams@Sun.COM 				reason = "unknown failure";
8838721SJonathan.Adams@Sun.COM 				break;
8848721SJonathan.Adams@Sun.COM 			}
8858721SJonathan.Adams@Sun.COM 			mdb_printf("%?s <%s>\n", "", reason);
8868721SJonathan.Adams@Sun.COM 		}
8878721SJonathan.Adams@Sun.COM 
8888721SJonathan.Adams@Sun.COM 		for (frame = 0; frame < sep->se_depth; frame++)
8898721SJonathan.Adams@Sun.COM 			mdb_printf("%?s %a\n", "", sep->se_stack[frame]);
8908721SJonathan.Adams@Sun.COM 		if (sep->se_overflow)
8918721SJonathan.Adams@Sun.COM 			mdb_printf("%?s ... truncated ...\n", "");
8928721SJonathan.Adams@Sun.COM 		mdb_printf("\n");
8938721SJonathan.Adams@Sun.COM 	}
8948721SJonathan.Adams@Sun.COM 
8958721SJonathan.Adams@Sun.COM 	if (flags & DCMD_ADDRSPEC) {
8968721SJonathan.Adams@Sun.COM 		for (idx = 0; idx < p.pipe_len; idx++)
8978721SJonathan.Adams@Sun.COM 			if (seen[idx] == 0)
8988721SJonathan.Adams@Sun.COM 				mdb_warn("stacks: %p not in thread list\n",
8998721SJonathan.Adams@Sun.COM 				    p.pipe_data[idx]);
9008721SJonathan.Adams@Sun.COM 	}
9018721SJonathan.Adams@Sun.COM 	return (DCMD_OK);
9028721SJonathan.Adams@Sun.COM }
903