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