1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 27 * Copyright (c) 2018, Joyent, Inc. 28 * Copyright 2025 Oxide Computer Company 29 */ 30 31 32 #include <mdb/mdb_modapi.h> 33 #include <mdb/mdb_ks.h> 34 #include <mdb/mdb_ctf.h> 35 #include <sys/types.h> 36 #include <sys/thread.h> 37 #include <sys/lwp.h> 38 #include <sys/proc.h> 39 #include <sys/cpuvar.h> 40 #include <sys/cpupart.h> 41 #include <sys/disp.h> 42 #include <sys/taskq_impl.h> 43 #include <sys/stack.h> 44 #include "thread.h" 45 46 #ifndef STACK_BIAS 47 #define STACK_BIAS 0 48 #endif 49 50 typedef struct thread_walk { 51 kthread_t *tw_thread; 52 uintptr_t tw_last; 53 uint_t tw_inproc; 54 uint_t tw_step; 55 } thread_walk_t; 56 57 int 58 thread_walk_init(mdb_walk_state_t *wsp) 59 { 60 thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP); 61 62 if (wsp->walk_addr == 0) { 63 if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) { 64 mdb_warn("failed to read 'allthreads'"); 65 mdb_free(twp, sizeof (thread_walk_t)); 66 return (WALK_ERR); 67 } 68 69 twp->tw_inproc = FALSE; 70 71 } else { 72 proc_t pr; 73 74 if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) { 75 mdb_warn("failed to read proc at %p", wsp->walk_addr); 76 mdb_free(twp, sizeof (thread_walk_t)); 77 return (WALK_ERR); 78 } 79 80 wsp->walk_addr = (uintptr_t)pr.p_tlist; 81 twp->tw_inproc = TRUE; 82 } 83 84 twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP); 85 twp->tw_last = wsp->walk_addr; 86 twp->tw_step = FALSE; 87 88 wsp->walk_data = twp; 89 return (WALK_NEXT); 90 } 91 92 int 93 thread_walk_step(mdb_walk_state_t *wsp) 94 { 95 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 96 int status; 97 98 if (wsp->walk_addr == 0) 99 return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */ 100 101 if (twp->tw_step && wsp->walk_addr == twp->tw_last) 102 return (WALK_DONE); /* We've wrapped around */ 103 104 if (mdb_vread(twp->tw_thread, sizeof (kthread_t), 105 wsp->walk_addr) == -1) { 106 mdb_warn("failed to read thread at %p", wsp->walk_addr); 107 return (WALK_DONE); 108 } 109 110 status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread, 111 wsp->walk_cbdata); 112 113 if (twp->tw_inproc) 114 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw; 115 else 116 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next; 117 118 twp->tw_step = TRUE; 119 return (status); 120 } 121 122 void 123 thread_walk_fini(mdb_walk_state_t *wsp) 124 { 125 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 126 127 mdb_free(twp->tw_thread, sizeof (kthread_t)); 128 mdb_free(twp, sizeof (thread_walk_t)); 129 } 130 131 int 132 deathrow_walk_init(mdb_walk_state_t *wsp) 133 { 134 if (mdb_layered_walk("thread_deathrow", wsp) == -1) { 135 mdb_warn("couldn't walk 'thread_deathrow'"); 136 return (WALK_ERR); 137 } 138 139 if (mdb_layered_walk("lwp_deathrow", wsp) == -1) { 140 mdb_warn("couldn't walk 'lwp_deathrow'"); 141 return (WALK_ERR); 142 } 143 144 return (WALK_NEXT); 145 } 146 147 int 148 deathrow_walk_step(mdb_walk_state_t *wsp) 149 { 150 kthread_t t; 151 uintptr_t addr = wsp->walk_addr; 152 153 if (addr == 0) 154 return (WALK_DONE); 155 156 if (mdb_vread(&t, sizeof (t), addr) == -1) { 157 mdb_warn("couldn't read deathrow thread at %p", addr); 158 return (WALK_ERR); 159 } 160 161 wsp->walk_addr = (uintptr_t)t.t_forw; 162 163 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 164 } 165 166 int 167 thread_deathrow_walk_init(mdb_walk_state_t *wsp) 168 { 169 if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) { 170 mdb_warn("couldn't read symbol 'thread_deathrow'"); 171 return (WALK_ERR); 172 } 173 174 return (WALK_NEXT); 175 } 176 177 int 178 lwp_deathrow_walk_init(mdb_walk_state_t *wsp) 179 { 180 if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) { 181 mdb_warn("couldn't read symbol 'lwp_deathrow'"); 182 return (WALK_ERR); 183 } 184 185 return (WALK_NEXT); 186 } 187 188 189 typedef struct dispq_walk { 190 int dw_npri; 191 uintptr_t dw_dispq; 192 uintptr_t dw_last; 193 } dispq_walk_t; 194 195 int 196 cpu_dispq_walk_init(mdb_walk_state_t *wsp) 197 { 198 uintptr_t addr = wsp->walk_addr; 199 dispq_walk_t *dw; 200 cpu_t cpu; 201 dispq_t dispq; 202 disp_t disp; 203 204 if (addr == 0) { 205 mdb_warn("cpu_dispq walk needs a cpu_t address\n"); 206 return (WALK_ERR); 207 } 208 209 if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) { 210 mdb_warn("failed to read cpu_t at %p", addr); 211 return (WALK_ERR); 212 } 213 214 if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) { 215 mdb_warn("failed to read disp_t at %p", cpu.cpu_disp); 216 return (WALK_ERR); 217 } 218 219 if (mdb_vread(&dispq, sizeof (dispq_t), 220 (uintptr_t)disp.disp_q) == -1) { 221 mdb_warn("failed to read dispq_t at %p", disp.disp_q); 222 return (WALK_ERR); 223 } 224 225 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 226 227 dw->dw_npri = disp.disp_npri; 228 dw->dw_dispq = (uintptr_t)disp.disp_q; 229 dw->dw_last = (uintptr_t)dispq.dq_last; 230 231 wsp->walk_addr = (uintptr_t)dispq.dq_first; 232 wsp->walk_data = dw; 233 234 return (WALK_NEXT); 235 } 236 237 int 238 cpupart_dispq_walk_init(mdb_walk_state_t *wsp) 239 { 240 uintptr_t addr = wsp->walk_addr; 241 dispq_walk_t *dw; 242 cpupart_t cpupart; 243 dispq_t dispq; 244 245 if (addr == 0) { 246 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n"); 247 return (WALK_ERR); 248 } 249 250 if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) { 251 mdb_warn("failed to read cpupart_t at %p", addr); 252 return (WALK_ERR); 253 } 254 255 if (mdb_vread(&dispq, sizeof (dispq_t), 256 (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) { 257 mdb_warn("failed to read dispq_t at %p", 258 cpupart.cp_kp_queue.disp_q); 259 return (WALK_ERR); 260 } 261 262 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 263 264 dw->dw_npri = cpupart.cp_kp_queue.disp_npri; 265 dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q; 266 dw->dw_last = (uintptr_t)dispq.dq_last; 267 268 wsp->walk_addr = (uintptr_t)dispq.dq_first; 269 wsp->walk_data = dw; 270 271 return (WALK_NEXT); 272 } 273 274 int 275 dispq_walk_step(mdb_walk_state_t *wsp) 276 { 277 uintptr_t addr = wsp->walk_addr; 278 dispq_walk_t *dw = wsp->walk_data; 279 dispq_t dispq; 280 kthread_t t; 281 282 while (addr == 0) { 283 if (--dw->dw_npri == 0) 284 return (WALK_DONE); 285 286 dw->dw_dispq += sizeof (dispq_t); 287 288 if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) { 289 mdb_warn("failed to read dispq_t at %p", dw->dw_dispq); 290 return (WALK_ERR); 291 } 292 293 dw->dw_last = (uintptr_t)dispq.dq_last; 294 addr = (uintptr_t)dispq.dq_first; 295 } 296 297 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 298 mdb_warn("failed to read kthread_t at %p", addr); 299 return (WALK_ERR); 300 } 301 302 if (addr == dw->dw_last) 303 wsp->walk_addr = 0; 304 else 305 wsp->walk_addr = (uintptr_t)t.t_link; 306 307 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 308 } 309 310 void 311 dispq_walk_fini(mdb_walk_state_t *wsp) 312 { 313 mdb_free(wsp->walk_data, sizeof (dispq_walk_t)); 314 } 315 316 struct thread_state { 317 uint_t ts_state; 318 const char *ts_name; 319 } thread_states[] = { 320 { TS_FREE, "free" }, 321 { TS_SLEEP, "sleep" }, 322 { TS_RUN, "run" }, 323 { TS_ONPROC, "onproc" }, 324 { TS_ZOMB, "zomb" }, 325 { TS_STOPPED, "stopped" }, 326 { TS_WAIT, "wait" } 327 }; 328 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states)) 329 330 void 331 thread_state_to_text(uint_t state, char *out, size_t out_sz) 332 { 333 int idx; 334 335 for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 336 struct thread_state *tsp = &thread_states[idx]; 337 if (tsp->ts_state == state) { 338 mdb_snprintf(out, out_sz, "%s", tsp->ts_name); 339 return; 340 } 341 } 342 mdb_snprintf(out, out_sz, "inval/%02x", state); 343 } 344 345 int 346 thread_text_to_state(const char *state, uint_t *out) 347 { 348 int idx; 349 350 for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 351 struct thread_state *tsp = &thread_states[idx]; 352 if (strcasecmp(tsp->ts_name, state) == 0) { 353 *out = tsp->ts_state; 354 return (0); 355 } 356 } 357 return (-1); 358 } 359 360 void 361 thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg) 362 { 363 int idx; 364 365 for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 366 struct thread_state *tsp = &thread_states[idx]; 367 cbfunc(tsp->ts_state, tsp->ts_name, cbarg); 368 } 369 } 370 371 #define TF_INTR 0x01 372 #define TF_PROC 0x02 373 #define TF_BLOCK 0x04 374 #define TF_SIG 0x08 375 #define TF_DISP 0x10 376 #define TF_MERGE 0x20 377 378 /* 379 * Display a kthread_t. 380 * This is a little complicated, as there is a lot of information that 381 * the user could be interested in. The flags "ipbsd" are used to 382 * indicate which subset of the thread's members are to be displayed 383 * ('i' is the default). If multiple options are specified, multiple 384 * sets of data will be displayed in a vaguely readable format. If the 385 * 'm' option is specified, all the selected sets will be merged onto a 386 * single line for the benefit of those using wider-than-normal 387 * terminals. Having a generic mechanism for doing this would be 388 * really useful, but is a project best left to another day. 389 */ 390 391 int 392 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 393 { 394 kthread_t t; 395 uint_t oflags = 0; 396 uint_t fflag = FALSE; 397 int first; 398 char stbuf[20]; 399 400 /* 401 * "Gracefully" handle printing a boatload of stuff to the 402 * screen. If we are not printing our first set of data, and 403 * we haven't been instructed to merge sets together, output a 404 * newline and indent such that the thread addresses form a 405 * column of their own. 406 */ 407 #define SPACER() \ 408 if (first) { \ 409 first = FALSE; \ 410 } else if (!(oflags & TF_MERGE)) { \ 411 mdb_printf("\n%?s", ""); \ 412 } 413 414 if (!(flags & DCMD_ADDRSPEC)) { 415 if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) { 416 mdb_warn("can't walk threads"); 417 return (DCMD_ERR); 418 } 419 return (DCMD_OK); 420 } 421 422 if (mdb_getopts(argc, argv, 423 'f', MDB_OPT_SETBITS, TRUE, &fflag, 424 'i', MDB_OPT_SETBITS, TF_INTR, &oflags, 425 'p', MDB_OPT_SETBITS, TF_PROC, &oflags, 426 'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags, 427 's', MDB_OPT_SETBITS, TF_SIG, &oflags, 428 'd', MDB_OPT_SETBITS, TF_DISP, &oflags, 429 'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc) 430 return (DCMD_USAGE); 431 432 /* 433 * If no sets were specified, choose the 'i' set. 434 */ 435 if (!(oflags & ~TF_MERGE)) 436 #ifdef _LP64 437 oflags = TF_INTR; 438 #else 439 oflags = TF_INTR | TF_DISP | TF_MERGE; 440 #endif 441 442 /* 443 * Print the relevant headers; note use of SPACER(). 444 */ 445 if (DCMD_HDRSPEC(flags)) { 446 first = TRUE; 447 mdb_printf("%<u>%?s%</u>", "ADDR"); 448 mdb_flush(); 449 450 if (oflags & TF_PROC) { 451 SPACER(); 452 mdb_printf("%<u> %?s %?s %?s%</u>", 453 "PROC", "LWP", "CRED"); 454 } 455 456 if (oflags & TF_INTR) { 457 SPACER(); 458 mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>", 459 "STATE", "FLG", "PFLG", 460 "SFLG", "PRI", "EPRI", "PIL", "INTR"); 461 } 462 463 if (oflags & TF_BLOCK) { 464 SPACER(); 465 mdb_printf("%<u> %?s %?s %?s %11s%</u>", 466 "WCHAN", "TS", "PITS", "SOBJ OPS"); 467 } 468 469 if (oflags & TF_SIG) { 470 SPACER(); 471 mdb_printf("%<u> %?s %16s %16s%</u>", 472 "SIGQUEUE", "SIG PEND", "SIG HELD"); 473 } 474 475 if (oflags & TF_DISP) { 476 SPACER(); 477 mdb_printf("%<u> %?s %5s %2s %-6s%</u>", 478 "DISPTIME", "BOUND", "PR", "SWITCH"); 479 } 480 mdb_printf("\n"); 481 } 482 483 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 484 mdb_warn("can't read kthread_t at %#lx", addr); 485 return (DCMD_ERR); 486 } 487 488 if (fflag && (t.t_state == TS_FREE)) 489 return (DCMD_OK); 490 491 first = TRUE; 492 mdb_printf("%0?lx", addr); 493 494 /* process information */ 495 if (oflags & TF_PROC) { 496 SPACER(); 497 mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred); 498 } 499 500 /* priority/interrupt information */ 501 if (oflags & TF_INTR) { 502 SPACER(); 503 thread_state_to_text(t.t_state, stbuf, sizeof (stbuf)); 504 if (t.t_intr == NULL) { 505 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s", 506 stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag, 507 t.t_pri, t.t_epri, t.t_pil, "n/a"); 508 } else { 509 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p", 510 stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag, 511 t.t_pri, t.t_epri, t.t_pil, t.t_intr); 512 } 513 } 514 515 /* blocking information */ 516 if (oflags & TF_BLOCK) { 517 SPACER(); 518 (void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops); 519 stbuf[11] = '\0'; 520 mdb_printf(" %?p %?p %?p %11s", 521 t.t_wchan, t.t_ts, t.t_prioinv, stbuf); 522 } 523 524 /* signal information */ 525 if (oflags & TF_SIG) { 526 SPACER(); 527 mdb_printf(" %?p %016llx %016llx", 528 t.t_sigqueue, t.t_sig, t.t_hold); 529 } 530 531 /* dispatcher stuff */ 532 if (oflags & TF_DISP) { 533 SPACER(); 534 mdb_printf(" %?lx %5d %2d ", 535 t.t_disp_time, t.t_bind_cpu, t.t_preempt); 536 if (t.t_disp_time != 0) 537 mdb_printf("t-%-4d", 538 (clock_t)mdb_get_lbolt() - t.t_disp_time); 539 else 540 mdb_printf("%-6s", "-"); 541 } 542 543 mdb_printf("\n"); 544 545 #undef SPACER 546 547 return (DCMD_OK); 548 } 549 550 void 551 thread_help(void) 552 { 553 mdb_printf( 554 "The flags -ipbsd control which information is displayed. When\n" 555 "combined, the fields are displayed on separate lines unless the\n" 556 "-m option is given.\n" 557 "\n" 558 "\t-b\tprint blocked thread state\n" 559 "\t-d\tprint dispatcher state\n" 560 "\t-f\tignore freed threads\n" 561 "\t-i\tprint basic thread state (default)\n" 562 "\t-m\tdisplay results on a single line\n" 563 "\t-p\tprint process and lwp state\n" 564 "\t-s\tprint signal state\n"); 565 } 566 567 /* 568 * Return a string description of the thread, including the ID and the thread 569 * name. 570 * 571 * If ->t_name is NULL, and we're a system thread, we'll do a little more 572 * spelunking to find a useful string to return. 573 */ 574 int 575 thread_getdesc(uintptr_t addr, boolean_t include_comm, 576 char *buf, size_t bufsize) 577 { 578 char name[THREAD_NAME_MAX] = ""; 579 kthread_t t; 580 proc_t p; 581 582 bzero(buf, bufsize); 583 584 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 585 mdb_warn("failed to read kthread_t at %p", addr); 586 return (-1); 587 } 588 589 if (t.t_tid == 0) { 590 taskq_t tq; 591 592 if (mdb_vread(&tq, sizeof (taskq_t), 593 (uintptr_t)t.t_taskq) == -1) 594 tq.tq_name[0] = '\0'; 595 596 if (t.t_name != NULL) { 597 if (mdb_readstr(buf, bufsize, 598 (uintptr_t)t.t_name) == -1) { 599 mdb_warn("error reading thread name"); 600 } 601 } else if (tq.tq_name[0] != '\0') { 602 (void) mdb_snprintf(buf, bufsize, "tq:%s", tq.tq_name); 603 } else { 604 mdb_snprintf(buf, bufsize, "%a()", t.t_startpc); 605 } 606 607 return (buf[0] == '\0' ? -1 : 0); 608 } 609 610 if (include_comm && mdb_vread(&p, sizeof (proc_t), 611 (uintptr_t)t.t_procp) == -1) { 612 mdb_warn("failed to read proc at %p", t.t_procp); 613 return (-1); 614 } 615 616 if (t.t_name != NULL) { 617 if (mdb_readstr(name, sizeof (name), (uintptr_t)t.t_name) == -1) 618 mdb_warn("error reading thread name"); 619 620 /* 621 * Just to be safe -- if mdb_readstr() succeeds, it always NUL 622 * terminates the output, but is unclear what it does on 623 * failure. In that case we attempt to show any partial content 624 * w/ the warning in case it's useful, but explicitly 625 * NUL-terminate to be safe. 626 */ 627 buf[bufsize - 1] = '\0'; 628 } 629 630 if (name[0] != '\0') { 631 if (include_comm) { 632 (void) mdb_snprintf(buf, bufsize, "%s/%u [%s]", 633 p.p_user.u_comm, t.t_tid, name); 634 } else { 635 (void) mdb_snprintf(buf, bufsize, "%u [%s]", 636 t.t_tid, name); 637 } 638 } else { 639 if (include_comm) { 640 (void) mdb_snprintf(buf, bufsize, "%s/%u", 641 p.p_user.u_comm, t.t_tid); 642 } else { 643 (void) mdb_snprintf(buf, bufsize, "%u", t.t_tid); 644 } 645 } 646 647 return (buf[0] == '\0' ? -1 : 0); 648 } 649 650 /* 651 * List a combination of kthread_t and proc_t. Add stack traces in verbose mode. 652 */ 653 int 654 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 655 { 656 int i; 657 uint_t count = 0; 658 uint_t verbose = FALSE; 659 uint_t notaskq = FALSE; 660 kthread_t t; 661 char cmd[80]; 662 mdb_arg_t cmdarg; 663 664 if (!(flags & DCMD_ADDRSPEC)) { 665 if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) { 666 mdb_warn("can't walk threads"); 667 return (DCMD_ERR); 668 } 669 return (DCMD_OK); 670 } 671 672 i = mdb_getopts(argc, argv, 673 't', MDB_OPT_SETBITS, TRUE, ¬askq, 674 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL); 675 676 if (i != argc) { 677 if (i != argc - 1 || !verbose) 678 return (DCMD_USAGE); 679 680 count = (uint_t)mdb_argtoull(&argv[i]); 681 } 682 683 if (DCMD_HDRSPEC(flags)) { 684 if (verbose) 685 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n", 686 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN"); 687 else 688 mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n", 689 "ADDR", "PROC", "LWP", "CMD", "LWPID"); 690 } 691 692 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 693 mdb_warn("failed to read kthread_t at %p", addr); 694 return (DCMD_ERR); 695 } 696 697 if (notaskq && t.t_taskq != NULL) 698 return (DCMD_OK); 699 700 if (t.t_state == TS_FREE) 701 return (DCMD_OK); 702 703 if (!verbose) { 704 char desc[128]; 705 706 if (thread_getdesc(addr, B_TRUE, desc, sizeof (desc)) == -1) 707 return (DCMD_ERR); 708 709 mdb_printf("%0?p %?p %?p %s\n", addr, t.t_procp, t.t_lwp, desc); 710 return (DCMD_OK); 711 } 712 713 mdb_printf("%0?p %?p %?p %3u %3d %?p\n", 714 addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan); 715 716 mdb_inc_indent(2); 717 718 mdb_printf("PC: %a\n", t.t_pc); 719 720 mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count); 721 cmdarg.a_type = MDB_TYPE_STRING; 722 cmdarg.a_un.a_str = cmd; 723 724 (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg); 725 726 mdb_dec_indent(2); 727 728 mdb_printf("\n"); 729 730 return (DCMD_OK); 731 } 732 733 void 734 threadlist_help(void) 735 { 736 mdb_printf( 737 " -v print verbose output including C stack trace\n" 738 " -t skip threads belonging to a taskq\n" 739 " count print no more than count arguments (default 0)\n"); 740 } 741 742 static size_t 743 stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp) 744 { 745 size_t percent; 746 size_t s; 747 748 if (t_stk > t_stkbase) { 749 /* stack grows down */ 750 if (sp > t_stk) { 751 return (0); 752 } 753 if (sp < t_stkbase) { 754 return (100); 755 } 756 percent = t_stk - sp + 1; 757 s = t_stk - t_stkbase + 1; 758 } else { 759 /* stack grows up */ 760 if (sp < t_stk) { 761 return (0); 762 } 763 if (sp > t_stkbase) { 764 return (100); 765 } 766 percent = sp - t_stk + 1; 767 s = t_stkbase - t_stk + 1; 768 } 769 percent = ((100 * percent) / s) + 1; 770 if (percent > 100) { 771 percent = 100; 772 } 773 return (percent); 774 } 775 776 /* 777 * Display kthread stack infos. 778 */ 779 int 780 stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 781 { 782 kthread_t t; 783 uint64_t *ptr; /* pattern pointer */ 784 caddr_t start; /* kernel stack start */ 785 caddr_t end; /* kernel stack end */ 786 caddr_t ustack; /* userland copy of kernel stack */ 787 size_t usize; /* userland copy of kernel stack size */ 788 caddr_t ustart; /* userland copy of kernel stack, aligned start */ 789 caddr_t uend; /* userland copy of kernel stack, aligned end */ 790 size_t percent = 0; 791 uint_t all = FALSE; /* don't show TS_FREE kthread by default */ 792 uint_t history = FALSE; 793 int i = 0; 794 unsigned int ukmem_stackinfo; 795 uintptr_t allthreads; 796 char tdesc[128] = ""; 797 798 /* handle options */ 799 if (mdb_getopts(argc, argv, 800 'a', MDB_OPT_SETBITS, TRUE, &all, 801 'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) { 802 return (DCMD_USAGE); 803 } 804 805 /* walk all kthread if needed */ 806 if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) { 807 if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) { 808 mdb_warn("can't walk threads"); 809 return (DCMD_ERR); 810 } 811 return (DCMD_OK); 812 } 813 814 /* read 'kmem_stackinfo' */ 815 if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo), 816 "kmem_stackinfo") == -1) { 817 mdb_warn("failed to read 'kmem_stackinfo'\n"); 818 ukmem_stackinfo = 0; 819 } 820 821 /* read 'allthreads' */ 822 if (mdb_readsym(&allthreads, sizeof (kthread_t *), 823 "allthreads") == -1) { 824 mdb_warn("failed to read 'allthreads'\n"); 825 allthreads = 0; 826 } 827 828 if (history == TRUE) { 829 kmem_stkinfo_t *log; 830 uintptr_t kaddr; 831 832 mdb_printf("Dead kthreads stack usage history:\n"); 833 if (ukmem_stackinfo == 0) { 834 mdb_printf("Tunable kmem_stackinfo is unset, history "); 835 mdb_printf("feature is off.\nUse ::help stackinfo "); 836 mdb_printf("for more details.\n"); 837 return (DCMD_OK); 838 } 839 840 mdb_printf("%<u>%?s%</u>", "THREAD"); 841 mdb_printf(" %<u>%?s%</u>", "STACK"); 842 mdb_printf("%<u>%s%</u>", " SIZE MAX LWP"); 843 mdb_printf("\n"); 844 usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t); 845 log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP); 846 if (mdb_readsym(&kaddr, sizeof (kaddr), 847 "kmem_stkinfo_log") == -1) { 848 mdb_free((void *)log, usize); 849 mdb_warn("failed to read 'kmem_stkinfo_log'\n"); 850 return (DCMD_ERR); 851 } 852 if (kaddr == 0) { 853 mdb_free((void *)log, usize); 854 return (DCMD_OK); 855 } 856 if (mdb_vread(log, usize, kaddr) == -1) { 857 mdb_free((void *)log, usize); 858 mdb_warn("failed to read %p\n", kaddr); 859 return (DCMD_ERR); 860 } 861 for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) { 862 if (log[i].kthread == NULL) { 863 continue; 864 } 865 866 (void) thread_getdesc((uintptr_t)log[i].kthread, 867 B_TRUE, tdesc, sizeof (tdesc)); 868 869 mdb_printf("%0?p %0?p %6x %3d%% %s\n", 870 log[i].kthread, 871 log[i].start, 872 (uint_t)log[i].stksz, 873 (int)log[i].percent, tdesc); 874 } 875 mdb_free((void *)log, usize); 876 return (DCMD_OK); 877 } 878 879 /* display header */ 880 if (DCMD_HDRSPEC(flags)) { 881 if (ukmem_stackinfo == 0) { 882 mdb_printf("Tunable kmem_stackinfo is unset, "); 883 mdb_printf("MAX value is not available.\n"); 884 mdb_printf("Use ::help stackinfo for more details.\n"); 885 } 886 mdb_printf("%<u>%?s%</u>", "THREAD"); 887 mdb_printf(" %<u>%?s%</u>", "STACK"); 888 mdb_printf("%<u>%s%</u>", " SIZE CUR MAX LWP"); 889 mdb_printf("\n"); 890 } 891 892 /* read kthread */ 893 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 894 mdb_warn("can't read kthread_t at %#lx\n", addr); 895 return (DCMD_ERR); 896 } 897 898 if (t.t_state == TS_FREE && all == FALSE) { 899 return (DCMD_OK); 900 } 901 902 /* 903 * Stack grows up or down, see thread_create(), 904 * compute stack memory aera start and end (start < end). 905 */ 906 if (t.t_stk > t.t_stkbase) { 907 /* stack grows down */ 908 start = t.t_stkbase; 909 end = t.t_stk; 910 } else { 911 /* stack grows up */ 912 start = t.t_stk; 913 end = t.t_stkbase; 914 } 915 916 /* display stack info */ 917 mdb_printf("%0?p %0?p", addr, start); 918 919 /* (end - start), kernel stack size as found in kthread_t */ 920 if ((end <= start) || ((end - start) > (1024 * 1024))) { 921 /* negative or stack size > 1 meg, assume bogus */ 922 mdb_warn(" t_stk/t_stkbase problem\n"); 923 return (DCMD_ERR); 924 } 925 926 /* display stack size */ 927 mdb_printf(" %6x", end - start); 928 929 /* display current stack usage */ 930 percent = stk_compute_percent(t.t_stk, t.t_stkbase, 931 (caddr_t)t.t_sp + STACK_BIAS); 932 933 mdb_printf(" %3d%%", percent); 934 percent = 0; 935 936 (void) thread_getdesc(addr, B_TRUE, tdesc, sizeof (tdesc)); 937 938 if (ukmem_stackinfo == 0) { 939 mdb_printf(" n/a %s\n", tdesc); 940 return (DCMD_OK); 941 } 942 943 if ((((uintptr_t)start) & 0x7) != 0) { 944 start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8); 945 } 946 end = (caddr_t)(((uintptr_t)end) & (~0x7)); 947 /* size to scan in userland copy of kernel stack */ 948 usize = end - start; /* is a multiple of 8 bytes */ 949 950 /* 951 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes 952 * alignement for ustart and uend, in boundaries. 953 */ 954 ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP); 955 if ((((uintptr_t)ustart) & 0x7) != 0) { 956 ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8); 957 } 958 uend = ustart + usize; 959 960 /* read the kernel stack */ 961 if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) { 962 mdb_free((void *)ustack, usize + 8); 963 mdb_printf("\n"); 964 mdb_warn("couldn't read entire stack\n"); 965 return (DCMD_ERR); 966 } 967 968 /* scan the stack */ 969 if (t.t_stk > t.t_stkbase) { 970 /* stack grows down */ 971 #if defined(__i386) || defined(__amd64) 972 /* 973 * 6 longs are pushed on stack, see thread_load(). Skip 974 * them, so if kthread has never run, percent is zero. 975 * 8 bytes alignement is preserved for a 32 bit kernel, 976 * 6 x 4 = 24, 24 is a multiple of 8. 977 */ 978 uend -= (6 * sizeof (long)); 979 #endif 980 ptr = (uint64_t *)((void *)ustart); 981 while (ptr < (uint64_t *)((void *)uend)) { 982 if (*ptr != KMEM_STKINFO_PATTERN) { 983 percent = stk_compute_percent(uend, 984 ustart, (caddr_t)ptr); 985 break; 986 } 987 ptr++; 988 } 989 } else { 990 /* stack grows up */ 991 ptr = (uint64_t *)((void *)uend); 992 ptr--; 993 while (ptr >= (uint64_t *)((void *)ustart)) { 994 if (*ptr != KMEM_STKINFO_PATTERN) { 995 percent = stk_compute_percent(ustart, 996 uend, (caddr_t)ptr); 997 break; 998 } 999 ptr--; 1000 } 1001 } 1002 1003 /* thread 't0' stack is not created by thread_create() */ 1004 if (addr == allthreads) { 1005 percent = 0; 1006 } 1007 if (percent != 0) { 1008 mdb_printf(" %3d%%", percent); 1009 } else { 1010 mdb_printf(" n/a"); 1011 } 1012 1013 mdb_printf(" %s\n", tdesc); 1014 1015 mdb_free((void *)ustack, usize + 8); 1016 return (DCMD_OK); 1017 } 1018 1019 void 1020 stackinfo_help(void) 1021 { 1022 mdb_printf( 1023 "Shows kernel stacks real utilization, if /etc/system " 1024 "kmem_stackinfo tunable\n"); 1025 mdb_printf( 1026 "(an unsigned integer) is non zero at kthread creation time. "); 1027 mdb_printf("For example:\n"); 1028 mdb_printf( 1029 " THREAD STACK SIZE CUR MAX LWP\n"); 1030 mdb_printf( 1031 "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n"); 1032 mdb_printf( 1033 "The stack size utilization for this kthread is at 4%%" 1034 " of its maximum size,\n"); 1035 mdb_printf( 1036 "but has already used up to 43%%, stack size is 4f00 bytes.\n"); 1037 mdb_printf( 1038 "MAX value can be shown as n/a (not available):\n"); 1039 mdb_printf( 1040 " - for the very first kthread (sched/1)\n"); 1041 mdb_printf( 1042 " - kmem_stackinfo was zero at kthread creation time\n"); 1043 mdb_printf( 1044 " - kthread has not yet run\n"); 1045 mdb_printf("\n"); 1046 mdb_printf("Options:\n"); 1047 mdb_printf( 1048 "-a shows also TS_FREE kthreads (interrupt kthreads)\n"); 1049 mdb_printf( 1050 "-h shows history, dead kthreads that used their " 1051 "kernel stack the most\n"); 1052 mdb_printf( 1053 "\nSee illumos Modular Debugger Guide for detailed usage.\n"); 1054 mdb_flush(); 1055 } 1056