dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / mdb / common / modules / genunix / thread.c
blob80c1cffc3cf4a9018ec6d1a8f50057aa0925a4e2
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
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>
42 #ifndef STACK_BIAS
43 #define STACK_BIAS 0
44 #endif
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;
53 int
54 thread_walk_init(mdb_walk_state_t *wsp)
56 thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP);
58 if (wsp->walk_addr == (uintptr_t)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);
65 twp->tw_inproc = FALSE;
67 } else {
68 proc_t pr;
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);
76 wsp->walk_addr = (uintptr_t)pr.p_tlist;
77 twp->tw_inproc = TRUE;
80 twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP);
81 twp->tw_last = wsp->walk_addr;
82 twp->tw_step = FALSE;
84 wsp->walk_data = twp;
85 return (WALK_NEXT);
88 int
89 thread_walk_step(mdb_walk_state_t *wsp)
91 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
92 int status;
94 if (wsp->walk_addr == (uintptr_t)NULL)
95 return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */
97 if (twp->tw_step && wsp->walk_addr == twp->tw_last)
98 return (WALK_DONE); /* We've wrapped around */
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);
106 status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
107 wsp->walk_cbdata);
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;
114 twp->tw_step = TRUE;
115 return (status);
118 void
119 thread_walk_fini(mdb_walk_state_t *wsp)
121 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
123 mdb_free(twp->tw_thread, sizeof (kthread_t));
124 mdb_free(twp, sizeof (thread_walk_t));
128 deathrow_walk_init(mdb_walk_state_t *wsp)
130 if (mdb_layered_walk("thread_deathrow", wsp) == -1) {
131 mdb_warn("couldn't walk 'thread_deathrow'");
132 return (WALK_ERR);
135 if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
136 mdb_warn("couldn't walk 'lwp_deathrow'");
137 return (WALK_ERR);
140 return (WALK_NEXT);
144 deathrow_walk_step(mdb_walk_state_t *wsp)
146 kthread_t t;
147 uintptr_t addr = wsp->walk_addr;
149 if (addr == (uintptr_t)NULL)
150 return (WALK_DONE);
152 if (mdb_vread(&t, sizeof (t), addr) == -1) {
153 mdb_warn("couldn't read deathrow thread at %p", addr);
154 return (WALK_ERR);
157 wsp->walk_addr = (uintptr_t)t.t_forw;
159 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
163 thread_deathrow_walk_init(mdb_walk_state_t *wsp)
165 if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) {
166 mdb_warn("couldn't read symbol 'thread_deathrow'");
167 return (WALK_ERR);
170 return (WALK_NEXT);
174 lwp_deathrow_walk_init(mdb_walk_state_t *wsp)
176 if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) {
177 mdb_warn("couldn't read symbol 'lwp_deathrow'");
178 return (WALK_ERR);
181 return (WALK_NEXT);
185 typedef struct dispq_walk {
186 int dw_npri;
187 uintptr_t dw_dispq;
188 uintptr_t dw_last;
189 } dispq_walk_t;
192 cpu_dispq_walk_init(mdb_walk_state_t *wsp)
194 uintptr_t addr = wsp->walk_addr;
195 dispq_walk_t *dw;
196 cpu_t cpu;
197 dispq_t dispq;
198 disp_t disp;
200 if (addr == (uintptr_t)NULL) {
201 mdb_warn("cpu_dispq walk needs a cpu_t address\n");
202 return (WALK_ERR);
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);
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);
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);
221 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
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;
227 wsp->walk_addr = (uintptr_t)dispq.dq_first;
228 wsp->walk_data = dw;
230 return (WALK_NEXT);
234 cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
236 uintptr_t addr = wsp->walk_addr;
237 dispq_walk_t *dw;
238 cpupart_t cpupart;
239 dispq_t dispq;
241 if (addr == (uintptr_t)NULL) {
242 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
243 return (WALK_ERR);
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);
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);
258 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
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;
264 wsp->walk_addr = (uintptr_t)dispq.dq_first;
265 wsp->walk_data = dw;
267 return (WALK_NEXT);
271 dispq_walk_step(mdb_walk_state_t *wsp)
273 uintptr_t addr = wsp->walk_addr;
274 dispq_walk_t *dw = wsp->walk_data;
275 dispq_t dispq;
276 kthread_t t;
278 while (addr == (uintptr_t)NULL) {
279 if (--dw->dw_npri == 0)
280 return (WALK_DONE);
282 dw->dw_dispq += sizeof (dispq_t);
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);
289 dw->dw_last = (uintptr_t)dispq.dq_last;
290 addr = (uintptr_t)dispq.dq_first;
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);
298 if (addr == dw->dw_last)
299 wsp->walk_addr = (uintptr_t)NULL;
300 else
301 wsp->walk_addr = (uintptr_t)t.t_link;
303 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
306 void
307 dispq_walk_fini(mdb_walk_state_t *wsp)
309 mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
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" }
324 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
326 void
327 thread_state_to_text(uint_t state, char *out, size_t out_sz)
329 int idx;
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;
338 mdb_snprintf(out, out_sz, "inval/%02x", state);
342 thread_text_to_state(const char *state, uint_t *out)
344 int idx;
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);
353 return (-1);
356 void
357 thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg)
359 int idx;
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);
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
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.
388 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
390 kthread_t t;
391 uint_t oflags = 0;
392 uint_t fflag = FALSE;
393 int first;
394 char stbuf[20];
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.
403 #define SPACER() \
404 if (first) { \
405 first = FALSE; \
406 } else if (!(oflags & TF_MERGE)) { \
407 mdb_printf("\n%?s", ""); \
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);
415 return (DCMD_OK);
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);
429 * If no sets were specified, choose the 'i' set.
431 if (!(oflags & ~TF_MERGE))
432 #ifdef _LP64
433 oflags = TF_INTR;
434 #else
435 oflags = TF_INTR | TF_DISP | TF_MERGE;
436 #endif
439 * Print the relevant headers; note use of SPACER().
441 if (DCMD_HDRSPEC(flags)) {
442 first = TRUE;
443 mdb_printf("%<u>%?s%</u>", "ADDR");
444 mdb_flush();
446 if (oflags & TF_PROC) {
447 SPACER();
448 mdb_printf("%<u> %?s %?s %?s%</u>",
449 "PROC", "LWP", "CRED");
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");
459 if (oflags & TF_BLOCK) {
460 SPACER();
461 mdb_printf("%<u> %?s %?s %?s %11s%</u>",
462 "WCHAN", "TS", "PITS", "SOBJ OPS");
465 if (oflags & TF_SIG) {
466 SPACER();
467 mdb_printf("%<u> %?s %16s %16s%</u>",
468 "SIGQUEUE", "SIG PEND", "SIG HELD");
471 if (oflags & TF_DISP) {
472 SPACER();
473 mdb_printf("%<u> %?s %5s %2s %-6s%</u>",
474 "DISPTIME", "BOUND", "PR", "SWITCH");
476 mdb_printf("\n");
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);
484 if (fflag && (t.t_state == TS_FREE))
485 return (DCMD_OK);
487 first = TRUE;
488 mdb_printf("%0?lx", addr);
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);
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);
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);
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);
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", "-");
539 mdb_printf("\n");
541 #undef SPACER
543 return (DCMD_OK);
546 void
547 thread_help(void)
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");
564 * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
567 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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;
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);
584 return (DCMD_OK);
587 i = mdb_getopts(argc, argv,
588 't', MDB_OPT_SETBITS, TRUE, &notaskq,
589 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
591 if (i != argc) {
592 if (i != argc - 1 || !verbose)
593 return (DCMD_USAGE);
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);
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");
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);
615 if (notaskq && t.t_taskq != NULL)
616 return (DCMD_OK);
618 if (t.t_state == TS_FREE)
619 return (DCMD_OK);
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);
626 if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1)
627 tq.tq_name[0] = '\0';
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);
633 mdb_inc_indent(2);
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);
645 mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count);
646 cmdarg.a_type = MDB_TYPE_STRING;
647 cmdarg.a_un.a_str = cmd;
649 (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg);
651 mdb_dec_indent(2);
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);
666 return (DCMD_OK);
669 void
670 threadlist_help(void)
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");
678 static size_t
679 stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
681 size_t percent;
682 size_t s;
684 if (t_stk > t_stkbase) {
685 /* stack grows down */
686 if (sp > t_stk) {
687 return (0);
689 if (sp < t_stkbase) {
690 return (100);
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);
699 if (sp > t_stkbase) {
700 return (100);
702 percent = sp - t_stk + 1;
703 s = t_stkbase - t_stk + 1;
705 percent = ((100 * percent) / s) + 1;
706 if (percent > 100) {
707 percent = 100;
709 return (percent);
713 * Display kthread stack infos.
716 stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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;
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);
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);
747 return (DCMD_OK);
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;
757 /* read 'allthreads' */
758 if (mdb_readsym(&allthreads, sizeof (kthread_t *),
759 "allthreads") == -1) {
760 mdb_warn("failed to read 'allthreads'\n");
761 allthreads = (uintptr_t)NULL;
764 if (history == TRUE) {
765 kmem_stkinfo_t *log;
766 uintptr_t kaddr;
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);
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);
788 if (kaddr == (uintptr_t)NULL) {
789 mdb_free((void *)log, usize);
790 return (DCMD_OK);
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);
797 for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
798 if (log[i].kthread == NULL) {
799 continue;
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);
814 mdb_free((void *)log, usize);
815 return (DCMD_OK);
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");
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");
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);
837 if (t.t_state == TS_FREE && all == FALSE) {
838 return (DCMD_OK);
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);
848 * Stack grows up or down, see thread_create(),
849 * compute stack memory aera start and end (start < end).
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;
861 /* display stack info */
862 mdb_printf("%0?p %0?p", addr, start);
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);
871 /* display stack size */
872 mdb_printf(" %6x", end - start);
874 /* display current stack usage */
875 percent = stk_compute_percent(t.t_stk, t.t_stkbase,
876 (caddr_t)t.t_sp + STACK_BIAS);
878 mdb_printf(" %3d%%", percent);
879 percent = 0;
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);
888 mdb_printf("\n");
889 return (DCMD_OK);
892 if ((((uintptr_t)start) & 0x7) != 0) {
893 start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
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 */
900 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes
901 * alignement for ustart and uend, in boundaries.
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);
907 uend = ustart + usize;
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);
917 /* scan the stack */
918 if (t.t_stk > t.t_stkbase) {
919 /* stack grows down */
920 #if defined(__i386) || defined(__amd64)
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.
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;
936 ptr++;
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;
948 ptr--;
952 /* thread 't0' stack is not created by thread_create() */
953 if (addr == allthreads) {
954 percent = 0;
956 if (percent != 0) {
957 mdb_printf(" %3d%%", percent);
958 } else {
959 mdb_printf(" n/a");
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);
966 mdb_printf("\n");
967 mdb_free((void *)ustack, usize + 8);
968 return (DCMD_OK);
971 void
972 stackinfo_help(void)
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();