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 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 struct thread_state { 309 uint_t ts_state; 310 const char *ts_name; 311 } thread_states[] = { 312 { TS_FREE, "free" }, 313 { TS_SLEEP, "sleep" }, 314 { TS_RUN, "run" }, 315 { TS_ONPROC, "onproc" }, 316 { TS_ZOMB, "zomb" }, 317 { TS_STOPPED, "stopped" }, 318 { TS_WAIT, "wait" } 319 }; 320 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states)) 321 322 void 323 thread_state_to_text(uint_t state, char *out, size_t out_sz) 324 { 325 int idx; 326 327 for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 328 struct thread_state *tsp = &thread_states[idx]; 329 if (tsp->ts_state == state) { 330 mdb_snprintf(out, out_sz, "%s", tsp->ts_name); 331 return; 332 } 333 } 334 mdb_snprintf(out, out_sz, "inval/%02x", state); 335 } 336 337 int 338 thread_text_to_state(const char *state, uint_t *out) 339 { 340 int idx; 341 342 for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 343 struct thread_state *tsp = &thread_states[idx]; 344 if (strcasecmp(tsp->ts_name, state) == 0) { 345 *out = tsp->ts_state; 346 return (0); 347 } 348 } 349 return (-1); 350 } 351 352 void 353 thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg) 354 { 355 int idx; 356 357 for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 358 struct thread_state *tsp = &thread_states[idx]; 359 cbfunc(tsp->ts_state, tsp->ts_name, cbarg); 360 } 361 } 362 363 #define TF_INTR 0x01 364 #define TF_PROC 0x02 365 #define TF_BLOCK 0x04 366 #define TF_SIG 0x08 367 #define TF_DISP 0x10 368 #define TF_MERGE 0x20 369 370 /* 371 * Display a kthread_t. 372 * This is a little complicated, as there is a lot of information that 373 * the user could be interested in. The flags "ipbsd" are used to 374 * indicate which subset of the thread's members are to be displayed 375 * ('i' is the default). If multiple options are specified, multiple 376 * sets of data will be displayed in a vaguely readable format. If the 377 * 'm' option is specified, all the selected sets will be merged onto a 378 * single line for the benefit of those using wider-than-normal 379 * terminals. Having a generic mechanism for doing this would be 380 * really useful, but is a project best left to another day. 381 */ 382 383 int 384 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 385 { 386 kthread_t t; 387 uint_t oflags = 0; 388 uint_t fflag = FALSE; 389 int first; 390 char stbuf[20]; 391 392 /* 393 * "Gracefully" handle printing a boatload of stuff to the 394 * screen. If we are not printing our first set of data, and 395 * we haven't been instructed to merge sets together, output a 396 * newline and indent such that the thread addresses form a 397 * column of their own. 398 */ 399 #define SPACER() \ 400 if (first) { \ 401 first = FALSE; \ 402 } else if (!(oflags & TF_MERGE)) { \ 403 mdb_printf("\n%?s", ""); \ 404 } 405 406 if (!(flags & DCMD_ADDRSPEC)) { 407 if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) { 408 mdb_warn("can't walk threads"); 409 return (DCMD_ERR); 410 } 411 return (DCMD_OK); 412 } 413 414 if (mdb_getopts(argc, argv, 415 'f', MDB_OPT_SETBITS, TRUE, &fflag, 416 'i', MDB_OPT_SETBITS, TF_INTR, &oflags, 417 'p', MDB_OPT_SETBITS, TF_PROC, &oflags, 418 'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags, 419 's', MDB_OPT_SETBITS, TF_SIG, &oflags, 420 'd', MDB_OPT_SETBITS, TF_DISP, &oflags, 421 'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc) 422 return (DCMD_USAGE); 423 424 /* 425 * If no sets were specified, choose the 'i' set. 426 */ 427 if (!(oflags & ~TF_MERGE)) 428 #ifdef _LP64 429 oflags = TF_INTR; 430 #else 431 oflags = TF_INTR | TF_DISP | TF_MERGE; 432 #endif 433 434 /* 435 * Print the relevant headers; note use of SPACER(). 436 */ 437 if (DCMD_HDRSPEC(flags)) { 438 first = TRUE; 439 mdb_printf("%<u>%?s%</u>", "ADDR"); 440 mdb_flush(); 441 442 if (oflags & TF_PROC) { 443 SPACER(); 444 mdb_printf("%<u> %?s %?s %?s%</u>", 445 "PROC", "LWP", "CRED"); 446 } 447 448 if (oflags & TF_INTR) { 449 SPACER(); 450 mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>", 451 "STATE", "FLG", "PFLG", 452 "SFLG", "PRI", "EPRI", "PIL", "INTR"); 453 } 454 455 if (oflags & TF_BLOCK) { 456 SPACER(); 457 mdb_printf("%<u> %?s %?s %?s %11s%</u>", 458 "WCHAN", "TS", "PITS", "SOBJ OPS"); 459 } 460 461 if (oflags & TF_SIG) { 462 SPACER(); 463 mdb_printf("%<u> %?s %16s %16s%</u>", 464 "SIGQUEUE", "SIG PEND", "SIG HELD"); 465 } 466 467 if (oflags & TF_DISP) { 468 SPACER(); 469 mdb_printf("%<u> %?s %5s %2s%</u>", 470 "DISPTIME", "BOUND", "PR"); 471 } 472 mdb_printf("\n"); 473 } 474 475 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 476 mdb_warn("can't read kthread_t at %#lx", addr); 477 return (DCMD_ERR); 478 } 479 480 if (fflag && (t.t_state == TS_FREE)) 481 return (DCMD_OK); 482 483 first = TRUE; 484 mdb_printf("%0?lx", addr); 485 486 /* process information */ 487 if (oflags & TF_PROC) { 488 SPACER(); 489 mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred); 490 } 491 492 /* priority/interrupt information */ 493 if (oflags & TF_INTR) { 494 SPACER(); 495 thread_state_to_text(t.t_state, stbuf, sizeof (stbuf)); 496 if (t.t_intr == NULL) { 497 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s", 498 stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag, 499 t.t_pri, t.t_epri, t.t_pil, "n/a"); 500 } else { 501 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p", 502 stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag, 503 t.t_pri, t.t_epri, t.t_pil, t.t_intr); 504 } 505 } 506 507 /* blocking information */ 508 if (oflags & TF_BLOCK) { 509 SPACER(); 510 (void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops); 511 stbuf[11] = '\0'; 512 mdb_printf(" %?p %?p %?p %11s", 513 t.t_wchan, t.t_ts, t.t_prioinv, stbuf); 514 } 515 516 /* signal information */ 517 if (oflags & TF_SIG) { 518 SPACER(); 519 mdb_printf(" %?p %016llx %016llx", 520 t.t_sigqueue, t.t_sig, t.t_hold); 521 } 522 523 /* dispatcher stuff */ 524 if (oflags & TF_DISP) { 525 SPACER(); 526 mdb_printf(" %?lx %5d %2d", 527 t.t_disp_time, t.t_bind_cpu, t.t_preempt); 528 } 529 530 mdb_printf("\n"); 531 532 #undef SPACER 533 534 return (DCMD_OK); 535 } 536 537 void 538 thread_help(void) 539 { 540 mdb_printf( 541 "The flags -ipbsd control which information is displayed. When\n" 542 "combined, the fields are displayed on separate lines unless the\n" 543 "-m option is given.\n" 544 "\n" 545 "\t-b\tprint blocked thread state\n" 546 "\t-d\tprint dispatcher state\n" 547 "\t-f\tignore freed threads\n" 548 "\t-i\tprint basic thread state (default)\n" 549 "\t-m\tdisplay results on a single line\n" 550 "\t-p\tprint process and lwp state\n" 551 "\t-s\tprint signal state\n"); 552 } 553 554 /* 555 * List a combination of kthread_t and proc_t. Add stack traces in verbose mode. 556 */ 557 int 558 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 559 { 560 int i; 561 uint_t count = 0; 562 uint_t verbose = FALSE; 563 uint_t notaskq = FALSE; 564 kthread_t t; 565 taskq_t tq; 566 proc_t p; 567 char cmd[80]; 568 mdb_arg_t cmdarg; 569 570 if (!(flags & DCMD_ADDRSPEC)) { 571 if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) { 572 mdb_warn("can't walk threads"); 573 return (DCMD_ERR); 574 } 575 return (DCMD_OK); 576 } 577 578 i = mdb_getopts(argc, argv, 579 't', MDB_OPT_SETBITS, TRUE, ¬askq, 580 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL); 581 582 if (i != argc) { 583 if (i != argc - 1 || !verbose) 584 return (DCMD_USAGE); 585 586 if (argv[i].a_type == MDB_TYPE_IMMEDIATE) 587 count = (uint_t)argv[i].a_un.a_val; 588 else 589 count = (uint_t)mdb_strtoull(argv[i].a_un.a_str); 590 } 591 592 if (DCMD_HDRSPEC(flags)) { 593 if (verbose) 594 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n", 595 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN"); 596 else 597 mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n", 598 "ADDR", "PROC", "LWP", "CMD", "LWPID"); 599 } 600 601 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 602 mdb_warn("failed to read kthread_t at %p", addr); 603 return (DCMD_ERR); 604 } 605 606 if (notaskq && t.t_taskq != NULL) 607 return (DCMD_OK); 608 609 if (t.t_state == TS_FREE) 610 return (DCMD_OK); 611 612 if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) { 613 mdb_warn("failed to read proc at %p", t.t_procp); 614 return (DCMD_ERR); 615 } 616 617 if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1) 618 tq.tq_name[0] = '\0'; 619 620 if (verbose) { 621 mdb_printf("%0?p %?p %?p %3u %3d %?p\n", 622 addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan); 623 624 mdb_inc_indent(2); 625 626 mdb_printf("PC: %a", t.t_pc); 627 if (t.t_tid == 0) { 628 if (tq.tq_name[0] != '\0') 629 mdb_printf(" TASKQ: %s\n", tq.tq_name); 630 else 631 mdb_printf(" THREAD: %a()\n", t.t_startpc); 632 } else { 633 mdb_printf(" CMD: %s\n", p.p_user.u_psargs); 634 } 635 636 mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count); 637 cmdarg.a_type = MDB_TYPE_STRING; 638 cmdarg.a_un.a_str = cmd; 639 640 (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg); 641 642 mdb_dec_indent(2); 643 644 mdb_printf("\n"); 645 } else { 646 mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp); 647 if (t.t_tid == 0) { 648 if (tq.tq_name[0] != '\0') 649 mdb_printf(" tq:%s\n", tq.tq_name); 650 else 651 mdb_printf(" %a()\n", t.t_startpc); 652 } else { 653 mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid); 654 } 655 } 656 657 return (DCMD_OK); 658 } 659 660 void 661 threadlist_help(void) 662 { 663 mdb_printf( 664 " -v print verbose output including C stack trace\n" 665 " -t skip threads belonging to a taskq\n" 666 " count print no more than count arguments (default 0)\n"); 667 } 668 669 static size_t 670 stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp) 671 { 672 size_t percent; 673 size_t s; 674 675 if (t_stk > t_stkbase) { 676 /* stack grows down */ 677 if (sp > t_stk) { 678 return (0); 679 } 680 if (sp < t_stkbase) { 681 return (100); 682 } 683 percent = t_stk - sp + 1; 684 s = t_stk - t_stkbase + 1; 685 } else { 686 /* stack grows up */ 687 if (sp < t_stk) { 688 return (0); 689 } 690 if (sp > t_stkbase) { 691 return (100); 692 } 693 percent = sp - t_stk + 1; 694 s = t_stkbase - t_stk + 1; 695 } 696 percent = ((100 * percent) / s) + 1; 697 if (percent > 100) { 698 percent = 100; 699 } 700 return (percent); 701 } 702 703 /* 704 * Display kthread stack infos. 705 */ 706 int 707 stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 708 { 709 kthread_t t; 710 proc_t p; 711 uint64_t *ptr; /* pattern pointer */ 712 caddr_t start; /* kernel stack start */ 713 caddr_t end; /* kernel stack end */ 714 caddr_t ustack; /* userland copy of kernel stack */ 715 size_t usize; /* userland copy of kernel stack size */ 716 caddr_t ustart; /* userland copy of kernel stack, aligned start */ 717 caddr_t uend; /* userland copy of kernel stack, aligned end */ 718 size_t percent = 0; 719 uint_t all = FALSE; /* don't show TS_FREE kthread by default */ 720 uint_t history = FALSE; 721 int i = 0; 722 unsigned int ukmem_stackinfo; 723 uintptr_t allthreads; 724 725 /* handle options */ 726 if (mdb_getopts(argc, argv, 727 'a', MDB_OPT_SETBITS, TRUE, &all, 728 'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) { 729 return (DCMD_USAGE); 730 } 731 732 /* walk all kthread if needed */ 733 if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) { 734 if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) { 735 mdb_warn("can't walk threads"); 736 return (DCMD_ERR); 737 } 738 return (DCMD_OK); 739 } 740 741 /* read 'kmem_stackinfo' */ 742 if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo), 743 "kmem_stackinfo") == -1) { 744 mdb_warn("failed to read 'kmem_stackinfo'\n"); 745 ukmem_stackinfo = 0; 746 } 747 748 /* read 'allthreads' */ 749 if (mdb_readsym(&allthreads, sizeof (kthread_t *), 750 "allthreads") == -1) { 751 mdb_warn("failed to read 'allthreads'\n"); 752 allthreads = NULL; 753 } 754 755 if (history == TRUE) { 756 kmem_stkinfo_t *log; 757 uintptr_t kaddr; 758 759 mdb_printf("Dead kthreads stack usage history:\n"); 760 if (ukmem_stackinfo == 0) { 761 mdb_printf("Tunable kmem_stackinfo is unset, history "); 762 mdb_printf("feature is off.\nUse ::help stackinfo "); 763 mdb_printf("for more details.\n"); 764 return (DCMD_OK); 765 } 766 767 mdb_printf("%<u>%?s%</u>", "THREAD"); 768 mdb_printf(" %<u>%?s%</u>", "STACK"); 769 mdb_printf("%<u>%s%</u>", " SIZE MAX CMD/LWPID or STARTPC"); 770 mdb_printf("\n"); 771 usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t); 772 log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP); 773 if (mdb_readsym(&kaddr, sizeof (kaddr), 774 "kmem_stkinfo_log") == -1) { 775 mdb_free((void *)log, usize); 776 mdb_warn("failed to read 'kmem_stkinfo_log'\n"); 777 return (DCMD_ERR); 778 } 779 if (kaddr == NULL) { 780 mdb_free((void *)log, usize); 781 return (DCMD_OK); 782 } 783 if (mdb_vread(log, usize, kaddr) == -1) { 784 mdb_free((void *)log, usize); 785 mdb_warn("failed to read %p\n", kaddr); 786 return (DCMD_ERR); 787 } 788 for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) { 789 if (log[i].kthread == NULL) { 790 continue; 791 } 792 mdb_printf("%0?p %0?p %6x %3d%%", 793 log[i].kthread, 794 log[i].start, 795 (uint_t)log[i].stksz, 796 (int)log[i].percent); 797 if (log[i].t_tid != 0) { 798 mdb_printf(" %s/%u\n", 799 log[i].cmd, log[i].t_tid); 800 } else { 801 mdb_printf(" %p (%a)\n", log[i].t_startpc, 802 log[i].t_startpc); 803 } 804 } 805 mdb_free((void *)log, usize); 806 return (DCMD_OK); 807 } 808 809 /* display header */ 810 if (DCMD_HDRSPEC(flags)) { 811 if (ukmem_stackinfo == 0) { 812 mdb_printf("Tunable kmem_stackinfo is unset, "); 813 mdb_printf("MAX value is not available.\n"); 814 mdb_printf("Use ::help stackinfo for more details.\n"); 815 } 816 mdb_printf("%<u>%?s%</u>", "THREAD"); 817 mdb_printf(" %<u>%?s%</u>", "STACK"); 818 mdb_printf("%<u>%s%</u>", " SIZE CUR MAX CMD/LWPID"); 819 mdb_printf("\n"); 820 } 821 822 /* read kthread */ 823 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 824 mdb_warn("can't read kthread_t at %#lx\n", addr); 825 return (DCMD_ERR); 826 } 827 828 if (t.t_state == TS_FREE && all == FALSE) { 829 return (DCMD_OK); 830 } 831 832 /* read proc */ 833 if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) { 834 mdb_warn("failed to read proc at %p\n", t.t_procp); 835 return (DCMD_ERR); 836 } 837 838 /* 839 * Stack grows up or down, see thread_create(), 840 * compute stack memory aera start and end (start < end). 841 */ 842 if (t.t_stk > t.t_stkbase) { 843 /* stack grows down */ 844 start = t.t_stkbase; 845 end = t.t_stk; 846 } else { 847 /* stack grows up */ 848 start = t.t_stk; 849 end = t.t_stkbase; 850 } 851 852 /* display stack info */ 853 mdb_printf("%0?p %0?p", addr, start); 854 855 /* (end - start), kernel stack size as found in kthread_t */ 856 if ((end <= start) || ((end - start) > (1024 * 1024))) { 857 /* negative or stack size > 1 meg, assume bogus */ 858 mdb_warn(" t_stk/t_stkbase problem\n"); 859 return (DCMD_ERR); 860 } 861 862 /* display stack size */ 863 mdb_printf(" %6x", end - start); 864 865 /* display current stack usage */ 866 percent = stk_compute_percent(t.t_stk, t.t_stkbase, 867 (caddr_t)t.t_sp + STACK_BIAS); 868 869 mdb_printf(" %3d%%", percent); 870 percent = 0; 871 872 if (ukmem_stackinfo == 0) { 873 mdb_printf(" n/a"); 874 if (t.t_tid == 0) { 875 mdb_printf(" %a()", t.t_startpc); 876 } else { 877 mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid); 878 } 879 mdb_printf("\n"); 880 return (DCMD_OK); 881 } 882 883 if ((((uintptr_t)start) & 0x7) != 0) { 884 start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8); 885 } 886 end = (caddr_t)(((uintptr_t)end) & (~0x7)); 887 /* size to scan in userland copy of kernel stack */ 888 usize = end - start; /* is a multiple of 8 bytes */ 889 890 /* 891 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes 892 * alignement for ustart and uend, in boundaries. 893 */ 894 ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP); 895 if ((((uintptr_t)ustart) & 0x7) != 0) { 896 ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8); 897 } 898 uend = ustart + usize; 899 900 /* read the kernel stack */ 901 if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) { 902 mdb_free((void *)ustack, usize + 8); 903 mdb_printf("\n"); 904 mdb_warn("couldn't read entire stack\n"); 905 return (DCMD_ERR); 906 } 907 908 /* scan the stack */ 909 if (t.t_stk > t.t_stkbase) { 910 /* stack grows down */ 911 #if defined(__i386) || defined(__amd64) 912 /* 913 * 6 longs are pushed on stack, see thread_load(). Skip 914 * them, so if kthread has never run, percent is zero. 915 * 8 bytes alignement is preserved for a 32 bit kernel, 916 * 6 x 4 = 24, 24 is a multiple of 8. 917 */ 918 uend -= (6 * sizeof (long)); 919 #endif 920 ptr = (uint64_t *)((void *)ustart); 921 while (ptr < (uint64_t *)((void *)uend)) { 922 if (*ptr != KMEM_STKINFO_PATTERN) { 923 percent = stk_compute_percent(uend, 924 ustart, (caddr_t)ptr); 925 break; 926 } 927 ptr++; 928 } 929 } else { 930 /* stack grows up */ 931 ptr = (uint64_t *)((void *)uend); 932 ptr--; 933 while (ptr >= (uint64_t *)((void *)ustart)) { 934 if (*ptr != KMEM_STKINFO_PATTERN) { 935 percent = stk_compute_percent(ustart, 936 uend, (caddr_t)ptr); 937 break; 938 } 939 ptr--; 940 } 941 } 942 943 /* thread 't0' stack is not created by thread_create() */ 944 if (addr == allthreads) { 945 percent = 0; 946 } 947 if (percent != 0) { 948 mdb_printf(" %3d%%", percent); 949 } else { 950 mdb_printf(" n/a"); 951 } 952 if (t.t_tid == 0) { 953 mdb_printf(" %a()", t.t_startpc); 954 } else { 955 mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid); 956 } 957 mdb_printf("\n"); 958 mdb_free((void *)ustack, usize + 8); 959 return (DCMD_OK); 960 } 961 962 void 963 stackinfo_help(void) 964 { 965 mdb_printf( 966 "Shows kernel stacks real utilization, if /etc/system " 967 "kmem_stackinfo tunable\n"); 968 mdb_printf( 969 "(an unsigned integer) is non zero at kthread creation time. "); 970 mdb_printf("For example:\n"); 971 mdb_printf( 972 " THREAD STACK SIZE CUR MAX CMD/LWPID\n"); 973 mdb_printf( 974 "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n"); 975 mdb_printf( 976 "The stack size utilization for this kthread is at 4%%" 977 " of its maximum size,\n"); 978 mdb_printf( 979 "but has already used up to 43%%, stack size is 4f00 bytes.\n"); 980 mdb_printf( 981 "MAX value can be shown as n/a (not available):\n"); 982 mdb_printf( 983 " - for the very first kthread (sched/1)\n"); 984 mdb_printf( 985 " - kmem_stackinfo was zero at kthread creation time\n"); 986 mdb_printf( 987 " - kthread has not yet run\n"); 988 mdb_printf("\n"); 989 mdb_printf("Options:\n"); 990 mdb_printf( 991 "-a shows also TS_FREE kthreads (interrupt kthreads)\n"); 992 mdb_printf( 993 "-h shows history, dead kthreads that used their " 994 "kernel stack the most\n"); 995 mdb_printf( 996 "\nSee Solaris Modular Debugger Guide for detailed usage.\n"); 997 mdb_flush(); 998 } 999