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