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.
27 * Copyright (c) 2018, Joyent, Inc.
31 #include <mdb/mdb_modapi.h>
32 #include <mdb/mdb_ks.h>
33 #include <mdb/mdb_ctf.h>
34 #include <sys/types.h>
35 #include <sys/thread.h>
38 #include <sys/cpuvar.h>
39 #include <sys/cpupart.h>
41 #include <sys/taskq_impl.h>
42 #include <sys/stack.h>
49 typedef struct thread_walk
{
57 thread_walk_init(mdb_walk_state_t
*wsp
)
59 thread_walk_t
*twp
= mdb_alloc(sizeof (thread_walk_t
), UM_SLEEP
);
61 if (wsp
->walk_addr
== NULL
) {
62 if (mdb_readvar(&wsp
->walk_addr
, "allthreads") == -1) {
63 mdb_warn("failed to read 'allthreads'");
64 mdb_free(twp
, sizeof (thread_walk_t
));
68 twp
->tw_inproc
= FALSE
;
73 if (mdb_vread(&pr
, sizeof (proc_t
), wsp
->walk_addr
) == -1) {
74 mdb_warn("failed to read proc at %p", wsp
->walk_addr
);
75 mdb_free(twp
, sizeof (thread_walk_t
));
79 wsp
->walk_addr
= (uintptr_t)pr
.p_tlist
;
80 twp
->tw_inproc
= TRUE
;
83 twp
->tw_thread
= mdb_alloc(sizeof (kthread_t
), UM_SLEEP
);
84 twp
->tw_last
= wsp
->walk_addr
;
92 thread_walk_step(mdb_walk_state_t
*wsp
)
94 thread_walk_t
*twp
= (thread_walk_t
*)wsp
->walk_data
;
97 if (wsp
->walk_addr
== NULL
)
98 return (WALK_DONE
); /* Proc has 0 threads or allthreads = 0 */
100 if (twp
->tw_step
&& wsp
->walk_addr
== twp
->tw_last
)
101 return (WALK_DONE
); /* We've wrapped around */
103 if (mdb_vread(twp
->tw_thread
, sizeof (kthread_t
),
104 wsp
->walk_addr
) == -1) {
105 mdb_warn("failed to read thread at %p", wsp
->walk_addr
);
109 status
= wsp
->walk_callback(wsp
->walk_addr
, twp
->tw_thread
,
113 wsp
->walk_addr
= (uintptr_t)twp
->tw_thread
->t_forw
;
115 wsp
->walk_addr
= (uintptr_t)twp
->tw_thread
->t_next
;
122 thread_walk_fini(mdb_walk_state_t
*wsp
)
124 thread_walk_t
*twp
= (thread_walk_t
*)wsp
->walk_data
;
126 mdb_free(twp
->tw_thread
, sizeof (kthread_t
));
127 mdb_free(twp
, sizeof (thread_walk_t
));
131 deathrow_walk_init(mdb_walk_state_t
*wsp
)
133 if (mdb_layered_walk("thread_deathrow", wsp
) == -1) {
134 mdb_warn("couldn't walk 'thread_deathrow'");
138 if (mdb_layered_walk("lwp_deathrow", wsp
) == -1) {
139 mdb_warn("couldn't walk 'lwp_deathrow'");
147 deathrow_walk_step(mdb_walk_state_t
*wsp
)
150 uintptr_t addr
= wsp
->walk_addr
;
155 if (mdb_vread(&t
, sizeof (t
), addr
) == -1) {
156 mdb_warn("couldn't read deathrow thread at %p", addr
);
160 wsp
->walk_addr
= (uintptr_t)t
.t_forw
;
162 return (wsp
->walk_callback(addr
, &t
, wsp
->walk_cbdata
));
166 thread_deathrow_walk_init(mdb_walk_state_t
*wsp
)
168 if (mdb_readvar(&wsp
->walk_addr
, "thread_deathrow") == -1) {
169 mdb_warn("couldn't read symbol 'thread_deathrow'");
177 lwp_deathrow_walk_init(mdb_walk_state_t
*wsp
)
179 if (mdb_readvar(&wsp
->walk_addr
, "lwp_deathrow") == -1) {
180 mdb_warn("couldn't read symbol 'lwp_deathrow'");
188 typedef struct dispq_walk
{
195 cpu_dispq_walk_init(mdb_walk_state_t
*wsp
)
197 uintptr_t addr
= wsp
->walk_addr
;
204 mdb_warn("cpu_dispq walk needs a cpu_t address\n");
208 if (mdb_vread(&cpu
, sizeof (cpu_t
), addr
) == -1) {
209 mdb_warn("failed to read cpu_t at %p", addr
);
213 if (mdb_vread(&disp
, sizeof (disp_t
), (uintptr_t)cpu
.cpu_disp
) == -1) {
214 mdb_warn("failed to read disp_t at %p", cpu
.cpu_disp
);
218 if (mdb_vread(&dispq
, sizeof (dispq_t
),
219 (uintptr_t)disp
.disp_q
) == -1) {
220 mdb_warn("failed to read dispq_t at %p", disp
.disp_q
);
224 dw
= mdb_alloc(sizeof (dispq_walk_t
), UM_SLEEP
);
226 dw
->dw_npri
= disp
.disp_npri
;
227 dw
->dw_dispq
= (uintptr_t)disp
.disp_q
;
228 dw
->dw_last
= (uintptr_t)dispq
.dq_last
;
230 wsp
->walk_addr
= (uintptr_t)dispq
.dq_first
;
237 cpupart_dispq_walk_init(mdb_walk_state_t
*wsp
)
239 uintptr_t addr
= wsp
->walk_addr
;
245 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
249 if (mdb_vread(&cpupart
, sizeof (cpupart_t
), addr
) == -1) {
250 mdb_warn("failed to read cpupart_t at %p", addr
);
254 if (mdb_vread(&dispq
, sizeof (dispq_t
),
255 (uintptr_t)cpupart
.cp_kp_queue
.disp_q
) == -1) {
256 mdb_warn("failed to read dispq_t at %p",
257 cpupart
.cp_kp_queue
.disp_q
);
261 dw
= mdb_alloc(sizeof (dispq_walk_t
), UM_SLEEP
);
263 dw
->dw_npri
= cpupart
.cp_kp_queue
.disp_npri
;
264 dw
->dw_dispq
= (uintptr_t)cpupart
.cp_kp_queue
.disp_q
;
265 dw
->dw_last
= (uintptr_t)dispq
.dq_last
;
267 wsp
->walk_addr
= (uintptr_t)dispq
.dq_first
;
274 dispq_walk_step(mdb_walk_state_t
*wsp
)
276 uintptr_t addr
= wsp
->walk_addr
;
277 dispq_walk_t
*dw
= wsp
->walk_data
;
281 while (addr
== NULL
) {
282 if (--dw
->dw_npri
== 0)
285 dw
->dw_dispq
+= sizeof (dispq_t
);
287 if (mdb_vread(&dispq
, sizeof (dispq_t
), dw
->dw_dispq
) == -1) {
288 mdb_warn("failed to read dispq_t at %p", dw
->dw_dispq
);
292 dw
->dw_last
= (uintptr_t)dispq
.dq_last
;
293 addr
= (uintptr_t)dispq
.dq_first
;
296 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
297 mdb_warn("failed to read kthread_t at %p", addr
);
301 if (addr
== dw
->dw_last
)
302 wsp
->walk_addr
= NULL
;
304 wsp
->walk_addr
= (uintptr_t)t
.t_link
;
306 return (wsp
->walk_callback(addr
, &t
, wsp
->walk_cbdata
));
310 dispq_walk_fini(mdb_walk_state_t
*wsp
)
312 mdb_free(wsp
->walk_data
, sizeof (dispq_walk_t
));
315 struct thread_state
{
318 } thread_states
[] = {
320 { TS_SLEEP
, "sleep" },
322 { TS_ONPROC
, "onproc" },
324 { TS_STOPPED
, "stopped" },
327 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
330 thread_state_to_text(uint_t state
, char *out
, size_t out_sz
)
334 for (idx
= 0; idx
< NUM_THREAD_STATES
; idx
++) {
335 struct thread_state
*tsp
= &thread_states
[idx
];
336 if (tsp
->ts_state
== state
) {
337 mdb_snprintf(out
, out_sz
, "%s", tsp
->ts_name
);
341 mdb_snprintf(out
, out_sz
, "inval/%02x", state
);
345 thread_text_to_state(const char *state
, uint_t
*out
)
349 for (idx
= 0; idx
< NUM_THREAD_STATES
; idx
++) {
350 struct thread_state
*tsp
= &thread_states
[idx
];
351 if (strcasecmp(tsp
->ts_name
, state
) == 0) {
352 *out
= tsp
->ts_state
;
360 thread_walk_states(void (*cbfunc
)(uint_t
, const char *, void *), void *cbarg
)
364 for (idx
= 0; idx
< NUM_THREAD_STATES
; idx
++) {
365 struct thread_state
*tsp
= &thread_states
[idx
];
366 cbfunc(tsp
->ts_state
, tsp
->ts_name
, cbarg
);
372 #define TF_BLOCK 0x04
375 #define TF_MERGE 0x20
378 * Display a kthread_t.
379 * This is a little complicated, as there is a lot of information that
380 * the user could be interested in. The flags "ipbsd" are used to
381 * indicate which subset of the thread's members are to be displayed
382 * ('i' is the default). If multiple options are specified, multiple
383 * sets of data will be displayed in a vaguely readable format. If the
384 * 'm' option is specified, all the selected sets will be merged onto a
385 * single line for the benefit of those using wider-than-normal
386 * terminals. Having a generic mechanism for doing this would be
387 * really useful, but is a project best left to another day.
391 thread(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
395 uint_t fflag
= FALSE
;
400 * "Gracefully" handle printing a boatload of stuff to the
401 * screen. If we are not printing our first set of data, and
402 * we haven't been instructed to merge sets together, output a
403 * newline and indent such that the thread addresses form a
404 * column of their own.
409 } else if (!(oflags & TF_MERGE)) { \
410 mdb_printf("\n%?s", ""); \
413 if (!(flags
& DCMD_ADDRSPEC
)) {
414 if (mdb_walk_dcmd("thread", "thread", argc
, argv
) == -1) {
415 mdb_warn("can't walk threads");
421 if (mdb_getopts(argc
, argv
,
422 'f', MDB_OPT_SETBITS
, TRUE
, &fflag
,
423 'i', MDB_OPT_SETBITS
, TF_INTR
, &oflags
,
424 'p', MDB_OPT_SETBITS
, TF_PROC
, &oflags
,
425 'b', MDB_OPT_SETBITS
, TF_BLOCK
, &oflags
,
426 's', MDB_OPT_SETBITS
, TF_SIG
, &oflags
,
427 'd', MDB_OPT_SETBITS
, TF_DISP
, &oflags
,
428 'm', MDB_OPT_SETBITS
, TF_MERGE
, &oflags
, NULL
) != argc
)
432 * If no sets were specified, choose the 'i' set.
434 if (!(oflags
& ~TF_MERGE
))
438 oflags
= TF_INTR
| TF_DISP
| TF_MERGE
;
442 * Print the relevant headers; note use of SPACER().
444 if (DCMD_HDRSPEC(flags
)) {
446 mdb_printf("%<u>%?s%</u>", "ADDR");
449 if (oflags
& TF_PROC
) {
451 mdb_printf("%<u> %?s %?s %?s%</u>",
452 "PROC", "LWP", "CRED");
455 if (oflags
& TF_INTR
) {
457 mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>",
458 "STATE", "FLG", "PFLG",
459 "SFLG", "PRI", "EPRI", "PIL", "INTR");
462 if (oflags
& TF_BLOCK
) {
464 mdb_printf("%<u> %?s %?s %?s %11s%</u>",
465 "WCHAN", "TS", "PITS", "SOBJ OPS");
468 if (oflags
& TF_SIG
) {
470 mdb_printf("%<u> %?s %16s %16s%</u>",
471 "SIGQUEUE", "SIG PEND", "SIG HELD");
474 if (oflags
& TF_DISP
) {
476 mdb_printf("%<u> %?s %5s %2s %-6s%</u>",
477 "DISPTIME", "BOUND", "PR", "SWITCH");
482 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
483 mdb_warn("can't read kthread_t at %#lx", addr
);
487 if (fflag
&& (t
.t_state
== TS_FREE
))
491 mdb_printf("%0?lx", addr
);
493 /* process information */
494 if (oflags
& TF_PROC
) {
496 mdb_printf(" %?p %?p %?p", t
.t_procp
, t
.t_lwp
, t
.t_cred
);
499 /* priority/interrupt information */
500 if (oflags
& TF_INTR
) {
502 thread_state_to_text(t
.t_state
, stbuf
, sizeof (stbuf
));
503 if (t
.t_intr
== NULL
) {
504 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s",
505 stbuf
, t
.t_flag
, t
.t_proc_flag
, t
.t_schedflag
,
506 t
.t_pri
, t
.t_epri
, t
.t_pil
, "n/a");
508 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p",
509 stbuf
, t
.t_flag
, t
.t_proc_flag
, t
.t_schedflag
,
510 t
.t_pri
, t
.t_epri
, t
.t_pil
, t
.t_intr
);
514 /* blocking information */
515 if (oflags
& TF_BLOCK
) {
517 (void) mdb_snprintf(stbuf
, 20, "%a", t
.t_sobj_ops
);
519 mdb_printf(" %?p %?p %?p %11s",
520 t
.t_wchan
, t
.t_ts
, t
.t_prioinv
, stbuf
);
523 /* signal information */
524 if (oflags
& TF_SIG
) {
526 mdb_printf(" %?p %016llx %016llx",
527 t
.t_sigqueue
, t
.t_sig
, t
.t_hold
);
530 /* dispatcher stuff */
531 if (oflags
& TF_DISP
) {
533 mdb_printf(" %?lx %5d %2d ",
534 t
.t_disp_time
, t
.t_bind_cpu
, t
.t_preempt
);
535 if (t
.t_disp_time
!= 0)
537 (clock_t)mdb_get_lbolt() - t
.t_disp_time
);
539 mdb_printf("%-6s", "-");
553 "The flags -ipbsd control which information is displayed. When\n"
554 "combined, the fields are displayed on separate lines unless the\n"
555 "-m option is given.\n"
557 "\t-b\tprint blocked thread state\n"
558 "\t-d\tprint dispatcher state\n"
559 "\t-f\tignore freed threads\n"
560 "\t-i\tprint basic thread state (default)\n"
561 "\t-m\tdisplay results on a single line\n"
562 "\t-p\tprint process and lwp state\n"
563 "\t-s\tprint signal state\n");
567 * Return a string description of the thread, including the ID and the thread
570 * If ->t_name is NULL, and we're a system thread, we'll do a little more
571 * spelunking to find a useful string to return.
574 thread_getdesc(uintptr_t addr
, boolean_t include_comm
,
575 char *buf
, size_t bufsize
)
577 char name
[THREAD_NAME_MAX
] = "";
583 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
584 mdb_warn("failed to read kthread_t at %p", addr
);
591 if (mdb_vread(&tq
, sizeof (taskq_t
),
592 (uintptr_t)t
.t_taskq
) == -1)
593 tq
.tq_name
[0] = '\0';
595 if (t
.t_name
!= NULL
) {
596 if (mdb_readstr(buf
, bufsize
,
597 (uintptr_t)t
.t_name
) == -1) {
598 mdb_warn("error reading thread name");
600 } else if (tq
.tq_name
[0] != '\0') {
601 (void) mdb_snprintf(buf
, bufsize
, "tq:%s", tq
.tq_name
);
603 mdb_snprintf(buf
, bufsize
, "%a()", t
.t_startpc
);
606 return (buf
[0] == '\0' ? -1 : 0);
609 if (include_comm
&& mdb_vread(&p
, sizeof (proc_t
),
610 (uintptr_t)t
.t_procp
) == -1) {
611 mdb_warn("failed to read proc at %p", t
.t_procp
);
615 if (t
.t_name
!= NULL
) {
616 if (mdb_readstr(name
, sizeof (name
), (uintptr_t)t
.t_name
) == -1)
617 mdb_warn("error reading thread name");
620 * Just to be safe -- if mdb_readstr() succeeds, it always NUL
621 * terminates the output, but is unclear what it does on
622 * failure. In that case we attempt to show any partial content
623 * w/ the warning in case it's useful, but explicitly
624 * NUL-terminate to be safe.
626 buf
[bufsize
- 1] = '\0';
629 if (name
[0] != '\0') {
631 (void) mdb_snprintf(buf
, bufsize
, "%s/%u [%s]",
632 p
.p_user
.u_comm
, t
.t_tid
, name
);
634 (void) mdb_snprintf(buf
, bufsize
, "%u [%s]",
639 (void) mdb_snprintf(buf
, bufsize
, "%s/%u",
640 p
.p_user
.u_comm
, t
.t_tid
);
642 (void) mdb_snprintf(buf
, bufsize
, "%u", t
.t_tid
);
646 return (buf
[0] == '\0' ? -1 : 0);
650 * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
653 threadlist(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
657 uint_t verbose
= FALSE
;
658 uint_t notaskq
= FALSE
;
663 if (!(flags
& DCMD_ADDRSPEC
)) {
664 if (mdb_walk_dcmd("thread", "threadlist", argc
, argv
) == -1) {
665 mdb_warn("can't walk threads");
671 i
= mdb_getopts(argc
, argv
,
672 't', MDB_OPT_SETBITS
, TRUE
, ¬askq
,
673 'v', MDB_OPT_SETBITS
, TRUE
, &verbose
, NULL
);
676 if (i
!= argc
- 1 || !verbose
)
679 if (argv
[i
].a_type
== MDB_TYPE_IMMEDIATE
)
680 count
= (uint_t
)argv
[i
].a_un
.a_val
;
682 count
= (uint_t
)mdb_strtoull(argv
[i
].a_un
.a_str
);
685 if (DCMD_HDRSPEC(flags
)) {
687 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
688 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
690 mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n",
691 "ADDR", "PROC", "LWP", "CMD", "LWPID");
694 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
695 mdb_warn("failed to read kthread_t at %p", addr
);
699 if (notaskq
&& t
.t_taskq
!= NULL
)
702 if (t
.t_state
== TS_FREE
)
708 if (thread_getdesc(addr
, B_TRUE
, desc
, sizeof (desc
)) == -1)
711 mdb_printf("%0?p %?p %?p %s\n", addr
, t
.t_procp
, t
.t_lwp
, desc
);
715 mdb_printf("%0?p %?p %?p %3u %3d %?p\n",
716 addr
, t
.t_procp
, t
.t_lwp
, t
.t_cid
, t
.t_pri
, t
.t_wchan
);
720 mdb_printf("PC: %a\n", t
.t_pc
);
722 mdb_snprintf(cmd
, sizeof (cmd
), "<.$c%d", count
);
723 cmdarg
.a_type
= MDB_TYPE_STRING
;
724 cmdarg
.a_un
.a_str
= cmd
;
726 (void) mdb_call_dcmd("findstack", addr
, flags
, 1, &cmdarg
);
736 threadlist_help(void)
739 " -v print verbose output including C stack trace\n"
740 " -t skip threads belonging to a taskq\n"
741 " count print no more than count arguments (default 0)\n");
745 stk_compute_percent(caddr_t t_stk
, caddr_t t_stkbase
, caddr_t sp
)
750 if (t_stk
> t_stkbase
) {
751 /* stack grows down */
755 if (sp
< t_stkbase
) {
758 percent
= t_stk
- sp
+ 1;
759 s
= t_stk
- t_stkbase
+ 1;
765 if (sp
> t_stkbase
) {
768 percent
= sp
- t_stk
+ 1;
769 s
= t_stkbase
- t_stk
+ 1;
771 percent
= ((100 * percent
) / s
) + 1;
779 * Display kthread stack infos.
782 stackinfo(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
785 uint64_t *ptr
; /* pattern pointer */
786 caddr_t start
; /* kernel stack start */
787 caddr_t end
; /* kernel stack end */
788 caddr_t ustack
; /* userland copy of kernel stack */
789 size_t usize
; /* userland copy of kernel stack size */
790 caddr_t ustart
; /* userland copy of kernel stack, aligned start */
791 caddr_t uend
; /* userland copy of kernel stack, aligned end */
793 uint_t all
= FALSE
; /* don't show TS_FREE kthread by default */
794 uint_t history
= FALSE
;
796 unsigned int ukmem_stackinfo
;
797 uintptr_t allthreads
;
798 char tdesc
[128] = "";
801 if (mdb_getopts(argc
, argv
,
802 'a', MDB_OPT_SETBITS
, TRUE
, &all
,
803 'h', MDB_OPT_SETBITS
, TRUE
, &history
, NULL
) != argc
) {
807 /* walk all kthread if needed */
808 if ((history
== FALSE
) && !(flags
& DCMD_ADDRSPEC
)) {
809 if (mdb_walk_dcmd("thread", "stackinfo", argc
, argv
) == -1) {
810 mdb_warn("can't walk threads");
816 /* read 'kmem_stackinfo' */
817 if (mdb_readsym(&ukmem_stackinfo
, sizeof (ukmem_stackinfo
),
818 "kmem_stackinfo") == -1) {
819 mdb_warn("failed to read 'kmem_stackinfo'\n");
823 /* read 'allthreads' */
824 if (mdb_readsym(&allthreads
, sizeof (kthread_t
*),
825 "allthreads") == -1) {
826 mdb_warn("failed to read 'allthreads'\n");
830 if (history
== TRUE
) {
834 mdb_printf("Dead kthreads stack usage history:\n");
835 if (ukmem_stackinfo
== 0) {
836 mdb_printf("Tunable kmem_stackinfo is unset, history ");
837 mdb_printf("feature is off.\nUse ::help stackinfo ");
838 mdb_printf("for more details.\n");
842 mdb_printf("%<u>%?s%</u>", "THREAD");
843 mdb_printf(" %<u>%?s%</u>", "STACK");
844 mdb_printf("%<u>%s%</u>", " SIZE MAX LWP");
846 usize
= KMEM_STKINFO_LOG_SIZE
* sizeof (kmem_stkinfo_t
);
847 log
= (kmem_stkinfo_t
*)mdb_alloc(usize
, UM_SLEEP
);
848 if (mdb_readsym(&kaddr
, sizeof (kaddr
),
849 "kmem_stkinfo_log") == -1) {
850 mdb_free((void *)log
, usize
);
851 mdb_warn("failed to read 'kmem_stkinfo_log'\n");
855 mdb_free((void *)log
, usize
);
858 if (mdb_vread(log
, usize
, kaddr
) == -1) {
859 mdb_free((void *)log
, usize
);
860 mdb_warn("failed to read %p\n", kaddr
);
863 for (i
= 0; i
< KMEM_STKINFO_LOG_SIZE
; i
++) {
864 if (log
[i
].kthread
== NULL
) {
868 (void) thread_getdesc((uintptr_t)log
[i
].kthread
,
869 B_TRUE
, tdesc
, sizeof (tdesc
));
871 mdb_printf("%0?p %0?p %6x %3d%% %s\n",
874 (uint_t
)log
[i
].stksz
,
875 (int)log
[i
].percent
, tdesc
);
877 mdb_free((void *)log
, usize
);
882 if (DCMD_HDRSPEC(flags
)) {
883 if (ukmem_stackinfo
== 0) {
884 mdb_printf("Tunable kmem_stackinfo is unset, ");
885 mdb_printf("MAX value is not available.\n");
886 mdb_printf("Use ::help stackinfo for more details.\n");
888 mdb_printf("%<u>%?s%</u>", "THREAD");
889 mdb_printf(" %<u>%?s%</u>", "STACK");
890 mdb_printf("%<u>%s%</u>", " SIZE CUR MAX LWP");
895 if (mdb_vread(&t
, sizeof (kthread_t
), addr
) == -1) {
896 mdb_warn("can't read kthread_t at %#lx\n", addr
);
900 if (t
.t_state
== TS_FREE
&& all
== FALSE
) {
905 * Stack grows up or down, see thread_create(),
906 * compute stack memory aera start and end (start < end).
908 if (t
.t_stk
> t
.t_stkbase
) {
909 /* stack grows down */
918 /* display stack info */
919 mdb_printf("%0?p %0?p", addr
, start
);
921 /* (end - start), kernel stack size as found in kthread_t */
922 if ((end
<= start
) || ((end
- start
) > (1024 * 1024))) {
923 /* negative or stack size > 1 meg, assume bogus */
924 mdb_warn(" t_stk/t_stkbase problem\n");
928 /* display stack size */
929 mdb_printf(" %6x", end
- start
);
931 /* display current stack usage */
932 percent
= stk_compute_percent(t
.t_stk
, t
.t_stkbase
,
933 (caddr_t
)t
.t_sp
+ STACK_BIAS
);
935 mdb_printf(" %3d%%", percent
);
938 (void) thread_getdesc(addr
, B_TRUE
, tdesc
, sizeof (tdesc
));
940 if (ukmem_stackinfo
== 0) {
941 mdb_printf(" n/a %s\n", tdesc
);
945 if ((((uintptr_t)start
) & 0x7) != 0) {
946 start
= (caddr_t
)((((uintptr_t)start
) & (~0x7)) + 8);
948 end
= (caddr_t
)(((uintptr_t)end
) & (~0x7));
949 /* size to scan in userland copy of kernel stack */
950 usize
= end
- start
; /* is a multiple of 8 bytes */
953 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes
954 * alignement for ustart and uend, in boundaries.
956 ustart
= ustack
= (caddr_t
)mdb_alloc(usize
+ 8, UM_SLEEP
);
957 if ((((uintptr_t)ustart
) & 0x7) != 0) {
958 ustart
= (caddr_t
)((((uintptr_t)ustart
) & (~0x7)) + 8);
960 uend
= ustart
+ usize
;
962 /* read the kernel stack */
963 if (mdb_vread(ustart
, usize
, (uintptr_t)start
) != usize
) {
964 mdb_free((void *)ustack
, usize
+ 8);
966 mdb_warn("couldn't read entire stack\n");
971 if (t
.t_stk
> t
.t_stkbase
) {
972 /* stack grows down */
973 #if defined(__i386) || defined(__amd64)
975 * 6 longs are pushed on stack, see thread_load(). Skip
976 * them, so if kthread has never run, percent is zero.
977 * 8 bytes alignement is preserved for a 32 bit kernel,
978 * 6 x 4 = 24, 24 is a multiple of 8.
980 uend
-= (6 * sizeof (long));
982 ptr
= (uint64_t *)((void *)ustart
);
983 while (ptr
< (uint64_t *)((void *)uend
)) {
984 if (*ptr
!= KMEM_STKINFO_PATTERN
) {
985 percent
= stk_compute_percent(uend
,
986 ustart
, (caddr_t
)ptr
);
993 ptr
= (uint64_t *)((void *)uend
);
995 while (ptr
>= (uint64_t *)((void *)ustart
)) {
996 if (*ptr
!= KMEM_STKINFO_PATTERN
) {
997 percent
= stk_compute_percent(ustart
,
1005 /* thread 't0' stack is not created by thread_create() */
1006 if (addr
== allthreads
) {
1010 mdb_printf(" %3d%%", percent
);
1015 mdb_printf(" %s\n", tdesc
);
1017 mdb_free((void *)ustack
, usize
+ 8);
1022 stackinfo_help(void)
1025 "Shows kernel stacks real utilization, if /etc/system "
1026 "kmem_stackinfo tunable\n");
1028 "(an unsigned integer) is non zero at kthread creation time. ");
1029 mdb_printf("For example:\n");
1031 " THREAD STACK SIZE CUR MAX LWP\n");
1033 "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n");
1035 "The stack size utilization for this kthread is at 4%%"
1036 " of its maximum size,\n");
1038 "but has already used up to 43%%, stack size is 4f00 bytes.\n");
1040 "MAX value can be shown as n/a (not available):\n");
1042 " - for the very first kthread (sched/1)\n");
1044 " - kmem_stackinfo was zero at kthread creation time\n");
1046 " - kthread has not yet run\n");
1048 mdb_printf("Options:\n");
1050 "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
1052 "-h shows history, dead kthreads that used their "
1053 "kernel stack the most\n");
1055 "\nSee illumos Modular Debugger Guide for detailed usage.\n");