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