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 #include <mdb/mdb_modapi.h> 27 #include <sys/types.h> 28 #include <sys/mutex.h> 29 #include <sys/thread.h> 30 #include <sys/condvar.h> 31 #include <sys/sleepq.h> 32 #include <sys/sobject.h> 33 #include <sys/rwlock_impl.h> 34 #include <sys/turnstile.h> 35 #include <sys/proc.h> 36 #include <sys/mutex_impl.h> 37 38 #include <stdio.h> 39 40 struct sobj_type_info { 41 int sobj_type; 42 const char *sobj_name; 43 const char *sobj_ops_name; 44 } sobj_types[] = { 45 { SOBJ_MUTEX, "mutex", "mutex_sobj_ops" }, 46 { SOBJ_RWLOCK, "rwlock", "rw_sobj_ops" }, 47 { SOBJ_CV, "cv", "cv_sobj_ops" }, 48 { SOBJ_SEMA, "sema", "sema_sobj_ops" }, 49 { SOBJ_USER, "user", "lwp_sobj_ops" }, 50 { SOBJ_USER_PI, "user_pi", "lwp_sobj_pi_ops" }, 51 { SOBJ_SHUTTLE, "shuttle", "shuttle_sobj_ops" } 52 }; 53 #define NUM_SOBJ_TYPES (sizeof (sobj_types) / sizeof (*sobj_types)) 54 55 void 56 sobj_type_to_text(int type, char *out, size_t sz) 57 { 58 int idx; 59 if (type == SOBJ_NONE) { 60 mdb_snprintf(out, sz, "<none>"); 61 return; 62 } 63 64 for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 65 struct sobj_type_info *info = &sobj_types[idx]; 66 if (info->sobj_type == type) { 67 mdb_snprintf(out, sz, "%s", 68 sobj_types[idx].sobj_name); 69 return; 70 } 71 } 72 mdb_snprintf(out, sz, "<unk:%02x>", type); 73 } 74 75 void 76 sobj_ops_to_text(uintptr_t addr, char *out, size_t sz) 77 { 78 sobj_ops_t ops; 79 80 if (addr == 0) { 81 mdb_snprintf(out, sz, "<none>"); 82 return; 83 } 84 if (mdb_vread(&ops, sizeof (ops), addr) == -1) { 85 mdb_snprintf(out, sz, "??", ops.sobj_type); 86 return; 87 } 88 89 sobj_type_to_text(ops.sobj_type, out, sz); 90 } 91 92 int 93 sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out) 94 { 95 int idx; 96 GElf_Sym sym; 97 98 for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 99 struct sobj_type_info *info = &sobj_types[idx]; 100 if (strcasecmp(info->sobj_name, name) == 0) { 101 if (mdb_lookup_by_name(info->sobj_ops_name, 102 &sym) == -1) { 103 mdb_warn("unable to find symbol \"%s\"", 104 info->sobj_ops_name); 105 return (-1); 106 } 107 *sobj_ops_out = (uintptr_t)sym.st_value; 108 return (0); 109 } 110 } 111 112 mdb_warn("sobj type \"%s\" unknown\n", name); 113 return (-1); 114 } 115 116 void 117 sobj_type_walk(void (*cbfunc)(int, const char *, const char *, void *), 118 void *cbarg) 119 { 120 int idx; 121 122 for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 123 struct sobj_type_info *info = &sobj_types[idx]; 124 cbfunc(info->sobj_type, info->sobj_name, info->sobj_ops_name, 125 cbarg); 126 } 127 } 128 129 typedef struct wchan_walk_data { 130 caddr_t *ww_seen; 131 int ww_seen_size; 132 int ww_seen_ndx; 133 uintptr_t ww_thr; 134 sleepq_head_t ww_sleepq[NSLEEPQ]; 135 int ww_sleepq_ndx; 136 uintptr_t ww_compare; 137 } wchan_walk_data_t; 138 139 int 140 wchan_walk_init(mdb_walk_state_t *wsp) 141 { 142 wchan_walk_data_t *ww = 143 mdb_zalloc(sizeof (wchan_walk_data_t), UM_SLEEP); 144 145 if (mdb_readvar(&ww->ww_sleepq[0], "sleepq_head") == -1) { 146 mdb_warn("failed to read sleepq"); 147 mdb_free(ww, sizeof (wchan_walk_data_t)); 148 return (WALK_ERR); 149 } 150 151 if ((ww->ww_compare = wsp->walk_addr) == NULL) { 152 if (mdb_readvar(&ww->ww_seen_size, "nthread") == -1) { 153 mdb_warn("failed to read nthread"); 154 mdb_free(ww, sizeof (wchan_walk_data_t)); 155 return (WALK_ERR); 156 } 157 158 ww->ww_seen = mdb_alloc(ww->ww_seen_size * 159 sizeof (caddr_t), UM_SLEEP); 160 } else { 161 ww->ww_sleepq_ndx = SQHASHINDEX(wsp->walk_addr); 162 } 163 164 wsp->walk_data = ww; 165 return (WALK_NEXT); 166 } 167 168 int 169 wchan_walk_step(mdb_walk_state_t *wsp) 170 { 171 wchan_walk_data_t *ww = wsp->walk_data; 172 sleepq_head_t *sq; 173 kthread_t thr; 174 uintptr_t t; 175 int i; 176 177 again: 178 /* 179 * Get the address of the first thread on the next sleepq in the 180 * sleepq hash. If ww_compare is set, ww_sleepq_ndx is already 181 * set to the appropriate sleepq index for the desired cv. 182 */ 183 for (t = ww->ww_thr; t == NULL; ) { 184 if (ww->ww_sleepq_ndx == NSLEEPQ) 185 return (WALK_DONE); 186 187 sq = &ww->ww_sleepq[ww->ww_sleepq_ndx++]; 188 t = (uintptr_t)sq->sq_queue.sq_first; 189 190 /* 191 * If we were looking for a specific cv and we're at the end 192 * of its sleepq, we're done walking. 193 */ 194 if (t == NULL && ww->ww_compare != NULL) 195 return (WALK_DONE); 196 } 197 198 /* 199 * Read in the thread. If it's t_wchan pointer is NULL, the thread has 200 * woken up since we took a snapshot of the sleepq (i.e. we are probably 201 * being applied to a live system); we can't believe the t_link pointer 202 * anymore either, so just skip to the next sleepq index. 203 */ 204 if (mdb_vread(&thr, sizeof (thr), t) != sizeof (thr)) { 205 mdb_warn("failed to read thread at %p", t); 206 return (WALK_ERR); 207 } 208 209 if (thr.t_wchan == NULL) { 210 ww->ww_thr = NULL; 211 goto again; 212 } 213 214 /* 215 * Set ww_thr to the address of the next thread in the sleepq list. 216 */ 217 ww->ww_thr = (uintptr_t)thr.t_link; 218 219 /* 220 * If we're walking a specific cv, invoke the callback if we've 221 * found a match, or loop back to the top and read the next thread. 222 */ 223 if (ww->ww_compare != NULL) { 224 if (ww->ww_compare == (uintptr_t)thr.t_wchan) 225 return (wsp->walk_callback(t, &thr, wsp->walk_cbdata)); 226 227 if (ww->ww_thr == NULL) 228 return (WALK_DONE); 229 230 goto again; 231 } 232 233 /* 234 * If we're walking all cvs, seen if we've already encountered this one 235 * on the current sleepq. If we have, skip to the next thread. 236 */ 237 for (i = 0; i < ww->ww_seen_ndx; i++) { 238 if (ww->ww_seen[i] == thr.t_wchan) 239 goto again; 240 } 241 242 /* 243 * If we're not at the end of a sleepq, save t_wchan; otherwise reset 244 * the seen index so our array is empty at the start of the next sleepq. 245 * If we hit seen_size this is a live kernel and nthread is now larger, 246 * cope by replacing the final element in our memory. 247 */ 248 if (ww->ww_thr != NULL) { 249 if (ww->ww_seen_ndx < ww->ww_seen_size) 250 ww->ww_seen[ww->ww_seen_ndx++] = thr.t_wchan; 251 else 252 ww->ww_seen[ww->ww_seen_size - 1] = thr.t_wchan; 253 } else 254 ww->ww_seen_ndx = 0; 255 256 return (wsp->walk_callback((uintptr_t)thr.t_wchan, 257 NULL, wsp->walk_cbdata)); 258 } 259 260 void 261 wchan_walk_fini(mdb_walk_state_t *wsp) 262 { 263 wchan_walk_data_t *ww = wsp->walk_data; 264 265 mdb_free(ww->ww_seen, ww->ww_seen_size * sizeof (uintptr_t)); 266 mdb_free(ww, sizeof (wchan_walk_data_t)); 267 } 268 269 struct wcdata { 270 sobj_ops_t sobj; 271 int nwaiters; 272 }; 273 274 /*ARGSUSED*/ 275 static int 276 wchaninfo_twalk(uintptr_t addr, const kthread_t *t, struct wcdata *wc) 277 { 278 if (wc->sobj.sobj_type == SOBJ_NONE) { 279 (void) mdb_vread(&wc->sobj, sizeof (sobj_ops_t), 280 (uintptr_t)t->t_sobj_ops); 281 } 282 283 wc->nwaiters++; 284 return (WALK_NEXT); 285 } 286 287 static int 288 wchaninfo_vtwalk(uintptr_t addr, const kthread_t *t, int *first) 289 { 290 proc_t p; 291 292 (void) mdb_vread(&p, sizeof (p), (uintptr_t)t->t_procp); 293 294 if (*first) { 295 *first = 0; 296 mdb_printf(": %0?p %s\n", addr, p.p_user.u_comm); 297 } else { 298 mdb_printf("%*s%0?p %s\n", (int)(sizeof (uintptr_t) * 2 + 17), 299 "", addr, p.p_user.u_comm); 300 } 301 302 return (WALK_NEXT); 303 } 304 305 /*ARGSUSED*/ 306 static int 307 wchaninfo_walk(uintptr_t addr, void *ignored, uint_t *verbose) 308 { 309 struct wcdata wc; 310 int first = 1; 311 312 bzero(&wc, sizeof (wc)); 313 wc.sobj.sobj_type = SOBJ_NONE; 314 315 if (mdb_pwalk("wchan", (mdb_walk_cb_t)wchaninfo_twalk, &wc, addr) < 0) { 316 mdb_warn("failed to walk wchan %p", addr); 317 return (WALK_NEXT); 318 } 319 320 mdb_printf("%0?p %4s %8d%s", addr, 321 wc.sobj.sobj_type == SOBJ_CV ? "cond" : 322 wc.sobj.sobj_type == SOBJ_SEMA ? "sema" : "??", 323 wc.nwaiters, (*verbose) ? "" : "\n"); 324 325 if (*verbose != 0 && wc.nwaiters != 0 && mdb_pwalk("wchan", 326 (mdb_walk_cb_t)wchaninfo_vtwalk, &first, addr) == -1) { 327 mdb_warn("failed to walk waiters for wchan %p", addr); 328 mdb_printf("\n"); 329 } 330 331 return (WALK_NEXT); 332 } 333 334 int 335 wchaninfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 336 { 337 uint_t v = FALSE; 338 339 if (mdb_getopts(argc, argv, 340 'v', MDB_OPT_SETBITS, TRUE, &v, NULL) != argc) 341 return (DCMD_USAGE); 342 343 if (v == TRUE) { 344 mdb_printf("%-?s %-4s %8s %-?s %s\n", 345 "ADDR", "TYPE", "NWAITERS", "THREAD", "PROC"); 346 } else 347 mdb_printf("%-?s %-4s %8s\n", "ADDR", "TYPE", "NWAITERS"); 348 349 if (flags & DCMD_ADDRSPEC) { 350 if (wchaninfo_walk(addr, NULL, &v) == WALK_ERR) 351 return (DCMD_ERR); 352 } else if (mdb_walk("wchan", (mdb_walk_cb_t)wchaninfo_walk, &v) == -1) { 353 mdb_warn("failed to walk wchans"); 354 return (DCMD_ERR); 355 } 356 357 return (DCMD_OK); 358 } 359 360 int 361 blocked_walk_init(mdb_walk_state_t *wsp) 362 { 363 if ((wsp->walk_data = (void *)wsp->walk_addr) == NULL) { 364 mdb_warn("must specify a sobj * for blocked walk"); 365 return (WALK_ERR); 366 } 367 368 wsp->walk_addr = NULL; 369 370 if (mdb_layered_walk("thread", wsp) == -1) { 371 mdb_warn("couldn't walk 'thread'"); 372 return (WALK_ERR); 373 } 374 375 return (WALK_NEXT); 376 } 377 378 int 379 blocked_walk_step(mdb_walk_state_t *wsp) 380 { 381 uintptr_t addr = (uintptr_t)((const kthread_t *)wsp->walk_layer)->t_ts; 382 uintptr_t taddr = wsp->walk_addr; 383 turnstile_t ts; 384 385 if (mdb_vread(&ts, sizeof (ts), addr) == -1) { 386 mdb_warn("couldn't read %p's turnstile at %p", taddr, addr); 387 return (WALK_ERR); 388 } 389 390 if (ts.ts_waiters == 0 || ts.ts_sobj != wsp->walk_data) 391 return (WALK_NEXT); 392 393 return (wsp->walk_callback(taddr, wsp->walk_layer, wsp->walk_cbdata)); 394 } 395 396 typedef struct rwlock_block { 397 struct rwlock_block *rw_next; 398 int rw_qnum; 399 uintptr_t rw_thread; 400 } rwlock_block_t; 401 402 static int 403 rwlock_walk(uintptr_t taddr, const kthread_t *t, rwlock_block_t **rwp) 404 { 405 turnstile_t ts; 406 uintptr_t addr = (uintptr_t)t->t_ts; 407 rwlock_block_t *rw; 408 int state, i; 409 410 if (mdb_vread(&ts, sizeof (ts), addr) == -1) { 411 mdb_warn("couldn't read %p's turnstile at %p", taddr, addr); 412 return (WALK_ERR); 413 } 414 415 for (i = 0; i < TS_NUM_Q; i++) { 416 if ((uintptr_t)t->t_sleepq == 417 (uintptr_t)&ts.ts_sleepq[i] - (uintptr_t)&ts + addr) 418 break; 419 } 420 421 if (i == TS_NUM_Q) { 422 if ((state = mdb_get_state()) == MDB_STATE_DEAD || 423 state == MDB_STATE_STOPPED) { 424 /* 425 * This shouldn't happen post-mortem or under kmdb; 426 * the blocked walk returned a thread which wasn't 427 * actually blocked on its turnstile. This may happen 428 * in-situ if the thread wakes up during the ::rwlock. 429 */ 430 mdb_warn("thread %p isn't blocked on ts %p\n", 431 taddr, addr); 432 return (WALK_ERR); 433 } 434 435 return (WALK_NEXT); 436 } 437 438 rw = mdb_alloc(sizeof (rwlock_block_t), UM_SLEEP | UM_GC); 439 440 rw->rw_next = *rwp; 441 rw->rw_qnum = i; 442 rw->rw_thread = taddr; 443 *rwp = rw; 444 445 return (WALK_NEXT); 446 } 447 448 /* 449 * > rwd_rwlock::rwlock 450 * ADDR OWNER/COUNT FLAGS WAITERS 451 * 7835dee8 READERS=1 B011 30004393d20 (W) 452 * || 453 * WRITE_WANTED -------+| 454 * HAS_WAITERS --------+ 455 * 456 * |--ADDR_WIDTH--| |--OWNR_WIDTH--| 457 * |--LBL_OFFSET--||-LBL_WIDTH| 458 * |--------------LONG-------------| 459 * |------------WAITER_OFFSET------------| 460 */ 461 462 #ifdef _LP64 463 #define RW_ADDR_WIDTH 16 464 #define RW_OWNR_WIDTH 16 465 #else 466 #define RW_ADDR_WIDTH 8 467 #define RW_OWNR_WIDTH 11 468 #endif 469 470 #define RW_LONG (RW_ADDR_WIDTH + 1 + RW_OWNR_WIDTH) 471 #define RW_LBL_WIDTH 12 472 #define RW_LBL_OFFSET (RW_ADDR_WIDTH + RW_OWNR_WIDTH - 3 - RW_LBL_WIDTH) 473 #define RW_WAITER_OFFSET (RW_LONG + 6) 474 475 /* Access rwlock bits */ 476 #define RW_BIT(n, offon) (wwwh & (1 << (n)) ? offon[1] : offon[0]) 477 #define RW_BIT_SET(n) (wwwh & (1 << (n))) 478 479 /* Print a waiter (if any) and a newline */ 480 #define RW_NEWLINE \ 481 if (rw != NULL) { \ 482 int q = rw->rw_qnum; \ 483 mdb_printf(" %?p (%s)", rw->rw_thread, \ 484 q == TS_READER_Q ? "R" : q == TS_WRITER_Q ? "W" : "?"); \ 485 rw = rw->rw_next; \ 486 } \ 487 mdb_printf("\n"); 488 489 /*ARGSUSED*/ 490 int 491 rwlock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 492 { 493 rwlock_impl_t lock; 494 rwlock_block_t *rw = NULL; 495 uintptr_t wwwh; 496 497 if (!(flags & DCMD_ADDRSPEC) || addr == NULL || argc != 0) 498 return (DCMD_USAGE); 499 500 if (mdb_vread(&lock, sizeof (lock), addr) == -1) { 501 mdb_warn("failed to read rwlock at 0x%p", addr); 502 return (DCMD_ERR); 503 } 504 505 if (mdb_pwalk("blocked", (mdb_walk_cb_t)rwlock_walk, &rw, addr) == -1) { 506 mdb_warn("couldn't walk 'blocked' for sobj %p", addr); 507 return (WALK_ERR); 508 } 509 510 mdb_printf("%?s %*s %5s %?s\n", "ADDR", 511 RW_OWNR_WIDTH, "OWNER/COUNT", "FLAGS", "WAITERS"); 512 513 mdb_printf("%?p ", addr); 514 515 if (((wwwh = lock.rw_wwwh) & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) 516 mdb_printf("%*s", RW_OWNR_WIDTH, "1"); 517 else if ((wwwh = lock.rw_wwwh) & RW_WRITE_LOCKED) 518 mdb_printf("%*p", RW_OWNR_WIDTH, wwwh & RW_OWNER); 519 else { 520 uintptr_t count = (wwwh & RW_HOLD_COUNT) >> RW_HOLD_COUNT_SHIFT; 521 char c[20]; 522 523 mdb_snprintf(c, 20, "READERS=%ld", count); 524 mdb_printf("%*s", RW_OWNR_WIDTH, count ? c : "-"); 525 } 526 527 mdb_printf(" B%c%c%c", 528 RW_BIT(2, "01"), RW_BIT(1, "01"), RW_BIT(0, "01")); 529 RW_NEWLINE; 530 531 mdb_printf("%*s%c %c%c%c", RW_LONG - 1, "", 532 " |"[(wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK], 533 RW_BIT(2, " |"), RW_BIT(1, " |"), RW_BIT(0, " |")); 534 RW_NEWLINE; 535 536 if ((wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) { 537 mdb_printf("%*s%*s --+---+", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 538 "DESTROYED"); 539 goto no_zero; 540 } 541 542 if (!RW_BIT_SET(2)) 543 goto no_two; 544 545 mdb_printf("%*s%*s ------+%c%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 546 "WRITE_LOCKED", RW_BIT(1, " |"), RW_BIT(0, " |")); 547 RW_NEWLINE; 548 549 no_two: 550 if (!RW_BIT_SET(1)) 551 goto no_one; 552 553 mdb_printf("%*s%*s -------+%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 554 "WRITE_WANTED", RW_BIT(0, " |")); 555 RW_NEWLINE; 556 557 no_one: 558 if (!RW_BIT_SET(0)) 559 goto no_zero; 560 561 mdb_printf("%*s%*s --------+", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 562 "HAS_WAITERS"); 563 RW_NEWLINE; 564 565 no_zero: 566 while (rw != NULL) { 567 mdb_printf("%*s", RW_WAITER_OFFSET, ""); 568 RW_NEWLINE; 569 } 570 571 return (DCMD_OK); 572 } 573 574 int 575 mutex(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 576 { 577 mutex_impl_t lock; 578 uint_t force = FALSE; 579 580 if (!(flags & DCMD_ADDRSPEC)) { 581 return (DCMD_USAGE); 582 } 583 584 if (mdb_getopts(argc, argv, 585 'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc) { 586 return (DCMD_USAGE); 587 } 588 589 if (mdb_vread(&lock, sizeof (lock), addr) == -1) { 590 mdb_warn("failed to read mutex at 0x%0?p", addr); 591 return (DCMD_ERR); 592 } 593 594 if (DCMD_HDRSPEC(flags)) { 595 mdb_printf("%<u>%?s %5s %?s %6s %6s %7s%</u>\n", 596 "ADDR", "TYPE", "HELD", "MINSPL", "OLDSPL", "WAITERS"); 597 } 598 599 if (MUTEX_TYPE_SPIN(&lock)) { 600 struct spin_mutex *sp = &lock.m_spin; 601 602 if (!force && (sp->m_filler != 0 || 603 sp->m_minspl > PIL_MAX || sp->m_oldspl > PIL_MAX || 604 (sp->m_spinlock != 0 && sp->m_spinlock != 0xff))) { 605 mdb_warn("%a: invalid spin lock " 606 "(-f to dump anyway)\n", addr); 607 return (DCMD_ERR); 608 } 609 610 if (sp->m_spinlock == 0xff) { 611 mdb_printf("%0?p %5s %?s %6d %6d %7s\n", 612 addr, "spin", "yes", sp->m_minspl, sp->m_oldspl, 613 "-"); 614 } else { 615 mdb_printf("%0?p %5s %?s %6d %6s %7s\n", 616 addr, "spin", "no", sp->m_minspl, "-", "-"); 617 } 618 619 } else { 620 kthread_t *owner = MUTEX_OWNER(&lock); 621 char *waiters = MUTEX_HAS_WAITERS(&lock) ? "yes" : "no"; 622 623 if (!force && (!MUTEX_TYPE_ADAPTIVE(&lock) || 624 (owner == NULL && MUTEX_HAS_WAITERS(&lock)))) { 625 mdb_warn("%a: invalid adaptive mutex " 626 "(-f to dump anyway)\n", addr); 627 return (DCMD_ERR); 628 } 629 630 if (owner != NULL) { 631 mdb_printf("%0?p %5s %?p %6s %6s %7s\n", 632 addr, "adapt", owner, "-", "-", waiters); 633 } else { 634 mdb_printf("%0?p %5s %?s %6s %6s %7s\n", 635 addr, "adapt", "no", "-", "-", waiters); 636 } 637 } 638 return (DCMD_OK); 639 } 640 641 void 642 mutex_help(void) 643 { 644 mdb_printf("Options:\n" 645 " -f force printing even if the data seems to be" 646 " inconsistent\n"); 647 } 648 649 int 650 turnstile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 651 { 652 turnstile_t t; 653 654 if (argc != 0) 655 return (DCMD_USAGE); 656 657 if (!(flags & DCMD_ADDRSPEC)) { 658 if (mdb_walk_dcmd("turnstile_cache", "turnstile", argc, argv) 659 == -1) { 660 mdb_warn("can't walk turnstiles"); 661 return (DCMD_ERR); 662 } 663 return (DCMD_OK); 664 } 665 666 if (DCMD_HDRSPEC(flags)) 667 mdb_printf("%<u>%?s %?s %5s %4s %?s %?s%</u>\n", 668 "ADDR", "SOBJ", "WTRS", "EPRI", "ITOR", "PRIOINV"); 669 670 if (mdb_vread(&t, sizeof (turnstile_t), addr) == -1) { 671 mdb_warn("can't read turnstile_t at %p", addr); 672 return (DCMD_ERR); 673 } 674 675 mdb_printf("%0?p %?p %5d %4d %?p %?p\n", 676 addr, t.ts_sobj, t.ts_waiters, t.ts_epri, 677 t.ts_inheritor, t.ts_prioinv); 678 679 return (DCMD_OK); 680 } 681 682 /* 683 * Macros and structure definition copied from turnstile.c. 684 * This is unfortunate, but half the macros we need aren't usable from 685 * within mdb anyway. 686 */ 687 #define TURNSTILE_HASH_SIZE 128 /* must be power of 2 */ 688 #define TURNSTILE_HASH_MASK (TURNSTILE_HASH_SIZE - 1) 689 #define TURNSTILE_SOBJ_HASH(sobj) \ 690 ((((int)sobj >> 2) + ((int)sobj >> 9)) & TURNSTILE_HASH_MASK) 691 692 typedef struct turnstile_chain { 693 turnstile_t *tc_first; /* first turnstile on hash chain */ 694 disp_lock_t tc_lock; /* lock for this hash chain */ 695 } turnstile_chain_t; 696 697 /* 698 * Given the address of a blocked-upon synchronization object, return 699 * the address of its turnstile. 700 */ 701 702 /*ARGSUSED*/ 703 int 704 sobj2ts(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 705 { 706 GElf_Sym sym; 707 int isupi; 708 int ttoff; 709 uintptr_t ttable; 710 turnstile_t ts, *tsp; 711 turnstile_chain_t tc; 712 713 if (!(flags & DCMD_ADDRSPEC) || argc != 0) 714 return (DCMD_USAGE); 715 716 if (mdb_lookup_by_name("upimutextab", &sym) == -1) { 717 mdb_warn("unable to reference upimutextab\n"); 718 return (DCMD_ERR); 719 } 720 isupi = addr - (uintptr_t)sym.st_value < sym.st_size; 721 ttoff = (isupi ? 0 : TURNSTILE_HASH_SIZE) + TURNSTILE_SOBJ_HASH(addr); 722 723 if (mdb_lookup_by_name("turnstile_table", &sym) == -1) { 724 mdb_warn("unable to reference turnstile_table"); 725 return (DCMD_ERR); 726 } 727 ttable = (uintptr_t)sym.st_value + sizeof (turnstile_chain_t) * ttoff; 728 729 if (mdb_vread(&tc, sizeof (turnstile_chain_t), ttable) == -1) { 730 mdb_warn("unable to read turnstile_chain_t at %#lx", ttable); 731 return (DCMD_ERR); 732 } 733 734 for (tsp = tc.tc_first; tsp != NULL; tsp = ts.ts_next) { 735 if (mdb_vread(&ts, sizeof (turnstile_t), 736 (uintptr_t)tsp) == -1) { 737 mdb_warn("unable to read turnstile_t at %#p", tsp); 738 return (DCMD_ERR); 739 } 740 if ((uintptr_t)ts.ts_sobj == addr) { 741 mdb_printf("%p\n", tsp); 742 break; 743 } 744 } 745 746 return (DCMD_OK); 747 } 748