xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/thread.c (revision 8721:4fd0a1c6e9ce)
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
53792Sakolb  * Common Development and Distribution License (the "License").
63792Sakolb  * 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 /*
22*8721SJonathan.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 
270Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/thread.h>
300Sstevel@tonic-gate #include <sys/lwp.h>
310Sstevel@tonic-gate #include <sys/proc.h>
320Sstevel@tonic-gate #include <sys/cpuvar.h>
330Sstevel@tonic-gate #include <sys/cpupart.h>
340Sstevel@tonic-gate #include <sys/disp.h>
355316Sjohnlev #include <sys/taskq_impl.h>
367854SPhilippe.Jung@Sun.COM #include <sys/stack.h>
377854SPhilippe.Jung@Sun.COM 
387854SPhilippe.Jung@Sun.COM #ifndef	STACK_BIAS
397854SPhilippe.Jung@Sun.COM #define	STACK_BIAS	0
407854SPhilippe.Jung@Sun.COM #endif
410Sstevel@tonic-gate 
420Sstevel@tonic-gate typedef struct thread_walk {
430Sstevel@tonic-gate 	kthread_t *tw_thread;
440Sstevel@tonic-gate 	uintptr_t tw_last;
450Sstevel@tonic-gate 	uint_t tw_inproc;
460Sstevel@tonic-gate 	uint_t tw_step;
470Sstevel@tonic-gate } thread_walk_t;
480Sstevel@tonic-gate 
490Sstevel@tonic-gate int
thread_walk_init(mdb_walk_state_t * wsp)500Sstevel@tonic-gate thread_walk_init(mdb_walk_state_t *wsp)
510Sstevel@tonic-gate {
520Sstevel@tonic-gate 	thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP);
530Sstevel@tonic-gate 
540Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
550Sstevel@tonic-gate 		if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) {
560Sstevel@tonic-gate 			mdb_warn("failed to read 'allthreads'");
570Sstevel@tonic-gate 			mdb_free(twp, sizeof (thread_walk_t));
580Sstevel@tonic-gate 			return (WALK_ERR);
590Sstevel@tonic-gate 		}
600Sstevel@tonic-gate 
610Sstevel@tonic-gate 		twp->tw_inproc = FALSE;
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 	} else {
640Sstevel@tonic-gate 		proc_t pr;
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 		if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) {
670Sstevel@tonic-gate 			mdb_warn("failed to read proc at %p", wsp->walk_addr);
680Sstevel@tonic-gate 			mdb_free(twp, sizeof (thread_walk_t));
690Sstevel@tonic-gate 			return (WALK_ERR);
700Sstevel@tonic-gate 		}
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)pr.p_tlist;
730Sstevel@tonic-gate 		twp->tw_inproc = TRUE;
740Sstevel@tonic-gate 	}
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 	twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP);
770Sstevel@tonic-gate 	twp->tw_last = wsp->walk_addr;
780Sstevel@tonic-gate 	twp->tw_step = FALSE;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	wsp->walk_data = twp;
810Sstevel@tonic-gate 	return (WALK_NEXT);
820Sstevel@tonic-gate }
830Sstevel@tonic-gate 
840Sstevel@tonic-gate int
thread_walk_step(mdb_walk_state_t * wsp)850Sstevel@tonic-gate thread_walk_step(mdb_walk_state_t *wsp)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
880Sstevel@tonic-gate 	int status;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
910Sstevel@tonic-gate 		return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 	if (twp->tw_step && wsp->walk_addr == twp->tw_last)
940Sstevel@tonic-gate 		return (WALK_DONE); /* We've wrapped around */
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	if (mdb_vread(twp->tw_thread, sizeof (kthread_t),
970Sstevel@tonic-gate 	    wsp->walk_addr) == -1) {
980Sstevel@tonic-gate 		mdb_warn("failed to read thread at %p", wsp->walk_addr);
990Sstevel@tonic-gate 		return (WALK_DONE);
1000Sstevel@tonic-gate 	}
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
1030Sstevel@tonic-gate 	    wsp->walk_cbdata);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	if (twp->tw_inproc)
1060Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw;
1070Sstevel@tonic-gate 	else
1080Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	twp->tw_step = TRUE;
1110Sstevel@tonic-gate 	return (status);
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate void
thread_walk_fini(mdb_walk_state_t * wsp)1150Sstevel@tonic-gate thread_walk_fini(mdb_walk_state_t *wsp)
1160Sstevel@tonic-gate {
1170Sstevel@tonic-gate 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	mdb_free(twp->tw_thread, sizeof (kthread_t));
1200Sstevel@tonic-gate 	mdb_free(twp, sizeof (thread_walk_t));
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate int
deathrow_walk_init(mdb_walk_state_t * wsp)1240Sstevel@tonic-gate deathrow_walk_init(mdb_walk_state_t *wsp)
1250Sstevel@tonic-gate {
1260Sstevel@tonic-gate 	if (mdb_layered_walk("thread_deathrow", wsp) == -1) {
1270Sstevel@tonic-gate 		mdb_warn("couldn't walk 'thread_deathrow'");
1280Sstevel@tonic-gate 		return (WALK_ERR);
1290Sstevel@tonic-gate 	}
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
1320Sstevel@tonic-gate 		mdb_warn("couldn't walk 'lwp_deathrow'");
1330Sstevel@tonic-gate 		return (WALK_ERR);
1340Sstevel@tonic-gate 	}
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	return (WALK_NEXT);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate int
deathrow_walk_step(mdb_walk_state_t * wsp)1400Sstevel@tonic-gate deathrow_walk_step(mdb_walk_state_t *wsp)
1410Sstevel@tonic-gate {
1420Sstevel@tonic-gate 	kthread_t t;
1430Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	if (addr == NULL)
1460Sstevel@tonic-gate 		return (WALK_DONE);
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (t), addr) == -1) {
1490Sstevel@tonic-gate 		mdb_warn("couldn't read deathrow thread at %p", addr);
1500Sstevel@tonic-gate 		return (WALK_ERR);
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)t.t_forw;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate int
thread_deathrow_walk_init(mdb_walk_state_t * wsp)1590Sstevel@tonic-gate thread_deathrow_walk_init(mdb_walk_state_t *wsp)
1600Sstevel@tonic-gate {
1610Sstevel@tonic-gate 	if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) {
1620Sstevel@tonic-gate 		mdb_warn("couldn't read symbol 'thread_deathrow'");
1630Sstevel@tonic-gate 		return (WALK_ERR);
1640Sstevel@tonic-gate 	}
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	return (WALK_NEXT);
1670Sstevel@tonic-gate }
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate int
lwp_deathrow_walk_init(mdb_walk_state_t * wsp)1700Sstevel@tonic-gate lwp_deathrow_walk_init(mdb_walk_state_t *wsp)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate 	if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) {
1730Sstevel@tonic-gate 		mdb_warn("couldn't read symbol 'lwp_deathrow'");
1740Sstevel@tonic-gate 		return (WALK_ERR);
1750Sstevel@tonic-gate 	}
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	return (WALK_NEXT);
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate typedef struct dispq_walk {
1820Sstevel@tonic-gate 	int dw_npri;
1830Sstevel@tonic-gate 	uintptr_t dw_dispq;
1840Sstevel@tonic-gate 	uintptr_t dw_last;
1850Sstevel@tonic-gate } dispq_walk_t;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate int
cpu_dispq_walk_init(mdb_walk_state_t * wsp)1880Sstevel@tonic-gate cpu_dispq_walk_init(mdb_walk_state_t *wsp)
1890Sstevel@tonic-gate {
1900Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
1910Sstevel@tonic-gate 	dispq_walk_t *dw;
1920Sstevel@tonic-gate 	cpu_t cpu;
1930Sstevel@tonic-gate 	dispq_t dispq;
1940Sstevel@tonic-gate 	disp_t disp;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	if (addr == NULL) {
1970Sstevel@tonic-gate 		mdb_warn("cpu_dispq walk needs a cpu_t address\n");
1980Sstevel@tonic-gate 		return (WALK_ERR);
1990Sstevel@tonic-gate 	}
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
2020Sstevel@tonic-gate 		mdb_warn("failed to read cpu_t at %p", addr);
2030Sstevel@tonic-gate 		return (WALK_ERR);
2040Sstevel@tonic-gate 	}
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) {
2070Sstevel@tonic-gate 		mdb_warn("failed to read disp_t at %p", cpu.cpu_disp);
2080Sstevel@tonic-gate 		return (WALK_ERR);
2090Sstevel@tonic-gate 	}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	if (mdb_vread(&dispq, sizeof (dispq_t),
2120Sstevel@tonic-gate 	    (uintptr_t)disp.disp_q) == -1) {
2130Sstevel@tonic-gate 		mdb_warn("failed to read dispq_t at %p", disp.disp_q);
2140Sstevel@tonic-gate 		return (WALK_ERR);
2150Sstevel@tonic-gate 	}
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	dw->dw_npri = disp.disp_npri;
2200Sstevel@tonic-gate 	dw->dw_dispq = (uintptr_t)disp.disp_q;
2210Sstevel@tonic-gate 	dw->dw_last = (uintptr_t)dispq.dq_last;
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
2240Sstevel@tonic-gate 	wsp->walk_data = dw;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	return (WALK_NEXT);
2270Sstevel@tonic-gate }
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate int
cpupart_dispq_walk_init(mdb_walk_state_t * wsp)2300Sstevel@tonic-gate cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
2310Sstevel@tonic-gate {
2320Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
2330Sstevel@tonic-gate 	dispq_walk_t *dw;
2340Sstevel@tonic-gate 	cpupart_t cpupart;
2350Sstevel@tonic-gate 	dispq_t dispq;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	if (addr == NULL) {
2380Sstevel@tonic-gate 		mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
2390Sstevel@tonic-gate 		return (WALK_ERR);
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) {
2430Sstevel@tonic-gate 		mdb_warn("failed to read cpupart_t at %p", addr);
2440Sstevel@tonic-gate 		return (WALK_ERR);
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	if (mdb_vread(&dispq, sizeof (dispq_t),
2480Sstevel@tonic-gate 	    (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) {
2490Sstevel@tonic-gate 		mdb_warn("failed to read dispq_t at %p",
2500Sstevel@tonic-gate 		    cpupart.cp_kp_queue.disp_q);
2510Sstevel@tonic-gate 		return (WALK_ERR);
2520Sstevel@tonic-gate 	}
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	dw->dw_npri = cpupart.cp_kp_queue.disp_npri;
2570Sstevel@tonic-gate 	dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q;
2580Sstevel@tonic-gate 	dw->dw_last = (uintptr_t)dispq.dq_last;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
2610Sstevel@tonic-gate 	wsp->walk_data = dw;
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	return (WALK_NEXT);
2640Sstevel@tonic-gate }
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate int
dispq_walk_step(mdb_walk_state_t * wsp)2670Sstevel@tonic-gate dispq_walk_step(mdb_walk_state_t *wsp)
2680Sstevel@tonic-gate {
2690Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
2700Sstevel@tonic-gate 	dispq_walk_t *dw = wsp->walk_data;
2710Sstevel@tonic-gate 	dispq_t dispq;
2720Sstevel@tonic-gate 	kthread_t t;
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	while (addr == NULL) {
2750Sstevel@tonic-gate 		if (--dw->dw_npri == 0)
2760Sstevel@tonic-gate 			return (WALK_DONE);
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 		dw->dw_dispq += sizeof (dispq_t);
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 		if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) {
2810Sstevel@tonic-gate 			mdb_warn("failed to read dispq_t at %p", dw->dw_dispq);
2820Sstevel@tonic-gate 			return (WALK_ERR);
2830Sstevel@tonic-gate 		}
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 		dw->dw_last = (uintptr_t)dispq.dq_last;
2860Sstevel@tonic-gate 		addr = (uintptr_t)dispq.dq_first;
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
2900Sstevel@tonic-gate 		mdb_warn("failed to read kthread_t at %p", addr);
2910Sstevel@tonic-gate 		return (WALK_ERR);
2920Sstevel@tonic-gate 	}
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	if (addr == dw->dw_last)
2950Sstevel@tonic-gate 		wsp->walk_addr = NULL;
2960Sstevel@tonic-gate 	else
2970Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)t.t_link;
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
3000Sstevel@tonic-gate }
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate void
dispq_walk_fini(mdb_walk_state_t * wsp)3030Sstevel@tonic-gate dispq_walk_fini(mdb_walk_state_t *wsp)
3040Sstevel@tonic-gate {
3050Sstevel@tonic-gate 	mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate 
308*8721SJonathan.Adams@Sun.COM struct thread_state {
309*8721SJonathan.Adams@Sun.COM 	uint_t ts_state;
310*8721SJonathan.Adams@Sun.COM 	const char *ts_name;
311*8721SJonathan.Adams@Sun.COM } thread_states[] = {
312*8721SJonathan.Adams@Sun.COM 	{ TS_FREE,	"free" },
313*8721SJonathan.Adams@Sun.COM 	{ TS_SLEEP,	"sleep" },
314*8721SJonathan.Adams@Sun.COM 	{ TS_RUN,	"run" },
315*8721SJonathan.Adams@Sun.COM 	{ TS_ONPROC,	"onproc" },
316*8721SJonathan.Adams@Sun.COM 	{ TS_ZOMB,	"zomb" },
317*8721SJonathan.Adams@Sun.COM 	{ TS_STOPPED,	"stopped" },
318*8721SJonathan.Adams@Sun.COM 	{ TS_WAIT,	"wait" }
319*8721SJonathan.Adams@Sun.COM };
320*8721SJonathan.Adams@Sun.COM #define	NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
321*8721SJonathan.Adams@Sun.COM 
322*8721SJonathan.Adams@Sun.COM void
thread_state_to_text(uint_t state,char * out,size_t out_sz)323*8721SJonathan.Adams@Sun.COM thread_state_to_text(uint_t state, char *out, size_t out_sz)
324*8721SJonathan.Adams@Sun.COM {
325*8721SJonathan.Adams@Sun.COM 	int idx;
326*8721SJonathan.Adams@Sun.COM 
327*8721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
328*8721SJonathan.Adams@Sun.COM 		struct thread_state *tsp = &thread_states[idx];
329*8721SJonathan.Adams@Sun.COM 		if (tsp->ts_state == state) {
330*8721SJonathan.Adams@Sun.COM 			mdb_snprintf(out, out_sz, "%s", tsp->ts_name);
331*8721SJonathan.Adams@Sun.COM 			return;
332*8721SJonathan.Adams@Sun.COM 		}
333*8721SJonathan.Adams@Sun.COM 	}
334*8721SJonathan.Adams@Sun.COM 	mdb_snprintf(out, out_sz, "inval/%02x", state);
335*8721SJonathan.Adams@Sun.COM }
336*8721SJonathan.Adams@Sun.COM 
337*8721SJonathan.Adams@Sun.COM int
thread_text_to_state(const char * state,uint_t * out)338*8721SJonathan.Adams@Sun.COM thread_text_to_state(const char *state, uint_t *out)
339*8721SJonathan.Adams@Sun.COM {
340*8721SJonathan.Adams@Sun.COM 	int idx;
341*8721SJonathan.Adams@Sun.COM 
342*8721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
343*8721SJonathan.Adams@Sun.COM 		struct thread_state *tsp = &thread_states[idx];
344*8721SJonathan.Adams@Sun.COM 		if (strcasecmp(tsp->ts_name, state) == 0) {
345*8721SJonathan.Adams@Sun.COM 			*out = tsp->ts_state;
346*8721SJonathan.Adams@Sun.COM 			return (0);
347*8721SJonathan.Adams@Sun.COM 		}
348*8721SJonathan.Adams@Sun.COM 	}
349*8721SJonathan.Adams@Sun.COM 	return (-1);
350*8721SJonathan.Adams@Sun.COM }
351*8721SJonathan.Adams@Sun.COM 
352*8721SJonathan.Adams@Sun.COM void
thread_walk_states(void (* cbfunc)(uint_t,const char *,void *),void * cbarg)353*8721SJonathan.Adams@Sun.COM thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg)
354*8721SJonathan.Adams@Sun.COM {
355*8721SJonathan.Adams@Sun.COM 	int idx;
356*8721SJonathan.Adams@Sun.COM 
357*8721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
358*8721SJonathan.Adams@Sun.COM 		struct thread_state *tsp = &thread_states[idx];
359*8721SJonathan.Adams@Sun.COM 		cbfunc(tsp->ts_state, tsp->ts_name, cbarg);
360*8721SJonathan.Adams@Sun.COM 	}
361*8721SJonathan.Adams@Sun.COM }
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate #define	TF_INTR		0x01
3640Sstevel@tonic-gate #define	TF_PROC		0x02
3650Sstevel@tonic-gate #define	TF_BLOCK	0x04
3660Sstevel@tonic-gate #define	TF_SIG		0x08
3670Sstevel@tonic-gate #define	TF_DISP		0x10
3680Sstevel@tonic-gate #define	TF_MERGE	0x20
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate /*
3710Sstevel@tonic-gate  * Display a kthread_t.
3720Sstevel@tonic-gate  * This is a little complicated, as there is a lot of information that
3730Sstevel@tonic-gate  * the user could be interested in.  The flags "ipbsd" are used to
3740Sstevel@tonic-gate  * indicate which subset of the thread's members are to be displayed
3750Sstevel@tonic-gate  * ('i' is the default).  If multiple options are specified, multiple
3760Sstevel@tonic-gate  * sets of data will be displayed in a vaguely readable format.  If the
3770Sstevel@tonic-gate  * 'm' option is specified, all the selected sets will be merged onto a
3780Sstevel@tonic-gate  * single line for the benefit of those using wider-than-normal
3790Sstevel@tonic-gate  * terminals.  Having a generic mechanism for doing this would be
3800Sstevel@tonic-gate  * really useful, but is a project best left to another day.
3810Sstevel@tonic-gate  */
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate int
thread(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)3840Sstevel@tonic-gate thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3850Sstevel@tonic-gate {
3860Sstevel@tonic-gate 	kthread_t	t;
3870Sstevel@tonic-gate 	uint_t		oflags = 0;
3880Sstevel@tonic-gate 	uint_t		fflag = FALSE;
3890Sstevel@tonic-gate 	int		first;
3900Sstevel@tonic-gate 	char		stbuf[20];
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	/*
3930Sstevel@tonic-gate 	 * "Gracefully" handle printing a boatload of stuff to the
3940Sstevel@tonic-gate 	 * screen.  If we are not printing our first set of data, and
3950Sstevel@tonic-gate 	 * we haven't been instructed to merge sets together, output a
3960Sstevel@tonic-gate 	 * newline and indent such that the thread addresses form a
3970Sstevel@tonic-gate 	 * column of their own.
3980Sstevel@tonic-gate 	 */
3990Sstevel@tonic-gate #define	SPACER()				\
4000Sstevel@tonic-gate 	if (first) {				\
4010Sstevel@tonic-gate 		first = FALSE;			\
4020Sstevel@tonic-gate 	} else if (!(oflags & TF_MERGE)) {	\
4030Sstevel@tonic-gate 		mdb_printf("\n%?s", "");	\
4040Sstevel@tonic-gate 	}
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
4070Sstevel@tonic-gate 		if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) {
4080Sstevel@tonic-gate 			mdb_warn("can't walk threads");
4090Sstevel@tonic-gate 			return (DCMD_ERR);
4100Sstevel@tonic-gate 		}
4110Sstevel@tonic-gate 		return (DCMD_OK);
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
4150Sstevel@tonic-gate 	    'f', MDB_OPT_SETBITS, TRUE, &fflag,
4160Sstevel@tonic-gate 	    'i', MDB_OPT_SETBITS, TF_INTR, &oflags,
4170Sstevel@tonic-gate 	    'p', MDB_OPT_SETBITS, TF_PROC, &oflags,
4180Sstevel@tonic-gate 	    'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags,
4190Sstevel@tonic-gate 	    's', MDB_OPT_SETBITS, TF_SIG, &oflags,
4200Sstevel@tonic-gate 	    'd', MDB_OPT_SETBITS, TF_DISP, &oflags,
4210Sstevel@tonic-gate 	    'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc)
4220Sstevel@tonic-gate 		return (DCMD_USAGE);
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	/*
4250Sstevel@tonic-gate 	 * If no sets were specified, choose the 'i' set.
4260Sstevel@tonic-gate 	 */
4270Sstevel@tonic-gate 	if (!(oflags & ~TF_MERGE))
4280Sstevel@tonic-gate #ifdef	_LP64
4290Sstevel@tonic-gate 		oflags = TF_INTR;
4300Sstevel@tonic-gate #else
4310Sstevel@tonic-gate 		oflags = TF_INTR | TF_DISP | TF_MERGE;
4320Sstevel@tonic-gate #endif
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	/*
4350Sstevel@tonic-gate 	 * Print the relevant headers; note use of SPACER().
4360Sstevel@tonic-gate 	 */
4370Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
4380Sstevel@tonic-gate 		first = TRUE;
4390Sstevel@tonic-gate 		mdb_printf("%<u>%?s%</u>", "ADDR");
4400Sstevel@tonic-gate 		mdb_flush();
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 		if (oflags & TF_PROC) {
4430Sstevel@tonic-gate 			SPACER();
4440Sstevel@tonic-gate 			mdb_printf("%<u> %?s %?s %?s%</u>",
4450Sstevel@tonic-gate 			    "PROC", "LWP", "CRED");
4460Sstevel@tonic-gate 		}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 		if (oflags & TF_INTR) {
4490Sstevel@tonic-gate 			SPACER();
4500Sstevel@tonic-gate 			mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>",
4510Sstevel@tonic-gate 			    "STATE", "FLG", "PFLG",
4520Sstevel@tonic-gate 			    "SFLG", "PRI", "EPRI", "PIL", "INTR");
4530Sstevel@tonic-gate 		}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		if (oflags & TF_BLOCK) {
4560Sstevel@tonic-gate 			SPACER();
4570Sstevel@tonic-gate 			mdb_printf("%<u> %?s %?s %?s %11s%</u>",
4580Sstevel@tonic-gate 			    "WCHAN", "TS", "PITS", "SOBJ OPS");
4590Sstevel@tonic-gate 		}
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 		if (oflags & TF_SIG) {
4620Sstevel@tonic-gate 			SPACER();
4630Sstevel@tonic-gate 			mdb_printf("%<u> %?s %16s %16s%</u>",
4640Sstevel@tonic-gate 			    "SIGQUEUE", "SIG PEND", "SIG HELD");
4650Sstevel@tonic-gate 		}
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 		if (oflags & TF_DISP) {
4680Sstevel@tonic-gate 			SPACER();
4690Sstevel@tonic-gate 			mdb_printf("%<u> %?s %5s %2s%</u>",
4700Sstevel@tonic-gate 			    "DISPTIME", "BOUND", "PR");
4710Sstevel@tonic-gate 		}
4720Sstevel@tonic-gate 		mdb_printf("\n");
4730Sstevel@tonic-gate 	}
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
4760Sstevel@tonic-gate 		mdb_warn("can't read kthread_t at %#lx", addr);
4770Sstevel@tonic-gate 		return (DCMD_ERR);
4780Sstevel@tonic-gate 	}
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	if (fflag && (t.t_state == TS_FREE))
4810Sstevel@tonic-gate 		return (DCMD_OK);
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	first = TRUE;
4840Sstevel@tonic-gate 	mdb_printf("%0?lx", addr);
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	/* process information */
4870Sstevel@tonic-gate 	if (oflags & TF_PROC) {
4880Sstevel@tonic-gate 		SPACER();
4895316Sjohnlev 		mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred);
4900Sstevel@tonic-gate 	}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	/* priority/interrupt information */
4930Sstevel@tonic-gate 	if (oflags & TF_INTR) {
4940Sstevel@tonic-gate 		SPACER();
495*8721SJonathan.Adams@Sun.COM 		thread_state_to_text(t.t_state, stbuf, sizeof (stbuf));
4960Sstevel@tonic-gate 		if (t.t_intr == NULL) {
4970Sstevel@tonic-gate 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s",
498*8721SJonathan.Adams@Sun.COM 			    stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
4990Sstevel@tonic-gate 			    t.t_pri, t.t_epri, t.t_pil, "n/a");
5000Sstevel@tonic-gate 		} else {
5010Sstevel@tonic-gate 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p",
502*8721SJonathan.Adams@Sun.COM 			    stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
5030Sstevel@tonic-gate 			    t.t_pri, t.t_epri, t.t_pil, t.t_intr);
5040Sstevel@tonic-gate 		}
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	/* blocking information */
5080Sstevel@tonic-gate 	if (oflags & TF_BLOCK) {
5090Sstevel@tonic-gate 		SPACER();
5100Sstevel@tonic-gate 		(void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops);
5110Sstevel@tonic-gate 		stbuf[11] = '\0';
5120Sstevel@tonic-gate 		mdb_printf(" %?p %?p %?p %11s",
5130Sstevel@tonic-gate 		    t.t_wchan, t.t_ts, t.t_prioinv, stbuf);
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	/* signal information */
5170Sstevel@tonic-gate 	if (oflags & TF_SIG) {
5180Sstevel@tonic-gate 		SPACER();
5190Sstevel@tonic-gate 		mdb_printf(" %?p %016llx %016llx",
5205316Sjohnlev 		    t.t_sigqueue, t.t_sig, t.t_hold);
5210Sstevel@tonic-gate 	}
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	/* dispatcher stuff */
5240Sstevel@tonic-gate 	if (oflags & TF_DISP) {
5250Sstevel@tonic-gate 		SPACER();
5260Sstevel@tonic-gate 		mdb_printf(" %?lx %5d %2d",
5270Sstevel@tonic-gate 		    t.t_disp_time, t.t_bind_cpu, t.t_preempt);
5280Sstevel@tonic-gate 	}
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	mdb_printf("\n");
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate #undef SPACER
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	return (DCMD_OK);
5350Sstevel@tonic-gate }
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate void
thread_help(void)5380Sstevel@tonic-gate thread_help(void)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate 	mdb_printf(
5410Sstevel@tonic-gate 	    "The flags -ipbsd control which information is displayed.  When\n"
5420Sstevel@tonic-gate 	    "combined, the fields are displayed on separate lines unless the\n"
5430Sstevel@tonic-gate 	    "-m option is given.\n"
5440Sstevel@tonic-gate 	    "\n"
5450Sstevel@tonic-gate 	    "\t-b\tprint blocked thread state\n"
5460Sstevel@tonic-gate 	    "\t-d\tprint dispatcher state\n"
5470Sstevel@tonic-gate 	    "\t-f\tignore freed threads\n"
5480Sstevel@tonic-gate 	    "\t-i\tprint basic thread state (default)\n"
5490Sstevel@tonic-gate 	    "\t-m\tdisplay results on a single line\n"
5500Sstevel@tonic-gate 	    "\t-p\tprint process and lwp state\n"
5510Sstevel@tonic-gate 	    "\t-s\tprint signal state\n");
5520Sstevel@tonic-gate }
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate /*
5550Sstevel@tonic-gate  * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
5560Sstevel@tonic-gate  */
5570Sstevel@tonic-gate int
threadlist(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)5580Sstevel@tonic-gate threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 	int i;
5610Sstevel@tonic-gate 	uint_t count =  0;
5620Sstevel@tonic-gate 	uint_t verbose = FALSE;
5635316Sjohnlev 	uint_t notaskq = FALSE;
5640Sstevel@tonic-gate 	kthread_t t;
5655316Sjohnlev 	taskq_t tq;
5660Sstevel@tonic-gate 	proc_t p;
5670Sstevel@tonic-gate 	char cmd[80];
5680Sstevel@tonic-gate 	mdb_arg_t cmdarg;
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
5710Sstevel@tonic-gate 		if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) {
5720Sstevel@tonic-gate 			mdb_warn("can't walk threads");
5730Sstevel@tonic-gate 			return (DCMD_ERR);
5740Sstevel@tonic-gate 		}
5750Sstevel@tonic-gate 		return (DCMD_OK);
5760Sstevel@tonic-gate 	}
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
5795316Sjohnlev 	    't', MDB_OPT_SETBITS, TRUE, &notaskq,
5800Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	if (i != argc) {
5830Sstevel@tonic-gate 		if (i != argc - 1 || !verbose)
5840Sstevel@tonic-gate 			return (DCMD_USAGE);
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 		if (argv[i].a_type == MDB_TYPE_IMMEDIATE)
5870Sstevel@tonic-gate 			count = (uint_t)argv[i].a_un.a_val;
5880Sstevel@tonic-gate 		else
5890Sstevel@tonic-gate 			count = (uint_t)mdb_strtoull(argv[i].a_un.a_str);
5900Sstevel@tonic-gate 	}
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
5930Sstevel@tonic-gate 		if (verbose)
5940Sstevel@tonic-gate 			mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
5950Sstevel@tonic-gate 			    "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
5960Sstevel@tonic-gate 		else
5970Sstevel@tonic-gate 			mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n",
5980Sstevel@tonic-gate 			    "ADDR", "PROC", "LWP", "CMD", "LWPID");
5990Sstevel@tonic-gate 	}
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
6020Sstevel@tonic-gate 		mdb_warn("failed to read kthread_t at %p", addr);
6030Sstevel@tonic-gate 		return (DCMD_ERR);
6040Sstevel@tonic-gate 	}
6050Sstevel@tonic-gate 
6065316Sjohnlev 	if (notaskq && t.t_taskq != NULL)
6075316Sjohnlev 		return (DCMD_OK);
6085316Sjohnlev 
6090Sstevel@tonic-gate 	if (t.t_state == TS_FREE)
6100Sstevel@tonic-gate 		return (DCMD_OK);
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
6130Sstevel@tonic-gate 		mdb_warn("failed to read proc at %p", t.t_procp);
6140Sstevel@tonic-gate 		return (DCMD_ERR);
6150Sstevel@tonic-gate 	}
6160Sstevel@tonic-gate 
6175316Sjohnlev 	if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1)
6185316Sjohnlev 		tq.tq_name[0] = '\0';
6195316Sjohnlev 
6200Sstevel@tonic-gate 	if (verbose) {
6210Sstevel@tonic-gate 		mdb_printf("%0?p %?p %?p %3u %3d %?p\n",
6220Sstevel@tonic-gate 		    addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan);
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 		mdb_inc_indent(2);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		mdb_printf("PC: %a", t.t_pc);
6275316Sjohnlev 		if (t.t_tid == 0) {
6285316Sjohnlev 			if (tq.tq_name[0] != '\0')
6295316Sjohnlev 				mdb_printf("    TASKQ: %s\n", tq.tq_name);
6305316Sjohnlev 			else
6315316Sjohnlev 				mdb_printf("    THREAD: %a()\n", t.t_startpc);
6325316Sjohnlev 		} else {
6330Sstevel@tonic-gate 			mdb_printf("    CMD: %s\n", p.p_user.u_psargs);
6345316Sjohnlev 		}
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count);
6370Sstevel@tonic-gate 		cmdarg.a_type = MDB_TYPE_STRING;
6380Sstevel@tonic-gate 		cmdarg.a_un.a_str = cmd;
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 		(void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg);
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 		mdb_dec_indent(2);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 		mdb_printf("\n");
6450Sstevel@tonic-gate 	} else {
6460Sstevel@tonic-gate 		mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp);
6475316Sjohnlev 		if (t.t_tid == 0) {
6485316Sjohnlev 			if (tq.tq_name[0] != '\0')
6495316Sjohnlev 				mdb_printf(" tq:%s\n", tq.tq_name);
6505316Sjohnlev 			else
6515316Sjohnlev 				mdb_printf(" %a()\n", t.t_startpc);
6525316Sjohnlev 		} else {
6530Sstevel@tonic-gate 			mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid);
6545316Sjohnlev 		}
6550Sstevel@tonic-gate 	}
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	return (DCMD_OK);
6580Sstevel@tonic-gate }
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate void
threadlist_help(void)6610Sstevel@tonic-gate threadlist_help(void)
6620Sstevel@tonic-gate {
6630Sstevel@tonic-gate 	mdb_printf(
6640Sstevel@tonic-gate 	    "   -v         print verbose output including C stack trace\n"
6655316Sjohnlev 	    "   -t         skip threads belonging to a taskq\n"
6660Sstevel@tonic-gate 	    "   count      print no more than count arguments (default 0)\n");
6670Sstevel@tonic-gate }
6687854SPhilippe.Jung@Sun.COM 
6697854SPhilippe.Jung@Sun.COM static size_t
stk_compute_percent(caddr_t t_stk,caddr_t t_stkbase,caddr_t sp)6707854SPhilippe.Jung@Sun.COM stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
6717854SPhilippe.Jung@Sun.COM {
6727854SPhilippe.Jung@Sun.COM 	size_t percent;
6737854SPhilippe.Jung@Sun.COM 	size_t s;
6747854SPhilippe.Jung@Sun.COM 
6757854SPhilippe.Jung@Sun.COM 	if (t_stk > t_stkbase) {
6767854SPhilippe.Jung@Sun.COM 		/* stack grows down */
6777854SPhilippe.Jung@Sun.COM 		if (sp > t_stk) {
6787854SPhilippe.Jung@Sun.COM 			return (0);
6797854SPhilippe.Jung@Sun.COM 		}
6807854SPhilippe.Jung@Sun.COM 		if (sp < t_stkbase) {
6817854SPhilippe.Jung@Sun.COM 			return (100);
6827854SPhilippe.Jung@Sun.COM 		}
6837854SPhilippe.Jung@Sun.COM 		percent = t_stk - sp + 1;
6847854SPhilippe.Jung@Sun.COM 		s = t_stk - t_stkbase + 1;
6857854SPhilippe.Jung@Sun.COM 	} else {
6867854SPhilippe.Jung@Sun.COM 		/* stack grows up */
6877854SPhilippe.Jung@Sun.COM 		if (sp < t_stk) {
6887854SPhilippe.Jung@Sun.COM 			return (0);
6897854SPhilippe.Jung@Sun.COM 		}
6907854SPhilippe.Jung@Sun.COM 		if (sp > t_stkbase) {
6917854SPhilippe.Jung@Sun.COM 			return (100);
6927854SPhilippe.Jung@Sun.COM 		}
6937854SPhilippe.Jung@Sun.COM 		percent = sp - t_stk + 1;
6947854SPhilippe.Jung@Sun.COM 		s = t_stkbase - t_stk + 1;
6957854SPhilippe.Jung@Sun.COM 	}
6967854SPhilippe.Jung@Sun.COM 	percent = ((100 * percent) / s) + 1;
6977854SPhilippe.Jung@Sun.COM 	if (percent > 100) {
6987854SPhilippe.Jung@Sun.COM 		percent = 100;
6997854SPhilippe.Jung@Sun.COM 	}
7007854SPhilippe.Jung@Sun.COM 	return (percent);
7017854SPhilippe.Jung@Sun.COM }
7027854SPhilippe.Jung@Sun.COM 
7037854SPhilippe.Jung@Sun.COM /*
7047854SPhilippe.Jung@Sun.COM  * Display kthread stack infos.
7057854SPhilippe.Jung@Sun.COM  */
7067854SPhilippe.Jung@Sun.COM int
stackinfo(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)7077854SPhilippe.Jung@Sun.COM stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7087854SPhilippe.Jung@Sun.COM {
7097854SPhilippe.Jung@Sun.COM 	kthread_t t;
7107854SPhilippe.Jung@Sun.COM 	proc_t p;
7117854SPhilippe.Jung@Sun.COM 	uint64_t *ptr;  /* pattern pointer */
7127854SPhilippe.Jung@Sun.COM 	caddr_t	start;	/* kernel stack start */
7137854SPhilippe.Jung@Sun.COM 	caddr_t end;	/* kernel stack end */
7147854SPhilippe.Jung@Sun.COM 	caddr_t ustack;	/* userland copy of kernel stack */
7157854SPhilippe.Jung@Sun.COM 	size_t usize;	/* userland copy of kernel stack size */
7167854SPhilippe.Jung@Sun.COM 	caddr_t ustart;	/* userland copy of kernel stack, aligned start */
7177854SPhilippe.Jung@Sun.COM 	caddr_t uend;	/* userland copy of kernel stack, aligned end */
7187854SPhilippe.Jung@Sun.COM 	size_t percent = 0;
7197854SPhilippe.Jung@Sun.COM 	uint_t all = FALSE; /* don't show TS_FREE kthread by default */
7207854SPhilippe.Jung@Sun.COM 	uint_t history = FALSE;
7217854SPhilippe.Jung@Sun.COM 	int i = 0;
7227854SPhilippe.Jung@Sun.COM 	unsigned int ukmem_stackinfo;
7237854SPhilippe.Jung@Sun.COM 	uintptr_t allthreads;
7247854SPhilippe.Jung@Sun.COM 
7257854SPhilippe.Jung@Sun.COM 	/* handle options */
7267854SPhilippe.Jung@Sun.COM 	if (mdb_getopts(argc, argv,
7277854SPhilippe.Jung@Sun.COM 	    'a', MDB_OPT_SETBITS, TRUE, &all,
7287854SPhilippe.Jung@Sun.COM 	    'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) {
7297854SPhilippe.Jung@Sun.COM 		return (DCMD_USAGE);
7307854SPhilippe.Jung@Sun.COM 	}
7317854SPhilippe.Jung@Sun.COM 
7327854SPhilippe.Jung@Sun.COM 	/* walk all kthread if needed */
7337854SPhilippe.Jung@Sun.COM 	if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) {
7347854SPhilippe.Jung@Sun.COM 		if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) {
7357854SPhilippe.Jung@Sun.COM 			mdb_warn("can't walk threads");
7367854SPhilippe.Jung@Sun.COM 			return (DCMD_ERR);
7377854SPhilippe.Jung@Sun.COM 		}
7387854SPhilippe.Jung@Sun.COM 		return (DCMD_OK);
7397854SPhilippe.Jung@Sun.COM 	}
7407854SPhilippe.Jung@Sun.COM 
7417854SPhilippe.Jung@Sun.COM 	/* read 'kmem_stackinfo' */
7427854SPhilippe.Jung@Sun.COM 	if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo),
7437854SPhilippe.Jung@Sun.COM 	    "kmem_stackinfo") == -1) {
7447854SPhilippe.Jung@Sun.COM 		mdb_warn("failed to read 'kmem_stackinfo'\n");
7457854SPhilippe.Jung@Sun.COM 		ukmem_stackinfo = 0;
7467854SPhilippe.Jung@Sun.COM 	}
7477854SPhilippe.Jung@Sun.COM 
7487854SPhilippe.Jung@Sun.COM 	/* read 'allthreads' */
7497854SPhilippe.Jung@Sun.COM 	if (mdb_readsym(&allthreads, sizeof (kthread_t *),
7507854SPhilippe.Jung@Sun.COM 	    "allthreads") == -1) {
7517854SPhilippe.Jung@Sun.COM 		mdb_warn("failed to read 'allthreads'\n");
7527854SPhilippe.Jung@Sun.COM 		allthreads = NULL;
7537854SPhilippe.Jung@Sun.COM 	}
7547854SPhilippe.Jung@Sun.COM 
7557854SPhilippe.Jung@Sun.COM 	if (history == TRUE) {
7567854SPhilippe.Jung@Sun.COM 		kmem_stkinfo_t *log;
7577854SPhilippe.Jung@Sun.COM 		uintptr_t kaddr;
7587854SPhilippe.Jung@Sun.COM 
7597854SPhilippe.Jung@Sun.COM 		mdb_printf("Dead kthreads stack usage history:\n");
7607854SPhilippe.Jung@Sun.COM 		if (ukmem_stackinfo == 0) {
7617854SPhilippe.Jung@Sun.COM 			mdb_printf("Tunable kmem_stackinfo is unset, history ");
7627854SPhilippe.Jung@Sun.COM 			mdb_printf("feature is off.\nUse ::help stackinfo ");
7637854SPhilippe.Jung@Sun.COM 			mdb_printf("for more details.\n");
7647854SPhilippe.Jung@Sun.COM 			return (DCMD_OK);
7657854SPhilippe.Jung@Sun.COM 		}
7667854SPhilippe.Jung@Sun.COM 
7677854SPhilippe.Jung@Sun.COM 		mdb_printf("%<u>%?s%</u>", "THREAD");
7687854SPhilippe.Jung@Sun.COM 		mdb_printf(" %<u>%?s%</u>", "STACK");
7697854SPhilippe.Jung@Sun.COM 		mdb_printf("%<u>%s%</u>", "   SIZE  MAX CMD/LWPID or STARTPC");
7707854SPhilippe.Jung@Sun.COM 		mdb_printf("\n");
7717854SPhilippe.Jung@Sun.COM 		usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t);
7727854SPhilippe.Jung@Sun.COM 		log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP);
7737854SPhilippe.Jung@Sun.COM 		if (mdb_readsym(&kaddr, sizeof (kaddr),
7747854SPhilippe.Jung@Sun.COM 		    "kmem_stkinfo_log") == -1) {
7757854SPhilippe.Jung@Sun.COM 			mdb_free((void *)log, usize);
7767854SPhilippe.Jung@Sun.COM 			mdb_warn("failed to read 'kmem_stkinfo_log'\n");
7777854SPhilippe.Jung@Sun.COM 			return (DCMD_ERR);
7787854SPhilippe.Jung@Sun.COM 		}
7797854SPhilippe.Jung@Sun.COM 		if (kaddr == NULL) {
7807854SPhilippe.Jung@Sun.COM 			mdb_free((void *)log, usize);
7817854SPhilippe.Jung@Sun.COM 			return (DCMD_OK);
7827854SPhilippe.Jung@Sun.COM 		}
7837854SPhilippe.Jung@Sun.COM 		if (mdb_vread(log, usize, kaddr) == -1) {
7847854SPhilippe.Jung@Sun.COM 			mdb_free((void *)log, usize);
7857854SPhilippe.Jung@Sun.COM 			mdb_warn("failed to read %p\n", kaddr);
7867854SPhilippe.Jung@Sun.COM 			return (DCMD_ERR);
7877854SPhilippe.Jung@Sun.COM 		}
7887854SPhilippe.Jung@Sun.COM 		for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
7897854SPhilippe.Jung@Sun.COM 			if (log[i].kthread == NULL) {
7907854SPhilippe.Jung@Sun.COM 				continue;
7917854SPhilippe.Jung@Sun.COM 			}
7927854SPhilippe.Jung@Sun.COM 			mdb_printf("%0?p %0?p %6x %3d%%",
7937854SPhilippe.Jung@Sun.COM 			    log[i].kthread,
7947854SPhilippe.Jung@Sun.COM 			    log[i].start,
7957854SPhilippe.Jung@Sun.COM 			    (uint_t)log[i].stksz,
7967854SPhilippe.Jung@Sun.COM 			    (int)log[i].percent);
7977854SPhilippe.Jung@Sun.COM 			if (log[i].t_tid != 0) {
7987854SPhilippe.Jung@Sun.COM 				mdb_printf(" %s/%u\n",
7997854SPhilippe.Jung@Sun.COM 				    log[i].cmd, log[i].t_tid);
8007854SPhilippe.Jung@Sun.COM 			} else {
8017854SPhilippe.Jung@Sun.COM 				mdb_printf(" %p (%a)\n", log[i].t_startpc,
8027854SPhilippe.Jung@Sun.COM 				    log[i].t_startpc);
8037854SPhilippe.Jung@Sun.COM 			}
8047854SPhilippe.Jung@Sun.COM 		}
8057854SPhilippe.Jung@Sun.COM 		mdb_free((void *)log, usize);
8067854SPhilippe.Jung@Sun.COM 		return (DCMD_OK);
8077854SPhilippe.Jung@Sun.COM 	}
8087854SPhilippe.Jung@Sun.COM 
8097854SPhilippe.Jung@Sun.COM 	/* display header */
8107854SPhilippe.Jung@Sun.COM 	if (DCMD_HDRSPEC(flags)) {
8117854SPhilippe.Jung@Sun.COM 		if (ukmem_stackinfo == 0) {
8127854SPhilippe.Jung@Sun.COM 			mdb_printf("Tunable kmem_stackinfo is unset, ");
8137854SPhilippe.Jung@Sun.COM 			mdb_printf("MAX value is not available.\n");
8147854SPhilippe.Jung@Sun.COM 			mdb_printf("Use ::help stackinfo for more details.\n");
8157854SPhilippe.Jung@Sun.COM 		}
8167854SPhilippe.Jung@Sun.COM 		mdb_printf("%<u>%?s%</u>", "THREAD");
8177854SPhilippe.Jung@Sun.COM 		mdb_printf(" %<u>%?s%</u>", "STACK");
8187854SPhilippe.Jung@Sun.COM 		mdb_printf("%<u>%s%</u>", "   SIZE  CUR  MAX CMD/LWPID");
8197854SPhilippe.Jung@Sun.COM 		mdb_printf("\n");
8207854SPhilippe.Jung@Sun.COM 	}
8217854SPhilippe.Jung@Sun.COM 
8227854SPhilippe.Jung@Sun.COM 	/* read kthread */
8237854SPhilippe.Jung@Sun.COM 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
8247854SPhilippe.Jung@Sun.COM 		mdb_warn("can't read kthread_t at %#lx\n", addr);
8257854SPhilippe.Jung@Sun.COM 		return (DCMD_ERR);
8267854SPhilippe.Jung@Sun.COM 	}
8277854SPhilippe.Jung@Sun.COM 
8287854SPhilippe.Jung@Sun.COM 	if (t.t_state == TS_FREE && all == FALSE) {
8297854SPhilippe.Jung@Sun.COM 		return (DCMD_OK);
8307854SPhilippe.Jung@Sun.COM 	}
8317854SPhilippe.Jung@Sun.COM 
8327854SPhilippe.Jung@Sun.COM 	/* read proc */
8337854SPhilippe.Jung@Sun.COM 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
8347854SPhilippe.Jung@Sun.COM 		mdb_warn("failed to read proc at %p\n", t.t_procp);
8357854SPhilippe.Jung@Sun.COM 		return (DCMD_ERR);
8367854SPhilippe.Jung@Sun.COM 	}
8377854SPhilippe.Jung@Sun.COM 
8387854SPhilippe.Jung@Sun.COM 	/*
8397854SPhilippe.Jung@Sun.COM 	 * Stack grows up or down, see thread_create(),
8407854SPhilippe.Jung@Sun.COM 	 * compute stack memory aera start and end (start < end).
8417854SPhilippe.Jung@Sun.COM 	 */
8427854SPhilippe.Jung@Sun.COM 	if (t.t_stk > t.t_stkbase) {
8437854SPhilippe.Jung@Sun.COM 		/* stack grows down */
8447854SPhilippe.Jung@Sun.COM 		start = t.t_stkbase;
8457854SPhilippe.Jung@Sun.COM 		end = t.t_stk;
8467854SPhilippe.Jung@Sun.COM 	} else {
8477854SPhilippe.Jung@Sun.COM 		/* stack grows up */
8487854SPhilippe.Jung@Sun.COM 		start = t.t_stk;
8497854SPhilippe.Jung@Sun.COM 		end = t.t_stkbase;
8507854SPhilippe.Jung@Sun.COM 	}
8517854SPhilippe.Jung@Sun.COM 
8527854SPhilippe.Jung@Sun.COM 	/* display stack info */
8537854SPhilippe.Jung@Sun.COM 	mdb_printf("%0?p %0?p", addr, start);
8547854SPhilippe.Jung@Sun.COM 
8557854SPhilippe.Jung@Sun.COM 	/* (end - start), kernel stack size as found in kthread_t */
8567854SPhilippe.Jung@Sun.COM 	if ((end <= start) || ((end - start) > (1024 * 1024))) {
8577854SPhilippe.Jung@Sun.COM 		/* negative or stack size > 1 meg, assume bogus */
8587854SPhilippe.Jung@Sun.COM 		mdb_warn(" t_stk/t_stkbase problem\n");
8597854SPhilippe.Jung@Sun.COM 		return (DCMD_ERR);
8607854SPhilippe.Jung@Sun.COM 	}
8617854SPhilippe.Jung@Sun.COM 
8627854SPhilippe.Jung@Sun.COM 	/* display stack size */
8637854SPhilippe.Jung@Sun.COM 	mdb_printf(" %6x", end - start);
8647854SPhilippe.Jung@Sun.COM 
8657854SPhilippe.Jung@Sun.COM 	/* display current stack usage */
8667854SPhilippe.Jung@Sun.COM 	percent = stk_compute_percent(t.t_stk, t.t_stkbase,
8677854SPhilippe.Jung@Sun.COM 	    (caddr_t)t.t_sp + STACK_BIAS);
8687854SPhilippe.Jung@Sun.COM 
8697854SPhilippe.Jung@Sun.COM 	mdb_printf(" %3d%%", percent);
8707854SPhilippe.Jung@Sun.COM 	percent = 0;
8717854SPhilippe.Jung@Sun.COM 
8727854SPhilippe.Jung@Sun.COM 	if (ukmem_stackinfo == 0) {
8737854SPhilippe.Jung@Sun.COM 		mdb_printf("  n/a");
8747854SPhilippe.Jung@Sun.COM 		if (t.t_tid == 0) {
8757854SPhilippe.Jung@Sun.COM 			mdb_printf(" %a()", t.t_startpc);
8767854SPhilippe.Jung@Sun.COM 		} else {
8777854SPhilippe.Jung@Sun.COM 			mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
8787854SPhilippe.Jung@Sun.COM 		}
8797854SPhilippe.Jung@Sun.COM 		mdb_printf("\n");
8807854SPhilippe.Jung@Sun.COM 		return (DCMD_OK);
8817854SPhilippe.Jung@Sun.COM 	}
8827854SPhilippe.Jung@Sun.COM 
8837854SPhilippe.Jung@Sun.COM 	if ((((uintptr_t)start) & 0x7) != 0) {
8847854SPhilippe.Jung@Sun.COM 		start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
8857854SPhilippe.Jung@Sun.COM 	}
8867854SPhilippe.Jung@Sun.COM 	end = (caddr_t)(((uintptr_t)end) & (~0x7));
8877854SPhilippe.Jung@Sun.COM 	/* size to scan in userland copy of kernel stack */
8887854SPhilippe.Jung@Sun.COM 	usize = end - start; /* is a multiple of 8 bytes */
8897854SPhilippe.Jung@Sun.COM 
8907854SPhilippe.Jung@Sun.COM 	/*
8917854SPhilippe.Jung@Sun.COM 	 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes
8927854SPhilippe.Jung@Sun.COM 	 * alignement for ustart and uend, in boundaries.
8937854SPhilippe.Jung@Sun.COM 	 */
8947854SPhilippe.Jung@Sun.COM 	ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP);
8957854SPhilippe.Jung@Sun.COM 	if ((((uintptr_t)ustart) & 0x7) != 0) {
8967854SPhilippe.Jung@Sun.COM 		ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8);
8977854SPhilippe.Jung@Sun.COM 	}
8987854SPhilippe.Jung@Sun.COM 	uend = ustart + usize;
8997854SPhilippe.Jung@Sun.COM 
9007854SPhilippe.Jung@Sun.COM 	/* read the kernel stack */
9017854SPhilippe.Jung@Sun.COM 	if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) {
9027854SPhilippe.Jung@Sun.COM 		mdb_free((void *)ustack, usize + 8);
9037854SPhilippe.Jung@Sun.COM 		mdb_printf("\n");
9047854SPhilippe.Jung@Sun.COM 		mdb_warn("couldn't read entire stack\n");
9057854SPhilippe.Jung@Sun.COM 		return (DCMD_ERR);
9067854SPhilippe.Jung@Sun.COM 	}
9077854SPhilippe.Jung@Sun.COM 
9087854SPhilippe.Jung@Sun.COM 	/* scan the stack */
9097854SPhilippe.Jung@Sun.COM 	if (t.t_stk > t.t_stkbase) {
9107854SPhilippe.Jung@Sun.COM 		/* stack grows down */
9117854SPhilippe.Jung@Sun.COM #if defined(__i386) || defined(__amd64)
9127854SPhilippe.Jung@Sun.COM 		/*
9137854SPhilippe.Jung@Sun.COM 		 * 6 longs are pushed on stack, see thread_load(). Skip
9147854SPhilippe.Jung@Sun.COM 		 * them, so if kthread has never run, percent is zero.
9157854SPhilippe.Jung@Sun.COM 		 * 8 bytes alignement is preserved for a 32 bit kernel,
9167854SPhilippe.Jung@Sun.COM 		 * 6 x 4 = 24, 24 is a multiple of 8.
9177854SPhilippe.Jung@Sun.COM 		 */
9187854SPhilippe.Jung@Sun.COM 		uend -= (6 * sizeof (long));
9197854SPhilippe.Jung@Sun.COM #endif
9207854SPhilippe.Jung@Sun.COM 		ptr = (uint64_t *)((void *)ustart);
9217854SPhilippe.Jung@Sun.COM 		while (ptr < (uint64_t *)((void *)uend)) {
9227854SPhilippe.Jung@Sun.COM 			if (*ptr != KMEM_STKINFO_PATTERN) {
9237854SPhilippe.Jung@Sun.COM 				percent = stk_compute_percent(uend,
9247854SPhilippe.Jung@Sun.COM 				    ustart, (caddr_t)ptr);
9257854SPhilippe.Jung@Sun.COM 				break;
9267854SPhilippe.Jung@Sun.COM 			}
9277854SPhilippe.Jung@Sun.COM 			ptr++;
9287854SPhilippe.Jung@Sun.COM 		}
9297854SPhilippe.Jung@Sun.COM 	} else {
9307854SPhilippe.Jung@Sun.COM 		/* stack grows up */
9317854SPhilippe.Jung@Sun.COM 		ptr = (uint64_t *)((void *)uend);
9327854SPhilippe.Jung@Sun.COM 		ptr--;
9337854SPhilippe.Jung@Sun.COM 		while (ptr >= (uint64_t *)((void *)ustart)) {
9347854SPhilippe.Jung@Sun.COM 			if (*ptr != KMEM_STKINFO_PATTERN) {
9357854SPhilippe.Jung@Sun.COM 				percent = stk_compute_percent(ustart,
9367854SPhilippe.Jung@Sun.COM 				    uend, (caddr_t)ptr);
9377854SPhilippe.Jung@Sun.COM 				break;
9387854SPhilippe.Jung@Sun.COM 			}
9397854SPhilippe.Jung@Sun.COM 			ptr--;
9407854SPhilippe.Jung@Sun.COM 		}
9417854SPhilippe.Jung@Sun.COM 	}
9427854SPhilippe.Jung@Sun.COM 
9437854SPhilippe.Jung@Sun.COM 	/* thread 't0' stack is not created by thread_create() */
9447854SPhilippe.Jung@Sun.COM 	if (addr == allthreads) {
9457854SPhilippe.Jung@Sun.COM 		percent = 0;
9467854SPhilippe.Jung@Sun.COM 	}
9477854SPhilippe.Jung@Sun.COM 	if (percent != 0) {
9487854SPhilippe.Jung@Sun.COM 		mdb_printf(" %3d%%", percent);
9497854SPhilippe.Jung@Sun.COM 	} else {
9507854SPhilippe.Jung@Sun.COM 		mdb_printf("  n/a");
9517854SPhilippe.Jung@Sun.COM 	}
9527854SPhilippe.Jung@Sun.COM 	if (t.t_tid == 0) {
9537854SPhilippe.Jung@Sun.COM 		mdb_printf(" %a()", t.t_startpc);
9547854SPhilippe.Jung@Sun.COM 	} else {
9557854SPhilippe.Jung@Sun.COM 		mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
9567854SPhilippe.Jung@Sun.COM 	}
9577854SPhilippe.Jung@Sun.COM 	mdb_printf("\n");
9587854SPhilippe.Jung@Sun.COM 	mdb_free((void *)ustack, usize + 8);
9597854SPhilippe.Jung@Sun.COM 	return (DCMD_OK);
9607854SPhilippe.Jung@Sun.COM }
9617854SPhilippe.Jung@Sun.COM 
9627854SPhilippe.Jung@Sun.COM void
stackinfo_help(void)9637854SPhilippe.Jung@Sun.COM stackinfo_help(void)
9647854SPhilippe.Jung@Sun.COM {
9657854SPhilippe.Jung@Sun.COM 	mdb_printf(
9667854SPhilippe.Jung@Sun.COM 	    "Shows kernel stacks real utilization, if /etc/system "
9677854SPhilippe.Jung@Sun.COM 	    "kmem_stackinfo tunable\n");
9687854SPhilippe.Jung@Sun.COM 	mdb_printf(
9697854SPhilippe.Jung@Sun.COM 	    "(an unsigned integer) is non zero at kthread creation time. ");
9707854SPhilippe.Jung@Sun.COM 	mdb_printf("For example:\n");
9717854SPhilippe.Jung@Sun.COM 	mdb_printf(
9727854SPhilippe.Jung@Sun.COM 	    "          THREAD            STACK   SIZE  CUR  MAX CMD/LWPID\n");
9737854SPhilippe.Jung@Sun.COM 	mdb_printf(
9747854SPhilippe.Jung@Sun.COM 	    "ffffff014f5f2c20 ffffff0004153000   4f00   4%%  43%% init/1\n");
9757854SPhilippe.Jung@Sun.COM 	mdb_printf(
9767854SPhilippe.Jung@Sun.COM 	    "The stack size utilization for this kthread is at 4%%"
9777854SPhilippe.Jung@Sun.COM 	    " of its maximum size,\n");
9787854SPhilippe.Jung@Sun.COM 	mdb_printf(
9797854SPhilippe.Jung@Sun.COM 	    "but has already used up to 43%%, stack size is 4f00 bytes.\n");
9807854SPhilippe.Jung@Sun.COM 	mdb_printf(
9817854SPhilippe.Jung@Sun.COM 	    "MAX value can be shown as n/a (not available):\n");
9827854SPhilippe.Jung@Sun.COM 	mdb_printf(
9837854SPhilippe.Jung@Sun.COM 	    "  - for the very first kthread (sched/1)\n");
9847854SPhilippe.Jung@Sun.COM 	mdb_printf(
9857854SPhilippe.Jung@Sun.COM 	    "  - kmem_stackinfo was zero at kthread creation time\n");
9867854SPhilippe.Jung@Sun.COM 	mdb_printf(
9877854SPhilippe.Jung@Sun.COM 	    "  - kthread has not yet run\n");
9887854SPhilippe.Jung@Sun.COM 	mdb_printf("\n");
9897854SPhilippe.Jung@Sun.COM 	mdb_printf("Options:\n");
9907854SPhilippe.Jung@Sun.COM 	mdb_printf(
9917854SPhilippe.Jung@Sun.COM 	    "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
9927854SPhilippe.Jung@Sun.COM 	mdb_printf(
9937854SPhilippe.Jung@Sun.COM 	    "-h shows history, dead kthreads that used their "
9947854SPhilippe.Jung@Sun.COM 	    "kernel stack the most\n");
9957854SPhilippe.Jung@Sun.COM 	mdb_printf(
9967854SPhilippe.Jung@Sun.COM 	    "\nSee Solaris Modular Debugger Guide for detailed usage.\n");
9977854SPhilippe.Jung@Sun.COM 	mdb_flush();
9987854SPhilippe.Jung@Sun.COM }
999