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 37 typedef struct thread_walk { 38 kthread_t *tw_thread; 39 uintptr_t tw_last; 40 uint_t tw_inproc; 41 uint_t tw_step; 42 } thread_walk_t; 43 44 int 45 thread_walk_init(mdb_walk_state_t *wsp) 46 { 47 thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP); 48 49 if (wsp->walk_addr == NULL) { 50 if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) { 51 mdb_warn("failed to read 'allthreads'"); 52 mdb_free(twp, sizeof (thread_walk_t)); 53 return (WALK_ERR); 54 } 55 56 twp->tw_inproc = FALSE; 57 58 } else { 59 proc_t pr; 60 61 if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) { 62 mdb_warn("failed to read proc at %p", wsp->walk_addr); 63 mdb_free(twp, sizeof (thread_walk_t)); 64 return (WALK_ERR); 65 } 66 67 wsp->walk_addr = (uintptr_t)pr.p_tlist; 68 twp->tw_inproc = TRUE; 69 } 70 71 twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP); 72 twp->tw_last = wsp->walk_addr; 73 twp->tw_step = FALSE; 74 75 wsp->walk_data = twp; 76 return (WALK_NEXT); 77 } 78 79 int 80 thread_walk_step(mdb_walk_state_t *wsp) 81 { 82 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 83 int status; 84 85 if (wsp->walk_addr == NULL) 86 return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */ 87 88 if (twp->tw_step && wsp->walk_addr == twp->tw_last) 89 return (WALK_DONE); /* We've wrapped around */ 90 91 if (mdb_vread(twp->tw_thread, sizeof (kthread_t), 92 wsp->walk_addr) == -1) { 93 mdb_warn("failed to read thread at %p", wsp->walk_addr); 94 return (WALK_DONE); 95 } 96 97 status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread, 98 wsp->walk_cbdata); 99 100 if (twp->tw_inproc) 101 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw; 102 else 103 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next; 104 105 twp->tw_step = TRUE; 106 return (status); 107 } 108 109 void 110 thread_walk_fini(mdb_walk_state_t *wsp) 111 { 112 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 113 114 mdb_free(twp->tw_thread, sizeof (kthread_t)); 115 mdb_free(twp, sizeof (thread_walk_t)); 116 } 117 118 int 119 deathrow_walk_init(mdb_walk_state_t *wsp) 120 { 121 if (mdb_layered_walk("thread_deathrow", wsp) == -1) { 122 mdb_warn("couldn't walk 'thread_deathrow'"); 123 return (WALK_ERR); 124 } 125 126 if (mdb_layered_walk("lwp_deathrow", wsp) == -1) { 127 mdb_warn("couldn't walk 'lwp_deathrow'"); 128 return (WALK_ERR); 129 } 130 131 return (WALK_NEXT); 132 } 133 134 int 135 deathrow_walk_step(mdb_walk_state_t *wsp) 136 { 137 kthread_t t; 138 uintptr_t addr = wsp->walk_addr; 139 140 if (addr == NULL) 141 return (WALK_DONE); 142 143 if (mdb_vread(&t, sizeof (t), addr) == -1) { 144 mdb_warn("couldn't read deathrow thread at %p", addr); 145 return (WALK_ERR); 146 } 147 148 wsp->walk_addr = (uintptr_t)t.t_forw; 149 150 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 151 } 152 153 int 154 thread_deathrow_walk_init(mdb_walk_state_t *wsp) 155 { 156 if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) { 157 mdb_warn("couldn't read symbol 'thread_deathrow'"); 158 return (WALK_ERR); 159 } 160 161 return (WALK_NEXT); 162 } 163 164 int 165 lwp_deathrow_walk_init(mdb_walk_state_t *wsp) 166 { 167 if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) { 168 mdb_warn("couldn't read symbol 'lwp_deathrow'"); 169 return (WALK_ERR); 170 } 171 172 return (WALK_NEXT); 173 } 174 175 176 typedef struct dispq_walk { 177 int dw_npri; 178 uintptr_t dw_dispq; 179 uintptr_t dw_last; 180 } dispq_walk_t; 181 182 int 183 cpu_dispq_walk_init(mdb_walk_state_t *wsp) 184 { 185 uintptr_t addr = wsp->walk_addr; 186 dispq_walk_t *dw; 187 cpu_t cpu; 188 dispq_t dispq; 189 disp_t disp; 190 191 if (addr == NULL) { 192 mdb_warn("cpu_dispq walk needs a cpu_t address\n"); 193 return (WALK_ERR); 194 } 195 196 if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) { 197 mdb_warn("failed to read cpu_t at %p", addr); 198 return (WALK_ERR); 199 } 200 201 if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) { 202 mdb_warn("failed to read disp_t at %p", cpu.cpu_disp); 203 return (WALK_ERR); 204 } 205 206 if (mdb_vread(&dispq, sizeof (dispq_t), 207 (uintptr_t)disp.disp_q) == -1) { 208 mdb_warn("failed to read dispq_t at %p", disp.disp_q); 209 return (WALK_ERR); 210 } 211 212 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 213 214 dw->dw_npri = disp.disp_npri; 215 dw->dw_dispq = (uintptr_t)disp.disp_q; 216 dw->dw_last = (uintptr_t)dispq.dq_last; 217 218 wsp->walk_addr = (uintptr_t)dispq.dq_first; 219 wsp->walk_data = dw; 220 221 return (WALK_NEXT); 222 } 223 224 int 225 cpupart_dispq_walk_init(mdb_walk_state_t *wsp) 226 { 227 uintptr_t addr = wsp->walk_addr; 228 dispq_walk_t *dw; 229 cpupart_t cpupart; 230 dispq_t dispq; 231 232 if (addr == NULL) { 233 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n"); 234 return (WALK_ERR); 235 } 236 237 if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) { 238 mdb_warn("failed to read cpupart_t at %p", addr); 239 return (WALK_ERR); 240 } 241 242 if (mdb_vread(&dispq, sizeof (dispq_t), 243 (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) { 244 mdb_warn("failed to read dispq_t at %p", 245 cpupart.cp_kp_queue.disp_q); 246 return (WALK_ERR); 247 } 248 249 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 250 251 dw->dw_npri = cpupart.cp_kp_queue.disp_npri; 252 dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q; 253 dw->dw_last = (uintptr_t)dispq.dq_last; 254 255 wsp->walk_addr = (uintptr_t)dispq.dq_first; 256 wsp->walk_data = dw; 257 258 return (WALK_NEXT); 259 } 260 261 int 262 dispq_walk_step(mdb_walk_state_t *wsp) 263 { 264 uintptr_t addr = wsp->walk_addr; 265 dispq_walk_t *dw = wsp->walk_data; 266 dispq_t dispq; 267 kthread_t t; 268 269 while (addr == NULL) { 270 if (--dw->dw_npri == 0) 271 return (WALK_DONE); 272 273 dw->dw_dispq += sizeof (dispq_t); 274 275 if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) { 276 mdb_warn("failed to read dispq_t at %p", dw->dw_dispq); 277 return (WALK_ERR); 278 } 279 280 dw->dw_last = (uintptr_t)dispq.dq_last; 281 addr = (uintptr_t)dispq.dq_first; 282 } 283 284 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 285 mdb_warn("failed to read kthread_t at %p", addr); 286 return (WALK_ERR); 287 } 288 289 if (addr == dw->dw_last) 290 wsp->walk_addr = NULL; 291 else 292 wsp->walk_addr = (uintptr_t)t.t_link; 293 294 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 295 } 296 297 void 298 dispq_walk_fini(mdb_walk_state_t *wsp) 299 { 300 mdb_free(wsp->walk_data, sizeof (dispq_walk_t)); 301 } 302 303 304 #define TF_INTR 0x01 305 #define TF_PROC 0x02 306 #define TF_BLOCK 0x04 307 #define TF_SIG 0x08 308 #define TF_DISP 0x10 309 #define TF_MERGE 0x20 310 311 /* 312 * Display a kthread_t. 313 * This is a little complicated, as there is a lot of information that 314 * the user could be interested in. The flags "ipbsd" are used to 315 * indicate which subset of the thread's members are to be displayed 316 * ('i' is the default). If multiple options are specified, multiple 317 * sets of data will be displayed in a vaguely readable format. If the 318 * 'm' option is specified, all the selected sets will be merged onto a 319 * single line for the benefit of those using wider-than-normal 320 * terminals. Having a generic mechanism for doing this would be 321 * really useful, but is a project best left to another day. 322 */ 323 324 int 325 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 326 { 327 kthread_t t; 328 uint_t oflags = 0; 329 uint_t fflag = FALSE; 330 int first; 331 char *state; 332 char stbuf[20]; 333 334 /* 335 * "Gracefully" handle printing a boatload of stuff to the 336 * screen. If we are not printing our first set of data, and 337 * we haven't been instructed to merge sets together, output a 338 * newline and indent such that the thread addresses form a 339 * column of their own. 340 */ 341 #define SPACER() \ 342 if (first) { \ 343 first = FALSE; \ 344 } else if (!(oflags & TF_MERGE)) { \ 345 mdb_printf("\n%?s", ""); \ 346 } 347 348 if (!(flags & DCMD_ADDRSPEC)) { 349 if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) { 350 mdb_warn("can't walk threads"); 351 return (DCMD_ERR); 352 } 353 return (DCMD_OK); 354 } 355 356 if (mdb_getopts(argc, argv, 357 'f', MDB_OPT_SETBITS, TRUE, &fflag, 358 'i', MDB_OPT_SETBITS, TF_INTR, &oflags, 359 'p', MDB_OPT_SETBITS, TF_PROC, &oflags, 360 'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags, 361 's', MDB_OPT_SETBITS, TF_SIG, &oflags, 362 'd', MDB_OPT_SETBITS, TF_DISP, &oflags, 363 'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc) 364 return (DCMD_USAGE); 365 366 /* 367 * If no sets were specified, choose the 'i' set. 368 */ 369 if (!(oflags & ~TF_MERGE)) 370 #ifdef _LP64 371 oflags = TF_INTR; 372 #else 373 oflags = TF_INTR | TF_DISP | TF_MERGE; 374 #endif 375 376 /* 377 * Print the relevant headers; note use of SPACER(). 378 */ 379 if (DCMD_HDRSPEC(flags)) { 380 first = TRUE; 381 mdb_printf("%<u>%?s%</u>", "ADDR"); 382 mdb_flush(); 383 384 if (oflags & TF_PROC) { 385 SPACER(); 386 mdb_printf("%<u> %?s %?s %?s%</u>", 387 "PROC", "LWP", "CRED"); 388 } 389 390 if (oflags & TF_INTR) { 391 SPACER(); 392 mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>", 393 "STATE", "FLG", "PFLG", 394 "SFLG", "PRI", "EPRI", "PIL", "INTR"); 395 } 396 397 if (oflags & TF_BLOCK) { 398 SPACER(); 399 mdb_printf("%<u> %?s %?s %?s %11s%</u>", 400 "WCHAN", "TS", "PITS", "SOBJ OPS"); 401 } 402 403 if (oflags & TF_SIG) { 404 SPACER(); 405 mdb_printf("%<u> %?s %16s %16s%</u>", 406 "SIGQUEUE", "SIG PEND", "SIG HELD"); 407 } 408 409 if (oflags & TF_DISP) { 410 SPACER(); 411 mdb_printf("%<u> %?s %5s %2s%</u>", 412 "DISPTIME", "BOUND", "PR"); 413 } 414 mdb_printf("\n"); 415 } 416 417 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 418 mdb_warn("can't read kthread_t at %#lx", addr); 419 return (DCMD_ERR); 420 } 421 422 if (fflag && (t.t_state == TS_FREE)) 423 return (DCMD_OK); 424 425 first = TRUE; 426 mdb_printf("%0?lx", addr); 427 428 /* process information */ 429 if (oflags & TF_PROC) { 430 SPACER(); 431 mdb_printf(" %?p %?p %?p", 432 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 kthread_t t; 532 proc_t p; 533 char cmd[80]; 534 mdb_arg_t cmdarg; 535 536 if (!(flags & DCMD_ADDRSPEC)) { 537 if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) { 538 mdb_warn("can't walk threads"); 539 return (DCMD_ERR); 540 } 541 return (DCMD_OK); 542 } 543 544 i = mdb_getopts(argc, argv, 545 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL); 546 547 if (i != argc) { 548 if (i != argc - 1 || !verbose) 549 return (DCMD_USAGE); 550 551 if (argv[i].a_type == MDB_TYPE_IMMEDIATE) 552 count = (uint_t)argv[i].a_un.a_val; 553 else 554 count = (uint_t)mdb_strtoull(argv[i].a_un.a_str); 555 } 556 557 if (DCMD_HDRSPEC(flags)) { 558 if (verbose) 559 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n", 560 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN"); 561 else 562 mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n", 563 "ADDR", "PROC", "LWP", "CMD", "LWPID"); 564 } 565 566 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 567 mdb_warn("failed to read kthread_t at %p", addr); 568 return (DCMD_ERR); 569 } 570 571 if (t.t_state == TS_FREE) 572 return (DCMD_OK); 573 574 if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) { 575 mdb_warn("failed to read proc at %p", t.t_procp); 576 return (DCMD_ERR); 577 } 578 579 if (verbose) { 580 mdb_printf("%0?p %?p %?p %3u %3d %?p\n", 581 addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan); 582 583 mdb_inc_indent(2); 584 585 mdb_printf("PC: %a", t.t_pc); 586 if (t.t_tid == 0) 587 mdb_printf(" THREAD: %a()\n", t.t_startpc); 588 else 589 mdb_printf(" CMD: %s\n", p.p_user.u_psargs); 590 591 mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count); 592 cmdarg.a_type = MDB_TYPE_STRING; 593 cmdarg.a_un.a_str = cmd; 594 595 (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg); 596 597 mdb_dec_indent(2); 598 599 mdb_printf("\n"); 600 } else { 601 mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp); 602 if (t.t_tid == 0) 603 mdb_printf(" %a()\n", t.t_startpc); 604 else 605 mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid); 606 } 607 608 return (DCMD_OK); 609 } 610 611 void 612 threadlist_help(void) 613 { 614 mdb_printf( 615 " -v print verbose output including C stack trace\n" 616 " count print no more than count arguments (default 0)\n"); 617 } 618