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