Home | History | Annotate | Download | only in genunix
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 #include <mdb/mdb_modapi.h>
     28 #include <sys/types.h>
     29 #include <sys/thread.h>
     30 #include <sys/lwp.h>
     31 #include <sys/proc.h>
     32 #include <sys/cpuvar.h>
     33 #include <sys/cpupart.h>
     34 #include <sys/disp.h>
     35 #include <sys/taskq_impl.h>
     36 #include <sys/stack.h>
     37 
     38 #ifndef	STACK_BIAS
     39 #define	STACK_BIAS	0
     40 #endif
     41 
     42 typedef struct thread_walk {
     43 	kthread_t *tw_thread;
     44 	uintptr_t tw_last;
     45 	uint_t tw_inproc;
     46 	uint_t tw_step;
     47 } thread_walk_t;
     48 
     49 int
     50 thread_walk_init(mdb_walk_state_t *wsp)
     51 {
     52 	thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP);
     53 
     54 	if (wsp->walk_addr == NULL) {
     55 		if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) {
     56 			mdb_warn("failed to read 'allthreads'");
     57 			mdb_free(twp, sizeof (thread_walk_t));
     58 			return (WALK_ERR);
     59 		}
     60 
     61 		twp->tw_inproc = FALSE;
     62 
     63 	} else {
     64 		proc_t pr;
     65 
     66 		if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) {
     67 			mdb_warn("failed to read proc at %p", wsp->walk_addr);
     68 			mdb_free(twp, sizeof (thread_walk_t));
     69 			return (WALK_ERR);
     70 		}
     71 
     72 		wsp->walk_addr = (uintptr_t)pr.p_tlist;
     73 		twp->tw_inproc = TRUE;
     74 	}
     75 
     76 	twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP);
     77 	twp->tw_last = wsp->walk_addr;
     78 	twp->tw_step = FALSE;
     79 
     80 	wsp->walk_data = twp;
     81 	return (WALK_NEXT);
     82 }
     83 
     84 int
     85 thread_walk_step(mdb_walk_state_t *wsp)
     86 {
     87 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
     88 	int status;
     89 
     90 	if (wsp->walk_addr == NULL)
     91 		return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */
     92 
     93 	if (twp->tw_step && wsp->walk_addr == twp->tw_last)
     94 		return (WALK_DONE); /* We've wrapped around */
     95 
     96 	if (mdb_vread(twp->tw_thread, sizeof (kthread_t),
     97 	    wsp->walk_addr) == -1) {
     98 		mdb_warn("failed to read thread at %p", wsp->walk_addr);
     99 		return (WALK_DONE);
    100 	}
    101 
    102 	status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
    103 	    wsp->walk_cbdata);
    104 
    105 	if (twp->tw_inproc)
    106 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw;
    107 	else
    108 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next;
    109 
    110 	twp->tw_step = TRUE;
    111 	return (status);
    112 }
    113 
    114 void
    115 thread_walk_fini(mdb_walk_state_t *wsp)
    116 {
    117 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
    118 
    119 	mdb_free(twp->tw_thread, sizeof (kthread_t));
    120 	mdb_free(twp, sizeof (thread_walk_t));
    121 }
    122 
    123 int
    124 deathrow_walk_init(mdb_walk_state_t *wsp)
    125 {
    126 	if (mdb_layered_walk("thread_deathrow", wsp) == -1) {
    127 		mdb_warn("couldn't walk 'thread_deathrow'");
    128 		return (WALK_ERR);
    129 	}
    130 
    131 	if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
    132 		mdb_warn("couldn't walk 'lwp_deathrow'");
    133 		return (WALK_ERR);
    134 	}
    135 
    136 	return (WALK_NEXT);
    137 }
    138 
    139 int
    140 deathrow_walk_step(mdb_walk_state_t *wsp)
    141 {
    142 	kthread_t t;
    143 	uintptr_t addr = wsp->walk_addr;
    144 
    145 	if (addr == NULL)
    146 		return (WALK_DONE);
    147 
    148 	if (mdb_vread(&t, sizeof (t), addr) == -1) {
    149 		mdb_warn("couldn't read deathrow thread at %p", addr);
    150 		return (WALK_ERR);
    151 	}
    152 
    153 	wsp->walk_addr = (uintptr_t)t.t_forw;
    154 
    155 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
    156 }
    157 
    158 int
    159 thread_deathrow_walk_init(mdb_walk_state_t *wsp)
    160 {
    161 	if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) {
    162 		mdb_warn("couldn't read symbol 'thread_deathrow'");
    163 		return (WALK_ERR);
    164 	}
    165 
    166 	return (WALK_NEXT);
    167 }
    168 
    169 int
    170 lwp_deathrow_walk_init(mdb_walk_state_t *wsp)
    171 {
    172 	if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) {
    173 		mdb_warn("couldn't read symbol 'lwp_deathrow'");
    174 		return (WALK_ERR);
    175 	}
    176 
    177 	return (WALK_NEXT);
    178 }
    179 
    180 
    181 typedef struct dispq_walk {
    182 	int dw_npri;
    183 	uintptr_t dw_dispq;
    184 	uintptr_t dw_last;
    185 } dispq_walk_t;
    186 
    187 int
    188 cpu_dispq_walk_init(mdb_walk_state_t *wsp)
    189 {
    190 	uintptr_t addr = wsp->walk_addr;
    191 	dispq_walk_t *dw;
    192 	cpu_t cpu;
    193 	dispq_t dispq;
    194 	disp_t disp;
    195 
    196 	if (addr == NULL) {
    197 		mdb_warn("cpu_dispq walk needs a cpu_t address\n");
    198 		return (WALK_ERR);
    199 	}
    200 
    201 	if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
    202 		mdb_warn("failed to read cpu_t at %p", addr);
    203 		return (WALK_ERR);
    204 	}
    205 
    206 	if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) {
    207 		mdb_warn("failed to read disp_t at %p", cpu.cpu_disp);
    208 		return (WALK_ERR);
    209 	}
    210 
    211 	if (mdb_vread(&dispq, sizeof (dispq_t),
    212 	    (uintptr_t)disp.disp_q) == -1) {
    213 		mdb_warn("failed to read dispq_t at %p", disp.disp_q);
    214 		return (WALK_ERR);
    215 	}
    216 
    217 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
    218 
    219 	dw->dw_npri = disp.disp_npri;
    220 	dw->dw_dispq = (uintptr_t)disp.disp_q;
    221 	dw->dw_last = (uintptr_t)dispq.dq_last;
    222 
    223 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
    224 	wsp->walk_data = dw;
    225 
    226 	return (WALK_NEXT);
    227 }
    228 
    229 int
    230 cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
    231 {
    232 	uintptr_t addr = wsp->walk_addr;
    233 	dispq_walk_t *dw;
    234 	cpupart_t cpupart;
    235 	dispq_t dispq;
    236 
    237 	if (addr == NULL) {
    238 		mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
    239 		return (WALK_ERR);
    240 	}
    241 
    242 	if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) {
    243 		mdb_warn("failed to read cpupart_t at %p", addr);
    244 		return (WALK_ERR);
    245 	}
    246 
    247 	if (mdb_vread(&dispq, sizeof (dispq_t),
    248 	    (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) {
    249 		mdb_warn("failed to read dispq_t at %p",
    250 		    cpupart.cp_kp_queue.disp_q);
    251 		return (WALK_ERR);
    252 	}
    253 
    254 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
    255 
    256 	dw->dw_npri = cpupart.cp_kp_queue.disp_npri;
    257 	dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q;
    258 	dw->dw_last = (uintptr_t)dispq.dq_last;
    259 
    260 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
    261 	wsp->walk_data = dw;
    262 
    263 	return (WALK_NEXT);
    264 }
    265 
    266 int
    267 dispq_walk_step(mdb_walk_state_t *wsp)
    268 {
    269 	uintptr_t addr = wsp->walk_addr;
    270 	dispq_walk_t *dw = wsp->walk_data;
    271 	dispq_t dispq;
    272 	kthread_t t;
    273 
    274 	while (addr == NULL) {
    275 		if (--dw->dw_npri == 0)
    276 			return (WALK_DONE);
    277 
    278 		dw->dw_dispq += sizeof (dispq_t);
    279 
    280 		if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) {
    281 			mdb_warn("failed to read dispq_t at %p", dw->dw_dispq);
    282 			return (WALK_ERR);
    283 		}
    284 
    285 		dw->dw_last = (uintptr_t)dispq.dq_last;
    286 		addr = (uintptr_t)dispq.dq_first;
    287 	}
    288 
    289 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
    290 		mdb_warn("failed to read kthread_t at %p", addr);
    291 		return (WALK_ERR);
    292 	}
    293 
    294 	if (addr == dw->dw_last)
    295 		wsp->walk_addr = NULL;
    296 	else
    297 		wsp->walk_addr = (uintptr_t)t.t_link;
    298 
    299 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
    300 }
    301 
    302 void
    303 dispq_walk_fini(mdb_walk_state_t *wsp)
    304 {
    305 	mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
    306 }
    307 
    308 struct thread_state {
    309 	uint_t ts_state;
    310 	const char *ts_name;
    311 } thread_states[] = {
    312 	{ TS_FREE,	"free" },
    313 	{ TS_SLEEP,	"sleep" },
    314 	{ TS_RUN,	"run" },
    315 	{ TS_ONPROC,	"onproc" },
    316 	{ TS_ZOMB,	"zomb" },
    317 	{ TS_STOPPED,	"stopped" },
    318 	{ TS_WAIT,	"wait" }
    319 };
    320 #define	NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
    321 
    322 void
    323 thread_state_to_text(uint_t state, char *out, size_t out_sz)
    324 {
    325 	int idx;
    326 
    327 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
    328 		struct thread_state *tsp = &thread_states[idx];
    329 		if (tsp->ts_state == state) {
    330 			mdb_snprintf(out, out_sz, "%s", tsp->ts_name);
    331 			return;
    332 		}
    333 	}
    334 	mdb_snprintf(out, out_sz, "inval/%02x", state);
    335 }
    336 
    337 int
    338 thread_text_to_state(const char *state, uint_t *out)
    339 {
    340 	int idx;
    341 
    342 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
    343 		struct thread_state *tsp = &thread_states[idx];
    344 		if (strcasecmp(tsp->ts_name, state) == 0) {
    345 			*out = tsp->ts_state;
    346 			return (0);
    347 		}
    348 	}
    349 	return (-1);
    350 }
    351 
    352 void
    353 thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg)
    354 {
    355 	int idx;
    356 
    357 	for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
    358 		struct thread_state *tsp = &thread_states[idx];
    359 		cbfunc(tsp->ts_state, tsp->ts_name, cbarg);
    360 	}
    361 }
    362 
    363 #define	TF_INTR		0x01
    364 #define	TF_PROC		0x02
    365 #define	TF_BLOCK	0x04
    366 #define	TF_SIG		0x08
    367 #define	TF_DISP		0x10
    368 #define	TF_MERGE	0x20
    369 
    370 /*
    371  * Display a kthread_t.
    372  * This is a little complicated, as there is a lot of information that
    373  * the user could be interested in.  The flags "ipbsd" are used to
    374  * indicate which subset of the thread's members are to be displayed
    375  * ('i' is the default).  If multiple options are specified, multiple
    376  * sets of data will be displayed in a vaguely readable format.  If the
    377  * 'm' option is specified, all the selected sets will be merged onto a
    378  * single line for the benefit of those using wider-than-normal
    379  * terminals.  Having a generic mechanism for doing this would be
    380  * really useful, but is a project best left to another day.
    381  */
    382 
    383 int
    384 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
    385 {
    386 	kthread_t	t;
    387 	uint_t		oflags = 0;
    388 	uint_t		fflag = FALSE;
    389 	int		first;
    390 	char		stbuf[20];
    391 
    392 	/*
    393 	 * "Gracefully" handle printing a boatload of stuff to the
    394 	 * screen.  If we are not printing our first set of data, and
    395 	 * we haven't been instructed to merge sets together, output a
    396 	 * newline and indent such that the thread addresses form a
    397 	 * column of their own.
    398 	 */
    399 #define	SPACER()				\
    400 	if (first) {				\
    401 		first = FALSE;			\
    402 	} else if (!(oflags & TF_MERGE)) {	\
    403 		mdb_printf("\n%?s", "");	\
    404 	}
    405 
    406 	if (!(flags & DCMD_ADDRSPEC)) {
    407 		if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) {
    408 			mdb_warn("can't walk threads");
    409 			return (DCMD_ERR);
    410 		}
    411 		return (DCMD_OK);
    412 	}
    413 
    414 	if (mdb_getopts(argc, argv,
    415 	    'f', MDB_OPT_SETBITS, TRUE, &fflag,
    416 	    'i', MDB_OPT_SETBITS, TF_INTR, &oflags,
    417 	    'p', MDB_OPT_SETBITS, TF_PROC, &oflags,
    418 	    'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags,
    419 	    's', MDB_OPT_SETBITS, TF_SIG, &oflags,
    420 	    'd', MDB_OPT_SETBITS, TF_DISP, &oflags,
    421 	    'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc)
    422 		return (DCMD_USAGE);
    423 
    424 	/*
    425 	 * If no sets were specified, choose the 'i' set.
    426 	 */
    427 	if (!(oflags & ~TF_MERGE))
    428 #ifdef	_LP64
    429 		oflags = TF_INTR;
    430 #else
    431 		oflags = TF_INTR | TF_DISP | TF_MERGE;
    432 #endif
    433 
    434 	/*
    435 	 * Print the relevant headers; note use of SPACER().
    436 	 */
    437 	if (DCMD_HDRSPEC(flags)) {
    438 		first = TRUE;
    439 		mdb_printf("%<u>%?s%</u>", "ADDR");
    440 		mdb_flush();
    441 
    442 		if (oflags & TF_PROC) {
    443 			SPACER();
    444 			mdb_printf("%<u> %?s %?s %?s%</u>",
    445 			    "PROC", "LWP", "CRED");
    446 		}
    447 
    448 		if (oflags & TF_INTR) {
    449 			SPACER();
    450 			mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>",
    451 			    "STATE", "FLG", "PFLG",
    452 			    "SFLG", "PRI", "EPRI", "PIL", "INTR");
    453 		}
    454 
    455 		if (oflags & TF_BLOCK) {
    456 			SPACER();
    457 			mdb_printf("%<u> %?s %?s %?s %11s%</u>",
    458 			    "WCHAN", "TS", "PITS", "SOBJ OPS");
    459 		}
    460 
    461 		if (oflags & TF_SIG) {
    462 			SPACER();
    463 			mdb_printf("%<u> %?s %16s %16s%</u>",
    464 			    "SIGQUEUE", "SIG PEND", "SIG HELD");
    465 		}
    466 
    467 		if (oflags & TF_DISP) {
    468 			SPACER();
    469 			mdb_printf("%<u> %?s %5s %2s%</u>",
    470 			    "DISPTIME", "BOUND", "PR");
    471 		}
    472 		mdb_printf("\n");
    473 	}
    474 
    475 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
    476 		mdb_warn("can't read kthread_t at %#lx", addr);
    477 		return (DCMD_ERR);
    478 	}
    479 
    480 	if (fflag && (t.t_state == TS_FREE))
    481 		return (DCMD_OK);
    482 
    483 	first = TRUE;
    484 	mdb_printf("%0?lx", addr);
    485 
    486 	/* process information */
    487 	if (oflags & TF_PROC) {
    488 		SPACER();
    489 		mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred);
    490 	}
    491 
    492 	/* priority/interrupt information */
    493 	if (oflags & TF_INTR) {
    494 		SPACER();
    495 		thread_state_to_text(t.t_state, stbuf, sizeof (stbuf));
    496 		if (t.t_intr == NULL) {
    497 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s",
    498 			    stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
    499 			    t.t_pri, t.t_epri, t.t_pil, "n/a");
    500 		} else {
    501 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p",
    502 			    stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
    503 			    t.t_pri, t.t_epri, t.t_pil, t.t_intr);
    504 		}
    505 	}
    506 
    507 	/* blocking information */
    508 	if (oflags & TF_BLOCK) {
    509 		SPACER();
    510 		(void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops);
    511 		stbuf[11] = '\0';
    512 		mdb_printf(" %?p %?p %?p %11s",
    513 		    t.t_wchan, t.t_ts, t.t_prioinv, stbuf);
    514 	}
    515 
    516 	/* signal information */
    517 	if (oflags & TF_SIG) {
    518 		SPACER();
    519 		mdb_printf(" %?p %016llx %016llx",
    520 		    t.t_sigqueue, t.t_sig, t.t_hold);
    521 	}
    522 
    523 	/* dispatcher stuff */
    524 	if (oflags & TF_DISP) {
    525 		SPACER();
    526 		mdb_printf(" %?lx %5d %2d",
    527 		    t.t_disp_time, t.t_bind_cpu, t.t_preempt);
    528 	}
    529 
    530 	mdb_printf("\n");
    531 
    532 #undef SPACER
    533 
    534 	return (DCMD_OK);
    535 }
    536 
    537 void
    538 thread_help(void)
    539 {
    540 	mdb_printf(
    541 	    "The flags -ipbsd control which information is displayed.  When\n"
    542 	    "combined, the fields are displayed on separate lines unless the\n"
    543 	    "-m option is given.\n"
    544 	    "\n"
    545 	    "\t-b\tprint blocked thread state\n"
    546 	    "\t-d\tprint dispatcher state\n"
    547 	    "\t-f\tignore freed threads\n"
    548 	    "\t-i\tprint basic thread state (default)\n"
    549 	    "\t-m\tdisplay results on a single line\n"
    550 	    "\t-p\tprint process and lwp state\n"
    551 	    "\t-s\tprint signal state\n");
    552 }
    553 
    554 /*
    555  * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
    556  */
    557 int
    558 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
    559 {
    560 	int i;
    561 	uint_t count =  0;
    562 	uint_t verbose = FALSE;
    563 	uint_t notaskq = FALSE;
    564 	kthread_t t;
    565 	taskq_t tq;
    566 	proc_t p;
    567 	char cmd[80];
    568 	mdb_arg_t cmdarg;
    569 
    570 	if (!(flags & DCMD_ADDRSPEC)) {
    571 		if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) {
    572 			mdb_warn("can't walk threads");
    573 			return (DCMD_ERR);
    574 		}
    575 		return (DCMD_OK);
    576 	}
    577 
    578 	i = mdb_getopts(argc, argv,
    579 	    't', MDB_OPT_SETBITS, TRUE, &notaskq,
    580 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
    581 
    582 	if (i != argc) {
    583 		if (i != argc - 1 || !verbose)
    584 			return (DCMD_USAGE);
    585 
    586 		if (argv[i].a_type == MDB_TYPE_IMMEDIATE)
    587 			count = (uint_t)argv[i].a_un.a_val;
    588 		else
    589 			count = (uint_t)mdb_strtoull(argv[i].a_un.a_str);
    590 	}
    591 
    592 	if (DCMD_HDRSPEC(flags)) {
    593 		if (verbose)
    594 			mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
    595 			    "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
    596 		else
    597 			mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n",
    598 			    "ADDR", "PROC", "LWP", "CMD", "LWPID");
    599 	}
    600 
    601 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
    602 		mdb_warn("failed to read kthread_t at %p", addr);
    603 		return (DCMD_ERR);
    604 	}
    605 
    606 	if (notaskq && t.t_taskq != NULL)
    607 		return (DCMD_OK);
    608 
    609 	if (t.t_state == TS_FREE)
    610 		return (DCMD_OK);
    611 
    612 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
    613 		mdb_warn("failed to read proc at %p", t.t_procp);
    614 		return (DCMD_ERR);
    615 	}
    616 
    617 	if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1)
    618 		tq.tq_name[0] = '\0';
    619 
    620 	if (verbose) {
    621 		mdb_printf("%0?p %?p %?p %3u %3d %?p\n",
    622 		    addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan);
    623 
    624 		mdb_inc_indent(2);
    625 
    626 		mdb_printf("PC: %a", t.t_pc);
    627 		if (t.t_tid == 0) {
    628 			if (tq.tq_name[0] != '\0')
    629 				mdb_printf("    TASKQ: %s\n", tq.tq_name);
    630 			else
    631 				mdb_printf("    THREAD: %a()\n", t.t_startpc);
    632 		} else {
    633 			mdb_printf("    CMD: %s\n", p.p_user.u_psargs);
    634 		}
    635 
    636 		mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count);
    637 		cmdarg.a_type = MDB_TYPE_STRING;
    638 		cmdarg.a_un.a_str = cmd;
    639 
    640 		(void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg);
    641 
    642 		mdb_dec_indent(2);
    643 
    644 		mdb_printf("\n");
    645 	} else {
    646 		mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp);
    647 		if (t.t_tid == 0) {
    648 			if (tq.tq_name[0] != '\0')
    649 				mdb_printf(" tq:%s\n", tq.tq_name);
    650 			else
    651 				mdb_printf(" %a()\n", t.t_startpc);
    652 		} else {
    653 			mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid);
    654 		}
    655 	}
    656 
    657 	return (DCMD_OK);
    658 }
    659 
    660 void
    661 threadlist_help(void)
    662 {
    663 	mdb_printf(
    664 	    "   -v         print verbose output including C stack trace\n"
    665 	    "   -t         skip threads belonging to a taskq\n"
    666 	    "   count      print no more than count arguments (default 0)\n");
    667 }
    668 
    669 static size_t
    670 stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
    671 {
    672 	size_t percent;
    673 	size_t s;
    674 
    675 	if (t_stk > t_stkbase) {
    676 		/* stack grows down */
    677 		if (sp > t_stk) {
    678 			return (0);
    679 		}
    680 		if (sp < t_stkbase) {
    681 			return (100);
    682 		}
    683 		percent = t_stk - sp + 1;
    684 		s = t_stk - t_stkbase + 1;
    685 	} else {
    686 		/* stack grows up */
    687 		if (sp < t_stk) {
    688 			return (0);
    689 		}
    690 		if (sp > t_stkbase) {
    691 			return (100);
    692 		}
    693 		percent = sp - t_stk + 1;
    694 		s = t_stkbase - t_stk + 1;
    695 	}
    696 	percent = ((100 * percent) / s) + 1;
    697 	if (percent > 100) {
    698 		percent = 100;
    699 	}
    700 	return (percent);
    701 }
    702 
    703 /*
    704  * Display kthread stack infos.
    705  */
    706 int
    707 stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
    708 {
    709 	kthread_t t;
    710 	proc_t p;
    711 	uint64_t *ptr;  /* pattern pointer */
    712 	caddr_t	start;	/* kernel stack start */
    713 	caddr_t end;	/* kernel stack end */
    714 	caddr_t ustack;	/* userland copy of kernel stack */
    715 	size_t usize;	/* userland copy of kernel stack size */
    716 	caddr_t ustart;	/* userland copy of kernel stack, aligned start */
    717 	caddr_t uend;	/* userland copy of kernel stack, aligned end */
    718 	size_t percent = 0;
    719 	uint_t all = FALSE; /* don't show TS_FREE kthread by default */
    720 	uint_t history = FALSE;
    721 	int i = 0;
    722 	unsigned int ukmem_stackinfo;
    723 	uintptr_t allthreads;
    724 
    725 	/* handle options */
    726 	if (mdb_getopts(argc, argv,
    727 	    'a', MDB_OPT_SETBITS, TRUE, &all,
    728 	    'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) {
    729 		return (DCMD_USAGE);
    730 	}
    731 
    732 	/* walk all kthread if needed */
    733 	if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) {
    734 		if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) {
    735 			mdb_warn("can't walk threads");
    736 			return (DCMD_ERR);
    737 		}
    738 		return (DCMD_OK);
    739 	}
    740 
    741 	/* read 'kmem_stackinfo' */
    742 	if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo),
    743 	    "kmem_stackinfo") == -1) {
    744 		mdb_warn("failed to read 'kmem_stackinfo'\n");
    745 		ukmem_stackinfo = 0;
    746 	}
    747 
    748 	/* read 'allthreads' */
    749 	if (mdb_readsym(&allthreads, sizeof (kthread_t *),
    750 	    "allthreads") == -1) {
    751 		mdb_warn("failed to read 'allthreads'\n");
    752 		allthreads = NULL;
    753 	}
    754 
    755 	if (history == TRUE) {
    756 		kmem_stkinfo_t *log;
    757 		uintptr_t kaddr;
    758 
    759 		mdb_printf("Dead kthreads stack usage history:\n");
    760 		if (ukmem_stackinfo == 0) {
    761 			mdb_printf("Tunable kmem_stackinfo is unset, history ");
    762 			mdb_printf("feature is off.\nUse ::help stackinfo ");
    763 			mdb_printf("for more details.\n");
    764 			return (DCMD_OK);
    765 		}
    766 
    767 		mdb_printf("%<u>%?s%</u>", "THREAD");
    768 		mdb_printf(" %<u>%?s%</u>", "STACK");
    769 		mdb_printf("%<u>%s%</u>", "   SIZE  MAX CMD/LWPID or STARTPC");
    770 		mdb_printf("\n");
    771 		usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t);
    772 		log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP);
    773 		if (mdb_readsym(&kaddr, sizeof (kaddr),
    774 		    "kmem_stkinfo_log") == -1) {
    775 			mdb_free((void *)log, usize);
    776 			mdb_warn("failed to read 'kmem_stkinfo_log'\n");
    777 			return (DCMD_ERR);
    778 		}
    779 		if (kaddr == NULL) {
    780 			mdb_free((void *)log, usize);
    781 			return (DCMD_OK);
    782 		}
    783 		if (mdb_vread(log, usize, kaddr) == -1) {
    784 			mdb_free((void *)log, usize);
    785 			mdb_warn("failed to read %p\n", kaddr);
    786 			return (DCMD_ERR);
    787 		}
    788 		for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
    789 			if (log[i].kthread == NULL) {
    790 				continue;
    791 			}
    792 			mdb_printf("%0?p %0?p %6x %3d%%",
    793 			    log[i].kthread,
    794 			    log[i].start,
    795 			    (uint_t)log[i].stksz,
    796 			    (int)log[i].percent);
    797 			if (log[i].t_tid != 0) {
    798 				mdb_printf(" %s/%u\n",
    799 				    log[i].cmd, log[i].t_tid);
    800 			} else {
    801 				mdb_printf(" %p (%a)\n", log[i].t_startpc,
    802 				    log[i].t_startpc);
    803 			}
    804 		}
    805 		mdb_free((void *)log, usize);
    806 		return (DCMD_OK);
    807 	}
    808 
    809 	/* display header */
    810 	if (DCMD_HDRSPEC(flags)) {
    811 		if (ukmem_stackinfo == 0) {
    812 			mdb_printf("Tunable kmem_stackinfo is unset, ");
    813 			mdb_printf("MAX value is not available.\n");
    814 			mdb_printf("Use ::help stackinfo for more details.\n");
    815 		}
    816 		mdb_printf("%<u>%?s%</u>", "THREAD");
    817 		mdb_printf(" %<u>%?s%</u>", "STACK");
    818 		mdb_printf("%<u>%s%</u>", "   SIZE  CUR  MAX CMD/LWPID");
    819 		mdb_printf("\n");
    820 	}
    821 
    822 	/* read kthread */
    823 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
    824 		mdb_warn("can't read kthread_t at %#lx\n", addr);
    825 		return (DCMD_ERR);
    826 	}
    827 
    828 	if (t.t_state == TS_FREE && all == FALSE) {
    829 		return (DCMD_OK);
    830 	}
    831 
    832 	/* read proc */
    833 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
    834 		mdb_warn("failed to read proc at %p\n", t.t_procp);
    835 		return (DCMD_ERR);
    836 	}
    837 
    838 	/*
    839 	 * Stack grows up or down, see thread_create(),
    840 	 * compute stack memory aera start and end (start < end).
    841 	 */
    842 	if (t.t_stk > t.t_stkbase) {
    843 		/* stack grows down */
    844 		start = t.t_stkbase;
    845 		end = t.t_stk;
    846 	} else {
    847 		/* stack grows up */
    848 		start = t.t_stk;
    849 		end = t.t_stkbase;
    850 	}
    851 
    852 	/* display stack info */
    853 	mdb_printf("%0?p %0?p", addr, start);
    854 
    855 	/* (end - start), kernel stack size as found in kthread_t */
    856 	if ((end <= start) || ((end - start) > (1024 * 1024))) {
    857 		/* negative or stack size > 1 meg, assume bogus */
    858 		mdb_warn(" t_stk/t_stkbase problem\n");
    859 		return (DCMD_ERR);
    860 	}
    861 
    862 	/* display stack size */
    863 	mdb_printf(" %6x", end - start);
    864 
    865 	/* display current stack usage */
    866 	percent = stk_compute_percent(t.t_stk, t.t_stkbase,
    867 	    (caddr_t)t.t_sp + STACK_BIAS);
    868 
    869 	mdb_printf(" %3d%%", percent);
    870 	percent = 0;
    871 
    872 	if (ukmem_stackinfo == 0) {
    873 		mdb_printf("  n/a");
    874 		if (t.t_tid == 0) {
    875 			mdb_printf(" %a()", t.t_startpc);
    876 		} else {
    877 			mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
    878 		}
    879 		mdb_printf("\n");
    880 		return (DCMD_OK);
    881 	}
    882 
    883 	if ((((uintptr_t)start) & 0x7) != 0) {
    884 		start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
    885 	}
    886 	end = (caddr_t)(((uintptr_t)end) & (~0x7));
    887 	/* size to scan in userland copy of kernel stack */
    888 	usize = end - start; /* is a multiple of 8 bytes */
    889 
    890 	/*
    891 	 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes
    892 	 * alignement for ustart and uend, in boundaries.
    893 	 */
    894 	ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP);
    895 	if ((((uintptr_t)ustart) & 0x7) != 0) {
    896 		ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8);
    897 	}
    898 	uend = ustart + usize;
    899 
    900 	/* read the kernel stack */
    901 	if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) {
    902 		mdb_free((void *)ustack, usize + 8);
    903 		mdb_printf("\n");
    904 		mdb_warn("couldn't read entire stack\n");
    905 		return (DCMD_ERR);
    906 	}
    907 
    908 	/* scan the stack */
    909 	if (t.t_stk > t.t_stkbase) {
    910 		/* stack grows down */
    911 #if defined(__i386) || defined(__amd64)
    912 		/*
    913 		 * 6 longs are pushed on stack, see thread_load(). Skip
    914 		 * them, so if kthread has never run, percent is zero.
    915 		 * 8 bytes alignement is preserved for a 32 bit kernel,
    916 		 * 6 x 4 = 24, 24 is a multiple of 8.
    917 		 */
    918 		uend -= (6 * sizeof (long));
    919 #endif
    920 		ptr = (uint64_t *)((void *)ustart);
    921 		while (ptr < (uint64_t *)((void *)uend)) {
    922 			if (*ptr != KMEM_STKINFO_PATTERN) {
    923 				percent = stk_compute_percent(uend,
    924 				    ustart, (caddr_t)ptr);
    925 				break;
    926 			}
    927 			ptr++;
    928 		}
    929 	} else {
    930 		/* stack grows up */
    931 		ptr = (uint64_t *)((void *)uend);
    932 		ptr--;
    933 		while (ptr >= (uint64_t *)((void *)ustart)) {
    934 			if (*ptr != KMEM_STKINFO_PATTERN) {
    935 				percent = stk_compute_percent(ustart,
    936 				    uend, (caddr_t)ptr);
    937 				break;
    938 			}
    939 			ptr--;
    940 		}
    941 	}
    942 
    943 	/* thread 't0' stack is not created by thread_create() */
    944 	if (addr == allthreads) {
    945 		percent = 0;
    946 	}
    947 	if (percent != 0) {
    948 		mdb_printf(" %3d%%", percent);
    949 	} else {
    950 		mdb_printf("  n/a");
    951 	}
    952 	if (t.t_tid == 0) {
    953 		mdb_printf(" %a()", t.t_startpc);
    954 	} else {
    955 		mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
    956 	}
    957 	mdb_printf("\n");
    958 	mdb_free((void *)ustack, usize + 8);
    959 	return (DCMD_OK);
    960 }
    961 
    962 void
    963 stackinfo_help(void)
    964 {
    965 	mdb_printf(
    966 	    "Shows kernel stacks real utilization, if /etc/system "
    967 	    "kmem_stackinfo tunable\n");
    968 	mdb_printf(
    969 	    "(an unsigned integer) is non zero at kthread creation time. ");
    970 	mdb_printf("For example:\n");
    971 	mdb_printf(
    972 	    "          THREAD            STACK   SIZE  CUR  MAX CMD/LWPID\n");
    973 	mdb_printf(
    974 	    "ffffff014f5f2c20 ffffff0004153000   4f00   4%%  43%% init/1\n");
    975 	mdb_printf(
    976 	    "The stack size utilization for this kthread is at 4%%"
    977 	    " of its maximum size,\n");
    978 	mdb_printf(
    979 	    "but has already used up to 43%%, stack size is 4f00 bytes.\n");
    980 	mdb_printf(
    981 	    "MAX value can be shown as n/a (not available):\n");
    982 	mdb_printf(
    983 	    "  - for the very first kthread (sched/1)\n");
    984 	mdb_printf(
    985 	    "  - kmem_stackinfo was zero at kthread creation time\n");
    986 	mdb_printf(
    987 	    "  - kthread has not yet run\n");
    988 	mdb_printf("\n");
    989 	mdb_printf("Options:\n");
    990 	mdb_printf(
    991 	    "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
    992 	mdb_printf(
    993 	    "-h shows history, dead kthreads that used their "
    994 	    "kernel stack the most\n");
    995 	mdb_printf(
    996 	    "\nSee Solaris Modular Debugger Guide for detailed usage.\n");
    997 	mdb_flush();
    998 }
    999