xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/thread.c (revision c97ad5cdc75eb73e3cc38542ca3ba783574b0a7a)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <mdb/mdb_modapi.h>
29 #include <sys/types.h>
30 #include <sys/thread.h>
31 #include <sys/lwp.h>
32 #include <sys/proc.h>
33 #include <sys/cpuvar.h>
34 #include <sys/cpupart.h>
35 #include <sys/disp.h>
36 
37 typedef struct thread_walk {
38 	kthread_t *tw_thread;
39 	uintptr_t tw_last;
40 	uint_t tw_inproc;
41 	uint_t tw_step;
42 } thread_walk_t;
43 
44 int
45 thread_walk_init(mdb_walk_state_t *wsp)
46 {
47 	thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP);
48 
49 	if (wsp->walk_addr == NULL) {
50 		if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) {
51 			mdb_warn("failed to read 'allthreads'");
52 			mdb_free(twp, sizeof (thread_walk_t));
53 			return (WALK_ERR);
54 		}
55 
56 		twp->tw_inproc = FALSE;
57 
58 	} else {
59 		proc_t pr;
60 
61 		if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) {
62 			mdb_warn("failed to read proc at %p", wsp->walk_addr);
63 			mdb_free(twp, sizeof (thread_walk_t));
64 			return (WALK_ERR);
65 		}
66 
67 		wsp->walk_addr = (uintptr_t)pr.p_tlist;
68 		twp->tw_inproc = TRUE;
69 	}
70 
71 	twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP);
72 	twp->tw_last = wsp->walk_addr;
73 	twp->tw_step = FALSE;
74 
75 	wsp->walk_data = twp;
76 	return (WALK_NEXT);
77 }
78 
79 int
80 thread_walk_step(mdb_walk_state_t *wsp)
81 {
82 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
83 	int status;
84 
85 	if (wsp->walk_addr == NULL)
86 		return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */
87 
88 	if (twp->tw_step && wsp->walk_addr == twp->tw_last)
89 		return (WALK_DONE); /* We've wrapped around */
90 
91 	if (mdb_vread(twp->tw_thread, sizeof (kthread_t),
92 	    wsp->walk_addr) == -1) {
93 		mdb_warn("failed to read thread at %p", wsp->walk_addr);
94 		return (WALK_DONE);
95 	}
96 
97 	status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
98 	    wsp->walk_cbdata);
99 
100 	if (twp->tw_inproc)
101 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw;
102 	else
103 		wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next;
104 
105 	twp->tw_step = TRUE;
106 	return (status);
107 }
108 
109 void
110 thread_walk_fini(mdb_walk_state_t *wsp)
111 {
112 	thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
113 
114 	mdb_free(twp->tw_thread, sizeof (kthread_t));
115 	mdb_free(twp, sizeof (thread_walk_t));
116 }
117 
118 int
119 deathrow_walk_init(mdb_walk_state_t *wsp)
120 {
121 	if (mdb_layered_walk("thread_deathrow", wsp) == -1) {
122 		mdb_warn("couldn't walk 'thread_deathrow'");
123 		return (WALK_ERR);
124 	}
125 
126 	if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
127 		mdb_warn("couldn't walk 'lwp_deathrow'");
128 		return (WALK_ERR);
129 	}
130 
131 	return (WALK_NEXT);
132 }
133 
134 int
135 deathrow_walk_step(mdb_walk_state_t *wsp)
136 {
137 	kthread_t t;
138 	uintptr_t addr = wsp->walk_addr;
139 
140 	if (addr == NULL)
141 		return (WALK_DONE);
142 
143 	if (mdb_vread(&t, sizeof (t), addr) == -1) {
144 		mdb_warn("couldn't read deathrow thread at %p", addr);
145 		return (WALK_ERR);
146 	}
147 
148 	wsp->walk_addr = (uintptr_t)t.t_forw;
149 
150 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
151 }
152 
153 int
154 thread_deathrow_walk_init(mdb_walk_state_t *wsp)
155 {
156 	if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) {
157 		mdb_warn("couldn't read symbol 'thread_deathrow'");
158 		return (WALK_ERR);
159 	}
160 
161 	return (WALK_NEXT);
162 }
163 
164 int
165 lwp_deathrow_walk_init(mdb_walk_state_t *wsp)
166 {
167 	if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) {
168 		mdb_warn("couldn't read symbol 'lwp_deathrow'");
169 		return (WALK_ERR);
170 	}
171 
172 	return (WALK_NEXT);
173 }
174 
175 
176 typedef struct dispq_walk {
177 	int dw_npri;
178 	uintptr_t dw_dispq;
179 	uintptr_t dw_last;
180 } dispq_walk_t;
181 
182 int
183 cpu_dispq_walk_init(mdb_walk_state_t *wsp)
184 {
185 	uintptr_t addr = wsp->walk_addr;
186 	dispq_walk_t *dw;
187 	cpu_t cpu;
188 	dispq_t dispq;
189 	disp_t disp;
190 
191 	if (addr == NULL) {
192 		mdb_warn("cpu_dispq walk needs a cpu_t address\n");
193 		return (WALK_ERR);
194 	}
195 
196 	if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
197 		mdb_warn("failed to read cpu_t at %p", addr);
198 		return (WALK_ERR);
199 	}
200 
201 	if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) {
202 		mdb_warn("failed to read disp_t at %p", cpu.cpu_disp);
203 		return (WALK_ERR);
204 	}
205 
206 	if (mdb_vread(&dispq, sizeof (dispq_t),
207 	    (uintptr_t)disp.disp_q) == -1) {
208 		mdb_warn("failed to read dispq_t at %p", disp.disp_q);
209 		return (WALK_ERR);
210 	}
211 
212 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
213 
214 	dw->dw_npri = disp.disp_npri;
215 	dw->dw_dispq = (uintptr_t)disp.disp_q;
216 	dw->dw_last = (uintptr_t)dispq.dq_last;
217 
218 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
219 	wsp->walk_data = dw;
220 
221 	return (WALK_NEXT);
222 }
223 
224 int
225 cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
226 {
227 	uintptr_t addr = wsp->walk_addr;
228 	dispq_walk_t *dw;
229 	cpupart_t cpupart;
230 	dispq_t dispq;
231 
232 	if (addr == NULL) {
233 		mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
234 		return (WALK_ERR);
235 	}
236 
237 	if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) {
238 		mdb_warn("failed to read cpupart_t at %p", addr);
239 		return (WALK_ERR);
240 	}
241 
242 	if (mdb_vread(&dispq, sizeof (dispq_t),
243 	    (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) {
244 		mdb_warn("failed to read dispq_t at %p",
245 		    cpupart.cp_kp_queue.disp_q);
246 		return (WALK_ERR);
247 	}
248 
249 	dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
250 
251 	dw->dw_npri = cpupart.cp_kp_queue.disp_npri;
252 	dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q;
253 	dw->dw_last = (uintptr_t)dispq.dq_last;
254 
255 	wsp->walk_addr = (uintptr_t)dispq.dq_first;
256 	wsp->walk_data = dw;
257 
258 	return (WALK_NEXT);
259 }
260 
261 int
262 dispq_walk_step(mdb_walk_state_t *wsp)
263 {
264 	uintptr_t addr = wsp->walk_addr;
265 	dispq_walk_t *dw = wsp->walk_data;
266 	dispq_t dispq;
267 	kthread_t t;
268 
269 	while (addr == NULL) {
270 		if (--dw->dw_npri == 0)
271 			return (WALK_DONE);
272 
273 		dw->dw_dispq += sizeof (dispq_t);
274 
275 		if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) {
276 			mdb_warn("failed to read dispq_t at %p", dw->dw_dispq);
277 			return (WALK_ERR);
278 		}
279 
280 		dw->dw_last = (uintptr_t)dispq.dq_last;
281 		addr = (uintptr_t)dispq.dq_first;
282 	}
283 
284 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
285 		mdb_warn("failed to read kthread_t at %p", addr);
286 		return (WALK_ERR);
287 	}
288 
289 	if (addr == dw->dw_last)
290 		wsp->walk_addr = NULL;
291 	else
292 		wsp->walk_addr = (uintptr_t)t.t_link;
293 
294 	return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
295 }
296 
297 void
298 dispq_walk_fini(mdb_walk_state_t *wsp)
299 {
300 	mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
301 }
302 
303 
304 #define	TF_INTR		0x01
305 #define	TF_PROC		0x02
306 #define	TF_BLOCK	0x04
307 #define	TF_SIG		0x08
308 #define	TF_DISP		0x10
309 #define	TF_MERGE	0x20
310 
311 /*
312  * Display a kthread_t.
313  * This is a little complicated, as there is a lot of information that
314  * the user could be interested in.  The flags "ipbsd" are used to
315  * indicate which subset of the thread's members are to be displayed
316  * ('i' is the default).  If multiple options are specified, multiple
317  * sets of data will be displayed in a vaguely readable format.  If the
318  * 'm' option is specified, all the selected sets will be merged onto a
319  * single line for the benefit of those using wider-than-normal
320  * terminals.  Having a generic mechanism for doing this would be
321  * really useful, but is a project best left to another day.
322  */
323 
324 int
325 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
326 {
327 	kthread_t	t;
328 	uint_t		oflags = 0;
329 	uint_t		fflag = FALSE;
330 	int		first;
331 	char		*state;
332 	char		stbuf[20];
333 
334 	/*
335 	 * "Gracefully" handle printing a boatload of stuff to the
336 	 * screen.  If we are not printing our first set of data, and
337 	 * we haven't been instructed to merge sets together, output a
338 	 * newline and indent such that the thread addresses form a
339 	 * column of their own.
340 	 */
341 #define	SPACER()				\
342 	if (first) {				\
343 		first = FALSE;			\
344 	} else if (!(oflags & TF_MERGE)) {	\
345 		mdb_printf("\n%?s", "");	\
346 	}
347 
348 	if (!(flags & DCMD_ADDRSPEC)) {
349 		if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) {
350 			mdb_warn("can't walk threads");
351 			return (DCMD_ERR);
352 		}
353 		return (DCMD_OK);
354 	}
355 
356 	if (mdb_getopts(argc, argv,
357 	    'f', MDB_OPT_SETBITS, TRUE, &fflag,
358 	    'i', MDB_OPT_SETBITS, TF_INTR, &oflags,
359 	    'p', MDB_OPT_SETBITS, TF_PROC, &oflags,
360 	    'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags,
361 	    's', MDB_OPT_SETBITS, TF_SIG, &oflags,
362 	    'd', MDB_OPT_SETBITS, TF_DISP, &oflags,
363 	    'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc)
364 		return (DCMD_USAGE);
365 
366 	/*
367 	 * If no sets were specified, choose the 'i' set.
368 	 */
369 	if (!(oflags & ~TF_MERGE))
370 #ifdef	_LP64
371 		oflags = TF_INTR;
372 #else
373 		oflags = TF_INTR | TF_DISP | TF_MERGE;
374 #endif
375 
376 	/*
377 	 * Print the relevant headers; note use of SPACER().
378 	 */
379 	if (DCMD_HDRSPEC(flags)) {
380 		first = TRUE;
381 		mdb_printf("%<u>%?s%</u>", "ADDR");
382 		mdb_flush();
383 
384 		if (oflags & TF_PROC) {
385 			SPACER();
386 			mdb_printf("%<u> %?s %?s %?s%</u>",
387 			    "PROC", "LWP", "CRED");
388 		}
389 
390 		if (oflags & TF_INTR) {
391 			SPACER();
392 			mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>",
393 			    "STATE", "FLG", "PFLG",
394 			    "SFLG", "PRI", "EPRI", "PIL", "INTR");
395 		}
396 
397 		if (oflags & TF_BLOCK) {
398 			SPACER();
399 			mdb_printf("%<u> %?s %?s %?s %11s%</u>",
400 			    "WCHAN", "TS", "PITS", "SOBJ OPS");
401 		}
402 
403 		if (oflags & TF_SIG) {
404 			SPACER();
405 			mdb_printf("%<u> %?s %16s %16s%</u>",
406 			    "SIGQUEUE", "SIG PEND", "SIG HELD");
407 		}
408 
409 		if (oflags & TF_DISP) {
410 			SPACER();
411 			mdb_printf("%<u> %?s %5s %2s%</u>",
412 			    "DISPTIME", "BOUND", "PR");
413 		}
414 		mdb_printf("\n");
415 	}
416 
417 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
418 		mdb_warn("can't read kthread_t at %#lx", addr);
419 		return (DCMD_ERR);
420 	}
421 
422 	if (fflag && (t.t_state == TS_FREE))
423 		return (DCMD_OK);
424 
425 	first = TRUE;
426 	mdb_printf("%0?lx", addr);
427 
428 	/* process information */
429 	if (oflags & TF_PROC) {
430 		SPACER();
431 		mdb_printf(" %?p %?p %?p",
432 			t.t_procp, t.t_lwp, t.t_cred);
433 	}
434 
435 	/* priority/interrupt information */
436 	if (oflags & TF_INTR) {
437 		SPACER();
438 		switch (t.t_state) {
439 		case TS_FREE:
440 			state = "free";
441 			break;
442 		case TS_SLEEP:
443 			state = "sleep";
444 			break;
445 		case TS_RUN:
446 			state = "run";
447 			break;
448 		case TS_ONPROC:
449 			state = "onproc";
450 			break;
451 		case TS_ZOMB:
452 			state = "zomb";
453 			break;
454 		case TS_STOPPED:
455 			state = "stopped";
456 			break;
457 		case TS_WAIT:
458 			state = "wait";
459 			break;
460 		default:
461 			(void) mdb_snprintf(stbuf, 11, "inval/%02x", t.t_state);
462 			state = stbuf;
463 		}
464 		if (t.t_intr == NULL) {
465 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s",
466 			    state, t.t_flag, t.t_proc_flag, t.t_schedflag,
467 			    t.t_pri, t.t_epri, t.t_pil, "n/a");
468 		} else {
469 			mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p",
470 			    state, t.t_flag, t.t_proc_flag, t.t_schedflag,
471 			    t.t_pri, t.t_epri, t.t_pil, t.t_intr);
472 		}
473 	}
474 
475 	/* blocking information */
476 	if (oflags & TF_BLOCK) {
477 		SPACER();
478 		(void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops);
479 		stbuf[11] = '\0';
480 		mdb_printf(" %?p %?p %?p %11s",
481 		    t.t_wchan, t.t_ts, t.t_prioinv, stbuf);
482 	}
483 
484 	/* signal information */
485 	if (oflags & TF_SIG) {
486 		SPACER();
487 		mdb_printf(" %?p %016llx %016llx",
488 			t.t_sigqueue, t.t_sig, t.t_hold);
489 	}
490 
491 	/* dispatcher stuff */
492 	if (oflags & TF_DISP) {
493 		SPACER();
494 		mdb_printf(" %?lx %5d %2d",
495 		    t.t_disp_time, t.t_bind_cpu, t.t_preempt);
496 	}
497 
498 	mdb_printf("\n");
499 
500 #undef SPACER
501 
502 	return (DCMD_OK);
503 }
504 
505 void
506 thread_help(void)
507 {
508 	mdb_printf(
509 	    "The flags -ipbsd control which information is displayed.  When\n"
510 	    "combined, the fields are displayed on separate lines unless the\n"
511 	    "-m option is given.\n"
512 	    "\n"
513 	    "\t-b\tprint blocked thread state\n"
514 	    "\t-d\tprint dispatcher state\n"
515 	    "\t-f\tignore freed threads\n"
516 	    "\t-i\tprint basic thread state (default)\n"
517 	    "\t-m\tdisplay results on a single line\n"
518 	    "\t-p\tprint process and lwp state\n"
519 	    "\t-s\tprint signal state\n");
520 }
521 
522 /*
523  * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
524  */
525 int
526 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
527 {
528 	int i;
529 	uint_t count =  0;
530 	uint_t verbose = FALSE;
531 	kthread_t t;
532 	proc_t p;
533 	char cmd[80];
534 	mdb_arg_t cmdarg;
535 
536 	if (!(flags & DCMD_ADDRSPEC)) {
537 		if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) {
538 			mdb_warn("can't walk threads");
539 			return (DCMD_ERR);
540 		}
541 		return (DCMD_OK);
542 	}
543 
544 	i = mdb_getopts(argc, argv,
545 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
546 
547 	if (i != argc) {
548 		if (i != argc - 1 || !verbose)
549 			return (DCMD_USAGE);
550 
551 		if (argv[i].a_type == MDB_TYPE_IMMEDIATE)
552 			count = (uint_t)argv[i].a_un.a_val;
553 		else
554 			count = (uint_t)mdb_strtoull(argv[i].a_un.a_str);
555 	}
556 
557 	if (DCMD_HDRSPEC(flags)) {
558 		if (verbose)
559 			mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
560 			    "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
561 		else
562 			mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n",
563 			    "ADDR", "PROC", "LWP", "CMD", "LWPID");
564 	}
565 
566 	if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
567 		mdb_warn("failed to read kthread_t at %p", addr);
568 		return (DCMD_ERR);
569 	}
570 
571 	if (t.t_state == TS_FREE)
572 		return (DCMD_OK);
573 
574 	if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
575 		mdb_warn("failed to read proc at %p", t.t_procp);
576 		return (DCMD_ERR);
577 	}
578 
579 	if (verbose) {
580 		mdb_printf("%0?p %?p %?p %3u %3d %?p\n",
581 		    addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan);
582 
583 		mdb_inc_indent(2);
584 
585 		mdb_printf("PC: %a", t.t_pc);
586 		if (t.t_tid == 0)
587 			mdb_printf("    THREAD: %a()\n", t.t_startpc);
588 		else
589 			mdb_printf("    CMD: %s\n", p.p_user.u_psargs);
590 
591 		mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count);
592 		cmdarg.a_type = MDB_TYPE_STRING;
593 		cmdarg.a_un.a_str = cmd;
594 
595 		(void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg);
596 
597 		mdb_dec_indent(2);
598 
599 		mdb_printf("\n");
600 	} else {
601 		mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp);
602 		if (t.t_tid == 0)
603 			mdb_printf(" %a()\n", t.t_startpc);
604 		else
605 			mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid);
606 	}
607 
608 	return (DCMD_OK);
609 }
610 
611 void
612 threadlist_help(void)
613 {
614 	mdb_printf(
615 	    "   -v         print verbose output including C stack trace\n"
616 	    "   count      print no more than count arguments (default 0)\n");
617 }
618