xref: /titanic_52/usr/src/cmd/mdb/common/modules/genunix/thread.c (revision 9583f7179cb3bb19c38f4a8567cb8d51786036f1)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5c97ad5cdSakolb  * Common Development and Distribution License (the "License").
6c97ad5cdSakolb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22346799e8SJonathan W Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
25*9583f717SMarcel Telka /*
26*9583f717SMarcel Telka  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
27*9583f717SMarcel Telka  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
31*9583f717SMarcel Telka #include <mdb/mdb_ks.h>
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/thread.h>
347c478bd9Sstevel@tonic-gate #include <sys/lwp.h>
357c478bd9Sstevel@tonic-gate #include <sys/proc.h>
367c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
377c478bd9Sstevel@tonic-gate #include <sys/cpupart.h>
387c478bd9Sstevel@tonic-gate #include <sys/disp.h>
39ab4a9bebSjohnlev #include <sys/taskq_impl.h>
40bff31d89SPhilippe Jung #include <sys/stack.h>
41bff31d89SPhilippe Jung 
42bff31d89SPhilippe Jung #ifndef	STACK_BIAS
43bff31d89SPhilippe Jung #define	STACK_BIAS	0
44bff31d89SPhilippe Jung #endif
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate typedef struct thread_walk {
477c478bd9Sstevel@tonic-gate 	kthread_t *tw_thread;
487c478bd9Sstevel@tonic-gate 	uintptr_t tw_last;
497c478bd9Sstevel@tonic-gate 	uint_t tw_inproc;
507c478bd9Sstevel@tonic-gate 	uint_t tw_step;
517c478bd9Sstevel@tonic-gate } thread_walk_t;
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate int
547c478bd9Sstevel@tonic-gate thread_walk_init(mdb_walk_state_t *wsp)
557c478bd9Sstevel@tonic-gate {
567c478bd9Sstevel@tonic-gate 	thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP);
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
597c478bd9Sstevel@tonic-gate 		if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) {
607c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read 'allthreads'");
617c478bd9Sstevel@tonic-gate 			mdb_free(twp, sizeof (thread_walk_t));
627c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
637c478bd9Sstevel@tonic-gate 		}
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate 		twp->tw_inproc = FALSE;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate 	} else {
687c478bd9Sstevel@tonic-gate 		proc_t pr;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 		if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) {
717c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read proc at %p", wsp->walk_addr);
727c478bd9Sstevel@tonic-gate 			mdb_free(twp, sizeof (thread_walk_t));
737c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
747c478bd9Sstevel@tonic-gate 		}
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)pr.p_tlist;
777c478bd9Sstevel@tonic-gate 		twp->tw_inproc = TRUE;
787c478bd9Sstevel@tonic-gate 	}
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP);
817c478bd9Sstevel@tonic-gate 	twp->tw_last = wsp->walk_addr;
827c478bd9Sstevel@tonic-gate 	twp->tw_step = FALSE;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	wsp->walk_data = twp;
857c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate int
897c478bd9Sstevel@tonic-gate thread_walk_step(mdb_walk_state_t *wsp)
907c478bd9Sstevel@tonic-gate {
917c478bd9Sstevel@tonic-gate 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
927c478bd9Sstevel@tonic-gate 	int status;
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
957c478bd9Sstevel@tonic-gate 		return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	if (twp->tw_step && wsp->walk_addr == twp->tw_last)
987c478bd9Sstevel@tonic-gate 		return (WALK_DONE); /* We've wrapped around */
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	if (mdb_vread(twp->tw_thread, sizeof (kthread_t),
1017c478bd9Sstevel@tonic-gate 	    wsp->walk_addr) == -1) {
1027c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read thread at %p", wsp->walk_addr);
1037c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
1047c478bd9Sstevel@tonic-gate 	}
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
1077c478bd9Sstevel@tonic-gate 	    wsp->walk_cbdata);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	if (twp->tw_inproc)
1107c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw;
1117c478bd9Sstevel@tonic-gate 	else
1127c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 	twp->tw_step = TRUE;
1157c478bd9Sstevel@tonic-gate 	return (status);
1167c478bd9Sstevel@tonic-gate }
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate void
1197c478bd9Sstevel@tonic-gate thread_walk_fini(mdb_walk_state_t *wsp)
1207c478bd9Sstevel@tonic-gate {
1217c478bd9Sstevel@tonic-gate 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	mdb_free(twp->tw_thread, sizeof (kthread_t));
1247c478bd9Sstevel@tonic-gate 	mdb_free(twp, sizeof (thread_walk_t));
1257c478bd9Sstevel@tonic-gate }
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate int
1287c478bd9Sstevel@tonic-gate deathrow_walk_init(mdb_walk_state_t *wsp)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate 	if (mdb_layered_walk("thread_deathrow", wsp) == -1) {
1317c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'thread_deathrow'");
1327c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1337c478bd9Sstevel@tonic-gate 	}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
1367c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'lwp_deathrow'");
1377c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1387c478bd9Sstevel@tonic-gate 	}
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate int
1447c478bd9Sstevel@tonic-gate deathrow_walk_step(mdb_walk_state_t *wsp)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	kthread_t t;
1477c478bd9Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	if (addr == NULL)
1507c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (t), addr) == -1) {
1537c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read deathrow thread at %p", addr);
1547c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1557c478bd9Sstevel@tonic-gate 	}
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)t.t_forw;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate int
1637c478bd9Sstevel@tonic-gate thread_deathrow_walk_init(mdb_walk_state_t *wsp)
1647c478bd9Sstevel@tonic-gate {
1657c478bd9Sstevel@tonic-gate 	if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) {
1667c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read symbol 'thread_deathrow'");
1677c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate int
1747c478bd9Sstevel@tonic-gate lwp_deathrow_walk_init(mdb_walk_state_t *wsp)
1757c478bd9Sstevel@tonic-gate {
1767c478bd9Sstevel@tonic-gate 	if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) {
1777c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read symbol 'lwp_deathrow'");
1787c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1797c478bd9Sstevel@tonic-gate 	}
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1827c478bd9Sstevel@tonic-gate }
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate typedef struct dispq_walk {
1867c478bd9Sstevel@tonic-gate 	int dw_npri;
1877c478bd9Sstevel@tonic-gate 	uintptr_t dw_dispq;
1887c478bd9Sstevel@tonic-gate 	uintptr_t dw_last;
1897c478bd9Sstevel@tonic-gate } dispq_walk_t;
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate int
1927c478bd9Sstevel@tonic-gate cpu_dispq_walk_init(mdb_walk_state_t *wsp)
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
1957c478bd9Sstevel@tonic-gate 	dispq_walk_t *dw;
1967c478bd9Sstevel@tonic-gate 	cpu_t cpu;
1977c478bd9Sstevel@tonic-gate 	dispq_t dispq;
1987c478bd9Sstevel@tonic-gate 	disp_t disp;
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	if (addr == NULL) {
2017c478bd9Sstevel@tonic-gate 		mdb_warn("cpu_dispq walk needs a cpu_t address\n");
2027c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2037c478bd9Sstevel@tonic-gate 	}
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
2067c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read cpu_t at %p", addr);
2077c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2087c478bd9Sstevel@tonic-gate 	}
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) {
2117c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read disp_t at %p", cpu.cpu_disp);
2127c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2137c478bd9Sstevel@tonic-gate 	}
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	if (mdb_vread(&dispq, sizeof (dispq_t),
2167c478bd9Sstevel@tonic-gate 	    (uintptr_t)disp.disp_q) == -1) {
2177c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read dispq_t at %p", disp.disp_q);
2187c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	dw->dw_npri = disp.disp_npri;
2247c478bd9Sstevel@tonic-gate 	dw->dw_dispq = (uintptr_t)disp.disp_q;
2257c478bd9Sstevel@tonic-gate 	dw->dw_last = (uintptr_t)dispq.dq_last;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
2287c478bd9Sstevel@tonic-gate 	wsp->walk_data = dw;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate int
2347c478bd9Sstevel@tonic-gate cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
2377c478bd9Sstevel@tonic-gate 	dispq_walk_t *dw;
2387c478bd9Sstevel@tonic-gate 	cpupart_t cpupart;
2397c478bd9Sstevel@tonic-gate 	dispq_t dispq;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	if (addr == NULL) {
2427c478bd9Sstevel@tonic-gate 		mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
2437c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) {
2477c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read cpupart_t at %p", addr);
2487c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	if (mdb_vread(&dispq, sizeof (dispq_t),
2527c478bd9Sstevel@tonic-gate 	    (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) {
2537c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read dispq_t at %p",
2547c478bd9Sstevel@tonic-gate 		    cpupart.cp_kp_queue.disp_q);
2557c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	dw->dw_npri = cpupart.cp_kp_queue.disp_npri;
2617c478bd9Sstevel@tonic-gate 	dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q;
2627c478bd9Sstevel@tonic-gate 	dw->dw_last = (uintptr_t)dispq.dq_last;
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
2657c478bd9Sstevel@tonic-gate 	wsp->walk_data = dw;
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate int
2717c478bd9Sstevel@tonic-gate dispq_walk_step(mdb_walk_state_t *wsp)
2727c478bd9Sstevel@tonic-gate {
2737c478bd9Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
2747c478bd9Sstevel@tonic-gate 	dispq_walk_t *dw = wsp->walk_data;
2757c478bd9Sstevel@tonic-gate 	dispq_t dispq;
2767c478bd9Sstevel@tonic-gate 	kthread_t t;
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	while (addr == NULL) {
2797c478bd9Sstevel@tonic-gate 		if (--dw->dw_npri == 0)
2807c478bd9Sstevel@tonic-gate 			return (WALK_DONE);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		dw->dw_dispq += sizeof (dispq_t);
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 		if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) {
2857c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read dispq_t at %p", dw->dw_dispq);
2867c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
2877c478bd9Sstevel@tonic-gate 		}
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 		dw->dw_last = (uintptr_t)dispq.dq_last;
2907c478bd9Sstevel@tonic-gate 		addr = (uintptr_t)dispq.dq_first;
2917c478bd9Sstevel@tonic-gate 	}
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
2947c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read kthread_t at %p", addr);
2957c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2967c478bd9Sstevel@tonic-gate 	}
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	if (addr == dw->dw_last)
2997c478bd9Sstevel@tonic-gate 		wsp->walk_addr = NULL;
3007c478bd9Sstevel@tonic-gate 	else
3017c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)t.t_link;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate void
3077c478bd9Sstevel@tonic-gate dispq_walk_fini(mdb_walk_state_t *wsp)
3087c478bd9Sstevel@tonic-gate {
3097c478bd9Sstevel@tonic-gate 	mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
312346799e8SJonathan W Adams struct thread_state {
313346799e8SJonathan W Adams 	uint_t ts_state;
314346799e8SJonathan W Adams 	const char *ts_name;
315346799e8SJonathan W Adams } thread_states[] = {
316346799e8SJonathan W Adams 	{ TS_FREE,	"free" },
317346799e8SJonathan W Adams 	{ TS_SLEEP,	"sleep" },
318346799e8SJonathan W Adams 	{ TS_RUN,	"run" },
319346799e8SJonathan W Adams 	{ TS_ONPROC,	"onproc" },
320346799e8SJonathan W Adams 	{ TS_ZOMB,	"zomb" },
321346799e8SJonathan W Adams 	{ TS_STOPPED,	"stopped" },
322346799e8SJonathan W Adams 	{ TS_WAIT,	"wait" }
323346799e8SJonathan W Adams };
324346799e8SJonathan W Adams #define	NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
325346799e8SJonathan W Adams 
326346799e8SJonathan W Adams void
327346799e8SJonathan W Adams thread_state_to_text(uint_t state, char *out, size_t out_sz)
328346799e8SJonathan W Adams {
329346799e8SJonathan W Adams 	int idx;
330346799e8SJonathan W Adams 
331346799e8SJonathan W Adams 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
332346799e8SJonathan W Adams 		struct thread_state *tsp = &thread_states[idx];
333346799e8SJonathan W Adams 		if (tsp->ts_state == state) {
334346799e8SJonathan W Adams 			mdb_snprintf(out, out_sz, "%s", tsp->ts_name);
335346799e8SJonathan W Adams 			return;
336346799e8SJonathan W Adams 		}
337346799e8SJonathan W Adams 	}
338346799e8SJonathan W Adams 	mdb_snprintf(out, out_sz, "inval/%02x", state);
339346799e8SJonathan W Adams }
340346799e8SJonathan W Adams 
341346799e8SJonathan W Adams int
342346799e8SJonathan W Adams thread_text_to_state(const char *state, uint_t *out)
343346799e8SJonathan W Adams {
344346799e8SJonathan W Adams 	int idx;
345346799e8SJonathan W Adams 
346346799e8SJonathan W Adams 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
347346799e8SJonathan W Adams 		struct thread_state *tsp = &thread_states[idx];
348346799e8SJonathan W Adams 		if (strcasecmp(tsp->ts_name, state) == 0) {
349346799e8SJonathan W Adams 			*out = tsp->ts_state;
350346799e8SJonathan W Adams 			return (0);
351346799e8SJonathan W Adams 		}
352346799e8SJonathan W Adams 	}
353346799e8SJonathan W Adams 	return (-1);
354346799e8SJonathan W Adams }
355346799e8SJonathan W Adams 
356346799e8SJonathan W Adams void
357346799e8SJonathan W Adams thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg)
358346799e8SJonathan W Adams {
359346799e8SJonathan W Adams 	int idx;
360346799e8SJonathan W Adams 
361346799e8SJonathan W Adams 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
362346799e8SJonathan W Adams 		struct thread_state *tsp = &thread_states[idx];
363346799e8SJonathan W Adams 		cbfunc(tsp->ts_state, tsp->ts_name, cbarg);
364346799e8SJonathan W Adams 	}
365346799e8SJonathan W Adams }
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate #define	TF_INTR		0x01
3687c478bd9Sstevel@tonic-gate #define	TF_PROC		0x02
3697c478bd9Sstevel@tonic-gate #define	TF_BLOCK	0x04
3707c478bd9Sstevel@tonic-gate #define	TF_SIG		0x08
3717c478bd9Sstevel@tonic-gate #define	TF_DISP		0x10
3727c478bd9Sstevel@tonic-gate #define	TF_MERGE	0x20
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate /*
3757c478bd9Sstevel@tonic-gate  * Display a kthread_t.
3767c478bd9Sstevel@tonic-gate  * This is a little complicated, as there is a lot of information that
3777c478bd9Sstevel@tonic-gate  * the user could be interested in.  The flags "ipbsd" are used to
3787c478bd9Sstevel@tonic-gate  * indicate which subset of the thread's members are to be displayed
3797c478bd9Sstevel@tonic-gate  * ('i' is the default).  If multiple options are specified, multiple
3807c478bd9Sstevel@tonic-gate  * sets of data will be displayed in a vaguely readable format.  If the
3817c478bd9Sstevel@tonic-gate  * 'm' option is specified, all the selected sets will be merged onto a
3827c478bd9Sstevel@tonic-gate  * single line for the benefit of those using wider-than-normal
3837c478bd9Sstevel@tonic-gate  * terminals.  Having a generic mechanism for doing this would be
3847c478bd9Sstevel@tonic-gate  * really useful, but is a project best left to another day.
3857c478bd9Sstevel@tonic-gate  */
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate int
3887c478bd9Sstevel@tonic-gate thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 	kthread_t	t;
3917c478bd9Sstevel@tonic-gate 	uint_t		oflags = 0;
3927c478bd9Sstevel@tonic-gate 	uint_t		fflag = FALSE;
3937c478bd9Sstevel@tonic-gate 	int		first;
3947c478bd9Sstevel@tonic-gate 	char		stbuf[20];
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	/*
3977c478bd9Sstevel@tonic-gate 	 * "Gracefully" handle printing a boatload of stuff to the
3987c478bd9Sstevel@tonic-gate 	 * screen.  If we are not printing our first set of data, and
3997c478bd9Sstevel@tonic-gate 	 * we haven't been instructed to merge sets together, output a
4007c478bd9Sstevel@tonic-gate 	 * newline and indent such that the thread addresses form a
4017c478bd9Sstevel@tonic-gate 	 * column of their own.
4027c478bd9Sstevel@tonic-gate 	 */
4037c478bd9Sstevel@tonic-gate #define	SPACER()				\
4047c478bd9Sstevel@tonic-gate 	if (first) {				\
4057c478bd9Sstevel@tonic-gate 		first = FALSE;			\
4067c478bd9Sstevel@tonic-gate 	} else if (!(oflags & TF_MERGE)) {	\
4077c478bd9Sstevel@tonic-gate 		mdb_printf("\n%?s", "");	\
4087c478bd9Sstevel@tonic-gate 	}
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
4117c478bd9Sstevel@tonic-gate 		if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) {
4127c478bd9Sstevel@tonic-gate 			mdb_warn("can't walk threads");
4137c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
4147c478bd9Sstevel@tonic-gate 		}
4157c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
4167c478bd9Sstevel@tonic-gate 	}
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
4197c478bd9Sstevel@tonic-gate 	    'f', MDB_OPT_SETBITS, TRUE, &fflag,
4207c478bd9Sstevel@tonic-gate 	    'i', MDB_OPT_SETBITS, TF_INTR, &oflags,
4217c478bd9Sstevel@tonic-gate 	    'p', MDB_OPT_SETBITS, TF_PROC, &oflags,
4227c478bd9Sstevel@tonic-gate 	    'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags,
4237c478bd9Sstevel@tonic-gate 	    's', MDB_OPT_SETBITS, TF_SIG, &oflags,
4247c478bd9Sstevel@tonic-gate 	    'd', MDB_OPT_SETBITS, TF_DISP, &oflags,
4257c478bd9Sstevel@tonic-gate 	    'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc)
4267c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	/*
4297c478bd9Sstevel@tonic-gate 	 * If no sets were specified, choose the 'i' set.
4307c478bd9Sstevel@tonic-gate 	 */
4317c478bd9Sstevel@tonic-gate 	if (!(oflags & ~TF_MERGE))
4327c478bd9Sstevel@tonic-gate #ifdef	_LP64
4337c478bd9Sstevel@tonic-gate 		oflags = TF_INTR;
4347c478bd9Sstevel@tonic-gate #else
4357c478bd9Sstevel@tonic-gate 		oflags = TF_INTR | TF_DISP | TF_MERGE;
4367c478bd9Sstevel@tonic-gate #endif
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	/*
4397c478bd9Sstevel@tonic-gate 	 * Print the relevant headers; note use of SPACER().
4407c478bd9Sstevel@tonic-gate 	 */
4417c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
4427c478bd9Sstevel@tonic-gate 		first = TRUE;
4437c478bd9Sstevel@tonic-gate 		mdb_printf("%<u>%?s%</u>", "ADDR");
4447c478bd9Sstevel@tonic-gate 		mdb_flush();
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 		if (oflags & TF_PROC) {
4477c478bd9Sstevel@tonic-gate 			SPACER();
4487c478bd9Sstevel@tonic-gate 			mdb_printf("%<u> %?s %?s %?s%</u>",
4497c478bd9Sstevel@tonic-gate 			    "PROC", "LWP", "CRED");
4507c478bd9Sstevel@tonic-gate 		}
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 		if (oflags & TF_INTR) {
4537c478bd9Sstevel@tonic-gate 			SPACER();
4547c478bd9Sstevel@tonic-gate 			mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>",
4557c478bd9Sstevel@tonic-gate 			    "STATE", "FLG", "PFLG",
4567c478bd9Sstevel@tonic-gate 			    "SFLG", "PRI", "EPRI", "PIL", "INTR");
4577c478bd9Sstevel@tonic-gate 		}
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 		if (oflags & TF_BLOCK) {
4607c478bd9Sstevel@tonic-gate 			SPACER();
4617c478bd9Sstevel@tonic-gate 			mdb_printf("%<u> %?s %?s %?s %11s%</u>",
4627c478bd9Sstevel@tonic-gate 			    "WCHAN", "TS", "PITS", "SOBJ OPS");
4637c478bd9Sstevel@tonic-gate 		}
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 		if (oflags & TF_SIG) {
4667c478bd9Sstevel@tonic-gate 			SPACER();
4677c478bd9Sstevel@tonic-gate 			mdb_printf("%<u> %?s %16s %16s%</u>",
4687c478bd9Sstevel@tonic-gate 			    "SIGQUEUE", "SIG PEND", "SIG HELD");
4697c478bd9Sstevel@tonic-gate 		}
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 		if (oflags & TF_DISP) {
4727c478bd9Sstevel@tonic-gate 			SPACER();
473*9583f717SMarcel Telka 			mdb_printf("%<u> %?s %5s %2s %-6s%</u>",
474*9583f717SMarcel Telka 			    "DISPTIME", "BOUND", "PR", "SWITCH");
4757c478bd9Sstevel@tonic-gate 		}
4767c478bd9Sstevel@tonic-gate 		mdb_printf("\n");
4777c478bd9Sstevel@tonic-gate 	}
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
4807c478bd9Sstevel@tonic-gate 		mdb_warn("can't read kthread_t at %#lx", addr);
4817c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
4827c478bd9Sstevel@tonic-gate 	}
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 	if (fflag && (t.t_state == TS_FREE))
4857c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	first = TRUE;
4887c478bd9Sstevel@tonic-gate 	mdb_printf("%0?lx", addr);
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	/* process information */
4917c478bd9Sstevel@tonic-gate 	if (oflags & TF_PROC) {
4927c478bd9Sstevel@tonic-gate 		SPACER();
493ab4a9bebSjohnlev 		mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred);
4947c478bd9Sstevel@tonic-gate 	}
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	/* priority/interrupt information */
4977c478bd9Sstevel@tonic-gate 	if (oflags & TF_INTR) {
4987c478bd9Sstevel@tonic-gate 		SPACER();
499346799e8SJonathan W Adams 		thread_state_to_text(t.t_state, stbuf, sizeof (stbuf));
5007c478bd9Sstevel@tonic-gate 		if (t.t_intr == NULL) {
5017c478bd9Sstevel@tonic-gate 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s",
502346799e8SJonathan W Adams 			    stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
5037c478bd9Sstevel@tonic-gate 			    t.t_pri, t.t_epri, t.t_pil, "n/a");
5047c478bd9Sstevel@tonic-gate 		} else {
5057c478bd9Sstevel@tonic-gate 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p",
506346799e8SJonathan W Adams 			    stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
5077c478bd9Sstevel@tonic-gate 			    t.t_pri, t.t_epri, t.t_pil, t.t_intr);
5087c478bd9Sstevel@tonic-gate 		}
5097c478bd9Sstevel@tonic-gate 	}
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	/* blocking information */
5127c478bd9Sstevel@tonic-gate 	if (oflags & TF_BLOCK) {
5137c478bd9Sstevel@tonic-gate 		SPACER();
5147c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops);
5157c478bd9Sstevel@tonic-gate 		stbuf[11] = '\0';
5167c478bd9Sstevel@tonic-gate 		mdb_printf(" %?p %?p %?p %11s",
5177c478bd9Sstevel@tonic-gate 		    t.t_wchan, t.t_ts, t.t_prioinv, stbuf);
5187c478bd9Sstevel@tonic-gate 	}
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	/* signal information */
5217c478bd9Sstevel@tonic-gate 	if (oflags & TF_SIG) {
5227c478bd9Sstevel@tonic-gate 		SPACER();
5237c478bd9Sstevel@tonic-gate 		mdb_printf(" %?p %016llx %016llx",
5247c478bd9Sstevel@tonic-gate 		    t.t_sigqueue, t.t_sig, t.t_hold);
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	/* dispatcher stuff */
5287c478bd9Sstevel@tonic-gate 	if (oflags & TF_DISP) {
5297c478bd9Sstevel@tonic-gate 		SPACER();
5307c478bd9Sstevel@tonic-gate 		mdb_printf(" %?lx %5d %2d ",
5317c478bd9Sstevel@tonic-gate 		    t.t_disp_time, t.t_bind_cpu, t.t_preempt);
532*9583f717SMarcel Telka 		if (t.t_disp_time != 0)
533*9583f717SMarcel Telka 			mdb_printf("t-%-4d",
534*9583f717SMarcel Telka 			    (clock_t)mdb_get_lbolt() - t.t_disp_time);
535*9583f717SMarcel Telka 		else
536*9583f717SMarcel Telka 			mdb_printf("%-6s", "-");
5377c478bd9Sstevel@tonic-gate 	}
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	mdb_printf("\n");
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate #undef SPACER
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
5447c478bd9Sstevel@tonic-gate }
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate void
5477c478bd9Sstevel@tonic-gate thread_help(void)
5487c478bd9Sstevel@tonic-gate {
5497c478bd9Sstevel@tonic-gate 	mdb_printf(
5507c478bd9Sstevel@tonic-gate 	    "The flags -ipbsd control which information is displayed.  When\n"
5517c478bd9Sstevel@tonic-gate 	    "combined, the fields are displayed on separate lines unless the\n"
5527c478bd9Sstevel@tonic-gate 	    "-m option is given.\n"
5537c478bd9Sstevel@tonic-gate 	    "\n"
5547c478bd9Sstevel@tonic-gate 	    "\t-b\tprint blocked thread state\n"
5557c478bd9Sstevel@tonic-gate 	    "\t-d\tprint dispatcher state\n"
5567c478bd9Sstevel@tonic-gate 	    "\t-f\tignore freed threads\n"
5577c478bd9Sstevel@tonic-gate 	    "\t-i\tprint basic thread state (default)\n"
5587c478bd9Sstevel@tonic-gate 	    "\t-m\tdisplay results on a single line\n"
5597c478bd9Sstevel@tonic-gate 	    "\t-p\tprint process and lwp state\n"
5607c478bd9Sstevel@tonic-gate 	    "\t-s\tprint signal state\n");
5617c478bd9Sstevel@tonic-gate }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate /*
5647c478bd9Sstevel@tonic-gate  * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
5657c478bd9Sstevel@tonic-gate  */
5667c478bd9Sstevel@tonic-gate int
5677c478bd9Sstevel@tonic-gate threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5687c478bd9Sstevel@tonic-gate {
5697c478bd9Sstevel@tonic-gate 	int i;
5707c478bd9Sstevel@tonic-gate 	uint_t count =  0;
5717c478bd9Sstevel@tonic-gate 	uint_t verbose = FALSE;
572ab4a9bebSjohnlev 	uint_t notaskq = FALSE;
5737c478bd9Sstevel@tonic-gate 	kthread_t t;
574ab4a9bebSjohnlev 	taskq_t tq;
5757c478bd9Sstevel@tonic-gate 	proc_t p;
5767c478bd9Sstevel@tonic-gate 	char cmd[80];
5777c478bd9Sstevel@tonic-gate 	mdb_arg_t cmdarg;
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
5807c478bd9Sstevel@tonic-gate 		if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) {
5817c478bd9Sstevel@tonic-gate 			mdb_warn("can't walk threads");
5827c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
588ab4a9bebSjohnlev 	    't', MDB_OPT_SETBITS, TRUE, &notaskq,
5897c478bd9Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	if (i != argc) {
5927c478bd9Sstevel@tonic-gate 		if (i != argc - 1 || !verbose)
5937c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 		if (argv[i].a_type == MDB_TYPE_IMMEDIATE)
5967c478bd9Sstevel@tonic-gate 			count = (uint_t)argv[i].a_un.a_val;
5977c478bd9Sstevel@tonic-gate 		else
5987c478bd9Sstevel@tonic-gate 			count = (uint_t)mdb_strtoull(argv[i].a_un.a_str);
5997c478bd9Sstevel@tonic-gate 	}
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
6027c478bd9Sstevel@tonic-gate 		if (verbose)
6037c478bd9Sstevel@tonic-gate 			mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
6047c478bd9Sstevel@tonic-gate 			    "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
6057c478bd9Sstevel@tonic-gate 		else
6067c478bd9Sstevel@tonic-gate 			mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n",
6077c478bd9Sstevel@tonic-gate 			    "ADDR", "PROC", "LWP", "CMD", "LWPID");
6087c478bd9Sstevel@tonic-gate 	}
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
6117c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read kthread_t at %p", addr);
6127c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6137c478bd9Sstevel@tonic-gate 	}
6147c478bd9Sstevel@tonic-gate 
615ab4a9bebSjohnlev 	if (notaskq && t.t_taskq != NULL)
616ab4a9bebSjohnlev 		return (DCMD_OK);
617ab4a9bebSjohnlev 
6187c478bd9Sstevel@tonic-gate 	if (t.t_state == TS_FREE)
6197c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
6227c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read proc at %p", t.t_procp);
6237c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6247c478bd9Sstevel@tonic-gate 	}
6257c478bd9Sstevel@tonic-gate 
626ab4a9bebSjohnlev 	if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1)
627ab4a9bebSjohnlev 		tq.tq_name[0] = '\0';
628ab4a9bebSjohnlev 
6297c478bd9Sstevel@tonic-gate 	if (verbose) {
6307c478bd9Sstevel@tonic-gate 		mdb_printf("%0?p %?p %?p %3u %3d %?p\n",
6317c478bd9Sstevel@tonic-gate 		    addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan);
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 		mdb_inc_indent(2);
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 		mdb_printf("PC: %a", t.t_pc);
636ab4a9bebSjohnlev 		if (t.t_tid == 0) {
637ab4a9bebSjohnlev 			if (tq.tq_name[0] != '\0')
638ab4a9bebSjohnlev 				mdb_printf("    TASKQ: %s\n", tq.tq_name);
6397c478bd9Sstevel@tonic-gate 			else
640ab4a9bebSjohnlev 				mdb_printf("    THREAD: %a()\n", t.t_startpc);
641ab4a9bebSjohnlev 		} else {
6427c478bd9Sstevel@tonic-gate 			mdb_printf("    CMD: %s\n", p.p_user.u_psargs);
643ab4a9bebSjohnlev 		}
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 		mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count);
6467c478bd9Sstevel@tonic-gate 		cmdarg.a_type = MDB_TYPE_STRING;
6477c478bd9Sstevel@tonic-gate 		cmdarg.a_un.a_str = cmd;
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 		(void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg);
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 		mdb_dec_indent(2);
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 		mdb_printf("\n");
6547c478bd9Sstevel@tonic-gate 	} else {
6557c478bd9Sstevel@tonic-gate 		mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp);
656ab4a9bebSjohnlev 		if (t.t_tid == 0) {
657ab4a9bebSjohnlev 			if (tq.tq_name[0] != '\0')
658ab4a9bebSjohnlev 				mdb_printf(" tq:%s\n", tq.tq_name);
6597c478bd9Sstevel@tonic-gate 			else
660ab4a9bebSjohnlev 				mdb_printf(" %a()\n", t.t_startpc);
661ab4a9bebSjohnlev 		} else {
6627c478bd9Sstevel@tonic-gate 			mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid);
6637c478bd9Sstevel@tonic-gate 		}
664ab4a9bebSjohnlev 	}
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
6677c478bd9Sstevel@tonic-gate }
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate void
6707c478bd9Sstevel@tonic-gate threadlist_help(void)
6717c478bd9Sstevel@tonic-gate {
6727c478bd9Sstevel@tonic-gate 	mdb_printf(
6737c478bd9Sstevel@tonic-gate 	    "   -v         print verbose output including C stack trace\n"
674ab4a9bebSjohnlev 	    "   -t         skip threads belonging to a taskq\n"
6757c478bd9Sstevel@tonic-gate 	    "   count      print no more than count arguments (default 0)\n");
6767c478bd9Sstevel@tonic-gate }
677bff31d89SPhilippe Jung 
678bff31d89SPhilippe Jung static size_t
679bff31d89SPhilippe Jung stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
680bff31d89SPhilippe Jung {
681bff31d89SPhilippe Jung 	size_t percent;
682bff31d89SPhilippe Jung 	size_t s;
683bff31d89SPhilippe Jung 
684bff31d89SPhilippe Jung 	if (t_stk > t_stkbase) {
685bff31d89SPhilippe Jung 		/* stack grows down */
686bff31d89SPhilippe Jung 		if (sp > t_stk) {
687bff31d89SPhilippe Jung 			return (0);
688bff31d89SPhilippe Jung 		}
689bff31d89SPhilippe Jung 		if (sp < t_stkbase) {
690bff31d89SPhilippe Jung 			return (100);
691bff31d89SPhilippe Jung 		}
692bff31d89SPhilippe Jung 		percent = t_stk - sp + 1;
693bff31d89SPhilippe Jung 		s = t_stk - t_stkbase + 1;
694bff31d89SPhilippe Jung 	} else {
695bff31d89SPhilippe Jung 		/* stack grows up */
696bff31d89SPhilippe Jung 		if (sp < t_stk) {
697bff31d89SPhilippe Jung 			return (0);
698bff31d89SPhilippe Jung 		}
699bff31d89SPhilippe Jung 		if (sp > t_stkbase) {
700bff31d89SPhilippe Jung 			return (100);
701bff31d89SPhilippe Jung 		}
702bff31d89SPhilippe Jung 		percent = sp - t_stk + 1;
703bff31d89SPhilippe Jung 		s = t_stkbase - t_stk + 1;
704bff31d89SPhilippe Jung 	}
705bff31d89SPhilippe Jung 	percent = ((100 * percent) / s) + 1;
706bff31d89SPhilippe Jung 	if (percent > 100) {
707bff31d89SPhilippe Jung 		percent = 100;
708bff31d89SPhilippe Jung 	}
709bff31d89SPhilippe Jung 	return (percent);
710bff31d89SPhilippe Jung }
711bff31d89SPhilippe Jung 
712bff31d89SPhilippe Jung /*
713bff31d89SPhilippe Jung  * Display kthread stack infos.
714bff31d89SPhilippe Jung  */
715bff31d89SPhilippe Jung int
716bff31d89SPhilippe Jung stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
717bff31d89SPhilippe Jung {
718bff31d89SPhilippe Jung 	kthread_t t;
719bff31d89SPhilippe Jung 	proc_t p;
720bff31d89SPhilippe Jung 	uint64_t *ptr;  /* pattern pointer */
721bff31d89SPhilippe Jung 	caddr_t	start;	/* kernel stack start */
722bff31d89SPhilippe Jung 	caddr_t end;	/* kernel stack end */
723bff31d89SPhilippe Jung 	caddr_t ustack;	/* userland copy of kernel stack */
724bff31d89SPhilippe Jung 	size_t usize;	/* userland copy of kernel stack size */
725bff31d89SPhilippe Jung 	caddr_t ustart;	/* userland copy of kernel stack, aligned start */
726bff31d89SPhilippe Jung 	caddr_t uend;	/* userland copy of kernel stack, aligned end */
727bff31d89SPhilippe Jung 	size_t percent = 0;
728bff31d89SPhilippe Jung 	uint_t all = FALSE; /* don't show TS_FREE kthread by default */
729bff31d89SPhilippe Jung 	uint_t history = FALSE;
730bff31d89SPhilippe Jung 	int i = 0;
731bff31d89SPhilippe Jung 	unsigned int ukmem_stackinfo;
732bff31d89SPhilippe Jung 	uintptr_t allthreads;
733bff31d89SPhilippe Jung 
734bff31d89SPhilippe Jung 	/* handle options */
735bff31d89SPhilippe Jung 	if (mdb_getopts(argc, argv,
736bff31d89SPhilippe Jung 	    'a', MDB_OPT_SETBITS, TRUE, &all,
737bff31d89SPhilippe Jung 	    'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) {
738bff31d89SPhilippe Jung 		return (DCMD_USAGE);
739bff31d89SPhilippe Jung 	}
740bff31d89SPhilippe Jung 
741bff31d89SPhilippe Jung 	/* walk all kthread if needed */
742bff31d89SPhilippe Jung 	if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) {
743bff31d89SPhilippe Jung 		if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) {
744bff31d89SPhilippe Jung 			mdb_warn("can't walk threads");
745bff31d89SPhilippe Jung 			return (DCMD_ERR);
746bff31d89SPhilippe Jung 		}
747bff31d89SPhilippe Jung 		return (DCMD_OK);
748bff31d89SPhilippe Jung 	}
749bff31d89SPhilippe Jung 
750bff31d89SPhilippe Jung 	/* read 'kmem_stackinfo' */
751bff31d89SPhilippe Jung 	if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo),
752bff31d89SPhilippe Jung 	    "kmem_stackinfo") == -1) {
753bff31d89SPhilippe Jung 		mdb_warn("failed to read 'kmem_stackinfo'\n");
754bff31d89SPhilippe Jung 		ukmem_stackinfo = 0;
755bff31d89SPhilippe Jung 	}
756bff31d89SPhilippe Jung 
757bff31d89SPhilippe Jung 	/* read 'allthreads' */
758bff31d89SPhilippe Jung 	if (mdb_readsym(&allthreads, sizeof (kthread_t *),
759bff31d89SPhilippe Jung 	    "allthreads") == -1) {
760bff31d89SPhilippe Jung 		mdb_warn("failed to read 'allthreads'\n");
761bff31d89SPhilippe Jung 		allthreads = NULL;
762bff31d89SPhilippe Jung 	}
763bff31d89SPhilippe Jung 
764bff31d89SPhilippe Jung 	if (history == TRUE) {
765bff31d89SPhilippe Jung 		kmem_stkinfo_t *log;
766bff31d89SPhilippe Jung 		uintptr_t kaddr;
767bff31d89SPhilippe Jung 
768bff31d89SPhilippe Jung 		mdb_printf("Dead kthreads stack usage history:\n");
769bff31d89SPhilippe Jung 		if (ukmem_stackinfo == 0) {
770bff31d89SPhilippe Jung 			mdb_printf("Tunable kmem_stackinfo is unset, history ");
771bff31d89SPhilippe Jung 			mdb_printf("feature is off.\nUse ::help stackinfo ");
772bff31d89SPhilippe Jung 			mdb_printf("for more details.\n");
773bff31d89SPhilippe Jung 			return (DCMD_OK);
774bff31d89SPhilippe Jung 		}
775bff31d89SPhilippe Jung 
776bff31d89SPhilippe Jung 		mdb_printf("%<u>%?s%</u>", "THREAD");
777bff31d89SPhilippe Jung 		mdb_printf(" %<u>%?s%</u>", "STACK");
778bff31d89SPhilippe Jung 		mdb_printf("%<u>%s%</u>", "   SIZE  MAX CMD/LWPID or STARTPC");
779bff31d89SPhilippe Jung 		mdb_printf("\n");
780bff31d89SPhilippe Jung 		usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t);
781bff31d89SPhilippe Jung 		log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP);
782bff31d89SPhilippe Jung 		if (mdb_readsym(&kaddr, sizeof (kaddr),
783bff31d89SPhilippe Jung 		    "kmem_stkinfo_log") == -1) {
784bff31d89SPhilippe Jung 			mdb_free((void *)log, usize);
785bff31d89SPhilippe Jung 			mdb_warn("failed to read 'kmem_stkinfo_log'\n");
786bff31d89SPhilippe Jung 			return (DCMD_ERR);
787bff31d89SPhilippe Jung 		}
788bff31d89SPhilippe Jung 		if (kaddr == NULL) {
789bff31d89SPhilippe Jung 			mdb_free((void *)log, usize);
790bff31d89SPhilippe Jung 			return (DCMD_OK);
791bff31d89SPhilippe Jung 		}
792bff31d89SPhilippe Jung 		if (mdb_vread(log, usize, kaddr) == -1) {
793bff31d89SPhilippe Jung 			mdb_free((void *)log, usize);
794bff31d89SPhilippe Jung 			mdb_warn("failed to read %p\n", kaddr);
795bff31d89SPhilippe Jung 			return (DCMD_ERR);
796bff31d89SPhilippe Jung 		}
797bff31d89SPhilippe Jung 		for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
798bff31d89SPhilippe Jung 			if (log[i].kthread == NULL) {
799bff31d89SPhilippe Jung 				continue;
800bff31d89SPhilippe Jung 			}
801bff31d89SPhilippe Jung 			mdb_printf("%0?p %0?p %6x %3d%%",
802bff31d89SPhilippe Jung 			    log[i].kthread,
803bff31d89SPhilippe Jung 			    log[i].start,
804bff31d89SPhilippe Jung 			    (uint_t)log[i].stksz,
805bff31d89SPhilippe Jung 			    (int)log[i].percent);
806bff31d89SPhilippe Jung 			if (log[i].t_tid != 0) {
807bff31d89SPhilippe Jung 				mdb_printf(" %s/%u\n",
808bff31d89SPhilippe Jung 				    log[i].cmd, log[i].t_tid);
809bff31d89SPhilippe Jung 			} else {
810bff31d89SPhilippe Jung 				mdb_printf(" %p (%a)\n", log[i].t_startpc,
811bff31d89SPhilippe Jung 				    log[i].t_startpc);
812bff31d89SPhilippe Jung 			}
813bff31d89SPhilippe Jung 		}
814bff31d89SPhilippe Jung 		mdb_free((void *)log, usize);
815bff31d89SPhilippe Jung 		return (DCMD_OK);
816bff31d89SPhilippe Jung 	}
817bff31d89SPhilippe Jung 
818bff31d89SPhilippe Jung 	/* display header */
819bff31d89SPhilippe Jung 	if (DCMD_HDRSPEC(flags)) {
820bff31d89SPhilippe Jung 		if (ukmem_stackinfo == 0) {
821bff31d89SPhilippe Jung 			mdb_printf("Tunable kmem_stackinfo is unset, ");
822bff31d89SPhilippe Jung 			mdb_printf("MAX value is not available.\n");
823bff31d89SPhilippe Jung 			mdb_printf("Use ::help stackinfo for more details.\n");
824bff31d89SPhilippe Jung 		}
825bff31d89SPhilippe Jung 		mdb_printf("%<u>%?s%</u>", "THREAD");
826bff31d89SPhilippe Jung 		mdb_printf(" %<u>%?s%</u>", "STACK");
827bff31d89SPhilippe Jung 		mdb_printf("%<u>%s%</u>", "   SIZE  CUR  MAX CMD/LWPID");
828bff31d89SPhilippe Jung 		mdb_printf("\n");
829bff31d89SPhilippe Jung 	}
830bff31d89SPhilippe Jung 
831bff31d89SPhilippe Jung 	/* read kthread */
832bff31d89SPhilippe Jung 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
833bff31d89SPhilippe Jung 		mdb_warn("can't read kthread_t at %#lx\n", addr);
834bff31d89SPhilippe Jung 		return (DCMD_ERR);
835bff31d89SPhilippe Jung 	}
836bff31d89SPhilippe Jung 
837bff31d89SPhilippe Jung 	if (t.t_state == TS_FREE && all == FALSE) {
838bff31d89SPhilippe Jung 		return (DCMD_OK);
839bff31d89SPhilippe Jung 	}
840bff31d89SPhilippe Jung 
841bff31d89SPhilippe Jung 	/* read proc */
842bff31d89SPhilippe Jung 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
843bff31d89SPhilippe Jung 		mdb_warn("failed to read proc at %p\n", t.t_procp);
844bff31d89SPhilippe Jung 		return (DCMD_ERR);
845bff31d89SPhilippe Jung 	}
846bff31d89SPhilippe Jung 
847bff31d89SPhilippe Jung 	/*
848bff31d89SPhilippe Jung 	 * Stack grows up or down, see thread_create(),
849bff31d89SPhilippe Jung 	 * compute stack memory aera start and end (start < end).
850bff31d89SPhilippe Jung 	 */
851bff31d89SPhilippe Jung 	if (t.t_stk > t.t_stkbase) {
852bff31d89SPhilippe Jung 		/* stack grows down */
853bff31d89SPhilippe Jung 		start = t.t_stkbase;
854bff31d89SPhilippe Jung 		end = t.t_stk;
855bff31d89SPhilippe Jung 	} else {
856bff31d89SPhilippe Jung 		/* stack grows up */
857bff31d89SPhilippe Jung 		start = t.t_stk;
858bff31d89SPhilippe Jung 		end = t.t_stkbase;
859bff31d89SPhilippe Jung 	}
860bff31d89SPhilippe Jung 
861bff31d89SPhilippe Jung 	/* display stack info */
862bff31d89SPhilippe Jung 	mdb_printf("%0?p %0?p", addr, start);
863bff31d89SPhilippe Jung 
864bff31d89SPhilippe Jung 	/* (end - start), kernel stack size as found in kthread_t */
865bff31d89SPhilippe Jung 	if ((end <= start) || ((end - start) > (1024 * 1024))) {
866bff31d89SPhilippe Jung 		/* negative or stack size > 1 meg, assume bogus */
867bff31d89SPhilippe Jung 		mdb_warn(" t_stk/t_stkbase problem\n");
868bff31d89SPhilippe Jung 		return (DCMD_ERR);
869bff31d89SPhilippe Jung 	}
870bff31d89SPhilippe Jung 
871bff31d89SPhilippe Jung 	/* display stack size */
872bff31d89SPhilippe Jung 	mdb_printf(" %6x", end - start);
873bff31d89SPhilippe Jung 
874bff31d89SPhilippe Jung 	/* display current stack usage */
875bff31d89SPhilippe Jung 	percent = stk_compute_percent(t.t_stk, t.t_stkbase,
876bff31d89SPhilippe Jung 	    (caddr_t)t.t_sp + STACK_BIAS);
877bff31d89SPhilippe Jung 
878bff31d89SPhilippe Jung 	mdb_printf(" %3d%%", percent);
879bff31d89SPhilippe Jung 	percent = 0;
880bff31d89SPhilippe Jung 
881bff31d89SPhilippe Jung 	if (ukmem_stackinfo == 0) {
882bff31d89SPhilippe Jung 		mdb_printf("  n/a");
883bff31d89SPhilippe Jung 		if (t.t_tid == 0) {
884bff31d89SPhilippe Jung 			mdb_printf(" %a()", t.t_startpc);
885bff31d89SPhilippe Jung 		} else {
886bff31d89SPhilippe Jung 			mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
887bff31d89SPhilippe Jung 		}
888bff31d89SPhilippe Jung 		mdb_printf("\n");
889bff31d89SPhilippe Jung 		return (DCMD_OK);
890bff31d89SPhilippe Jung 	}
891bff31d89SPhilippe Jung 
892bff31d89SPhilippe Jung 	if ((((uintptr_t)start) & 0x7) != 0) {
893bff31d89SPhilippe Jung 		start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
894bff31d89SPhilippe Jung 	}
895bff31d89SPhilippe Jung 	end = (caddr_t)(((uintptr_t)end) & (~0x7));
896bff31d89SPhilippe Jung 	/* size to scan in userland copy of kernel stack */
897bff31d89SPhilippe Jung 	usize = end - start; /* is a multiple of 8 bytes */
898bff31d89SPhilippe Jung 
899bff31d89SPhilippe Jung 	/*
900bff31d89SPhilippe Jung 	 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes
901bff31d89SPhilippe Jung 	 * alignement for ustart and uend, in boundaries.
902bff31d89SPhilippe Jung 	 */
903bff31d89SPhilippe Jung 	ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP);
904bff31d89SPhilippe Jung 	if ((((uintptr_t)ustart) & 0x7) != 0) {
905bff31d89SPhilippe Jung 		ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8);
906bff31d89SPhilippe Jung 	}
907bff31d89SPhilippe Jung 	uend = ustart + usize;
908bff31d89SPhilippe Jung 
909bff31d89SPhilippe Jung 	/* read the kernel stack */
910bff31d89SPhilippe Jung 	if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) {
911bff31d89SPhilippe Jung 		mdb_free((void *)ustack, usize + 8);
912bff31d89SPhilippe Jung 		mdb_printf("\n");
913bff31d89SPhilippe Jung 		mdb_warn("couldn't read entire stack\n");
914bff31d89SPhilippe Jung 		return (DCMD_ERR);
915bff31d89SPhilippe Jung 	}
916bff31d89SPhilippe Jung 
917bff31d89SPhilippe Jung 	/* scan the stack */
918bff31d89SPhilippe Jung 	if (t.t_stk > t.t_stkbase) {
919bff31d89SPhilippe Jung 		/* stack grows down */
920bff31d89SPhilippe Jung #if defined(__i386) || defined(__amd64)
921bff31d89SPhilippe Jung 		/*
922bff31d89SPhilippe Jung 		 * 6 longs are pushed on stack, see thread_load(). Skip
923bff31d89SPhilippe Jung 		 * them, so if kthread has never run, percent is zero.
924bff31d89SPhilippe Jung 		 * 8 bytes alignement is preserved for a 32 bit kernel,
925bff31d89SPhilippe Jung 		 * 6 x 4 = 24, 24 is a multiple of 8.
926bff31d89SPhilippe Jung 		 */
927bff31d89SPhilippe Jung 		uend -= (6 * sizeof (long));
928bff31d89SPhilippe Jung #endif
929bff31d89SPhilippe Jung 		ptr = (uint64_t *)((void *)ustart);
930bff31d89SPhilippe Jung 		while (ptr < (uint64_t *)((void *)uend)) {
931bff31d89SPhilippe Jung 			if (*ptr != KMEM_STKINFO_PATTERN) {
932bff31d89SPhilippe Jung 				percent = stk_compute_percent(uend,
933bff31d89SPhilippe Jung 				    ustart, (caddr_t)ptr);
934bff31d89SPhilippe Jung 				break;
935bff31d89SPhilippe Jung 			}
936bff31d89SPhilippe Jung 			ptr++;
937bff31d89SPhilippe Jung 		}
938bff31d89SPhilippe Jung 	} else {
939bff31d89SPhilippe Jung 		/* stack grows up */
940bff31d89SPhilippe Jung 		ptr = (uint64_t *)((void *)uend);
941bff31d89SPhilippe Jung 		ptr--;
942bff31d89SPhilippe Jung 		while (ptr >= (uint64_t *)((void *)ustart)) {
943bff31d89SPhilippe Jung 			if (*ptr != KMEM_STKINFO_PATTERN) {
944bff31d89SPhilippe Jung 				percent = stk_compute_percent(ustart,
945bff31d89SPhilippe Jung 				    uend, (caddr_t)ptr);
946bff31d89SPhilippe Jung 				break;
947bff31d89SPhilippe Jung 			}
948bff31d89SPhilippe Jung 			ptr--;
949bff31d89SPhilippe Jung 		}
950bff31d89SPhilippe Jung 	}
951bff31d89SPhilippe Jung 
952bff31d89SPhilippe Jung 	/* thread 't0' stack is not created by thread_create() */
953bff31d89SPhilippe Jung 	if (addr == allthreads) {
954bff31d89SPhilippe Jung 		percent = 0;
955bff31d89SPhilippe Jung 	}
956bff31d89SPhilippe Jung 	if (percent != 0) {
957bff31d89SPhilippe Jung 		mdb_printf(" %3d%%", percent);
958bff31d89SPhilippe Jung 	} else {
959bff31d89SPhilippe Jung 		mdb_printf("  n/a");
960bff31d89SPhilippe Jung 	}
961bff31d89SPhilippe Jung 	if (t.t_tid == 0) {
962bff31d89SPhilippe Jung 		mdb_printf(" %a()", t.t_startpc);
963bff31d89SPhilippe Jung 	} else {
964bff31d89SPhilippe Jung 		mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
965bff31d89SPhilippe Jung 	}
966bff31d89SPhilippe Jung 	mdb_printf("\n");
967bff31d89SPhilippe Jung 	mdb_free((void *)ustack, usize + 8);
968bff31d89SPhilippe Jung 	return (DCMD_OK);
969bff31d89SPhilippe Jung }
970bff31d89SPhilippe Jung 
971bff31d89SPhilippe Jung void
972bff31d89SPhilippe Jung stackinfo_help(void)
973bff31d89SPhilippe Jung {
974bff31d89SPhilippe Jung 	mdb_printf(
975bff31d89SPhilippe Jung 	    "Shows kernel stacks real utilization, if /etc/system "
976bff31d89SPhilippe Jung 	    "kmem_stackinfo tunable\n");
977bff31d89SPhilippe Jung 	mdb_printf(
978bff31d89SPhilippe Jung 	    "(an unsigned integer) is non zero at kthread creation time. ");
979bff31d89SPhilippe Jung 	mdb_printf("For example:\n");
980bff31d89SPhilippe Jung 	mdb_printf(
981bff31d89SPhilippe Jung 	    "          THREAD            STACK   SIZE  CUR  MAX CMD/LWPID\n");
982bff31d89SPhilippe Jung 	mdb_printf(
983bff31d89SPhilippe Jung 	    "ffffff014f5f2c20 ffffff0004153000   4f00   4%%  43%% init/1\n");
984bff31d89SPhilippe Jung 	mdb_printf(
985bff31d89SPhilippe Jung 	    "The stack size utilization for this kthread is at 4%%"
986bff31d89SPhilippe Jung 	    " of its maximum size,\n");
987bff31d89SPhilippe Jung 	mdb_printf(
988bff31d89SPhilippe Jung 	    "but has already used up to 43%%, stack size is 4f00 bytes.\n");
989bff31d89SPhilippe Jung 	mdb_printf(
990bff31d89SPhilippe Jung 	    "MAX value can be shown as n/a (not available):\n");
991bff31d89SPhilippe Jung 	mdb_printf(
992bff31d89SPhilippe Jung 	    "  - for the very first kthread (sched/1)\n");
993bff31d89SPhilippe Jung 	mdb_printf(
994bff31d89SPhilippe Jung 	    "  - kmem_stackinfo was zero at kthread creation time\n");
995bff31d89SPhilippe Jung 	mdb_printf(
996bff31d89SPhilippe Jung 	    "  - kthread has not yet run\n");
997bff31d89SPhilippe Jung 	mdb_printf("\n");
998bff31d89SPhilippe Jung 	mdb_printf("Options:\n");
999bff31d89SPhilippe Jung 	mdb_printf(
1000bff31d89SPhilippe Jung 	    "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
1001bff31d89SPhilippe Jung 	mdb_printf(
1002bff31d89SPhilippe Jung 	    "-h shows history, dead kthreads that used their "
1003bff31d89SPhilippe Jung 	    "kernel stack the most\n");
1004bff31d89SPhilippe Jung 	mdb_printf(
1005bff31d89SPhilippe Jung 	    "\nSee Solaris Modular Debugger Guide for detailed usage.\n");
1006bff31d89SPhilippe Jung 	mdb_flush();
1007bff31d89SPhilippe Jung }
1008