xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/findstack.c (revision 8745:6864f0b4a0e6)
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  */
210Sstevel@tonic-gate /*
228721SJonathan.Adams@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
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>
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include "findstack.h"
368721SJonathan.Adams@Sun.COM #include "thread.h"
378721SJonathan.Adams@Sun.COM #include "sobj.h"
388721SJonathan.Adams@Sun.COM 
398721SJonathan.Adams@Sun.COM typedef struct findstack_info {
408721SJonathan.Adams@Sun.COM 	uintptr_t	*fsi_stack;	/* place to record frames */
418721SJonathan.Adams@Sun.COM 
428721SJonathan.Adams@Sun.COM 	uintptr_t	fsi_sp;		/* stack pointer */
438721SJonathan.Adams@Sun.COM 	uintptr_t	fsi_pc;		/* pc */
448721SJonathan.Adams@Sun.COM 	uintptr_t	fsi_sobj_ops;	/* sobj_ops */
458721SJonathan.Adams@Sun.COM 
468721SJonathan.Adams@Sun.COM 	uint_t		fsi_tstate;	/* t_state */
478721SJonathan.Adams@Sun.COM 
488721SJonathan.Adams@Sun.COM 	uchar_t		fsi_depth;	/* stack depth */
498721SJonathan.Adams@Sun.COM 	uchar_t		fsi_failed;	/* search failed */
508721SJonathan.Adams@Sun.COM 	uchar_t		fsi_overflow;	/* stack was deeper than max_depth */
518721SJonathan.Adams@Sun.COM 	uchar_t		fsi_panic;	/* thread called panic() */
528721SJonathan.Adams@Sun.COM 
538721SJonathan.Adams@Sun.COM 	uchar_t		fsi_max_depth;	/* stack frames available */
548721SJonathan.Adams@Sun.COM } findstack_info_t;
558721SJonathan.Adams@Sun.COM #define	FSI_FAIL_BADTHREAD	1
568721SJonathan.Adams@Sun.COM #define	FSI_FAIL_NOTINMEMORY	2
578721SJonathan.Adams@Sun.COM #define	FSI_FAIL_THREADCORRUPT	3
588721SJonathan.Adams@Sun.COM #define	FSI_FAIL_STACKNOTFOUND	4
590Sstevel@tonic-gate 
600Sstevel@tonic-gate #ifndef STACK_BIAS
610Sstevel@tonic-gate #define	STACK_BIAS	0
620Sstevel@tonic-gate #endif
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	fs_dprintf(x)					\
650Sstevel@tonic-gate 	if (findstack_debug_on) {			\
660Sstevel@tonic-gate 		mdb_printf("findstack debug: ");	\
670Sstevel@tonic-gate 		/*CSTYLED*/				\
680Sstevel@tonic-gate 		mdb_printf x ;				\
690Sstevel@tonic-gate 	}
700Sstevel@tonic-gate 
710Sstevel@tonic-gate static int findstack_debug_on = 0;
720Sstevel@tonic-gate 
730Sstevel@tonic-gate #if defined(__i386) || defined(__amd64)
740Sstevel@tonic-gate struct rwindow {
750Sstevel@tonic-gate 	uintptr_t rw_fp;
768721SJonathan.Adams@Sun.COM 	uintptr_t rw_rtn;
770Sstevel@tonic-gate };
780Sstevel@tonic-gate #endif
790Sstevel@tonic-gate 
800Sstevel@tonic-gate #define	TOO_BIG_FOR_A_STACK (1024 * 1024)
810Sstevel@tonic-gate 
820Sstevel@tonic-gate #define	KTOU(p) ((p) - kbase + ubase)
830Sstevel@tonic-gate #define	UTOK(p) ((p) - ubase + kbase)
840Sstevel@tonic-gate 
850Sstevel@tonic-gate #define	CRAWL_FOUNDALL	(-1)
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * Given a stack pointer, try to crawl down it to the bottom.
890Sstevel@tonic-gate  * "frame" is a VA in MDB's address space.
900Sstevel@tonic-gate  *
910Sstevel@tonic-gate  * Returns the number of frames successfully crawled down, or
920Sstevel@tonic-gate  * CRAWL_FOUNDALL if it got to the bottom of the stack.
930Sstevel@tonic-gate  */
940Sstevel@tonic-gate static int
950Sstevel@tonic-gate crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase,
968721SJonathan.Adams@Sun.COM     int kill_fp, findstack_info_t *fsip)
970Sstevel@tonic-gate {
980Sstevel@tonic-gate 	int levels = 0;
990Sstevel@tonic-gate 
1008721SJonathan.Adams@Sun.COM 	fsip->fsi_depth = 0;
1018721SJonathan.Adams@Sun.COM 	fsip->fsi_overflow = 0;
1028721SJonathan.Adams@Sun.COM 
1030Sstevel@tonic-gate 	fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n",
1040Sstevel@tonic-gate 	    frame, kbase, ktop, ubase));
1050Sstevel@tonic-gate 	for (;;) {
1060Sstevel@tonic-gate 		uintptr_t fp;
1070Sstevel@tonic-gate 		long *fpp = (long *)&((struct rwindow *)frame)->rw_fp;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 		fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame));
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 		if ((frame & (STACK_ALIGN - 1)) != 0)
1120Sstevel@tonic-gate 			break;
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate 		fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS;
1158721SJonathan.Adams@Sun.COM 		if (fsip->fsi_depth < fsip->fsi_max_depth)
1168721SJonathan.Adams@Sun.COM 			fsip->fsi_stack[fsip->fsi_depth++] =
1178721SJonathan.Adams@Sun.COM 			    ((struct rwindow *)frame)->rw_rtn;
1188721SJonathan.Adams@Sun.COM 		else
1198721SJonathan.Adams@Sun.COM 			fsip->fsi_overflow = 1;
1208721SJonathan.Adams@Sun.COM 
1210Sstevel@tonic-gate 		fs_dprintf(("<2> fp = %p\n", fp));
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 		if (fp == ktop)
1240Sstevel@tonic-gate 			return (CRAWL_FOUNDALL);
1250Sstevel@tonic-gate 		fs_dprintf(("<3> not at base\n"));
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate #if defined(__i386) || defined(__amd64)
1280Sstevel@tonic-gate 		if (ktop - fp == sizeof (struct rwindow)) {
1290Sstevel@tonic-gate 			fs_dprintf(("<4> found base\n"));
1300Sstevel@tonic-gate 			return (CRAWL_FOUNDALL);
1310Sstevel@tonic-gate 		}
1320Sstevel@tonic-gate #endif
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 		fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n",
1350Sstevel@tonic-gate 		    fp, kbase, ktop - sizeof (struct rwindow)));
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 		if (fp < kbase || fp >= (ktop - sizeof (struct rwindow)))
1380Sstevel@tonic-gate 			break;
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 		frame = KTOU(fp);
1410Sstevel@tonic-gate 		fs_dprintf(("<6> frame = %p\n", frame));
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 		/*
1440Sstevel@tonic-gate 		 * NULL out the old %fp so we don't go down this stack
1450Sstevel@tonic-gate 		 * more than once.
1460Sstevel@tonic-gate 		 */
1470Sstevel@tonic-gate 		if (kill_fp) {
1480Sstevel@tonic-gate 			fs_dprintf(("<7> fpp = %p\n", fpp));
1490Sstevel@tonic-gate 			*fpp = NULL;
1500Sstevel@tonic-gate 		}
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 		fs_dprintf(("<8> levels = %d\n", levels));
1530Sstevel@tonic-gate 		levels++;
1540Sstevel@tonic-gate 	}
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	return (levels);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate /*
1600Sstevel@tonic-gate  * "sp" is a kernel VA.
1610Sstevel@tonic-gate  */
1620Sstevel@tonic-gate static int
1630Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr,
1640Sstevel@tonic-gate     int argc, const mdb_arg_t *argv, int free_state)
1650Sstevel@tonic-gate {
1660Sstevel@tonic-gate 	int showargs = 0, count, err;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	count = mdb_getopts(argc, argv,
1690Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL);
1700Sstevel@tonic-gate 	argc -= count;
1710Sstevel@tonic-gate 	argv += count;
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING))
1740Sstevel@tonic-gate 		return (DCMD_USAGE);
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	mdb_printf("stack pointer for thread %p%s: %p\n",
1770Sstevel@tonic-gate 	    addr, (free_state ? " (TS_FREE)" : ""), sp);
1780Sstevel@tonic-gate 	if (pc != 0)
1790Sstevel@tonic-gate 		mdb_printf("[ %0?lr %a() ]\n", sp, pc);
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	mdb_inc_indent(2);
1820Sstevel@tonic-gate 	mdb_set_dot(sp);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	if (argc == 1)
1850Sstevel@tonic-gate 		err = mdb_eval(argv->a_un.a_str);
1860Sstevel@tonic-gate 	else if (showargs)
1870Sstevel@tonic-gate 		err = mdb_eval("<.$C");
1880Sstevel@tonic-gate 	else
1890Sstevel@tonic-gate 		err = mdb_eval("<.$C0");
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	mdb_dec_indent(2);
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	return ((err == -1) ? DCMD_ABORT : DCMD_OK);
1940Sstevel@tonic-gate }
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate /*ARGSUSED*/
1978721SJonathan.Adams@Sun.COM static int
1988721SJonathan.Adams@Sun.COM do_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
1990Sstevel@tonic-gate {
2000Sstevel@tonic-gate 	kthread_t thr;
2010Sstevel@tonic-gate 	size_t stksz;
2020Sstevel@tonic-gate 	uintptr_t ubase, utop;
2030Sstevel@tonic-gate 	uintptr_t kbase, ktop;
2040Sstevel@tonic-gate 	uintptr_t win, sp;
2050Sstevel@tonic-gate 
2068721SJonathan.Adams@Sun.COM 	fsip->fsi_failed = 0;
2078721SJonathan.Adams@Sun.COM 	fsip->fsi_pc = 0;
2088721SJonathan.Adams@Sun.COM 	fsip->fsi_sp = 0;
2098721SJonathan.Adams@Sun.COM 	fsip->fsi_depth = 0;
2108721SJonathan.Adams@Sun.COM 	fsip->fsi_overflow = 0;
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	bzero(&thr, sizeof (thr));
2130Sstevel@tonic-gate 	if (mdb_ctf_vread(&thr, "kthread_t", addr,
2140Sstevel@tonic-gate 	    MDB_CTF_VREAD_IGNORE_ALL) == -1) {
2158721SJonathan.Adams@Sun.COM 		if (print_warnings)
2168721SJonathan.Adams@Sun.COM 			mdb_warn("couldn't read thread at %p\n", addr);
2178721SJonathan.Adams@Sun.COM 		fsip->fsi_failed = FSI_FAIL_BADTHREAD;
2180Sstevel@tonic-gate 		return (DCMD_ERR);
2190Sstevel@tonic-gate 	}
2200Sstevel@tonic-gate 
2218721SJonathan.Adams@Sun.COM 	fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops;
2228721SJonathan.Adams@Sun.COM 	fsip->fsi_tstate = thr.t_state;
2238721SJonathan.Adams@Sun.COM 	fsip->fsi_panic = !!(thr.t_flag & T_PANIC);
2248721SJonathan.Adams@Sun.COM 
2250Sstevel@tonic-gate 	if ((thr.t_schedflag & TS_LOAD) == 0) {
2268721SJonathan.Adams@Sun.COM 		if (print_warnings)
2278721SJonathan.Adams@Sun.COM 			mdb_warn("thread %p isn't in memory\n", addr);
2288721SJonathan.Adams@Sun.COM 		fsip->fsi_failed = FSI_FAIL_NOTINMEMORY;
2290Sstevel@tonic-gate 		return (DCMD_ERR);
2300Sstevel@tonic-gate 	}
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	if (thr.t_stk < thr.t_stkbase) {
2338721SJonathan.Adams@Sun.COM 		if (print_warnings)
2348721SJonathan.Adams@Sun.COM 			mdb_warn(
2358721SJonathan.Adams@Sun.COM 			    "stack base or stack top corrupt for thread %p\n",
2368721SJonathan.Adams@Sun.COM 			    addr);
2378721SJonathan.Adams@Sun.COM 		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
2380Sstevel@tonic-gate 		return (DCMD_ERR);
2390Sstevel@tonic-gate 	}
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	kbase = (uintptr_t)thr.t_stkbase;
2420Sstevel@tonic-gate 	ktop = (uintptr_t)thr.t_stk;
2430Sstevel@tonic-gate 	stksz = ktop - kbase;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate #ifdef __amd64
2460Sstevel@tonic-gate 	/*
2470Sstevel@tonic-gate 	 * The stack on amd64 is intentionally misaligned, so ignore the top
2480Sstevel@tonic-gate 	 * half-frame.  See thread_stk_init().  When handling traps, the frame
2490Sstevel@tonic-gate 	 * is automatically aligned by the hardware, so we only alter ktop if
2500Sstevel@tonic-gate 	 * needed.
2510Sstevel@tonic-gate 	 */
2520Sstevel@tonic-gate 	if ((ktop & (STACK_ALIGN - 1)) != 0)
2530Sstevel@tonic-gate 		ktop -= STACK_ENTRY_ALIGN;
2540Sstevel@tonic-gate #endif
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	/*
2570Sstevel@tonic-gate 	 * If the stack size is larger than a meg, assume that it's bogus.
2580Sstevel@tonic-gate 	 */
2590Sstevel@tonic-gate 	if (stksz > TOO_BIG_FOR_A_STACK) {
2608721SJonathan.Adams@Sun.COM 		if (print_warnings)
2618721SJonathan.Adams@Sun.COM 			mdb_warn("stack size for thread %p is too big to be "
2628721SJonathan.Adams@Sun.COM 			    "reasonable\n", addr);
2638721SJonathan.Adams@Sun.COM 		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
2640Sstevel@tonic-gate 		return (DCMD_ERR);
2650Sstevel@tonic-gate 	}
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	/*
2680Sstevel@tonic-gate 	 * This could be (and was) a UM_GC allocation.  Unfortunately,
2690Sstevel@tonic-gate 	 * stksz tends to be very large.  As currently implemented, dcmds
2700Sstevel@tonic-gate 	 * invoked as part of pipelines don't have their UM_GC-allocated
2710Sstevel@tonic-gate 	 * memory freed until the pipeline completes.  With stksz in the
2720Sstevel@tonic-gate 	 * neighborhood of 20k, the popular ::walk thread |::findstack
2730Sstevel@tonic-gate 	 * pipeline can easily run memory-constrained debuggers (kmdb) out
2740Sstevel@tonic-gate 	 * of memory.  This can be changed back to a gc-able allocation when
2750Sstevel@tonic-gate 	 * the debugger is changed to free UM_GC memory more promptly.
2760Sstevel@tonic-gate 	 */
2770Sstevel@tonic-gate 	ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP);
2780Sstevel@tonic-gate 	utop = ubase + stksz;
2790Sstevel@tonic-gate 	if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) {
2800Sstevel@tonic-gate 		mdb_free((void *)ubase, stksz);
2818721SJonathan.Adams@Sun.COM 		if (print_warnings)
2828721SJonathan.Adams@Sun.COM 			mdb_warn("couldn't read entire stack for thread %p\n",
2838721SJonathan.Adams@Sun.COM 			    addr);
2848721SJonathan.Adams@Sun.COM 		fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
2850Sstevel@tonic-gate 		return (DCMD_ERR);
2860Sstevel@tonic-gate 	}
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	/*
2890Sstevel@tonic-gate 	 * Try the saved %sp first, if it looks reasonable.
2900Sstevel@tonic-gate 	 */
2910Sstevel@tonic-gate 	sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS);
2920Sstevel@tonic-gate 	if (sp >= ubase && sp <= utop) {
2938721SJonathan.Adams@Sun.COM 		if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) {
2948721SJonathan.Adams@Sun.COM 			fsip->fsi_sp = (uintptr_t)thr.t_sp;
2958721SJonathan.Adams@Sun.COM #if !defined(__i386)
2968721SJonathan.Adams@Sun.COM 			fsip->fsi_pc = (uintptr_t)thr.t_pc;
2970Sstevel@tonic-gate #endif
2988721SJonathan.Adams@Sun.COM 			goto found;
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 	}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/*
3030Sstevel@tonic-gate 	 * Now walk through the whole stack, starting at the base,
3040Sstevel@tonic-gate 	 * trying every possible "window".
3050Sstevel@tonic-gate 	 */
3060Sstevel@tonic-gate 	for (win = ubase;
3070Sstevel@tonic-gate 	    win + sizeof (struct rwindow) <= utop;
3080Sstevel@tonic-gate 	    win += sizeof (struct rwindow *)) {
3098721SJonathan.Adams@Sun.COM 		if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) {
3108721SJonathan.Adams@Sun.COM 			fsip->fsi_sp = UTOK(win) - STACK_BIAS;
3118721SJonathan.Adams@Sun.COM 			goto found;
3120Sstevel@tonic-gate 		}
3130Sstevel@tonic-gate 	}
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	/*
3160Sstevel@tonic-gate 	 * We didn't conclusively find the stack.  So we'll take another lap,
3170Sstevel@tonic-gate 	 * and print out anything that looks possible.
3180Sstevel@tonic-gate 	 */
3198721SJonathan.Adams@Sun.COM 	if (print_warnings)
3208721SJonathan.Adams@Sun.COM 		mdb_printf("Possible stack pointers for thread %p:\n", addr);
3210Sstevel@tonic-gate 	(void) mdb_vread((caddr_t)ubase, stksz, kbase);
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	for (win = ubase;
3240Sstevel@tonic-gate 	    win + sizeof (struct rwindow) <= utop;
3250Sstevel@tonic-gate 	    win += sizeof (struct rwindow *)) {
3260Sstevel@tonic-gate 		uintptr_t fp = ((struct rwindow *)win)->rw_fp;
3270Sstevel@tonic-gate 		int levels;
3280Sstevel@tonic-gate 
3298721SJonathan.Adams@Sun.COM 		if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) {
3308721SJonathan.Adams@Sun.COM 			if (print_warnings)
3318721SJonathan.Adams@Sun.COM 				mdb_printf("  %p (%d)\n", fp, levels);
3320Sstevel@tonic-gate 		} else if (levels == CRAWL_FOUNDALL) {
3330Sstevel@tonic-gate 			/*
3340Sstevel@tonic-gate 			 * If this is a live system, the stack could change
3350Sstevel@tonic-gate 			 * between the two mdb_vread(ubase, utop, kbase)'s,
3360Sstevel@tonic-gate 			 * and we could have a fully valid stack here.
3370Sstevel@tonic-gate 			 */
3388721SJonathan.Adams@Sun.COM 			fsip->fsi_sp = UTOK(win) - STACK_BIAS;
3398721SJonathan.Adams@Sun.COM 			goto found;
3400Sstevel@tonic-gate 		}
3410Sstevel@tonic-gate 	}
3420Sstevel@tonic-gate 
3438721SJonathan.Adams@Sun.COM 	fsip->fsi_depth = 0;
3448721SJonathan.Adams@Sun.COM 	fsip->fsi_overflow = 0;
3458721SJonathan.Adams@Sun.COM 	fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND;
3468721SJonathan.Adams@Sun.COM 
3478721SJonathan.Adams@Sun.COM 	mdb_free((void *)ubase, stksz);
3488721SJonathan.Adams@Sun.COM 	return (DCMD_ERR);
3498721SJonathan.Adams@Sun.COM found:
3500Sstevel@tonic-gate 	mdb_free((void *)ubase, stksz);
3510Sstevel@tonic-gate 	return (DCMD_OK);
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate 
3548721SJonathan.Adams@Sun.COM int
3558721SJonathan.Adams@Sun.COM findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3568721SJonathan.Adams@Sun.COM {
3578721SJonathan.Adams@Sun.COM 	findstack_info_t fsi;
3588721SJonathan.Adams@Sun.COM 	int retval;
3598721SJonathan.Adams@Sun.COM 
3608721SJonathan.Adams@Sun.COM 	if (!(flags & DCMD_ADDRSPEC))
3618721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
3628721SJonathan.Adams@Sun.COM 
3638721SJonathan.Adams@Sun.COM 	bzero(&fsi, sizeof (fsi));
3648721SJonathan.Adams@Sun.COM 
3658721SJonathan.Adams@Sun.COM 	if ((retval = do_findstack(addr, &fsi, 1)) != DCMD_OK ||
3668721SJonathan.Adams@Sun.COM 	    fsi.fsi_failed)
3678721SJonathan.Adams@Sun.COM 		return (retval);
3688721SJonathan.Adams@Sun.COM 
3698721SJonathan.Adams@Sun.COM 	return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr,
3708721SJonathan.Adams@Sun.COM 	    argc, argv, fsi.fsi_tstate == TS_FREE));
3718721SJonathan.Adams@Sun.COM }
3728721SJonathan.Adams@Sun.COM 
3730Sstevel@tonic-gate /*ARGSUSED*/
3740Sstevel@tonic-gate int
3750Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av)
3760Sstevel@tonic-gate {
3770Sstevel@tonic-gate 	findstack_debug_on ^= 1;
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	mdb_printf("findstack: debugging is now %s\n",
3800Sstevel@tonic-gate 	    findstack_debug_on ? "on" : "off");
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	return (DCMD_OK);
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate 
3858721SJonathan.Adams@Sun.COM static void
3868721SJonathan.Adams@Sun.COM uppercase(char *p)
3878721SJonathan.Adams@Sun.COM {
3888721SJonathan.Adams@Sun.COM 	for (; *p != '\0'; p++) {
3898721SJonathan.Adams@Sun.COM 		if (*p >= 'a' && *p <= 'z')
3908721SJonathan.Adams@Sun.COM 			*p += 'A' - 'a';
3918721SJonathan.Adams@Sun.COM 	}
3928721SJonathan.Adams@Sun.COM }
3938721SJonathan.Adams@Sun.COM 
3948721SJonathan.Adams@Sun.COM static void
3958721SJonathan.Adams@Sun.COM sobj_to_text(uintptr_t addr, char *out, size_t out_sz)
3968721SJonathan.Adams@Sun.COM {
3978721SJonathan.Adams@Sun.COM 	sobj_ops_to_text(addr, out, out_sz);
3988721SJonathan.Adams@Sun.COM 	uppercase(out);
3998721SJonathan.Adams@Sun.COM }
4008721SJonathan.Adams@Sun.COM 
4018721SJonathan.Adams@Sun.COM #define	SOBJ_ALL	1
4028721SJonathan.Adams@Sun.COM static int
4038721SJonathan.Adams@Sun.COM text_to_sobj(const char *text, uintptr_t *out)
4048721SJonathan.Adams@Sun.COM {
4058721SJonathan.Adams@Sun.COM 	if (strcasecmp(text, "ALL") == 0) {
4068721SJonathan.Adams@Sun.COM 		*out = SOBJ_ALL;
4078721SJonathan.Adams@Sun.COM 		return (0);
4088721SJonathan.Adams@Sun.COM 	}
4098721SJonathan.Adams@Sun.COM 	return (sobj_text_to_ops(text, out));
4108721SJonathan.Adams@Sun.COM }
4118721SJonathan.Adams@Sun.COM 
4128721SJonathan.Adams@Sun.COM #define	TSTATE_PANIC	-2U
4138721SJonathan.Adams@Sun.COM static int
4148721SJonathan.Adams@Sun.COM text_to_tstate(const char *text, uint_t *out)
4158721SJonathan.Adams@Sun.COM {
4168721SJonathan.Adams@Sun.COM 	if (strcasecmp(text, "panic") == 0)
4178721SJonathan.Adams@Sun.COM 		*out = TSTATE_PANIC;
4188721SJonathan.Adams@Sun.COM 	else if (thread_text_to_state(text, out) != 0) {
4198721SJonathan.Adams@Sun.COM 		mdb_warn("tstate \"%s\" not recognized\n", text);
4208721SJonathan.Adams@Sun.COM 		return (-1);
4218721SJonathan.Adams@Sun.COM 	}
4228721SJonathan.Adams@Sun.COM 	return (0);
4238721SJonathan.Adams@Sun.COM }
4248721SJonathan.Adams@Sun.COM 
4258721SJonathan.Adams@Sun.COM static void
4268721SJonathan.Adams@Sun.COM tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz)
4278721SJonathan.Adams@Sun.COM {
4288721SJonathan.Adams@Sun.COM 	if (paniced)
4298721SJonathan.Adams@Sun.COM 		mdb_snprintf(out, out_sz, "panic");
4308721SJonathan.Adams@Sun.COM 	else
4318721SJonathan.Adams@Sun.COM 		thread_state_to_text(tstate, out, out_sz);
4328721SJonathan.Adams@Sun.COM 	uppercase(out);
4338721SJonathan.Adams@Sun.COM }
4348721SJonathan.Adams@Sun.COM 
4358721SJonathan.Adams@Sun.COM typedef struct stacks_entry {
4368721SJonathan.Adams@Sun.COM 	struct stacks_entry	*se_next;
4378721SJonathan.Adams@Sun.COM 	struct stacks_entry	*se_dup;	/* dups of this stack */
4388721SJonathan.Adams@Sun.COM 	uintptr_t		se_thread;
4398721SJonathan.Adams@Sun.COM 	uintptr_t		se_sp;
4408721SJonathan.Adams@Sun.COM 	uintptr_t		se_sobj_ops;
4418721SJonathan.Adams@Sun.COM 	uint32_t		se_tstate;
4428721SJonathan.Adams@Sun.COM 	uint32_t		se_count;	/* # threads w/ this stack */
4438721SJonathan.Adams@Sun.COM 	uint8_t			se_overflow;
4448721SJonathan.Adams@Sun.COM 	uint8_t			se_depth;
4458721SJonathan.Adams@Sun.COM 	uint8_t			se_failed;	/* failure reason; FSI_FAIL_* */
4468721SJonathan.Adams@Sun.COM 	uint8_t			se_panic;
4478721SJonathan.Adams@Sun.COM 	uintptr_t		se_stack[1];
4488721SJonathan.Adams@Sun.COM } stacks_entry_t;
4498721SJonathan.Adams@Sun.COM #define	STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)])
4508721SJonathan.Adams@Sun.COM 
4518721SJonathan.Adams@Sun.COM #define	STACKS_HSIZE 127
4528721SJonathan.Adams@Sun.COM 
4538721SJonathan.Adams@Sun.COM /* Maximum stack depth reported in stacks */
4548721SJonathan.Adams@Sun.COM #define	STACKS_MAX_DEPTH	254
4558721SJonathan.Adams@Sun.COM 
4568721SJonathan.Adams@Sun.COM typedef struct stacks_info {
4578721SJonathan.Adams@Sun.COM 	size_t		si_count;	/* total stacks_entry_ts (incl dups) */
4588721SJonathan.Adams@Sun.COM 	size_t		si_entries;	/* # entries in hash table */
4598721SJonathan.Adams@Sun.COM 	stacks_entry_t	**si_hash;	/* hash table */
4608721SJonathan.Adams@Sun.COM 
4618721SJonathan.Adams@Sun.COM 	findstack_info_t si_fsi;	/* transient callback state */
4628721SJonathan.Adams@Sun.COM } stacks_info_t;
4638721SJonathan.Adams@Sun.COM 
4648721SJonathan.Adams@Sun.COM 
4658721SJonathan.Adams@Sun.COM /* global state cached between invocations */
4668721SJonathan.Adams@Sun.COM #define	STACKS_STATE_CLEAN	0
4678721SJonathan.Adams@Sun.COM #define	STACKS_STATE_DIRTY	1
4688721SJonathan.Adams@Sun.COM #define	STACKS_STATE_DONE	2
4698721SJonathan.Adams@Sun.COM static uint_t stacks_state = STACKS_STATE_CLEAN;
4708721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_hash;
4718721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_array;
4728721SJonathan.Adams@Sun.COM static size_t stacks_array_size;
4738721SJonathan.Adams@Sun.COM 
4748721SJonathan.Adams@Sun.COM size_t
4758721SJonathan.Adams@Sun.COM stacks_hash_entry(stacks_entry_t *sep)
4768721SJonathan.Adams@Sun.COM {
4778721SJonathan.Adams@Sun.COM 	size_t depth = sep->se_depth;
4788721SJonathan.Adams@Sun.COM 	uintptr_t *stack = sep->se_stack;
4798721SJonathan.Adams@Sun.COM 
4808721SJonathan.Adams@Sun.COM 	uint64_t total = depth;
4818721SJonathan.Adams@Sun.COM 
4828721SJonathan.Adams@Sun.COM 	while (depth > 0) {
4838721SJonathan.Adams@Sun.COM 		total += *stack;
4848721SJonathan.Adams@Sun.COM 		stack++; depth--;
4858721SJonathan.Adams@Sun.COM 	}
4868721SJonathan.Adams@Sun.COM 
4878721SJonathan.Adams@Sun.COM 	return (total % STACKS_HSIZE);
4888721SJonathan.Adams@Sun.COM }
4898721SJonathan.Adams@Sun.COM 
4908721SJonathan.Adams@Sun.COM /*
4918721SJonathan.Adams@Sun.COM  * This is used to both compare stacks for equality and to sort the final
4928721SJonathan.Adams@Sun.COM  * list of unique stacks.  forsort specifies the latter behavior, which
4938721SJonathan.Adams@Sun.COM  * additionally:
4948721SJonathan.Adams@Sun.COM  *	compares se_count, and
4958721SJonathan.Adams@Sun.COM  *	sorts the stacks by text function name.
4968721SJonathan.Adams@Sun.COM  *
4978721SJonathan.Adams@Sun.COM  * The equality test is independent of se_count, and doesn't care about
4988721SJonathan.Adams@Sun.COM  * relative ordering, so we don't do the extra work of looking up symbols
4998721SJonathan.Adams@Sun.COM  * for the stack addresses.
5008721SJonathan.Adams@Sun.COM  */
5010Sstevel@tonic-gate int
5028721SJonathan.Adams@Sun.COM stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r,
5038721SJonathan.Adams@Sun.COM     uint_t forsort)
5040Sstevel@tonic-gate {
5058721SJonathan.Adams@Sun.COM 	int idx;
5068721SJonathan.Adams@Sun.COM 
5078721SJonathan.Adams@Sun.COM 	int depth = MIN(l->se_depth, r->se_depth);
5088721SJonathan.Adams@Sun.COM 
5098721SJonathan.Adams@Sun.COM 	/* no matter what, panic stacks come last. */
5108721SJonathan.Adams@Sun.COM 	if (l->se_panic > r->se_panic)
5118721SJonathan.Adams@Sun.COM 		return (1);
5128721SJonathan.Adams@Sun.COM 	if (l->se_panic < r->se_panic)
5138721SJonathan.Adams@Sun.COM 		return (-1);
5148721SJonathan.Adams@Sun.COM 
5158721SJonathan.Adams@Sun.COM 	if (forsort) {
5168721SJonathan.Adams@Sun.COM 		/* put large counts earlier */
5178721SJonathan.Adams@Sun.COM 		if (l->se_count > r->se_count)
5188721SJonathan.Adams@Sun.COM 			return (-1);
5198721SJonathan.Adams@Sun.COM 		if (l->se_count < r->se_count)
5208721SJonathan.Adams@Sun.COM 			return (1);
5218721SJonathan.Adams@Sun.COM 	}
5228721SJonathan.Adams@Sun.COM 
5238721SJonathan.Adams@Sun.COM 	if (l->se_tstate > r->se_tstate)
5248721SJonathan.Adams@Sun.COM 		return (1);
5258721SJonathan.Adams@Sun.COM 	if (l->se_tstate < r->se_tstate)
5268721SJonathan.Adams@Sun.COM 		return (-1);
5278721SJonathan.Adams@Sun.COM 
5288721SJonathan.Adams@Sun.COM 	if (l->se_failed > r->se_failed)
5298721SJonathan.Adams@Sun.COM 		return (1);
5308721SJonathan.Adams@Sun.COM 	if (l->se_failed < r->se_failed)
5318721SJonathan.Adams@Sun.COM 		return (-1);
5328721SJonathan.Adams@Sun.COM 
5338721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < depth; idx++) {
5348721SJonathan.Adams@Sun.COM 		char lbuf[MDB_SYM_NAMLEN];
5358721SJonathan.Adams@Sun.COM 		char rbuf[MDB_SYM_NAMLEN];
5368721SJonathan.Adams@Sun.COM 
5378721SJonathan.Adams@Sun.COM 		int rval;
5388721SJonathan.Adams@Sun.COM 		uintptr_t laddr = l->se_stack[idx];
5398721SJonathan.Adams@Sun.COM 		uintptr_t raddr = r->se_stack[idx];
5408721SJonathan.Adams@Sun.COM 
5418721SJonathan.Adams@Sun.COM 		if (laddr == raddr)
5428721SJonathan.Adams@Sun.COM 			continue;
5438721SJonathan.Adams@Sun.COM 
5448721SJonathan.Adams@Sun.COM 		if (forsort &&
5458721SJonathan.Adams@Sun.COM 		    mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY,
5468721SJonathan.Adams@Sun.COM 		    lbuf, sizeof (lbuf), NULL) != -1 &&
5478721SJonathan.Adams@Sun.COM 		    mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY,
5488721SJonathan.Adams@Sun.COM 		    rbuf, sizeof (rbuf), NULL) != -1 &&
5498721SJonathan.Adams@Sun.COM 		    (rval = strcmp(lbuf, rbuf)) != 0)
5508721SJonathan.Adams@Sun.COM 			return (rval);
5518721SJonathan.Adams@Sun.COM 
5528721SJonathan.Adams@Sun.COM 		if (laddr > raddr)
5538721SJonathan.Adams@Sun.COM 			return (1);
5548721SJonathan.Adams@Sun.COM 		return (-1);
5550Sstevel@tonic-gate 	}
5568721SJonathan.Adams@Sun.COM 
5578721SJonathan.Adams@Sun.COM 	if (l->se_overflow > r->se_overflow)
5588721SJonathan.Adams@Sun.COM 		return (-1);
5598721SJonathan.Adams@Sun.COM 	if (l->se_overflow < r->se_overflow)
5608721SJonathan.Adams@Sun.COM 		return (1);
5618721SJonathan.Adams@Sun.COM 
5628721SJonathan.Adams@Sun.COM 	if (l->se_depth > r->se_depth)
5638721SJonathan.Adams@Sun.COM 		return (1);
5648721SJonathan.Adams@Sun.COM 	if (l->se_depth < r->se_depth)
5658721SJonathan.Adams@Sun.COM 		return (-1);
5668721SJonathan.Adams@Sun.COM 
5678721SJonathan.Adams@Sun.COM 	if (l->se_sobj_ops > r->se_sobj_ops)
5688721SJonathan.Adams@Sun.COM 		return (1);
5698721SJonathan.Adams@Sun.COM 	if (l->se_sobj_ops < r->se_sobj_ops)
5708721SJonathan.Adams@Sun.COM 		return (-1);
5718721SJonathan.Adams@Sun.COM 
5728721SJonathan.Adams@Sun.COM 	return (0);
5738721SJonathan.Adams@Sun.COM }
5748721SJonathan.Adams@Sun.COM 
5758721SJonathan.Adams@Sun.COM int
5768721SJonathan.Adams@Sun.COM stacks_entry_comp(const void *l_arg, const void *r_arg)
5778721SJonathan.Adams@Sun.COM {
5788721SJonathan.Adams@Sun.COM 	stacks_entry_t * const *lp = l_arg;
5798721SJonathan.Adams@Sun.COM 	stacks_entry_t * const *rp = r_arg;
5808721SJonathan.Adams@Sun.COM 
5818721SJonathan.Adams@Sun.COM 	return (stacks_entry_comp_impl(*lp, *rp, 1));
5828721SJonathan.Adams@Sun.COM }
5838721SJonathan.Adams@Sun.COM 
5848721SJonathan.Adams@Sun.COM void
5858721SJonathan.Adams@Sun.COM stacks_cleanup(int force)
5868721SJonathan.Adams@Sun.COM {
5878721SJonathan.Adams@Sun.COM 	int idx = 0;
5888721SJonathan.Adams@Sun.COM 	stacks_entry_t *cur, *next;
5898721SJonathan.Adams@Sun.COM 
5908721SJonathan.Adams@Sun.COM 	if (stacks_state == STACKS_STATE_CLEAN)
5918721SJonathan.Adams@Sun.COM 		return;
5928721SJonathan.Adams@Sun.COM 
5938721SJonathan.Adams@Sun.COM 	if (!force && stacks_state == STACKS_STATE_DONE)
5948721SJonathan.Adams@Sun.COM 		return;
5958721SJonathan.Adams@Sun.COM 
5968721SJonathan.Adams@Sun.COM 	/*
5978721SJonathan.Adams@Sun.COM 	 * Until the array is sorted and stable, stacks_hash will be non-NULL.
5988721SJonathan.Adams@Sun.COM 	 * This way, we can get at all of the data, even if qsort() was
5998721SJonathan.Adams@Sun.COM 	 * interrupted while mucking with the array.
6008721SJonathan.Adams@Sun.COM 	 */
6018721SJonathan.Adams@Sun.COM 	if (stacks_hash != NULL) {
6028721SJonathan.Adams@Sun.COM 		for (idx = 0; idx < STACKS_HSIZE; idx++) {
6038721SJonathan.Adams@Sun.COM 			while ((cur = stacks_hash[idx]) != NULL) {
6048721SJonathan.Adams@Sun.COM 				while ((next = cur->se_dup) != NULL) {
6058721SJonathan.Adams@Sun.COM 					cur->se_dup = next->se_dup;
6068721SJonathan.Adams@Sun.COM 					mdb_free(next,
6078721SJonathan.Adams@Sun.COM 					    STACKS_ENTRY_SIZE(next->se_depth));
6088721SJonathan.Adams@Sun.COM 				}
6098721SJonathan.Adams@Sun.COM 				next = cur->se_next;
6108721SJonathan.Adams@Sun.COM 				stacks_hash[idx] = next;
6118721SJonathan.Adams@Sun.COM 				mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
6128721SJonathan.Adams@Sun.COM 			}
6138721SJonathan.Adams@Sun.COM 		}
6148721SJonathan.Adams@Sun.COM 		if (stacks_array != NULL)
6158721SJonathan.Adams@Sun.COM 			mdb_free(stacks_array,
6168721SJonathan.Adams@Sun.COM 			    stacks_array_size * sizeof (*stacks_array));
6178721SJonathan.Adams@Sun.COM 
6188721SJonathan.Adams@Sun.COM 	} else if (stacks_array != NULL) {
6198721SJonathan.Adams@Sun.COM 		for (idx = 0; idx < stacks_array_size; idx++) {
6208721SJonathan.Adams@Sun.COM 			if ((cur = stacks_array[idx]) != NULL) {
6218721SJonathan.Adams@Sun.COM 				while ((next = cur->se_dup) != NULL) {
6228721SJonathan.Adams@Sun.COM 					cur->se_dup = next->se_dup;
6238721SJonathan.Adams@Sun.COM 					mdb_free(next,
6248721SJonathan.Adams@Sun.COM 					    STACKS_ENTRY_SIZE(next->se_depth));
6258721SJonathan.Adams@Sun.COM 				}
6268721SJonathan.Adams@Sun.COM 				stacks_array[idx] = NULL;
6278721SJonathan.Adams@Sun.COM 				mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
6288721SJonathan.Adams@Sun.COM 			}
6298721SJonathan.Adams@Sun.COM 		}
6308721SJonathan.Adams@Sun.COM 		mdb_free(stacks_array,
6318721SJonathan.Adams@Sun.COM 		    stacks_array_size * sizeof (*stacks_array));
6328721SJonathan.Adams@Sun.COM 	}
6338721SJonathan.Adams@Sun.COM 
6348721SJonathan.Adams@Sun.COM 	stacks_array_size = 0;
6358721SJonathan.Adams@Sun.COM 	stacks_state = STACKS_STATE_CLEAN;
6368721SJonathan.Adams@Sun.COM }
6378721SJonathan.Adams@Sun.COM 
6388721SJonathan.Adams@Sun.COM /*ARGSUSED*/
6398721SJonathan.Adams@Sun.COM int
6408721SJonathan.Adams@Sun.COM stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg)
6418721SJonathan.Adams@Sun.COM {
6428721SJonathan.Adams@Sun.COM 	stacks_info_t *sip = cbarg;
6438721SJonathan.Adams@Sun.COM 	findstack_info_t *fsip = &sip->si_fsi;
6448721SJonathan.Adams@Sun.COM 
6458721SJonathan.Adams@Sun.COM 	stacks_entry_t **sepp, *nsep, *sep;
6468721SJonathan.Adams@Sun.COM 	int idx;
6478721SJonathan.Adams@Sun.COM 	size_t depth;
6488721SJonathan.Adams@Sun.COM 
6498721SJonathan.Adams@Sun.COM 	if (do_findstack(addr, fsip, 0) != DCMD_OK &&
6508721SJonathan.Adams@Sun.COM 	    fsip->fsi_failed == FSI_FAIL_BADTHREAD) {
6518721SJonathan.Adams@Sun.COM 		mdb_warn("couldn't read thread at %p\n", addr);
6528721SJonathan.Adams@Sun.COM 		return (WALK_NEXT);
6538721SJonathan.Adams@Sun.COM 	}
6548721SJonathan.Adams@Sun.COM 
6558721SJonathan.Adams@Sun.COM 	sip->si_count++;
6568721SJonathan.Adams@Sun.COM 
6578721SJonathan.Adams@Sun.COM 	depth = fsip->fsi_depth;
6588721SJonathan.Adams@Sun.COM 	nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP);
6598721SJonathan.Adams@Sun.COM 	nsep->se_thread = addr;
6608721SJonathan.Adams@Sun.COM 	nsep->se_sp = fsip->fsi_sp;
6618721SJonathan.Adams@Sun.COM 	nsep->se_sobj_ops = fsip->fsi_sobj_ops;
6628721SJonathan.Adams@Sun.COM 	nsep->se_tstate = fsip->fsi_tstate;
6638721SJonathan.Adams@Sun.COM 	nsep->se_count = 1;
6648721SJonathan.Adams@Sun.COM 	nsep->se_overflow = fsip->fsi_overflow;
6658721SJonathan.Adams@Sun.COM 	nsep->se_depth = depth;
6668721SJonathan.Adams@Sun.COM 	nsep->se_failed = fsip->fsi_failed;
6678721SJonathan.Adams@Sun.COM 	nsep->se_panic = fsip->fsi_panic;
6688721SJonathan.Adams@Sun.COM 
6698721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < depth; idx++)
6708721SJonathan.Adams@Sun.COM 		nsep->se_stack[idx] = fsip->fsi_stack[idx];
6718721SJonathan.Adams@Sun.COM 
6728721SJonathan.Adams@Sun.COM 	for (sepp = &sip->si_hash[stacks_hash_entry(nsep)];
6738721SJonathan.Adams@Sun.COM 	    (sep = *sepp) != NULL;
6748721SJonathan.Adams@Sun.COM 	    sepp = &sep->se_next) {
6758721SJonathan.Adams@Sun.COM 
6768721SJonathan.Adams@Sun.COM 		if (stacks_entry_comp_impl(sep, nsep, 0) != 0)
6778721SJonathan.Adams@Sun.COM 			continue;
6788721SJonathan.Adams@Sun.COM 
6798721SJonathan.Adams@Sun.COM 		nsep->se_dup = sep->se_dup;
6808721SJonathan.Adams@Sun.COM 		sep->se_dup = nsep;
6818721SJonathan.Adams@Sun.COM 		sep->se_count++;
6828721SJonathan.Adams@Sun.COM 		return (WALK_NEXT);
6838721SJonathan.Adams@Sun.COM 	}
6848721SJonathan.Adams@Sun.COM 
6858721SJonathan.Adams@Sun.COM 	nsep->se_next = NULL;
6868721SJonathan.Adams@Sun.COM 	*sepp = nsep;
6878721SJonathan.Adams@Sun.COM 	sip->si_entries++;
6888721SJonathan.Adams@Sun.COM 
6898721SJonathan.Adams@Sun.COM 	return (WALK_NEXT);
6908721SJonathan.Adams@Sun.COM }
6918721SJonathan.Adams@Sun.COM 
6928721SJonathan.Adams@Sun.COM int
6938721SJonathan.Adams@Sun.COM stacks_run(int verbose)
6948721SJonathan.Adams@Sun.COM {
6958721SJonathan.Adams@Sun.COM 	stacks_info_t si;
6968721SJonathan.Adams@Sun.COM 	findstack_info_t *fsip = &si.si_fsi;
6978721SJonathan.Adams@Sun.COM 	size_t idx;
6988721SJonathan.Adams@Sun.COM 	stacks_entry_t **cur;
6998721SJonathan.Adams@Sun.COM 
7008721SJonathan.Adams@Sun.COM 	bzero(&si, sizeof (si));
7018721SJonathan.Adams@Sun.COM 
7028721SJonathan.Adams@Sun.COM 	stacks_state = STACKS_STATE_DIRTY;
7038721SJonathan.Adams@Sun.COM 
7048721SJonathan.Adams@Sun.COM 	stacks_hash = si.si_hash =
7058721SJonathan.Adams@Sun.COM 	    mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP);
7068721SJonathan.Adams@Sun.COM 	si.si_entries = 0;
7078721SJonathan.Adams@Sun.COM 	si.si_count = 0;
7088721SJonathan.Adams@Sun.COM 
7098721SJonathan.Adams@Sun.COM 	fsip->fsi_max_depth = STACKS_MAX_DEPTH;
7108721SJonathan.Adams@Sun.COM 	fsip->fsi_stack =
7118721SJonathan.Adams@Sun.COM 	    mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack),
7128721SJonathan.Adams@Sun.COM 	    UM_SLEEP | UM_GC);
7138721SJonathan.Adams@Sun.COM 
7148721SJonathan.Adams@Sun.COM 	if (verbose)
7158721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: processing kernel threads\n");
7168721SJonathan.Adams@Sun.COM 
7178721SJonathan.Adams@Sun.COM 	if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
7188721SJonathan.Adams@Sun.COM 		mdb_warn("cannot walk \"thread\"");
7198721SJonathan.Adams@Sun.COM 		return (DCMD_ERR);
7208721SJonathan.Adams@Sun.COM 	}
7218721SJonathan.Adams@Sun.COM 
7228721SJonathan.Adams@Sun.COM 	if (verbose)
7238721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: %d unique stacks / %d threads\n",
7248721SJonathan.Adams@Sun.COM 		    si.si_entries, si.si_count);
7258721SJonathan.Adams@Sun.COM 
7268721SJonathan.Adams@Sun.COM 	stacks_array_size = si.si_entries;
7278721SJonathan.Adams@Sun.COM 	stacks_array =
7288721SJonathan.Adams@Sun.COM 	    mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP);
7298721SJonathan.Adams@Sun.COM 	cur = stacks_array;
7308721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < STACKS_HSIZE; idx++) {
7318721SJonathan.Adams@Sun.COM 		stacks_entry_t *sep;
7328721SJonathan.Adams@Sun.COM 		for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next)
7338721SJonathan.Adams@Sun.COM 			*(cur++) = sep;
7348721SJonathan.Adams@Sun.COM 	}
7358721SJonathan.Adams@Sun.COM 
7368721SJonathan.Adams@Sun.COM 	if (cur != stacks_array + si.si_entries) {
7378721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: miscounted array size (%d != size: %d)\n",
7388721SJonathan.Adams@Sun.COM 		    (cur - stacks_array), stacks_array_size);
7398721SJonathan.Adams@Sun.COM 		return (DCMD_ERR);
7408721SJonathan.Adams@Sun.COM 	}
7418721SJonathan.Adams@Sun.COM 	qsort(stacks_array, si.si_entries, sizeof (*stacks_array),
7428721SJonathan.Adams@Sun.COM 	    stacks_entry_comp);
7438721SJonathan.Adams@Sun.COM 
7448721SJonathan.Adams@Sun.COM 	/* Now that we're done, free the hash table */
7458721SJonathan.Adams@Sun.COM 	stacks_hash = NULL;
7468721SJonathan.Adams@Sun.COM 	mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash));
7478721SJonathan.Adams@Sun.COM 
7488721SJonathan.Adams@Sun.COM 	stacks_state = STACKS_STATE_DONE;
7498721SJonathan.Adams@Sun.COM 
7508721SJonathan.Adams@Sun.COM 	if (verbose)
7518721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: done\n");
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	return (DCMD_OK);
7540Sstevel@tonic-gate }
7558721SJonathan.Adams@Sun.COM 
7568721SJonathan.Adams@Sun.COM static int
7578721SJonathan.Adams@Sun.COM stacks_has_caller(stacks_entry_t *sep, uintptr_t addr)
7588721SJonathan.Adams@Sun.COM {
7598721SJonathan.Adams@Sun.COM 	uintptr_t laddr = addr;
7608721SJonathan.Adams@Sun.COM 	uintptr_t haddr = addr + 1;
7618721SJonathan.Adams@Sun.COM 	int idx;
7628721SJonathan.Adams@Sun.COM 	char c[MDB_SYM_NAMLEN];
7638721SJonathan.Adams@Sun.COM 	GElf_Sym sym;
7648721SJonathan.Adams@Sun.COM 
7658721SJonathan.Adams@Sun.COM 	if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY,
7668721SJonathan.Adams@Sun.COM 	    c, sizeof (c), &sym) != -1 &&
7678721SJonathan.Adams@Sun.COM 	    addr == (uintptr_t)sym.st_value) {
7688721SJonathan.Adams@Sun.COM 		laddr = (uintptr_t)sym.st_value;
7698721SJonathan.Adams@Sun.COM 		haddr = (uintptr_t)sym.st_value + sym.st_size;
7708721SJonathan.Adams@Sun.COM 	}
7718721SJonathan.Adams@Sun.COM 
7728721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < sep->se_depth; idx++)
7738721SJonathan.Adams@Sun.COM 		if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr)
7748721SJonathan.Adams@Sun.COM 			return (1);
7758721SJonathan.Adams@Sun.COM 
7768721SJonathan.Adams@Sun.COM 	return (0);
7778721SJonathan.Adams@Sun.COM }
7788721SJonathan.Adams@Sun.COM 
7798742Sgap@sun.com typedef struct find_module_struct {
7808742Sgap@sun.com 	struct modctl *mcp;
7818742Sgap@sun.com 	const char *name;
7828742Sgap@sun.com } find_module_struct_t;
7838742Sgap@sun.com 
784*8745Sgap@sun.com /*ARGSUSED*/
7858742Sgap@sun.com int
7868742Sgap@sun.com find_module_cb(uintptr_t addr, const void *modctl_arg, void *cbarg)
7878742Sgap@sun.com {
7888742Sgap@sun.com 	find_module_struct_t *sp = cbarg;
7898742Sgap@sun.com 	char mod_modname[MODMAXNAMELEN + 1];
7908742Sgap@sun.com 	const struct modctl *mp = modctl_arg;
7918742Sgap@sun.com 
7928742Sgap@sun.com 	if (!mp->mod_modname)
7938742Sgap@sun.com 		return (WALK_NEXT);
7948742Sgap@sun.com 
7958742Sgap@sun.com 	if (mdb_readstr(mod_modname, sizeof (mod_modname),
7968742Sgap@sun.com 	    (uintptr_t)mp->mod_modname) == -1) {
7978742Sgap@sun.com 		mdb_warn("failed to read mod_modname in \"modctl\" walk");
7988742Sgap@sun.com 		return (WALK_ERR);
7998742Sgap@sun.com 	}
8008742Sgap@sun.com 
8018742Sgap@sun.com 	if (strcmp(sp->name, mod_modname))
8028742Sgap@sun.com 		return (WALK_NEXT);
8038742Sgap@sun.com 
8048742Sgap@sun.com 	sp->mcp = mdb_alloc(sizeof (*sp->mcp), UM_SLEEP | UM_GC);
8058742Sgap@sun.com 	bcopy(mp, sp->mcp, sizeof (*sp->mcp));
8068742Sgap@sun.com 	return (WALK_DONE);
8078742Sgap@sun.com }
8088742Sgap@sun.com 
8098742Sgap@sun.com static struct modctl *
8108742Sgap@sun.com find_module(const char *name)
8118742Sgap@sun.com {
8128742Sgap@sun.com 	find_module_struct_t mptr;
8138742Sgap@sun.com 
8148742Sgap@sun.com 	mptr.name = name;
8158742Sgap@sun.com 	mptr.mcp = NULL;
8168742Sgap@sun.com 
8178742Sgap@sun.com 	if (mdb_walk("modctl", find_module_cb, &mptr) != 0)
8188742Sgap@sun.com 		mdb_warn("cannot walk \"modctl\"");
8198742Sgap@sun.com 	return (mptr.mcp);
8208742Sgap@sun.com }
8218742Sgap@sun.com 
8228742Sgap@sun.com static int
8238742Sgap@sun.com stacks_has_module(stacks_entry_t *sep, struct modctl *mp)
8248742Sgap@sun.com {
8258742Sgap@sun.com 	int idx;
8268742Sgap@sun.com 
8278742Sgap@sun.com 	if (mp == NULL)
8288742Sgap@sun.com 		return (0);
8298742Sgap@sun.com 
8308742Sgap@sun.com 	for (idx = 0; idx < sep->se_depth; idx++)
8318742Sgap@sun.com 		if (sep->se_stack[idx] >= (uintptr_t)mp->mod_text &&
8328742Sgap@sun.com 		    sep->se_stack[idx] <
8338742Sgap@sun.com 		    ((uintptr_t)mp->mod_text + mp->mod_text_size))
8348742Sgap@sun.com 			return (1);
8358742Sgap@sun.com 	return (0);
8368742Sgap@sun.com }
8378742Sgap@sun.com 
8388742Sgap@sun.com 
8398721SJonathan.Adams@Sun.COM static int
8408721SJonathan.Adams@Sun.COM uintptrcomp(const void *lp, const void *rp)
8418721SJonathan.Adams@Sun.COM {
8428721SJonathan.Adams@Sun.COM 	uintptr_t lhs = *(const uintptr_t *)lp;
8438721SJonathan.Adams@Sun.COM 	uintptr_t rhs = *(const uintptr_t *)rp;
8448721SJonathan.Adams@Sun.COM 	if (lhs > rhs)
8458721SJonathan.Adams@Sun.COM 		return (1);
8468721SJonathan.Adams@Sun.COM 	if (lhs < rhs)
8478721SJonathan.Adams@Sun.COM 		return (-1);
8488721SJonathan.Adams@Sun.COM 	return (0);
8498721SJonathan.Adams@Sun.COM }
8508721SJonathan.Adams@Sun.COM 
8518721SJonathan.Adams@Sun.COM /*ARGSUSED*/
8528721SJonathan.Adams@Sun.COM static void
8538721SJonathan.Adams@Sun.COM print_sobj_help(int type, const char *name, const char *ops_name, void *ign)
8548721SJonathan.Adams@Sun.COM {
8558721SJonathan.Adams@Sun.COM 	mdb_printf(" %s", name);
8568721SJonathan.Adams@Sun.COM }
8578721SJonathan.Adams@Sun.COM 
8588721SJonathan.Adams@Sun.COM /*ARGSUSED*/
8598721SJonathan.Adams@Sun.COM static void
8608721SJonathan.Adams@Sun.COM print_tstate_help(uint_t state, const char *name, void *ignored)
8618721SJonathan.Adams@Sun.COM {
8628721SJonathan.Adams@Sun.COM 	mdb_printf(" %s", name);
8638721SJonathan.Adams@Sun.COM }
8648721SJonathan.Adams@Sun.COM 
8658721SJonathan.Adams@Sun.COM void
8668721SJonathan.Adams@Sun.COM stacks_help(void)
8678721SJonathan.Adams@Sun.COM {
8688721SJonathan.Adams@Sun.COM 	mdb_printf(
8698721SJonathan.Adams@Sun.COM "::stacks processes all of the thread stacks on the system, grouping\n"
8708721SJonathan.Adams@Sun.COM "together threads which have the same:\n"
8718721SJonathan.Adams@Sun.COM "\n"
8728721SJonathan.Adams@Sun.COM "  * Thread state,\n"
8738721SJonathan.Adams@Sun.COM "  * Sync object type, and\n"
8748721SJonathan.Adams@Sun.COM "  * PCs in their stack trace.\n"
8758721SJonathan.Adams@Sun.COM "\n"
8768721SJonathan.Adams@Sun.COM "The default output (no address or options) is just a dump of the thread\n"
8778721SJonathan.Adams@Sun.COM "groups in the system.  For a view of active threads, use \"::stacks -i\",\n"
8788721SJonathan.Adams@Sun.COM "which filters out FREE threads (interrupt threads which are currently\n"
8798721SJonathan.Adams@Sun.COM "inactive) and threads sleeping on a CV. (Note that those threads may still\n"
8808721SJonathan.Adams@Sun.COM "be noteworthy; this is just for a first glance.)  More general filtering\n"
8818721SJonathan.Adams@Sun.COM "options are described below, in the \"FILTERS\" section.\n"
8828721SJonathan.Adams@Sun.COM "\n"
8838721SJonathan.Adams@Sun.COM "::stacks can be used in a pipeline.  The input to ::stacks is one or more\n"
8848721SJonathan.Adams@Sun.COM "thread pointers.  For example, to get a summary of threads in a process,\n"
8858721SJonathan.Adams@Sun.COM "you can do:\n"
8868721SJonathan.Adams@Sun.COM "\n"
8878721SJonathan.Adams@Sun.COM "  %<b>procp%</b>::walk thread | ::stacks\n"
8888721SJonathan.Adams@Sun.COM "\n"
8898721SJonathan.Adams@Sun.COM "When output into a pipe, ::stacks prints all of the threads input,\n"
8908721SJonathan.Adams@Sun.COM "filtered by the given filtering options.  This means that multiple\n"
8918721SJonathan.Adams@Sun.COM "::stacks invocations can be piped together to achieve more complicated\n"
8928721SJonathan.Adams@Sun.COM "filters.  For example, to get threads which have both 'fop_read' and\n"
8938721SJonathan.Adams@Sun.COM "'cv_wait_sig_swap' in their stack trace, you could do:\n"
8948721SJonathan.Adams@Sun.COM "\n"
8958721SJonathan.Adams@Sun.COM "  ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n"
8968721SJonathan.Adams@Sun.COM "\n"
8978721SJonathan.Adams@Sun.COM "To get the full list of threads in each group, use the '-a' flag:\n"
8988721SJonathan.Adams@Sun.COM "\n"
8998721SJonathan.Adams@Sun.COM "  ::stacks -a\n"
9008721SJonathan.Adams@Sun.COM "\n");
9018721SJonathan.Adams@Sun.COM 	mdb_dec_indent(2);
9028721SJonathan.Adams@Sun.COM 	mdb_printf("%<b>OPTIONS%</b>\n");
9038721SJonathan.Adams@Sun.COM 	mdb_inc_indent(2);
9048721SJonathan.Adams@Sun.COM 	mdb_printf("%s",
9058721SJonathan.Adams@Sun.COM "  -a    Print all of the grouped threads, instead of just a count.\n"
9068721SJonathan.Adams@Sun.COM "  -f    Force a re-run of the thread stack gathering.\n"
9078721SJonathan.Adams@Sun.COM "  -v    Be verbose about thread stack gathering.\n"
9088721SJonathan.Adams@Sun.COM "\n");
9098721SJonathan.Adams@Sun.COM 	mdb_dec_indent(2);
9108721SJonathan.Adams@Sun.COM 	mdb_printf("%<b>FILTERS%</b>\n");
9118721SJonathan.Adams@Sun.COM 	mdb_inc_indent(2);
9128721SJonathan.Adams@Sun.COM 	mdb_printf("%s",
9138721SJonathan.Adams@Sun.COM "  -i    Show active threads; equivalent to '-S CV -T FREE'.\n"
9148721SJonathan.Adams@Sun.COM "  -c func[+offset]\n"
9158721SJonathan.Adams@Sun.COM "        Only print threads whose stacks contain func/func+offset.\n"
9168721SJonathan.Adams@Sun.COM "  -C func[+offset]\n"
9178721SJonathan.Adams@Sun.COM "        Only print threads whose stacks do not contain func/func+offset.\n"
9188742Sgap@sun.com "  -m module\n"
9198742Sgap@sun.com "        Only print threads whose stacks contain functions from module.\n"
9208742Sgap@sun.com "  -M module\n"
9218742Sgap@sun.com "        Only print threads whose stacks do not contain functions from\n"
9228742Sgap@sun.com "        module.\n"
9238721SJonathan.Adams@Sun.COM "  -s {type | ALL}\n"
9248721SJonathan.Adams@Sun.COM "        Only print threads which are on a 'type' synchronization object\n"
9258721SJonathan.Adams@Sun.COM "        (SOBJ).\n"
9268721SJonathan.Adams@Sun.COM "  -S {type | ALL}\n"
9278721SJonathan.Adams@Sun.COM "        Only print threads which are not on a 'type' SOBJ.\n"
9288721SJonathan.Adams@Sun.COM "  -t tstate\n"
9298721SJonathan.Adams@Sun.COM "        Only print threads which are in thread state 'tstate'.\n"
9308721SJonathan.Adams@Sun.COM "  -T tstate\n"
9318721SJonathan.Adams@Sun.COM "        Only print threads which are not in thread state 'tstate'.\n"
9328721SJonathan.Adams@Sun.COM "\n");
9338721SJonathan.Adams@Sun.COM 	mdb_printf("   SOBJ types:");
9348721SJonathan.Adams@Sun.COM 	sobj_type_walk(print_sobj_help, NULL);
9358721SJonathan.Adams@Sun.COM 	mdb_printf("\n");
9368721SJonathan.Adams@Sun.COM 	mdb_printf("Thread states:");
9378721SJonathan.Adams@Sun.COM 	thread_walk_states(print_tstate_help, NULL);
9388721SJonathan.Adams@Sun.COM 	mdb_printf(" panic\n");
9398721SJonathan.Adams@Sun.COM }
9408721SJonathan.Adams@Sun.COM 
9418721SJonathan.Adams@Sun.COM /*ARGSUSED*/
9428721SJonathan.Adams@Sun.COM int
9438721SJonathan.Adams@Sun.COM stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
9448721SJonathan.Adams@Sun.COM {
9458721SJonathan.Adams@Sun.COM 	size_t idx;
9468721SJonathan.Adams@Sun.COM 
9478721SJonathan.Adams@Sun.COM 	char *seen = NULL;
9488721SJonathan.Adams@Sun.COM 
9498721SJonathan.Adams@Sun.COM 	const char *caller_str = NULL;
9508721SJonathan.Adams@Sun.COM 	const char *excl_caller_str = NULL;
9518721SJonathan.Adams@Sun.COM 	uintptr_t caller = 0, excl_caller = 0;
9528742Sgap@sun.com 	const char *module_str = NULL;
9538742Sgap@sun.com 	const char *excl_module_str = NULL;
9548742Sgap@sun.com 	struct modctl *module = NULL, *excl_module = NULL;
9558721SJonathan.Adams@Sun.COM 	const char *sobj = NULL;
9568721SJonathan.Adams@Sun.COM 	const char *excl_sobj = NULL;
9578721SJonathan.Adams@Sun.COM 	uintptr_t sobj_ops = 0, excl_sobj_ops = 0;
9588721SJonathan.Adams@Sun.COM 	const char *tstate_str = NULL;
9598721SJonathan.Adams@Sun.COM 	const char *excl_tstate_str = NULL;
9608721SJonathan.Adams@Sun.COM 	uint_t tstate = -1U;
9618721SJonathan.Adams@Sun.COM 	uint_t excl_tstate = -1U;
9628721SJonathan.Adams@Sun.COM 
9638721SJonathan.Adams@Sun.COM 	uint_t all = 0;
9648721SJonathan.Adams@Sun.COM 	uint_t force = 0;
9658721SJonathan.Adams@Sun.COM 	uint_t interesting = 0;
9668721SJonathan.Adams@Sun.COM 	uint_t verbose = 0;
9678721SJonathan.Adams@Sun.COM 
9688721SJonathan.Adams@Sun.COM 	/*
9698721SJonathan.Adams@Sun.COM 	 * We have a slight behavior difference between having piped
9708721SJonathan.Adams@Sun.COM 	 * input and 'addr::stacks'.  Without a pipe, we assume the
9718721SJonathan.Adams@Sun.COM 	 * thread pointer given is a representative thread, and so
9728721SJonathan.Adams@Sun.COM 	 * we include all similar threads in the system in our output.
9738721SJonathan.Adams@Sun.COM 	 *
9748721SJonathan.Adams@Sun.COM 	 * With a pipe, we filter down to just the threads in our
9758721SJonathan.Adams@Sun.COM 	 * input.
9768721SJonathan.Adams@Sun.COM 	 */
9778721SJonathan.Adams@Sun.COM 	uint_t addrspec = (flags & DCMD_ADDRSPEC);
9788721SJonathan.Adams@Sun.COM 	uint_t only_matching = addrspec && (flags & DCMD_PIPE);
9798721SJonathan.Adams@Sun.COM 
9808721SJonathan.Adams@Sun.COM 	mdb_pipe_t p;
9818721SJonathan.Adams@Sun.COM 
9828721SJonathan.Adams@Sun.COM 	if (mdb_getopts(argc, argv,
9838721SJonathan.Adams@Sun.COM 	    'a', MDB_OPT_SETBITS, TRUE, &all,
9848721SJonathan.Adams@Sun.COM 	    'f', MDB_OPT_SETBITS, TRUE, &force,
9858721SJonathan.Adams@Sun.COM 	    'i', MDB_OPT_SETBITS, TRUE, &interesting,
9868721SJonathan.Adams@Sun.COM 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
9878721SJonathan.Adams@Sun.COM 	    'c', MDB_OPT_STR, &caller_str,
9888721SJonathan.Adams@Sun.COM 	    'C', MDB_OPT_STR, &excl_caller_str,
9898742Sgap@sun.com 	    'm', MDB_OPT_STR, &module_str,
9908742Sgap@sun.com 	    'M', MDB_OPT_STR, &excl_module_str,
9918721SJonathan.Adams@Sun.COM 	    's', MDB_OPT_STR, &sobj,
9928721SJonathan.Adams@Sun.COM 	    'S', MDB_OPT_STR, &excl_sobj,
9938721SJonathan.Adams@Sun.COM 	    't', MDB_OPT_STR, &tstate_str,
9948721SJonathan.Adams@Sun.COM 	    'T', MDB_OPT_STR, &excl_tstate_str,
9958721SJonathan.Adams@Sun.COM 	    NULL) != argc)
9968721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
9978721SJonathan.Adams@Sun.COM 
9988721SJonathan.Adams@Sun.COM 	if (interesting) {
9998721SJonathan.Adams@Sun.COM 		if (sobj != NULL || excl_sobj != NULL ||
10008721SJonathan.Adams@Sun.COM 		    tstate_str != NULL || excl_tstate_str != NULL) {
10018721SJonathan.Adams@Sun.COM 			mdb_warn(
10028721SJonathan.Adams@Sun.COM 			    "stacks: -i is incompatible with -[sStT]\n");
10038721SJonathan.Adams@Sun.COM 			return (DCMD_USAGE);
10048721SJonathan.Adams@Sun.COM 		}
10058721SJonathan.Adams@Sun.COM 		excl_sobj = "CV";
10068721SJonathan.Adams@Sun.COM 		excl_tstate_str = "FREE";
10078721SJonathan.Adams@Sun.COM 	}
10088721SJonathan.Adams@Sun.COM 
10098721SJonathan.Adams@Sun.COM 	if (caller_str != NULL) {
10108721SJonathan.Adams@Sun.COM 		mdb_set_dot(0);
10118721SJonathan.Adams@Sun.COM 		if (mdb_eval(caller_str) != 0) {
10128721SJonathan.Adams@Sun.COM 			mdb_warn("stacks: evaluation of \"%s\" failed",
10138721SJonathan.Adams@Sun.COM 			    caller_str);
10148721SJonathan.Adams@Sun.COM 			return (DCMD_ABORT);
10158721SJonathan.Adams@Sun.COM 		}
10168721SJonathan.Adams@Sun.COM 		caller = mdb_get_dot();
10178721SJonathan.Adams@Sun.COM 	}
10188721SJonathan.Adams@Sun.COM 
10198721SJonathan.Adams@Sun.COM 	if (excl_caller_str != NULL) {
10208721SJonathan.Adams@Sun.COM 		mdb_set_dot(0);
10218721SJonathan.Adams@Sun.COM 		if (mdb_eval(excl_caller_str) != 0) {
10228721SJonathan.Adams@Sun.COM 			mdb_warn("stacks: evaluation of \"%s\" failed",
10238721SJonathan.Adams@Sun.COM 			    excl_caller_str);
10248721SJonathan.Adams@Sun.COM 			return (DCMD_ABORT);
10258721SJonathan.Adams@Sun.COM 		}
10268721SJonathan.Adams@Sun.COM 		excl_caller = mdb_get_dot();
10278721SJonathan.Adams@Sun.COM 	}
10288721SJonathan.Adams@Sun.COM 	mdb_set_dot(addr);
10298721SJonathan.Adams@Sun.COM 
10308742Sgap@sun.com 	if (module_str != NULL &&
10318742Sgap@sun.com 	    (module = find_module(module_str)) == NULL) {
10328742Sgap@sun.com 		mdb_warn("stacks: module \"%s\" is unknown", module_str);
10338742Sgap@sun.com 		return (DCMD_ABORT);
10348742Sgap@sun.com 	}
10358742Sgap@sun.com 
10368742Sgap@sun.com 	if (excl_module_str != NULL &&
10378742Sgap@sun.com 	    (excl_module = find_module(excl_module_str)) == NULL) {
10388742Sgap@sun.com 		mdb_warn("stacks: module \"%s\" is unknown", excl_module_str);
10398742Sgap@sun.com 		return (DCMD_ABORT);
10408742Sgap@sun.com 	}
10418742Sgap@sun.com 
10428721SJonathan.Adams@Sun.COM 	if (sobj != NULL &&
10438721SJonathan.Adams@Sun.COM 	    text_to_sobj(sobj, &sobj_ops) != 0)
10448721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
10458721SJonathan.Adams@Sun.COM 
10468721SJonathan.Adams@Sun.COM 	if (excl_sobj != NULL &&
10478721SJonathan.Adams@Sun.COM 	    text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
10488721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
10498721SJonathan.Adams@Sun.COM 
10508721SJonathan.Adams@Sun.COM 	if (sobj_ops != 0 && excl_sobj_ops != 0) {
10518721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: only one of -s and -S can be specified\n");
10528721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
10538721SJonathan.Adams@Sun.COM 	}
10548721SJonathan.Adams@Sun.COM 
10558742Sgap@sun.com 	if (tstate_str != NULL &&
10568721SJonathan.Adams@Sun.COM 	    text_to_tstate(tstate_str, &tstate) != 0)
10578721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
10588742Sgap@sun.com 
10598742Sgap@sun.com 	if (excl_tstate_str != NULL &&
10608721SJonathan.Adams@Sun.COM 	    text_to_tstate(excl_tstate_str, &excl_tstate) != 0)
10618721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
10628721SJonathan.Adams@Sun.COM 
10638721SJonathan.Adams@Sun.COM 	if (tstate != -1U && excl_tstate != -1U) {
10648721SJonathan.Adams@Sun.COM 		mdb_warn("stacks: only one of -t and -T can be specified\n");
10658721SJonathan.Adams@Sun.COM 		return (DCMD_USAGE);
10668721SJonathan.Adams@Sun.COM 	}
10678721SJonathan.Adams@Sun.COM 
10688721SJonathan.Adams@Sun.COM 	/*
10698721SJonathan.Adams@Sun.COM 	 * Force a cleanup if we're connected to a live system. Never
10708721SJonathan.Adams@Sun.COM 	 * do a cleanup after the first invocation around the loop.
10718721SJonathan.Adams@Sun.COM 	 */
10728721SJonathan.Adams@Sun.COM 	force |= (mdb_get_state() == MDB_STATE_RUNNING);
10738721SJonathan.Adams@Sun.COM 	if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
10748721SJonathan.Adams@Sun.COM 		force = 0;
10758721SJonathan.Adams@Sun.COM 
10768721SJonathan.Adams@Sun.COM 	stacks_cleanup(force);
10778721SJonathan.Adams@Sun.COM 
10788721SJonathan.Adams@Sun.COM 	if (stacks_state == STACKS_STATE_CLEAN) {
10798721SJonathan.Adams@Sun.COM 		int res = stacks_run(verbose);
10808721SJonathan.Adams@Sun.COM 		if (res != DCMD_OK)
10818721SJonathan.Adams@Sun.COM 			return (res);
10828721SJonathan.Adams@Sun.COM 	}
10838721SJonathan.Adams@Sun.COM 
10848721SJonathan.Adams@Sun.COM 	if (!all && DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
10858721SJonathan.Adams@Sun.COM 		mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
10868721SJonathan.Adams@Sun.COM 		    "THREAD", "STATE", "SOBJ", "COUNT");
10878721SJonathan.Adams@Sun.COM 	}
10888721SJonathan.Adams@Sun.COM 
10898721SJonathan.Adams@Sun.COM 	/*
10908721SJonathan.Adams@Sun.COM 	 * If there's an address specified, we're going to further filter
10918721SJonathan.Adams@Sun.COM 	 * to only entries which have an address in the input.  To reduce
10928721SJonathan.Adams@Sun.COM 	 * overhead (and make the sorted output come out right), we
10938721SJonathan.Adams@Sun.COM 	 * use mdb_get_pipe() to grab the entire pipeline of input, then
10948721SJonathan.Adams@Sun.COM 	 * use qsort() and bsearch() to speed up the search.
10958721SJonathan.Adams@Sun.COM 	 */
10968721SJonathan.Adams@Sun.COM 	if (addrspec) {
10978721SJonathan.Adams@Sun.COM 		mdb_get_pipe(&p);
10988721SJonathan.Adams@Sun.COM 		if (p.pipe_data == NULL || p.pipe_len == 0) {
10998721SJonathan.Adams@Sun.COM 			p.pipe_data = &addr;
11008721SJonathan.Adams@Sun.COM 			p.pipe_len = 1;
11018721SJonathan.Adams@Sun.COM 		}
11028721SJonathan.Adams@Sun.COM 		qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t),
11038721SJonathan.Adams@Sun.COM 		    uintptrcomp);
11048721SJonathan.Adams@Sun.COM 
11058721SJonathan.Adams@Sun.COM 		/* remove any duplicates in the data */
11068721SJonathan.Adams@Sun.COM 		idx = 0;
11078721SJonathan.Adams@Sun.COM 		while (idx < p.pipe_len - 1) {
11088721SJonathan.Adams@Sun.COM 			uintptr_t *data = &p.pipe_data[idx];
11098721SJonathan.Adams@Sun.COM 			size_t len = p.pipe_len - idx;
11108721SJonathan.Adams@Sun.COM 
11118721SJonathan.Adams@Sun.COM 			if (data[0] == data[1]) {
11128721SJonathan.Adams@Sun.COM 				memmove(data, data + 1,
11138721SJonathan.Adams@Sun.COM 				    (len - 1) * sizeof (*data));
11148721SJonathan.Adams@Sun.COM 				p.pipe_len--;
11158721SJonathan.Adams@Sun.COM 				continue; /* repeat without incrementing idx */
11168721SJonathan.Adams@Sun.COM 			}
11178721SJonathan.Adams@Sun.COM 			idx++;
11188721SJonathan.Adams@Sun.COM 		}
11198721SJonathan.Adams@Sun.COM 
11208721SJonathan.Adams@Sun.COM 		seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC);
11218721SJonathan.Adams@Sun.COM 	}
11228721SJonathan.Adams@Sun.COM 
11238721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < stacks_array_size; idx++) {
11248721SJonathan.Adams@Sun.COM 		stacks_entry_t *sep = stacks_array[idx];
11258721SJonathan.Adams@Sun.COM 		stacks_entry_t *cur = sep;
11268721SJonathan.Adams@Sun.COM 		int frame;
11278721SJonathan.Adams@Sun.COM 		size_t count = sep->se_count;
11288721SJonathan.Adams@Sun.COM 
11298721SJonathan.Adams@Sun.COM 		if (addrspec) {
11308721SJonathan.Adams@Sun.COM 			stacks_entry_t *head = NULL, *tail = NULL, *sp;
11318721SJonathan.Adams@Sun.COM 			size_t foundcount = 0;
11328721SJonathan.Adams@Sun.COM 			/*
11338721SJonathan.Adams@Sun.COM 			 * We use the now-unused hash chain field se_next to
11348721SJonathan.Adams@Sun.COM 			 * link together the dups which match our list.
11358721SJonathan.Adams@Sun.COM 			 */
11368721SJonathan.Adams@Sun.COM 			for (sp = sep; sp != NULL; sp = sp->se_dup) {
11378721SJonathan.Adams@Sun.COM 				uintptr_t *entry = bsearch(&sp->se_thread,
11388721SJonathan.Adams@Sun.COM 				    p.pipe_data, p.pipe_len, sizeof (uintptr_t),
11398721SJonathan.Adams@Sun.COM 				    uintptrcomp);
11408721SJonathan.Adams@Sun.COM 				if (entry != NULL) {
11418721SJonathan.Adams@Sun.COM 					foundcount++;
11428721SJonathan.Adams@Sun.COM 					seen[entry - p.pipe_data]++;
11438721SJonathan.Adams@Sun.COM 					if (head == NULL)
11448721SJonathan.Adams@Sun.COM 						head = sp;
11458721SJonathan.Adams@Sun.COM 					else
11468721SJonathan.Adams@Sun.COM 						tail->se_next = sp;
11478721SJonathan.Adams@Sun.COM 					tail = sp;
11488721SJonathan.Adams@Sun.COM 					sp->se_next = NULL;
11498721SJonathan.Adams@Sun.COM 				}
11508721SJonathan.Adams@Sun.COM 			}
11518721SJonathan.Adams@Sun.COM 			if (head == NULL)
11528721SJonathan.Adams@Sun.COM 				continue;	/* no match, skip entry */
11538721SJonathan.Adams@Sun.COM 
11548721SJonathan.Adams@Sun.COM 			if (only_matching) {
11558721SJonathan.Adams@Sun.COM 				cur = sep = head;
11568721SJonathan.Adams@Sun.COM 				count = foundcount;
11578721SJonathan.Adams@Sun.COM 			}
11588721SJonathan.Adams@Sun.COM 		}
11598721SJonathan.Adams@Sun.COM 
11608721SJonathan.Adams@Sun.COM 		if (caller != 0 && !stacks_has_caller(sep, caller))
11618721SJonathan.Adams@Sun.COM 			continue;
11628721SJonathan.Adams@Sun.COM 		if (excl_caller != 0 && stacks_has_caller(sep, excl_caller))
11638721SJonathan.Adams@Sun.COM 			continue;
11648742Sgap@sun.com 		if (module != 0 && !stacks_has_module(sep, module))
11658742Sgap@sun.com 			continue;
11668742Sgap@sun.com 		if (excl_module != 0 && stacks_has_module(sep, excl_module))
11678742Sgap@sun.com 			continue;
11688721SJonathan.Adams@Sun.COM 
11698721SJonathan.Adams@Sun.COM 		if (tstate != -1U) {
11708721SJonathan.Adams@Sun.COM 			if (tstate == TSTATE_PANIC) {
11718721SJonathan.Adams@Sun.COM 				if (!sep->se_panic)
11728721SJonathan.Adams@Sun.COM 					continue;
11738721SJonathan.Adams@Sun.COM 			} else if (sep->se_panic || sep->se_tstate != tstate)
11748721SJonathan.Adams@Sun.COM 				continue;
11758721SJonathan.Adams@Sun.COM 		}
11768721SJonathan.Adams@Sun.COM 		if (excl_tstate != -1U) {
11778721SJonathan.Adams@Sun.COM 			if (excl_tstate == TSTATE_PANIC) {
11788721SJonathan.Adams@Sun.COM 				if (sep->se_panic)
11798721SJonathan.Adams@Sun.COM 					continue;
11808721SJonathan.Adams@Sun.COM 			} else if (!sep->se_panic &&
11818721SJonathan.Adams@Sun.COM 			    sep->se_tstate == excl_tstate)
11828721SJonathan.Adams@Sun.COM 				continue;
11838721SJonathan.Adams@Sun.COM 		}
11848721SJonathan.Adams@Sun.COM 
11858721SJonathan.Adams@Sun.COM 		if (sobj_ops == SOBJ_ALL) {
11868721SJonathan.Adams@Sun.COM 			if (sep->se_sobj_ops == 0)
11878721SJonathan.Adams@Sun.COM 				continue;
11888721SJonathan.Adams@Sun.COM 		} else if (sobj_ops != 0) {
11898721SJonathan.Adams@Sun.COM 			if (sobj_ops != sep->se_sobj_ops)
11908721SJonathan.Adams@Sun.COM 				continue;
11918721SJonathan.Adams@Sun.COM 		}
11928721SJonathan.Adams@Sun.COM 
11938721SJonathan.Adams@Sun.COM 		if (!(interesting && sep->se_panic)) {
11948721SJonathan.Adams@Sun.COM 			if (excl_sobj_ops == SOBJ_ALL) {
11958721SJonathan.Adams@Sun.COM 				if (sep->se_sobj_ops != 0)
11968721SJonathan.Adams@Sun.COM 					continue;
11978721SJonathan.Adams@Sun.COM 			} else if (excl_sobj_ops != 0) {
11988721SJonathan.Adams@Sun.COM 				if (excl_sobj_ops == sep->se_sobj_ops)
11998721SJonathan.Adams@Sun.COM 					continue;
12008721SJonathan.Adams@Sun.COM 			}
12018721SJonathan.Adams@Sun.COM 		}
12028721SJonathan.Adams@Sun.COM 
12038721SJonathan.Adams@Sun.COM 		if (flags & DCMD_PIPE_OUT) {
12048721SJonathan.Adams@Sun.COM 			while (sep != NULL) {
12058721SJonathan.Adams@Sun.COM 				mdb_printf("%lr\n", sep->se_thread);
12068721SJonathan.Adams@Sun.COM 				sep = only_matching ?
12078721SJonathan.Adams@Sun.COM 				    sep->se_next : sep->se_dup;
12088721SJonathan.Adams@Sun.COM 			}
12098721SJonathan.Adams@Sun.COM 			continue;
12108721SJonathan.Adams@Sun.COM 		}
12118721SJonathan.Adams@Sun.COM 
12128721SJonathan.Adams@Sun.COM 		if (all) {
12138721SJonathan.Adams@Sun.COM 			mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
12148721SJonathan.Adams@Sun.COM 			    "THREAD", "STATE", "SOBJTYPE", "COUNT");
12158721SJonathan.Adams@Sun.COM 		}
12168721SJonathan.Adams@Sun.COM 
12178721SJonathan.Adams@Sun.COM 		do {
12188721SJonathan.Adams@Sun.COM 			char state[20];
12198721SJonathan.Adams@Sun.COM 			char sobj[100];
12208721SJonathan.Adams@Sun.COM 
12218721SJonathan.Adams@Sun.COM 			tstate_to_text(cur->se_tstate, cur->se_panic,
12228721SJonathan.Adams@Sun.COM 			    state, sizeof (state));
12238721SJonathan.Adams@Sun.COM 			sobj_to_text(cur->se_sobj_ops,
12248721SJonathan.Adams@Sun.COM 			    sobj, sizeof (sobj));
12258721SJonathan.Adams@Sun.COM 
12268721SJonathan.Adams@Sun.COM 			if (cur == sep)
12278721SJonathan.Adams@Sun.COM 				mdb_printf("%?p %-8s %-?s %8d\n",
12288721SJonathan.Adams@Sun.COM 				    cur->se_thread, state, sobj, count);
12298721SJonathan.Adams@Sun.COM 			else
12308721SJonathan.Adams@Sun.COM 				mdb_printf("%?p %-8s %-?s %8s\n",
12318721SJonathan.Adams@Sun.COM 				    cur->se_thread, state, sobj, "-");
12328721SJonathan.Adams@Sun.COM 
12338721SJonathan.Adams@Sun.COM 			cur = only_matching ? cur->se_next : cur->se_dup;
12348721SJonathan.Adams@Sun.COM 		} while (all && cur != NULL);
12358721SJonathan.Adams@Sun.COM 
12368721SJonathan.Adams@Sun.COM 		if (sep->se_failed != 0) {
12378721SJonathan.Adams@Sun.COM 			char *reason;
12388721SJonathan.Adams@Sun.COM 			switch (sep->se_failed) {
12398721SJonathan.Adams@Sun.COM 			case FSI_FAIL_NOTINMEMORY:
12408721SJonathan.Adams@Sun.COM 				reason = "thread not in memory";
12418721SJonathan.Adams@Sun.COM 				break;
12428721SJonathan.Adams@Sun.COM 			case FSI_FAIL_THREADCORRUPT:
12438721SJonathan.Adams@Sun.COM 				reason = "thread structure stack info corrupt";
12448721SJonathan.Adams@Sun.COM 				break;
12458721SJonathan.Adams@Sun.COM 			case FSI_FAIL_STACKNOTFOUND:
12468721SJonathan.Adams@Sun.COM 				reason = "no consistent stack found";
12478721SJonathan.Adams@Sun.COM 				break;
12488721SJonathan.Adams@Sun.COM 			default:
12498721SJonathan.Adams@Sun.COM 				reason = "unknown failure";
12508721SJonathan.Adams@Sun.COM 				break;
12518721SJonathan.Adams@Sun.COM 			}
12528721SJonathan.Adams@Sun.COM 			mdb_printf("%?s <%s>\n", "", reason);
12538721SJonathan.Adams@Sun.COM 		}
12548721SJonathan.Adams@Sun.COM 
12558721SJonathan.Adams@Sun.COM 		for (frame = 0; frame < sep->se_depth; frame++)
12568721SJonathan.Adams@Sun.COM 			mdb_printf("%?s %a\n", "", sep->se_stack[frame]);
12578721SJonathan.Adams@Sun.COM 		if (sep->se_overflow)
12588721SJonathan.Adams@Sun.COM 			mdb_printf("%?s ... truncated ...\n", "");
12598721SJonathan.Adams@Sun.COM 		mdb_printf("\n");
12608721SJonathan.Adams@Sun.COM 	}
12618721SJonathan.Adams@Sun.COM 
12628721SJonathan.Adams@Sun.COM 	if (flags & DCMD_ADDRSPEC) {
12638721SJonathan.Adams@Sun.COM 		for (idx = 0; idx < p.pipe_len; idx++)
12648721SJonathan.Adams@Sun.COM 			if (seen[idx] == 0)
12658721SJonathan.Adams@Sun.COM 				mdb_warn("stacks: %p not in thread list\n",
12668721SJonathan.Adams@Sun.COM 				    p.pipe_data[idx]);
12678721SJonathan.Adams@Sun.COM 	}
12688721SJonathan.Adams@Sun.COM 	return (DCMD_OK);
12698721SJonathan.Adams@Sun.COM }
1270