xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/thread.c (revision bb9b6b3f59b8820022416cea99b49c50fef6e391)
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