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]
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>
36 #include <sys/cpuvar.h>
37 #include <sys/cpupart.h>
39 #include <sys/taskq_impl.h>
40 #include <sys/stack.h>
46 typedef struct thread_walk
{
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
));
65 twp
->tw_inproc
= FALSE
;
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
));
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
;
89 thread_walk_step(mdb_walk_state_t
*wsp
)
91 thread_walk_t
*twp
= (thread_walk_t
*)wsp
->walk_data
;
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
);
106 status
= wsp
->walk_callback(wsp
->walk_addr
, twp
->tw_thread
,
110 wsp
->walk_addr
= (uintptr_t)twp
->tw_thread
->t_forw
;
112 wsp
->walk_addr
= (uintptr_t)twp
->tw_thread
->t_next
;
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'");
135 if (mdb_layered_walk("lwp_deathrow", wsp
) == -1) {
136 mdb_warn("couldn't walk 'lwp_deathrow'");
144 deathrow_walk_step(mdb_walk_state_t
*wsp
)
147 uintptr_t addr
= wsp
->walk_addr
;
149 if (addr
== (uintptr_t)NULL
)
152 if (mdb_vread(&t
, sizeof (t
), addr
) == -1) {
153 mdb_warn("couldn't read deathrow thread at %p", addr
);
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'");
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'");
185 typedef struct dispq_walk
{
192 cpu_dispq_walk_init(mdb_walk_state_t
*wsp
)
194 uintptr_t addr
= wsp
->walk_addr
;
200 if (addr
== (uintptr_t)NULL
) {
201 mdb_warn("cpu_dispq walk needs a cpu_t address\n");
205 if (mdb_vread(&cpu
, sizeof (cpu_t
), addr
) == -1) {
206 mdb_warn("failed to read cpu_t at %p", addr
);
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
);
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
);
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
;
234 cpupart_dispq_walk_init(mdb_walk_state_t
*wsp
)
236 uintptr_t addr
= wsp
->walk_addr
;
241 if (addr
== (uintptr_t)NULL
) {
242 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
246 if (mdb_vread(&cpupart
, sizeof (cpupart_t
), addr
) == -1) {
247 mdb_warn("failed to read cpupart_t at %p", addr
);
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
);
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
;
271 dispq_walk_step(mdb_walk_state_t
*wsp
)
273 uintptr_t addr
= wsp
->walk_addr
;
274 dispq_walk_t
*dw
= wsp
->walk_data
;
278 while (addr
== (uintptr_t)NULL
) {
279 if (--dw
->dw_npri
== 0)
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
);
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
);
298 if (addr
== dw
->dw_last
)
299 wsp
->walk_addr
= (uintptr_t)NULL
;
301 wsp
->walk_addr
= (uintptr_t)t
.t_link
;
303 return (wsp
->walk_callback(addr
, &t
, wsp
->walk_cbdata
));
307 dispq_walk_fini(mdb_walk_state_t
*wsp
)
309 mdb_free(wsp
->walk_data
, sizeof (dispq_walk_t
));
312 struct thread_state
{
315 } thread_states
[] = {
317 { TS_SLEEP
, "sleep" },
319 { TS_ONPROC
, "onproc" },
321 { TS_STOPPED
, "stopped" },
324 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
327 thread_state_to_text(uint_t state
, char *out
, size_t out_sz
)
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
);
338 mdb_snprintf(out
, out_sz
, "inval/%02x", state
);
342 thread_text_to_state(const char *state
, uint_t
*out
)
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
;
357 thread_walk_states(void (*cbfunc
)(uint_t
, const char *, void *), void *cbarg
)
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
);
369 #define TF_BLOCK 0x04
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
)
392 uint_t fflag
= FALSE
;
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.
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");
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
)
429 * If no sets were specified, choose the 'i' set.
431 if (!(oflags
& ~TF_MERGE
))
435 oflags
= TF_INTR
| TF_DISP
| TF_MERGE
;
439 * Print the relevant headers; note use of SPACER().
441 if (DCMD_HDRSPEC(flags
)) {
443 mdb_printf("%<u>%?s%</u>", "ADDR");
446 if (oflags
& TF_PROC
) {
448 mdb_printf("%<u> %?s %?s %?s%</u>",
449 "PROC", "LWP", "CRED");
452 if (oflags
& TF_INTR
) {
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
) {
461 mdb_printf("%<u> %?s %?s %?s %11s%</u>",
462 "WCHAN", "TS", "PITS", "SOBJ OPS");
465 if (oflags
& TF_SIG
) {
467 mdb_printf("%<u> %?s %16s %16s%</u>",
468 "SIGQUEUE", "SIG PEND", "SIG HELD");
471 if (oflags
& TF_DISP
) {
473 mdb_printf("%<u> %?s %5s %2s %-6s%</u>",
474 "DISPTIME", "BOUND", "PR", "SWITCH");
479 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
480 mdb_warn("can't read kthread_t at %#lx", addr
);
484 if (fflag
&& (t
.t_state
== TS_FREE
))
488 mdb_printf("%0?lx", addr
);
490 /* process information */
491 if (oflags
& TF_PROC
) {
493 mdb_printf(" %?p %?p %?p", t
.t_procp
, t
.t_lwp
, t
.t_cred
);
496 /* priority/interrupt information */
497 if (oflags
& TF_INTR
) {
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");
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
) {
514 (void) mdb_snprintf(stbuf
, 20, "%a", t
.t_sobj_ops
);
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
) {
523 mdb_printf(" %?p %016llx %016llx",
524 t
.t_sigqueue
, t
.t_sig
, t
.t_hold
);
527 /* dispatcher stuff */
528 if (oflags
& TF_DISP
) {
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)
534 (clock_t)mdb_get_lbolt() - t
.t_disp_time
);
536 mdb_printf("%-6s", "-");
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"
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
)
571 uint_t verbose
= FALSE
;
572 uint_t notaskq
= FALSE
;
579 if (!(flags
& DCMD_ADDRSPEC
)) {
580 if (mdb_walk_dcmd("thread", "threadlist", argc
, argv
) == -1) {
581 mdb_warn("can't walk threads");
587 i
= mdb_getopts(argc
, argv
,
588 't', MDB_OPT_SETBITS
, TRUE
, ¬askq
,
589 'v', MDB_OPT_SETBITS
, TRUE
, &verbose
, NULL
);
592 if (i
!= argc
- 1 || !verbose
)
595 if (argv
[i
].a_type
== MDB_TYPE_IMMEDIATE
)
596 count
= (uint_t
)argv
[i
].a_un
.a_val
;
598 count
= (uint_t
)mdb_strtoull(argv
[i
].a_un
.a_str
);
601 if (DCMD_HDRSPEC(flags
)) {
603 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
604 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
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
);
615 if (notaskq
&& t
.t_taskq
!= NULL
)
618 if (t
.t_state
== TS_FREE
)
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
);
626 if (mdb_vread(&tq
, sizeof (taskq_t
), (uintptr_t)t
.t_taskq
) == -1)
627 tq
.tq_name
[0] = '\0';
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
);
635 mdb_printf("PC: %a", t
.t_pc
);
637 if (tq
.tq_name
[0] != '\0')
638 mdb_printf(" TASKQ: %s\n", tq
.tq_name
);
640 mdb_printf(" THREAD: %a()\n", t
.t_startpc
);
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
);
655 mdb_printf("%0?p %?p %?p", addr
, t
.t_procp
, t
.t_lwp
);
657 if (tq
.tq_name
[0] != '\0')
658 mdb_printf(" tq:%s\n", tq
.tq_name
);
660 mdb_printf(" %a()\n", t
.t_startpc
);
662 mdb_printf(" %s/%u\n", p
.p_user
.u_comm
, t
.t_tid
);
670 threadlist_help(void)
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");
679 stk_compute_percent(caddr_t t_stk
, caddr_t t_stkbase
, caddr_t sp
)
684 if (t_stk
> t_stkbase
) {
685 /* stack grows down */
689 if (sp
< t_stkbase
) {
692 percent
= t_stk
- sp
+ 1;
693 s
= t_stk
- t_stkbase
+ 1;
699 if (sp
> t_stkbase
) {
702 percent
= sp
- t_stk
+ 1;
703 s
= t_stkbase
- t_stk
+ 1;
705 percent
= ((100 * percent
) / s
) + 1;
713 * Display kthread stack infos.
716 stackinfo(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
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 */
728 uint_t all
= FALSE
; /* don't show TS_FREE kthread by default */
729 uint_t history
= FALSE
;
731 unsigned int ukmem_stackinfo
;
732 uintptr_t allthreads
;
735 if (mdb_getopts(argc
, argv
,
736 'a', MDB_OPT_SETBITS
, TRUE
, &all
,
737 'h', MDB_OPT_SETBITS
, TRUE
, &history
, NULL
) != argc
) {
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");
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");
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
) {
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");
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");
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");
788 if (kaddr
== (uintptr_t)NULL
) {
789 mdb_free((void *)log
, usize
);
792 if (mdb_vread(log
, usize
, kaddr
) == -1) {
793 mdb_free((void *)log
, usize
);
794 mdb_warn("failed to read %p\n", kaddr
);
797 for (i
= 0; i
< KMEM_STKINFO_LOG_SIZE
; i
++) {
798 if (log
[i
].kthread
== NULL
) {
801 mdb_printf("%0?p %0?p %6x %3d%%",
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
);
810 mdb_printf(" %p (%a)\n", log
[i
].t_startpc
,
814 mdb_free((void *)log
, usize
);
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");
832 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
833 mdb_warn("can't read kthread_t at %#lx\n", addr
);
837 if (t
.t_state
== TS_FREE
&& all
== FALSE
) {
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
);
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 */
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");
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
);
881 if (ukmem_stackinfo
== 0) {
884 mdb_printf(" %a()", t
.t_startpc
);
886 mdb_printf(" %s/%u", p
.p_user
.u_comm
, t
.t_tid
);
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);
913 mdb_warn("couldn't read entire stack\n");
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));
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
);
940 ptr
= (uint64_t *)((void *)uend
);
942 while (ptr
>= (uint64_t *)((void *)ustart
)) {
943 if (*ptr
!= KMEM_STKINFO_PATTERN
) {
944 percent
= stk_compute_percent(ustart
,
952 /* thread 't0' stack is not created by thread_create() */
953 if (addr
== allthreads
) {
957 mdb_printf(" %3d%%", percent
);
962 mdb_printf(" %a()", t
.t_startpc
);
964 mdb_printf(" %s/%u", p
.p_user
.u_comm
, t
.t_tid
);
967 mdb_free((void *)ustack
, usize
+ 8);
975 "Shows kernel stacks real utilization, if /etc/system "
976 "kmem_stackinfo tunable\n");
978 "(an unsigned integer) is non zero at kthread creation time. ");
979 mdb_printf("For example:\n");
981 " THREAD STACK SIZE CUR MAX CMD/LWPID\n");
983 "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n");
985 "The stack size utilization for this kthread is at 4%%"
986 " of its maximum size,\n");
988 "but has already used up to 43%%, stack size is 4f00 bytes.\n");
990 "MAX value can be shown as n/a (not available):\n");
992 " - for the very first kthread (sched/1)\n");
994 " - kmem_stackinfo was zero at kthread creation time\n");
996 " - kthread has not yet run\n");
998 mdb_printf("Options:\n");
1000 "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
1002 "-h shows history, dead kthreads that used their "
1003 "kernel stack the most\n");
1005 "\nSee Solaris Modular Debugger Guide for detailed usage.\n");