8158 Want named threads API
[unleashed.git] / usr / src / cmd / mdb / common / modules / genunix / thread.c
blob410f9973d8d6ea8320903772646dcac4194a5d81
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.
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>
36 #include <sys/lwp.h>
37 #include <sys/proc.h>
38 #include <sys/cpuvar.h>
39 #include <sys/cpupart.h>
40 #include <sys/disp.h>
41 #include <sys/taskq_impl.h>
42 #include <sys/stack.h>
43 #include "thread.h"
45 #ifndef STACK_BIAS
46 #define STACK_BIAS 0
47 #endif
49 typedef struct thread_walk {
50 kthread_t *tw_thread;
51 uintptr_t tw_last;
52 uint_t tw_inproc;
53 uint_t tw_step;
54 } thread_walk_t;
56 int
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));
65 return (WALK_ERR);
68 twp->tw_inproc = FALSE;
70 } else {
71 proc_t pr;
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));
76 return (WALK_ERR);
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;
85 twp->tw_step = FALSE;
87 wsp->walk_data = twp;
88 return (WALK_NEXT);
91 int
92 thread_walk_step(mdb_walk_state_t *wsp)
94 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
95 int status;
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);
106 return (WALK_DONE);
109 status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
110 wsp->walk_cbdata);
112 if (twp->tw_inproc)
113 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw;
114 else
115 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next;
117 twp->tw_step = TRUE;
118 return (status);
121 void
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'");
135 return (WALK_ERR);
138 if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
139 mdb_warn("couldn't walk 'lwp_deathrow'");
140 return (WALK_ERR);
143 return (WALK_NEXT);
147 deathrow_walk_step(mdb_walk_state_t *wsp)
149 kthread_t t;
150 uintptr_t addr = wsp->walk_addr;
152 if (addr == NULL)
153 return (WALK_DONE);
155 if (mdb_vread(&t, sizeof (t), addr) == -1) {
156 mdb_warn("couldn't read deathrow thread at %p", addr);
157 return (WALK_ERR);
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'");
170 return (WALK_ERR);
173 return (WALK_NEXT);
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'");
181 return (WALK_ERR);
184 return (WALK_NEXT);
188 typedef struct dispq_walk {
189 int dw_npri;
190 uintptr_t dw_dispq;
191 uintptr_t dw_last;
192 } dispq_walk_t;
195 cpu_dispq_walk_init(mdb_walk_state_t *wsp)
197 uintptr_t addr = wsp->walk_addr;
198 dispq_walk_t *dw;
199 cpu_t cpu;
200 dispq_t dispq;
201 disp_t disp;
203 if (addr == NULL) {
204 mdb_warn("cpu_dispq walk needs a cpu_t address\n");
205 return (WALK_ERR);
208 if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
209 mdb_warn("failed to read cpu_t at %p", addr);
210 return (WALK_ERR);
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);
215 return (WALK_ERR);
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);
221 return (WALK_ERR);
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;
231 wsp->walk_data = dw;
233 return (WALK_NEXT);
237 cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
239 uintptr_t addr = wsp->walk_addr;
240 dispq_walk_t *dw;
241 cpupart_t cpupart;
242 dispq_t dispq;
244 if (addr == NULL) {
245 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
246 return (WALK_ERR);
249 if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) {
250 mdb_warn("failed to read cpupart_t at %p", addr);
251 return (WALK_ERR);
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);
258 return (WALK_ERR);
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;
268 wsp->walk_data = dw;
270 return (WALK_NEXT);
274 dispq_walk_step(mdb_walk_state_t *wsp)
276 uintptr_t addr = wsp->walk_addr;
277 dispq_walk_t *dw = wsp->walk_data;
278 dispq_t dispq;
279 kthread_t t;
281 while (addr == NULL) {
282 if (--dw->dw_npri == 0)
283 return (WALK_DONE);
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);
289 return (WALK_ERR);
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);
298 return (WALK_ERR);
301 if (addr == dw->dw_last)
302 wsp->walk_addr = NULL;
303 else
304 wsp->walk_addr = (uintptr_t)t.t_link;
306 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
309 void
310 dispq_walk_fini(mdb_walk_state_t *wsp)
312 mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
315 struct thread_state {
316 uint_t ts_state;
317 const char *ts_name;
318 } thread_states[] = {
319 { TS_FREE, "free" },
320 { TS_SLEEP, "sleep" },
321 { TS_RUN, "run" },
322 { TS_ONPROC, "onproc" },
323 { TS_ZOMB, "zomb" },
324 { TS_STOPPED, "stopped" },
325 { TS_WAIT, "wait" }
327 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
329 void
330 thread_state_to_text(uint_t state, char *out, size_t out_sz)
332 int idx;
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);
338 return;
341 mdb_snprintf(out, out_sz, "inval/%02x", state);
345 thread_text_to_state(const char *state, uint_t *out)
347 int idx;
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;
353 return (0);
356 return (-1);
359 void
360 thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg)
362 int idx;
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);
370 #define TF_INTR 0x01
371 #define TF_PROC 0x02
372 #define TF_BLOCK 0x04
373 #define TF_SIG 0x08
374 #define TF_DISP 0x10
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)
393 kthread_t t;
394 uint_t oflags = 0;
395 uint_t fflag = FALSE;
396 int first;
397 char stbuf[20];
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.
406 #define SPACER() \
407 if (first) { \
408 first = FALSE; \
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");
416 return (DCMD_ERR);
418 return (DCMD_OK);
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)
429 return (DCMD_USAGE);
432 * If no sets were specified, choose the 'i' set.
434 if (!(oflags & ~TF_MERGE))
435 #ifdef _LP64
436 oflags = TF_INTR;
437 #else
438 oflags = TF_INTR | TF_DISP | TF_MERGE;
439 #endif
442 * Print the relevant headers; note use of SPACER().
444 if (DCMD_HDRSPEC(flags)) {
445 first = TRUE;
446 mdb_printf("%<u>%?s%</u>", "ADDR");
447 mdb_flush();
449 if (oflags & TF_PROC) {
450 SPACER();
451 mdb_printf("%<u> %?s %?s %?s%</u>",
452 "PROC", "LWP", "CRED");
455 if (oflags & TF_INTR) {
456 SPACER();
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) {
463 SPACER();
464 mdb_printf("%<u> %?s %?s %?s %11s%</u>",
465 "WCHAN", "TS", "PITS", "SOBJ OPS");
468 if (oflags & TF_SIG) {
469 SPACER();
470 mdb_printf("%<u> %?s %16s %16s%</u>",
471 "SIGQUEUE", "SIG PEND", "SIG HELD");
474 if (oflags & TF_DISP) {
475 SPACER();
476 mdb_printf("%<u> %?s %5s %2s %-6s%</u>",
477 "DISPTIME", "BOUND", "PR", "SWITCH");
479 mdb_printf("\n");
482 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
483 mdb_warn("can't read kthread_t at %#lx", addr);
484 return (DCMD_ERR);
487 if (fflag && (t.t_state == TS_FREE))
488 return (DCMD_OK);
490 first = TRUE;
491 mdb_printf("%0?lx", addr);
493 /* process information */
494 if (oflags & TF_PROC) {
495 SPACER();
496 mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred);
499 /* priority/interrupt information */
500 if (oflags & TF_INTR) {
501 SPACER();
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");
507 } else {
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) {
516 SPACER();
517 (void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops);
518 stbuf[11] = '\0';
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) {
525 SPACER();
526 mdb_printf(" %?p %016llx %016llx",
527 t.t_sigqueue, t.t_sig, t.t_hold);
530 /* dispatcher stuff */
531 if (oflags & TF_DISP) {
532 SPACER();
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)
536 mdb_printf("t-%-4d",
537 (clock_t)mdb_get_lbolt() - t.t_disp_time);
538 else
539 mdb_printf("%-6s", "-");
542 mdb_printf("\n");
544 #undef SPACER
546 return (DCMD_OK);
549 void
550 thread_help(void)
552 mdb_printf(
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"
556 "\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
568 * name.
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] = "";
578 kthread_t t;
579 proc_t p;
581 bzero(buf, bufsize);
583 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
584 mdb_warn("failed to read kthread_t at %p", addr);
585 return (-1);
588 if (t.t_tid == 0) {
589 taskq_t tq;
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);
602 } else {
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);
612 return (-1);
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') {
630 if (include_comm) {
631 (void) mdb_snprintf(buf, bufsize, "%s/%u [%s]",
632 p.p_user.u_comm, t.t_tid, name);
633 } else {
634 (void) mdb_snprintf(buf, bufsize, "%u [%s]",
635 t.t_tid, name);
637 } else {
638 if (include_comm) {
639 (void) mdb_snprintf(buf, bufsize, "%s/%u",
640 p.p_user.u_comm, t.t_tid);
641 } else {
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)
655 int i;
656 uint_t count = 0;
657 uint_t verbose = FALSE;
658 uint_t notaskq = FALSE;
659 kthread_t t;
660 char cmd[80];
661 mdb_arg_t cmdarg;
663 if (!(flags & DCMD_ADDRSPEC)) {
664 if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) {
665 mdb_warn("can't walk threads");
666 return (DCMD_ERR);
668 return (DCMD_OK);
671 i = mdb_getopts(argc, argv,
672 't', MDB_OPT_SETBITS, TRUE, &notaskq,
673 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
675 if (i != argc) {
676 if (i != argc - 1 || !verbose)
677 return (DCMD_USAGE);
679 if (argv[i].a_type == MDB_TYPE_IMMEDIATE)
680 count = (uint_t)argv[i].a_un.a_val;
681 else
682 count = (uint_t)mdb_strtoull(argv[i].a_un.a_str);
685 if (DCMD_HDRSPEC(flags)) {
686 if (verbose)
687 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
688 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
689 else
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);
696 return (DCMD_ERR);
699 if (notaskq && t.t_taskq != NULL)
700 return (DCMD_OK);
702 if (t.t_state == TS_FREE)
703 return (DCMD_OK);
705 if (!verbose) {
706 char desc[128];
708 if (thread_getdesc(addr, B_TRUE, desc, sizeof (desc)) == -1)
709 return (DCMD_ERR);
711 mdb_printf("%0?p %?p %?p %s\n", addr, t.t_procp, t.t_lwp, desc);
712 return (DCMD_OK);
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);
718 mdb_inc_indent(2);
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);
728 mdb_dec_indent(2);
730 mdb_printf("\n");
732 return (DCMD_OK);
735 void
736 threadlist_help(void)
738 mdb_printf(
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");
744 static size_t
745 stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
747 size_t percent;
748 size_t s;
750 if (t_stk > t_stkbase) {
751 /* stack grows down */
752 if (sp > t_stk) {
753 return (0);
755 if (sp < t_stkbase) {
756 return (100);
758 percent = t_stk - sp + 1;
759 s = t_stk - t_stkbase + 1;
760 } else {
761 /* stack grows up */
762 if (sp < t_stk) {
763 return (0);
765 if (sp > t_stkbase) {
766 return (100);
768 percent = sp - t_stk + 1;
769 s = t_stkbase - t_stk + 1;
771 percent = ((100 * percent) / s) + 1;
772 if (percent > 100) {
773 percent = 100;
775 return (percent);
779 * Display kthread stack infos.
782 stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
784 kthread_t t;
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 */
792 size_t percent = 0;
793 uint_t all = FALSE; /* don't show TS_FREE kthread by default */
794 uint_t history = FALSE;
795 int i = 0;
796 unsigned int ukmem_stackinfo;
797 uintptr_t allthreads;
798 char tdesc[128] = "";
800 /* handle options */
801 if (mdb_getopts(argc, argv,
802 'a', MDB_OPT_SETBITS, TRUE, &all,
803 'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) {
804 return (DCMD_USAGE);
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");
811 return (DCMD_ERR);
813 return (DCMD_OK);
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");
820 ukmem_stackinfo = 0;
823 /* read 'allthreads' */
824 if (mdb_readsym(&allthreads, sizeof (kthread_t *),
825 "allthreads") == -1) {
826 mdb_warn("failed to read 'allthreads'\n");
827 allthreads = NULL;
830 if (history == TRUE) {
831 kmem_stkinfo_t *log;
832 uintptr_t kaddr;
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");
839 return (DCMD_OK);
842 mdb_printf("%<u>%?s%</u>", "THREAD");
843 mdb_printf(" %<u>%?s%</u>", "STACK");
844 mdb_printf("%<u>%s%</u>", " SIZE MAX LWP");
845 mdb_printf("\n");
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");
852 return (DCMD_ERR);
854 if (kaddr == NULL) {
855 mdb_free((void *)log, usize);
856 return (DCMD_OK);
858 if (mdb_vread(log, usize, kaddr) == -1) {
859 mdb_free((void *)log, usize);
860 mdb_warn("failed to read %p\n", kaddr);
861 return (DCMD_ERR);
863 for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
864 if (log[i].kthread == NULL) {
865 continue;
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",
872 log[i].kthread,
873 log[i].start,
874 (uint_t)log[i].stksz,
875 (int)log[i].percent, tdesc);
877 mdb_free((void *)log, usize);
878 return (DCMD_OK);
881 /* display header */
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");
891 mdb_printf("\n");
894 /* read kthread */
895 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
896 mdb_warn("can't read kthread_t at %#lx\n", addr);
897 return (DCMD_ERR);
900 if (t.t_state == TS_FREE && all == FALSE) {
901 return (DCMD_OK);
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 */
910 start = t.t_stkbase;
911 end = t.t_stk;
912 } else {
913 /* stack grows up */
914 start = t.t_stk;
915 end = t.t_stkbase;
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");
925 return (DCMD_ERR);
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);
936 percent = 0;
938 (void) thread_getdesc(addr, B_TRUE, tdesc, sizeof (tdesc));
940 if (ukmem_stackinfo == 0) {
941 mdb_printf(" n/a %s\n", tdesc);
942 return (DCMD_OK);
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);
965 mdb_printf("\n");
966 mdb_warn("couldn't read entire stack\n");
967 return (DCMD_ERR);
970 /* scan the stack */
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));
981 #endif
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);
987 break;
989 ptr++;
991 } else {
992 /* stack grows up */
993 ptr = (uint64_t *)((void *)uend);
994 ptr--;
995 while (ptr >= (uint64_t *)((void *)ustart)) {
996 if (*ptr != KMEM_STKINFO_PATTERN) {
997 percent = stk_compute_percent(ustart,
998 uend, (caddr_t)ptr);
999 break;
1001 ptr--;
1005 /* thread 't0' stack is not created by thread_create() */
1006 if (addr == allthreads) {
1007 percent = 0;
1009 if (percent != 0) {
1010 mdb_printf(" %3d%%", percent);
1011 } else {
1012 mdb_printf(" n/a");
1015 mdb_printf(" %s\n", tdesc);
1017 mdb_free((void *)ustack, usize + 8);
1018 return (DCMD_OK);
1021 void
1022 stackinfo_help(void)
1024 mdb_printf(
1025 "Shows kernel stacks real utilization, if /etc/system "
1026 "kmem_stackinfo tunable\n");
1027 mdb_printf(
1028 "(an unsigned integer) is non zero at kthread creation time. ");
1029 mdb_printf("For example:\n");
1030 mdb_printf(
1031 " THREAD STACK SIZE CUR MAX LWP\n");
1032 mdb_printf(
1033 "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n");
1034 mdb_printf(
1035 "The stack size utilization for this kthread is at 4%%"
1036 " of its maximum size,\n");
1037 mdb_printf(
1038 "but has already used up to 43%%, stack size is 4f00 bytes.\n");
1039 mdb_printf(
1040 "MAX value can be shown as n/a (not available):\n");
1041 mdb_printf(
1042 " - for the very first kthread (sched/1)\n");
1043 mdb_printf(
1044 " - kmem_stackinfo was zero at kthread creation time\n");
1045 mdb_printf(
1046 " - kthread has not yet run\n");
1047 mdb_printf("\n");
1048 mdb_printf("Options:\n");
1049 mdb_printf(
1050 "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
1051 mdb_printf(
1052 "-h shows history, dead kthreads that used their "
1053 "kernel stack the most\n");
1054 mdb_printf(
1055 "\nSee illumos Modular Debugger Guide for detailed usage.\n");
1056 mdb_flush();