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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <mdb/mdb_modapi.h> 29 #include <sys/types.h> 30 #include <sys/thread.h> 31 #include <sys/lwp.h> 32 #include <sys/proc.h> 33 #include <sys/cpuvar.h> 34 #include <sys/cpupart.h> 35 #include <sys/disp.h> 36 #include <sys/taskq_impl.h> 37 38 typedef struct thread_walk { 39 kthread_t *tw_thread; 40 uintptr_t tw_last; 41 uint_t tw_inproc; 42 uint_t tw_step; 43 } thread_walk_t; 44 45 int 46 thread_walk_init(mdb_walk_state_t *wsp) 47 { 48 thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP); 49 50 if (wsp->walk_addr == NULL) { 51 if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) { 52 mdb_warn("failed to read 'allthreads'"); 53 mdb_free(twp, sizeof (thread_walk_t)); 54 return (WALK_ERR); 55 } 56 57 twp->tw_inproc = FALSE; 58 59 } else { 60 proc_t pr; 61 62 if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) { 63 mdb_warn("failed to read proc at %p", wsp->walk_addr); 64 mdb_free(twp, sizeof (thread_walk_t)); 65 return (WALK_ERR); 66 } 67 68 wsp->walk_addr = (uintptr_t)pr.p_tlist; 69 twp->tw_inproc = TRUE; 70 } 71 72 twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP); 73 twp->tw_last = wsp->walk_addr; 74 twp->tw_step = FALSE; 75 76 wsp->walk_data = twp; 77 return (WALK_NEXT); 78 } 79 80 int 81 thread_walk_step(mdb_walk_state_t *wsp) 82 { 83 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 84 int status; 85 86 if (wsp->walk_addr == NULL) 87 return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */ 88 89 if (twp->tw_step && wsp->walk_addr == twp->tw_last) 90 return (WALK_DONE); /* We've wrapped around */ 91 92 if (mdb_vread(twp->tw_thread, sizeof (kthread_t), 93 wsp->walk_addr) == -1) { 94 mdb_warn("failed to read thread at %p", wsp->walk_addr); 95 return (WALK_DONE); 96 } 97 98 status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread, 99 wsp->walk_cbdata); 100 101 if (twp->tw_inproc) 102 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw; 103 else 104 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next; 105 106 twp->tw_step = TRUE; 107 return (status); 108 } 109 110 void 111 thread_walk_fini(mdb_walk_state_t *wsp) 112 { 113 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 114 115 mdb_free(twp->tw_thread, sizeof (kthread_t)); 116 mdb_free(twp, sizeof (thread_walk_t)); 117 } 118 119 int 120 deathrow_walk_init(mdb_walk_state_t *wsp) 121 { 122 if (mdb_layered_walk("thread_deathrow", wsp) == -1) { 123 mdb_warn("couldn't walk 'thread_deathrow'"); 124 return (WALK_ERR); 125 } 126 127 if (mdb_layered_walk("lwp_deathrow", wsp) == -1) { 128 mdb_warn("couldn't walk 'lwp_deathrow'"); 129 return (WALK_ERR); 130 } 131 132 return (WALK_NEXT); 133 } 134 135 int 136 deathrow_walk_step(mdb_walk_state_t *wsp) 137 { 138 kthread_t t; 139 uintptr_t addr = wsp->walk_addr; 140 141 if (addr == NULL) 142 return (WALK_DONE); 143 144 if (mdb_vread(&t, sizeof (t), addr) == -1) { 145 mdb_warn("couldn't read deathrow thread at %p", addr); 146 return (WALK_ERR); 147 } 148 149 wsp->walk_addr = (uintptr_t)t.t_forw; 150 151 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 152 } 153 154 int 155 thread_deathrow_walk_init(mdb_walk_state_t *wsp) 156 { 157 if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) { 158 mdb_warn("couldn't read symbol 'thread_deathrow'"); 159 return (WALK_ERR); 160 } 161 162 return (WALK_NEXT); 163 } 164 165 int 166 lwp_deathrow_walk_init(mdb_walk_state_t *wsp) 167 { 168 if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) { 169 mdb_warn("couldn't read symbol 'lwp_deathrow'"); 170 return (WALK_ERR); 171 } 172 173 return (WALK_NEXT); 174 } 175 176 177 typedef struct dispq_walk { 178 int dw_npri; 179 uintptr_t dw_dispq; 180 uintptr_t dw_last; 181 } dispq_walk_t; 182 183 int 184 cpu_dispq_walk_init(mdb_walk_state_t *wsp) 185 { 186 uintptr_t addr = wsp->walk_addr; 187 dispq_walk_t *dw; 188 cpu_t cpu; 189 dispq_t dispq; 190 disp_t disp; 191 192 if (addr == NULL) { 193 mdb_warn("cpu_dispq walk needs a cpu_t address\n"); 194 return (WALK_ERR); 195 } 196 197 if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) { 198 mdb_warn("failed to read cpu_t at %p", addr); 199 return (WALK_ERR); 200 } 201 202 if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) { 203 mdb_warn("failed to read disp_t at %p", cpu.cpu_disp); 204 return (WALK_ERR); 205 } 206 207 if (mdb_vread(&dispq, sizeof (dispq_t), 208 (uintptr_t)disp.disp_q) == -1) { 209 mdb_warn("failed to read dispq_t at %p", disp.disp_q); 210 return (WALK_ERR); 211 } 212 213 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 214 215 dw->dw_npri = disp.disp_npri; 216 dw->dw_dispq = (uintptr_t)disp.disp_q; 217 dw->dw_last = (uintptr_t)dispq.dq_last; 218 219 wsp->walk_addr = (uintptr_t)dispq.dq_first; 220 wsp->walk_data = dw; 221 222 return (WALK_NEXT); 223 } 224 225 int 226 cpupart_dispq_walk_init(mdb_walk_state_t *wsp) 227 { 228 uintptr_t addr = wsp->walk_addr; 229 dispq_walk_t *dw; 230 cpupart_t cpupart; 231 dispq_t dispq; 232 233 if (addr == NULL) { 234 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n"); 235 return (WALK_ERR); 236 } 237 238 if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) { 239 mdb_warn("failed to read cpupart_t at %p", addr); 240 return (WALK_ERR); 241 } 242 243 if (mdb_vread(&dispq, sizeof (dispq_t), 244 (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) { 245 mdb_warn("failed to read dispq_t at %p", 246 cpupart.cp_kp_queue.disp_q); 247 return (WALK_ERR); 248 } 249 250 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 251 252 dw->dw_npri = cpupart.cp_kp_queue.disp_npri; 253 dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q; 254 dw->dw_last = (uintptr_t)dispq.dq_last; 255 256 wsp->walk_addr = (uintptr_t)dispq.dq_first; 257 wsp->walk_data = dw; 258 259 return (WALK_NEXT); 260 } 261 262 int 263 dispq_walk_step(mdb_walk_state_t *wsp) 264 { 265 uintptr_t addr = wsp->walk_addr; 266 dispq_walk_t *dw = wsp->walk_data; 267 dispq_t dispq; 268 kthread_t t; 269 270 while (addr == NULL) { 271 if (--dw->dw_npri == 0) 272 return (WALK_DONE); 273 274 dw->dw_dispq += sizeof (dispq_t); 275 276 if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) { 277 mdb_warn("failed to read dispq_t at %p", dw->dw_dispq); 278 return (WALK_ERR); 279 } 280 281 dw->dw_last = (uintptr_t)dispq.dq_last; 282 addr = (uintptr_t)dispq.dq_first; 283 } 284 285 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 286 mdb_warn("failed to read kthread_t at %p", addr); 287 return (WALK_ERR); 288 } 289 290 if (addr == dw->dw_last) 291 wsp->walk_addr = NULL; 292 else 293 wsp->walk_addr = (uintptr_t)t.t_link; 294 295 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 296 } 297 298 void 299 dispq_walk_fini(mdb_walk_state_t *wsp) 300 { 301 mdb_free(wsp->walk_data, sizeof (dispq_walk_t)); 302 } 303 304 305 #define TF_INTR 0x01 306 #define TF_PROC 0x02 307 #define TF_BLOCK 0x04 308 #define TF_SIG 0x08 309 #define TF_DISP 0x10 310 #define TF_MERGE 0x20 311 312 /* 313 * Display a kthread_t. 314 * This is a little complicated, as there is a lot of information that 315 * the user could be interested in. The flags "ipbsd" are used to 316 * indicate which subset of the thread's members are to be displayed 317 * ('i' is the default). If multiple options are specified, multiple 318 * sets of data will be displayed in a vaguely readable format. If the 319 * 'm' option is specified, all the selected sets will be merged onto a 320 * single line for the benefit of those using wider-than-normal 321 * terminals. Having a generic mechanism for doing this would be 322 * really useful, but is a project best left to another day. 323 */ 324 325 int 326 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 327 { 328 kthread_t t; 329 uint_t oflags = 0; 330 uint_t fflag = FALSE; 331 int first; 332 char *state; 333 char stbuf[20]; 334 335 /* 336 * "Gracefully" handle printing a boatload of stuff to the 337 * screen. If we are not printing our first set of data, and 338 * we haven't been instructed to merge sets together, output a 339 * newline and indent such that the thread addresses form a 340 * column of their own. 341 */ 342 #define SPACER() \ 343 if (first) { \ 344 first = FALSE; \ 345 } else if (!(oflags & TF_MERGE)) { \ 346 mdb_printf("\n%?s", ""); \ 347 } 348 349 if (!(flags & DCMD_ADDRSPEC)) { 350 if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) { 351 mdb_warn("can't walk threads"); 352 return (DCMD_ERR); 353 } 354 return (DCMD_OK); 355 } 356 357 if (mdb_getopts(argc, argv, 358 'f', MDB_OPT_SETBITS, TRUE, &fflag, 359 'i', MDB_OPT_SETBITS, TF_INTR, &oflags, 360 'p', MDB_OPT_SETBITS, TF_PROC, &oflags, 361 'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags, 362 's', MDB_OPT_SETBITS, TF_SIG, &oflags, 363 'd', MDB_OPT_SETBITS, TF_DISP, &oflags, 364 'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc) 365 return (DCMD_USAGE); 366 367 /* 368 * If no sets were specified, choose the 'i' set. 369 */ 370 if (!(oflags & ~TF_MERGE)) 371 #ifdef _LP64 372 oflags = TF_INTR; 373 #else 374 oflags = TF_INTR | TF_DISP | TF_MERGE; 375 #endif 376 377 /* 378 * Print the relevant headers; note use of SPACER(). 379 */ 380 if (DCMD_HDRSPEC(flags)) { 381 first = TRUE; 382 mdb_printf("%<u>%?s%</u>", "ADDR"); 383 mdb_flush(); 384 385 if (oflags & TF_PROC) { 386 SPACER(); 387 mdb_printf("%<u> %?s %?s %?s%</u>", 388 "PROC", "LWP", "CRED"); 389 } 390 391 if (oflags & TF_INTR) { 392 SPACER(); 393 mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>", 394 "STATE", "FLG", "PFLG", 395 "SFLG", "PRI", "EPRI", "PIL", "INTR"); 396 } 397 398 if (oflags & TF_BLOCK) { 399 SPACER(); 400 mdb_printf("%<u> %?s %?s %?s %11s%</u>", 401 "WCHAN", "TS", "PITS", "SOBJ OPS"); 402 } 403 404 if (oflags & TF_SIG) { 405 SPACER(); 406 mdb_printf("%<u> %?s %16s %16s%</u>", 407 "SIGQUEUE", "SIG PEND", "SIG HELD"); 408 } 409 410 if (oflags & TF_DISP) { 411 SPACER(); 412 mdb_printf("%<u> %?s %5s %2s%</u>", 413 "DISPTIME", "BOUND", "PR"); 414 } 415 mdb_printf("\n"); 416 } 417 418 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 419 mdb_warn("can't read kthread_t at %#lx", addr); 420 return (DCMD_ERR); 421 } 422 423 if (fflag && (t.t_state == TS_FREE)) 424 return (DCMD_OK); 425 426 first = TRUE; 427 mdb_printf("%0?lx", addr); 428 429 /* process information */ 430 if (oflags & TF_PROC) { 431 SPACER(); 432 mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred); 433 } 434 435 /* priority/interrupt information */ 436 if (oflags & TF_INTR) { 437 SPACER(); 438 switch (t.t_state) { 439 case TS_FREE: 440 state = "free"; 441 break; 442 case TS_SLEEP: 443 state = "sleep"; 444 break; 445 case TS_RUN: 446 state = "run"; 447 break; 448 case TS_ONPROC: 449 state = "onproc"; 450 break; 451 case TS_ZOMB: 452 state = "zomb"; 453 break; 454 case TS_STOPPED: 455 state = "stopped"; 456 break; 457 case TS_WAIT: 458 state = "wait"; 459 break; 460 default: 461 (void) mdb_snprintf(stbuf, 11, "inval/%02x", t.t_state); 462 state = stbuf; 463 } 464 if (t.t_intr == NULL) { 465 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s", 466 state, t.t_flag, t.t_proc_flag, t.t_schedflag, 467 t.t_pri, t.t_epri, t.t_pil, "n/a"); 468 } else { 469 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p", 470 state, t.t_flag, t.t_proc_flag, t.t_schedflag, 471 t.t_pri, t.t_epri, t.t_pil, t.t_intr); 472 } 473 } 474 475 /* blocking information */ 476 if (oflags & TF_BLOCK) { 477 SPACER(); 478 (void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops); 479 stbuf[11] = '\0'; 480 mdb_printf(" %?p %?p %?p %11s", 481 t.t_wchan, t.t_ts, t.t_prioinv, stbuf); 482 } 483 484 /* signal information */ 485 if (oflags & TF_SIG) { 486 SPACER(); 487 mdb_printf(" %?p %016llx %016llx", 488 t.t_sigqueue, t.t_sig, t.t_hold); 489 } 490 491 /* dispatcher stuff */ 492 if (oflags & TF_DISP) { 493 SPACER(); 494 mdb_printf(" %?lx %5d %2d", 495 t.t_disp_time, t.t_bind_cpu, t.t_preempt); 496 } 497 498 mdb_printf("\n"); 499 500 #undef SPACER 501 502 return (DCMD_OK); 503 } 504 505 void 506 thread_help(void) 507 { 508 mdb_printf( 509 "The flags -ipbsd control which information is displayed. When\n" 510 "combined, the fields are displayed on separate lines unless the\n" 511 "-m option is given.\n" 512 "\n" 513 "\t-b\tprint blocked thread state\n" 514 "\t-d\tprint dispatcher state\n" 515 "\t-f\tignore freed threads\n" 516 "\t-i\tprint basic thread state (default)\n" 517 "\t-m\tdisplay results on a single line\n" 518 "\t-p\tprint process and lwp state\n" 519 "\t-s\tprint signal state\n"); 520 } 521 522 /* 523 * List a combination of kthread_t and proc_t. Add stack traces in verbose mode. 524 */ 525 int 526 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 527 { 528 int i; 529 uint_t count = 0; 530 uint_t verbose = FALSE; 531 uint_t notaskq = FALSE; 532 kthread_t t; 533 taskq_t tq; 534 proc_t p; 535 char cmd[80]; 536 mdb_arg_t cmdarg; 537 538 if (!(flags & DCMD_ADDRSPEC)) { 539 if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) { 540 mdb_warn("can't walk threads"); 541 return (DCMD_ERR); 542 } 543 return (DCMD_OK); 544 } 545 546 i = mdb_getopts(argc, argv, 547 't', MDB_OPT_SETBITS, TRUE, ¬askq, 548 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL); 549 550 if (i != argc) { 551 if (i != argc - 1 || !verbose) 552 return (DCMD_USAGE); 553 554 if (argv[i].a_type == MDB_TYPE_IMMEDIATE) 555 count = (uint_t)argv[i].a_un.a_val; 556 else 557 count = (uint_t)mdb_strtoull(argv[i].a_un.a_str); 558 } 559 560 if (DCMD_HDRSPEC(flags)) { 561 if (verbose) 562 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n", 563 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN"); 564 else 565 mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n", 566 "ADDR", "PROC", "LWP", "CMD", "LWPID"); 567 } 568 569 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 570 mdb_warn("failed to read kthread_t at %p", addr); 571 return (DCMD_ERR); 572 } 573 574 if (notaskq && t.t_taskq != NULL) 575 return (DCMD_OK); 576 577 if (t.t_state == TS_FREE) 578 return (DCMD_OK); 579 580 if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) { 581 mdb_warn("failed to read proc at %p", t.t_procp); 582 return (DCMD_ERR); 583 } 584 585 if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1) 586 tq.tq_name[0] = '\0'; 587 588 if (verbose) { 589 mdb_printf("%0?p %?p %?p %3u %3d %?p\n", 590 addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan); 591 592 mdb_inc_indent(2); 593 594 mdb_printf("PC: %a", t.t_pc); 595 if (t.t_tid == 0) { 596 if (tq.tq_name[0] != '\0') 597 mdb_printf(" TASKQ: %s\n", tq.tq_name); 598 else 599 mdb_printf(" THREAD: %a()\n", t.t_startpc); 600 } else { 601 mdb_printf(" CMD: %s\n", p.p_user.u_psargs); 602 } 603 604 mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count); 605 cmdarg.a_type = MDB_TYPE_STRING; 606 cmdarg.a_un.a_str = cmd; 607 608 (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg); 609 610 mdb_dec_indent(2); 611 612 mdb_printf("\n"); 613 } else { 614 mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp); 615 if (t.t_tid == 0) { 616 if (tq.tq_name[0] != '\0') 617 mdb_printf(" tq:%s\n", tq.tq_name); 618 else 619 mdb_printf(" %a()\n", t.t_startpc); 620 } else { 621 mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid); 622 } 623 } 624 625 return (DCMD_OK); 626 } 627 628 void 629 threadlist_help(void) 630 { 631 mdb_printf( 632 " -v print verbose output including C stack trace\n" 633 " -t skip threads belonging to a taskq\n" 634 " count print no more than count arguments (default 0)\n"); 635 } 636