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