10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*8721SJonathan.Adams@Sun.COM * Common Development and Distribution License (the "License"). 6*8721SJonathan.Adams@Sun.COM * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*8721SJonathan.Adams@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 270Sstevel@tonic-gate #include <sys/types.h> 280Sstevel@tonic-gate #include <sys/mutex.h> 290Sstevel@tonic-gate #include <sys/thread.h> 300Sstevel@tonic-gate #include <sys/condvar.h> 310Sstevel@tonic-gate #include <sys/sleepq.h> 320Sstevel@tonic-gate #include <sys/sobject.h> 330Sstevel@tonic-gate #include <sys/rwlock_impl.h> 340Sstevel@tonic-gate #include <sys/turnstile.h> 350Sstevel@tonic-gate #include <sys/proc.h> 360Sstevel@tonic-gate #include <sys/mutex_impl.h> 370Sstevel@tonic-gate 380Sstevel@tonic-gate #include <stdio.h> 390Sstevel@tonic-gate 40*8721SJonathan.Adams@Sun.COM struct sobj_type_info { 41*8721SJonathan.Adams@Sun.COM int sobj_type; 42*8721SJonathan.Adams@Sun.COM const char *sobj_name; 43*8721SJonathan.Adams@Sun.COM const char *sobj_ops_name; 44*8721SJonathan.Adams@Sun.COM } sobj_types[] = { 45*8721SJonathan.Adams@Sun.COM { SOBJ_MUTEX, "mutex", "mutex_sobj_ops" }, 46*8721SJonathan.Adams@Sun.COM { SOBJ_RWLOCK, "rwlock", "rw_sobj_ops" }, 47*8721SJonathan.Adams@Sun.COM { SOBJ_CV, "cv", "cv_sobj_ops" }, 48*8721SJonathan.Adams@Sun.COM { SOBJ_SEMA, "sema", "sema_sobj_ops" }, 49*8721SJonathan.Adams@Sun.COM { SOBJ_USER, "user", "lwp_sobj_ops" }, 50*8721SJonathan.Adams@Sun.COM { SOBJ_USER_PI, "user_pi", "lwp_sobj_pi_ops" }, 51*8721SJonathan.Adams@Sun.COM { SOBJ_SHUTTLE, "shuttle", "shuttle_sobj_ops" } 52*8721SJonathan.Adams@Sun.COM }; 53*8721SJonathan.Adams@Sun.COM #define NUM_SOBJ_TYPES (sizeof (sobj_types) / sizeof (*sobj_types)) 54*8721SJonathan.Adams@Sun.COM 55*8721SJonathan.Adams@Sun.COM void 56*8721SJonathan.Adams@Sun.COM sobj_type_to_text(int type, char *out, size_t sz) 57*8721SJonathan.Adams@Sun.COM { 58*8721SJonathan.Adams@Sun.COM int idx; 59*8721SJonathan.Adams@Sun.COM if (type == SOBJ_NONE) { 60*8721SJonathan.Adams@Sun.COM mdb_snprintf(out, sz, "<none>"); 61*8721SJonathan.Adams@Sun.COM return; 62*8721SJonathan.Adams@Sun.COM } 63*8721SJonathan.Adams@Sun.COM 64*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 65*8721SJonathan.Adams@Sun.COM struct sobj_type_info *info = &sobj_types[idx]; 66*8721SJonathan.Adams@Sun.COM if (info->sobj_type == type) { 67*8721SJonathan.Adams@Sun.COM mdb_snprintf(out, sz, "%s", 68*8721SJonathan.Adams@Sun.COM sobj_types[idx].sobj_name); 69*8721SJonathan.Adams@Sun.COM return; 70*8721SJonathan.Adams@Sun.COM } 71*8721SJonathan.Adams@Sun.COM } 72*8721SJonathan.Adams@Sun.COM mdb_snprintf(out, sz, "<unk:%02x>", type); 73*8721SJonathan.Adams@Sun.COM } 74*8721SJonathan.Adams@Sun.COM 75*8721SJonathan.Adams@Sun.COM void 76*8721SJonathan.Adams@Sun.COM sobj_ops_to_text(uintptr_t addr, char *out, size_t sz) 77*8721SJonathan.Adams@Sun.COM { 78*8721SJonathan.Adams@Sun.COM sobj_ops_t ops; 79*8721SJonathan.Adams@Sun.COM 80*8721SJonathan.Adams@Sun.COM if (addr == 0) { 81*8721SJonathan.Adams@Sun.COM mdb_snprintf(out, sz, "<none>"); 82*8721SJonathan.Adams@Sun.COM return; 83*8721SJonathan.Adams@Sun.COM } 84*8721SJonathan.Adams@Sun.COM if (mdb_vread(&ops, sizeof (ops), addr) == -1) { 85*8721SJonathan.Adams@Sun.COM mdb_snprintf(out, sz, "??", ops.sobj_type); 86*8721SJonathan.Adams@Sun.COM return; 87*8721SJonathan.Adams@Sun.COM } 88*8721SJonathan.Adams@Sun.COM 89*8721SJonathan.Adams@Sun.COM sobj_type_to_text(ops.sobj_type, out, sz); 90*8721SJonathan.Adams@Sun.COM } 91*8721SJonathan.Adams@Sun.COM 92*8721SJonathan.Adams@Sun.COM int 93*8721SJonathan.Adams@Sun.COM sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out) 94*8721SJonathan.Adams@Sun.COM { 95*8721SJonathan.Adams@Sun.COM int idx; 96*8721SJonathan.Adams@Sun.COM GElf_Sym sym; 97*8721SJonathan.Adams@Sun.COM 98*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 99*8721SJonathan.Adams@Sun.COM struct sobj_type_info *info = &sobj_types[idx]; 100*8721SJonathan.Adams@Sun.COM if (strcasecmp(info->sobj_name, name) == 0) { 101*8721SJonathan.Adams@Sun.COM if (mdb_lookup_by_name(info->sobj_ops_name, 102*8721SJonathan.Adams@Sun.COM &sym) == -1) { 103*8721SJonathan.Adams@Sun.COM mdb_warn("unable to find symbol \"%s\"", 104*8721SJonathan.Adams@Sun.COM info->sobj_ops_name); 105*8721SJonathan.Adams@Sun.COM return (-1); 106*8721SJonathan.Adams@Sun.COM } 107*8721SJonathan.Adams@Sun.COM *sobj_ops_out = (uintptr_t)sym.st_value; 108*8721SJonathan.Adams@Sun.COM return (0); 109*8721SJonathan.Adams@Sun.COM } 110*8721SJonathan.Adams@Sun.COM } 111*8721SJonathan.Adams@Sun.COM 112*8721SJonathan.Adams@Sun.COM mdb_warn("sobj type \"%s\" unknown\n", name); 113*8721SJonathan.Adams@Sun.COM return (-1); 114*8721SJonathan.Adams@Sun.COM } 115*8721SJonathan.Adams@Sun.COM 116*8721SJonathan.Adams@Sun.COM void 117*8721SJonathan.Adams@Sun.COM sobj_type_walk(void (*cbfunc)(int, const char *, const char *, void *), 118*8721SJonathan.Adams@Sun.COM void *cbarg) 119*8721SJonathan.Adams@Sun.COM { 120*8721SJonathan.Adams@Sun.COM int idx; 121*8721SJonathan.Adams@Sun.COM 122*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 123*8721SJonathan.Adams@Sun.COM struct sobj_type_info *info = &sobj_types[idx]; 124*8721SJonathan.Adams@Sun.COM cbfunc(info->sobj_type, info->sobj_name, info->sobj_ops_name, 125*8721SJonathan.Adams@Sun.COM cbarg); 126*8721SJonathan.Adams@Sun.COM } 127*8721SJonathan.Adams@Sun.COM } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate typedef struct wchan_walk_data { 1300Sstevel@tonic-gate caddr_t *ww_seen; 1310Sstevel@tonic-gate int ww_seen_size; 1320Sstevel@tonic-gate int ww_seen_ndx; 1330Sstevel@tonic-gate uintptr_t ww_thr; 1340Sstevel@tonic-gate sleepq_head_t ww_sleepq[NSLEEPQ]; 1350Sstevel@tonic-gate int ww_sleepq_ndx; 1360Sstevel@tonic-gate uintptr_t ww_compare; 1370Sstevel@tonic-gate } wchan_walk_data_t; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate int 1400Sstevel@tonic-gate wchan_walk_init(mdb_walk_state_t *wsp) 1410Sstevel@tonic-gate { 1420Sstevel@tonic-gate wchan_walk_data_t *ww = 1430Sstevel@tonic-gate mdb_zalloc(sizeof (wchan_walk_data_t), UM_SLEEP); 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate if (mdb_readvar(&ww->ww_sleepq[0], "sleepq_head") == -1) { 1460Sstevel@tonic-gate mdb_warn("failed to read sleepq"); 1470Sstevel@tonic-gate mdb_free(ww, sizeof (wchan_walk_data_t)); 1480Sstevel@tonic-gate return (WALK_ERR); 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate if ((ww->ww_compare = wsp->walk_addr) == NULL) { 1520Sstevel@tonic-gate if (mdb_readvar(&ww->ww_seen_size, "nthread") == -1) { 1530Sstevel@tonic-gate mdb_warn("failed to read nthread"); 1540Sstevel@tonic-gate mdb_free(ww, sizeof (wchan_walk_data_t)); 1550Sstevel@tonic-gate return (WALK_ERR); 1560Sstevel@tonic-gate } 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate ww->ww_seen = mdb_alloc(ww->ww_seen_size * 1590Sstevel@tonic-gate sizeof (caddr_t), UM_SLEEP); 1600Sstevel@tonic-gate } else { 1610Sstevel@tonic-gate ww->ww_sleepq_ndx = SQHASHINDEX(wsp->walk_addr); 1620Sstevel@tonic-gate } 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate wsp->walk_data = ww; 1650Sstevel@tonic-gate return (WALK_NEXT); 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate int 1690Sstevel@tonic-gate wchan_walk_step(mdb_walk_state_t *wsp) 1700Sstevel@tonic-gate { 1710Sstevel@tonic-gate wchan_walk_data_t *ww = wsp->walk_data; 1720Sstevel@tonic-gate sleepq_head_t *sq; 1730Sstevel@tonic-gate kthread_t thr; 1740Sstevel@tonic-gate uintptr_t t; 1750Sstevel@tonic-gate int i; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate again: 1780Sstevel@tonic-gate /* 1790Sstevel@tonic-gate * Get the address of the first thread on the next sleepq in the 1800Sstevel@tonic-gate * sleepq hash. If ww_compare is set, ww_sleepq_ndx is already 1810Sstevel@tonic-gate * set to the appropriate sleepq index for the desired cv. 1820Sstevel@tonic-gate */ 1830Sstevel@tonic-gate for (t = ww->ww_thr; t == NULL; ) { 1840Sstevel@tonic-gate if (ww->ww_sleepq_ndx == NSLEEPQ) 1850Sstevel@tonic-gate return (WALK_DONE); 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate sq = &ww->ww_sleepq[ww->ww_sleepq_ndx++]; 1880Sstevel@tonic-gate t = (uintptr_t)sq->sq_queue.sq_first; 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate /* 1910Sstevel@tonic-gate * If we were looking for a specific cv and we're at the end 1920Sstevel@tonic-gate * of its sleepq, we're done walking. 1930Sstevel@tonic-gate */ 1940Sstevel@tonic-gate if (t == NULL && ww->ww_compare != NULL) 1950Sstevel@tonic-gate return (WALK_DONE); 1960Sstevel@tonic-gate } 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /* 1990Sstevel@tonic-gate * Read in the thread. If it's t_wchan pointer is NULL, the thread has 2000Sstevel@tonic-gate * woken up since we took a snapshot of the sleepq (i.e. we are probably 2010Sstevel@tonic-gate * being applied to a live system); we can't believe the t_link pointer 2020Sstevel@tonic-gate * anymore either, so just skip to the next sleepq index. 2030Sstevel@tonic-gate */ 2040Sstevel@tonic-gate if (mdb_vread(&thr, sizeof (thr), t) != sizeof (thr)) { 2050Sstevel@tonic-gate mdb_warn("failed to read thread at %p", t); 2060Sstevel@tonic-gate return (WALK_ERR); 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate if (thr.t_wchan == NULL) { 2100Sstevel@tonic-gate ww->ww_thr = NULL; 2110Sstevel@tonic-gate goto again; 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate /* 2150Sstevel@tonic-gate * Set ww_thr to the address of the next thread in the sleepq list. 2160Sstevel@tonic-gate */ 2170Sstevel@tonic-gate ww->ww_thr = (uintptr_t)thr.t_link; 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * If we're walking a specific cv, invoke the callback if we've 2210Sstevel@tonic-gate * found a match, or loop back to the top and read the next thread. 2220Sstevel@tonic-gate */ 2230Sstevel@tonic-gate if (ww->ww_compare != NULL) { 2240Sstevel@tonic-gate if (ww->ww_compare == (uintptr_t)thr.t_wchan) 2250Sstevel@tonic-gate return (wsp->walk_callback(t, &thr, wsp->walk_cbdata)); 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate if (ww->ww_thr == NULL) 2280Sstevel@tonic-gate return (WALK_DONE); 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate goto again; 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate /* 2340Sstevel@tonic-gate * If we're walking all cvs, seen if we've already encountered this one 2350Sstevel@tonic-gate * on the current sleepq. If we have, skip to the next thread. 2360Sstevel@tonic-gate */ 2370Sstevel@tonic-gate for (i = 0; i < ww->ww_seen_ndx; i++) { 2380Sstevel@tonic-gate if (ww->ww_seen[i] == thr.t_wchan) 2390Sstevel@tonic-gate goto again; 2400Sstevel@tonic-gate } 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2430Sstevel@tonic-gate * If we're not at the end of a sleepq, save t_wchan; otherwise reset 2440Sstevel@tonic-gate * the seen index so our array is empty at the start of the next sleepq. 2450Sstevel@tonic-gate * If we hit seen_size this is a live kernel and nthread is now larger, 2460Sstevel@tonic-gate * cope by replacing the final element in our memory. 2470Sstevel@tonic-gate */ 2480Sstevel@tonic-gate if (ww->ww_thr != NULL) { 2490Sstevel@tonic-gate if (ww->ww_seen_ndx < ww->ww_seen_size) 2500Sstevel@tonic-gate ww->ww_seen[ww->ww_seen_ndx++] = thr.t_wchan; 2510Sstevel@tonic-gate else 2520Sstevel@tonic-gate ww->ww_seen[ww->ww_seen_size - 1] = thr.t_wchan; 2530Sstevel@tonic-gate } else 2540Sstevel@tonic-gate ww->ww_seen_ndx = 0; 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate return (wsp->walk_callback((uintptr_t)thr.t_wchan, 2570Sstevel@tonic-gate NULL, wsp->walk_cbdata)); 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate void 2610Sstevel@tonic-gate wchan_walk_fini(mdb_walk_state_t *wsp) 2620Sstevel@tonic-gate { 2630Sstevel@tonic-gate wchan_walk_data_t *ww = wsp->walk_data; 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate mdb_free(ww->ww_seen, ww->ww_seen_size * sizeof (uintptr_t)); 2660Sstevel@tonic-gate mdb_free(ww, sizeof (wchan_walk_data_t)); 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate struct wcdata { 2700Sstevel@tonic-gate sobj_ops_t sobj; 2710Sstevel@tonic-gate int nwaiters; 2720Sstevel@tonic-gate }; 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate /*ARGSUSED*/ 2750Sstevel@tonic-gate static int 2760Sstevel@tonic-gate wchaninfo_twalk(uintptr_t addr, const kthread_t *t, struct wcdata *wc) 2770Sstevel@tonic-gate { 2780Sstevel@tonic-gate if (wc->sobj.sobj_type == SOBJ_NONE) { 2790Sstevel@tonic-gate (void) mdb_vread(&wc->sobj, sizeof (sobj_ops_t), 2800Sstevel@tonic-gate (uintptr_t)t->t_sobj_ops); 2810Sstevel@tonic-gate } 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate wc->nwaiters++; 2840Sstevel@tonic-gate return (WALK_NEXT); 2850Sstevel@tonic-gate } 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate static int 2880Sstevel@tonic-gate wchaninfo_vtwalk(uintptr_t addr, const kthread_t *t, int *first) 2890Sstevel@tonic-gate { 2900Sstevel@tonic-gate proc_t p; 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate (void) mdb_vread(&p, sizeof (p), (uintptr_t)t->t_procp); 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate if (*first) { 2950Sstevel@tonic-gate *first = 0; 2960Sstevel@tonic-gate mdb_printf(": %0?p %s\n", addr, p.p_user.u_comm); 2970Sstevel@tonic-gate } else { 2980Sstevel@tonic-gate mdb_printf("%*s%0?p %s\n", (int)(sizeof (uintptr_t) * 2 + 17), 2990Sstevel@tonic-gate "", addr, p.p_user.u_comm); 3000Sstevel@tonic-gate } 3010Sstevel@tonic-gate 3020Sstevel@tonic-gate return (WALK_NEXT); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /*ARGSUSED*/ 3060Sstevel@tonic-gate static int 3070Sstevel@tonic-gate wchaninfo_walk(uintptr_t addr, void *ignored, uint_t *verbose) 3080Sstevel@tonic-gate { 3090Sstevel@tonic-gate struct wcdata wc; 3100Sstevel@tonic-gate int first = 1; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate bzero(&wc, sizeof (wc)); 3130Sstevel@tonic-gate wc.sobj.sobj_type = SOBJ_NONE; 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate if (mdb_pwalk("wchan", (mdb_walk_cb_t)wchaninfo_twalk, &wc, addr) < 0) { 3160Sstevel@tonic-gate mdb_warn("failed to walk wchan %p", addr); 3170Sstevel@tonic-gate return (WALK_NEXT); 3180Sstevel@tonic-gate } 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate mdb_printf("%0?p %4s %8d%s", addr, 3210Sstevel@tonic-gate wc.sobj.sobj_type == SOBJ_CV ? "cond" : 3220Sstevel@tonic-gate wc.sobj.sobj_type == SOBJ_SEMA ? "sema" : "??", 3230Sstevel@tonic-gate wc.nwaiters, (*verbose) ? "" : "\n"); 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate if (*verbose != 0 && wc.nwaiters != 0 && mdb_pwalk("wchan", 3260Sstevel@tonic-gate (mdb_walk_cb_t)wchaninfo_vtwalk, &first, addr) == -1) { 3270Sstevel@tonic-gate mdb_warn("failed to walk waiters for wchan %p", addr); 3280Sstevel@tonic-gate mdb_printf("\n"); 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate return (WALK_NEXT); 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate int 3350Sstevel@tonic-gate wchaninfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate uint_t v = FALSE; 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate if (mdb_getopts(argc, argv, 3400Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &v, NULL) != argc) 3410Sstevel@tonic-gate return (DCMD_USAGE); 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate if (v == TRUE) { 3440Sstevel@tonic-gate mdb_printf("%-?s %-4s %8s %-?s %s\n", 3450Sstevel@tonic-gate "ADDR", "TYPE", "NWAITERS", "THREAD", "PROC"); 3460Sstevel@tonic-gate } else 3470Sstevel@tonic-gate mdb_printf("%-?s %-4s %8s\n", "ADDR", "TYPE", "NWAITERS"); 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 3500Sstevel@tonic-gate if (wchaninfo_walk(addr, NULL, &v) == WALK_ERR) 3510Sstevel@tonic-gate return (DCMD_ERR); 3520Sstevel@tonic-gate } else if (mdb_walk("wchan", (mdb_walk_cb_t)wchaninfo_walk, &v) == -1) { 3530Sstevel@tonic-gate mdb_warn("failed to walk wchans"); 3540Sstevel@tonic-gate return (DCMD_ERR); 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate return (DCMD_OK); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate int 3610Sstevel@tonic-gate blocked_walk_init(mdb_walk_state_t *wsp) 3620Sstevel@tonic-gate { 3630Sstevel@tonic-gate if ((wsp->walk_data = (void *)wsp->walk_addr) == NULL) { 3640Sstevel@tonic-gate mdb_warn("must specify a sobj * for blocked walk"); 3650Sstevel@tonic-gate return (WALK_ERR); 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate wsp->walk_addr = NULL; 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate if (mdb_layered_walk("thread", wsp) == -1) { 3710Sstevel@tonic-gate mdb_warn("couldn't walk 'thread'"); 3720Sstevel@tonic-gate return (WALK_ERR); 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate return (WALK_NEXT); 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate int 3790Sstevel@tonic-gate blocked_walk_step(mdb_walk_state_t *wsp) 3800Sstevel@tonic-gate { 3810Sstevel@tonic-gate uintptr_t addr = (uintptr_t)((const kthread_t *)wsp->walk_layer)->t_ts; 3820Sstevel@tonic-gate uintptr_t taddr = wsp->walk_addr; 3830Sstevel@tonic-gate turnstile_t ts; 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate if (mdb_vread(&ts, sizeof (ts), addr) == -1) { 3860Sstevel@tonic-gate mdb_warn("couldn't read %p's turnstile at %p", taddr, addr); 3870Sstevel@tonic-gate return (WALK_ERR); 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate if (ts.ts_waiters == 0 || ts.ts_sobj != wsp->walk_data) 3910Sstevel@tonic-gate return (WALK_NEXT); 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate return (wsp->walk_callback(taddr, wsp->walk_layer, wsp->walk_cbdata)); 3940Sstevel@tonic-gate } 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate typedef struct rwlock_block { 3970Sstevel@tonic-gate struct rwlock_block *rw_next; 3980Sstevel@tonic-gate int rw_qnum; 3990Sstevel@tonic-gate uintptr_t rw_thread; 4000Sstevel@tonic-gate } rwlock_block_t; 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate static int 4030Sstevel@tonic-gate rwlock_walk(uintptr_t taddr, const kthread_t *t, rwlock_block_t **rwp) 4040Sstevel@tonic-gate { 4050Sstevel@tonic-gate turnstile_t ts; 4060Sstevel@tonic-gate uintptr_t addr = (uintptr_t)t->t_ts; 4070Sstevel@tonic-gate rwlock_block_t *rw; 4080Sstevel@tonic-gate int state, i; 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate if (mdb_vread(&ts, sizeof (ts), addr) == -1) { 4110Sstevel@tonic-gate mdb_warn("couldn't read %p's turnstile at %p", taddr, addr); 4120Sstevel@tonic-gate return (WALK_ERR); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate for (i = 0; i < TS_NUM_Q; i++) { 4160Sstevel@tonic-gate if ((uintptr_t)t->t_sleepq == 4170Sstevel@tonic-gate (uintptr_t)&ts.ts_sleepq[i] - (uintptr_t)&ts + addr) 4180Sstevel@tonic-gate break; 4190Sstevel@tonic-gate } 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate if (i == TS_NUM_Q) { 4220Sstevel@tonic-gate if ((state = mdb_get_state()) == MDB_STATE_DEAD || 4230Sstevel@tonic-gate state == MDB_STATE_STOPPED) { 4240Sstevel@tonic-gate /* 4250Sstevel@tonic-gate * This shouldn't happen post-mortem or under kmdb; 4260Sstevel@tonic-gate * the blocked walk returned a thread which wasn't 4270Sstevel@tonic-gate * actually blocked on its turnstile. This may happen 4280Sstevel@tonic-gate * in-situ if the thread wakes up during the ::rwlock. 4290Sstevel@tonic-gate */ 4300Sstevel@tonic-gate mdb_warn("thread %p isn't blocked on ts %p\n", 4310Sstevel@tonic-gate taddr, addr); 4320Sstevel@tonic-gate return (WALK_ERR); 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate return (WALK_NEXT); 4360Sstevel@tonic-gate } 4370Sstevel@tonic-gate 4380Sstevel@tonic-gate rw = mdb_alloc(sizeof (rwlock_block_t), UM_SLEEP | UM_GC); 4390Sstevel@tonic-gate 4400Sstevel@tonic-gate rw->rw_next = *rwp; 4410Sstevel@tonic-gate rw->rw_qnum = i; 4420Sstevel@tonic-gate rw->rw_thread = taddr; 4430Sstevel@tonic-gate *rwp = rw; 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate return (WALK_NEXT); 4460Sstevel@tonic-gate } 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate /* 4490Sstevel@tonic-gate * > rwd_rwlock::rwlock 4500Sstevel@tonic-gate * ADDR OWNER/COUNT FLAGS WAITERS 4510Sstevel@tonic-gate * 7835dee8 READERS=1 B011 30004393d20 (W) 4520Sstevel@tonic-gate * || 4530Sstevel@tonic-gate * WRITE_WANTED -------+| 4540Sstevel@tonic-gate * HAS_WAITERS --------+ 4550Sstevel@tonic-gate * 4560Sstevel@tonic-gate * |--ADDR_WIDTH--| |--OWNR_WIDTH--| 4570Sstevel@tonic-gate * |--LBL_OFFSET--||-LBL_WIDTH| 4580Sstevel@tonic-gate * |--------------LONG-------------| 4590Sstevel@tonic-gate * |------------WAITER_OFFSET------------| 4600Sstevel@tonic-gate */ 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate #ifdef _LP64 4630Sstevel@tonic-gate #define RW_ADDR_WIDTH 16 4640Sstevel@tonic-gate #define RW_OWNR_WIDTH 16 4650Sstevel@tonic-gate #else 4660Sstevel@tonic-gate #define RW_ADDR_WIDTH 8 4670Sstevel@tonic-gate #define RW_OWNR_WIDTH 11 4680Sstevel@tonic-gate #endif 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate #define RW_LONG (RW_ADDR_WIDTH + 1 + RW_OWNR_WIDTH) 4710Sstevel@tonic-gate #define RW_LBL_WIDTH 12 4720Sstevel@tonic-gate #define RW_LBL_OFFSET (RW_ADDR_WIDTH + RW_OWNR_WIDTH - 3 - RW_LBL_WIDTH) 4730Sstevel@tonic-gate #define RW_WAITER_OFFSET (RW_LONG + 6) 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate /* Access rwlock bits */ 4760Sstevel@tonic-gate #define RW_BIT(n, offon) (wwwh & (1 << (n)) ? offon[1] : offon[0]) 4770Sstevel@tonic-gate #define RW_BIT_SET(n) (wwwh & (1 << (n))) 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* Print a waiter (if any) and a newline */ 4800Sstevel@tonic-gate #define RW_NEWLINE \ 4810Sstevel@tonic-gate if (rw != NULL) { \ 4820Sstevel@tonic-gate int q = rw->rw_qnum; \ 4830Sstevel@tonic-gate mdb_printf(" %?p (%s)", rw->rw_thread, \ 4840Sstevel@tonic-gate q == TS_READER_Q ? "R" : q == TS_WRITER_Q ? "W" : "?"); \ 4850Sstevel@tonic-gate rw = rw->rw_next; \ 4860Sstevel@tonic-gate } \ 4870Sstevel@tonic-gate mdb_printf("\n"); 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate /*ARGSUSED*/ 4900Sstevel@tonic-gate int 4910Sstevel@tonic-gate rwlock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 4920Sstevel@tonic-gate { 4930Sstevel@tonic-gate rwlock_impl_t lock; 4940Sstevel@tonic-gate rwlock_block_t *rw = NULL; 4950Sstevel@tonic-gate uintptr_t wwwh; 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC) || addr == NULL || argc != 0) 4980Sstevel@tonic-gate return (DCMD_USAGE); 4990Sstevel@tonic-gate 5000Sstevel@tonic-gate if (mdb_vread(&lock, sizeof (lock), addr) == -1) { 5010Sstevel@tonic-gate mdb_warn("failed to read rwlock at 0x%p", addr); 5020Sstevel@tonic-gate return (DCMD_ERR); 5030Sstevel@tonic-gate } 5040Sstevel@tonic-gate 5050Sstevel@tonic-gate if (mdb_pwalk("blocked", (mdb_walk_cb_t)rwlock_walk, &rw, addr) == -1) { 5060Sstevel@tonic-gate mdb_warn("couldn't walk 'blocked' for sobj %p", addr); 5070Sstevel@tonic-gate return (WALK_ERR); 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate mdb_printf("%?s %*s %5s %?s\n", "ADDR", 5110Sstevel@tonic-gate RW_OWNR_WIDTH, "OWNER/COUNT", "FLAGS", "WAITERS"); 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate mdb_printf("%?p ", addr); 5140Sstevel@tonic-gate 5150Sstevel@tonic-gate if (((wwwh = lock.rw_wwwh) & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) 5160Sstevel@tonic-gate mdb_printf("%*s", RW_OWNR_WIDTH, "1"); 5170Sstevel@tonic-gate else if ((wwwh = lock.rw_wwwh) & RW_WRITE_LOCKED) 5180Sstevel@tonic-gate mdb_printf("%*p", RW_OWNR_WIDTH, wwwh & RW_OWNER); 5190Sstevel@tonic-gate else { 5200Sstevel@tonic-gate uintptr_t count = (wwwh & RW_HOLD_COUNT) >> RW_HOLD_COUNT_SHIFT; 5210Sstevel@tonic-gate char c[20]; 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate mdb_snprintf(c, 20, "READERS=%ld", count); 5240Sstevel@tonic-gate mdb_printf("%*s", RW_OWNR_WIDTH, count ? c : "-"); 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate mdb_printf(" B%c%c%c", 5280Sstevel@tonic-gate RW_BIT(2, "01"), RW_BIT(1, "01"), RW_BIT(0, "01")); 5290Sstevel@tonic-gate RW_NEWLINE; 5300Sstevel@tonic-gate 5310Sstevel@tonic-gate mdb_printf("%*s%c %c%c%c", RW_LONG - 1, "", 5320Sstevel@tonic-gate " |"[(wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK], 5330Sstevel@tonic-gate RW_BIT(2, " |"), RW_BIT(1, " |"), RW_BIT(0, " |")); 5340Sstevel@tonic-gate RW_NEWLINE; 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate if ((wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) { 5370Sstevel@tonic-gate mdb_printf("%*s%*s --+---+", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5380Sstevel@tonic-gate "DESTROYED"); 5390Sstevel@tonic-gate goto no_zero; 5400Sstevel@tonic-gate } 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate if (!RW_BIT_SET(2)) 5430Sstevel@tonic-gate goto no_two; 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate mdb_printf("%*s%*s ------+%c%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5460Sstevel@tonic-gate "WRITE_LOCKED", RW_BIT(1, " |"), RW_BIT(0, " |")); 5470Sstevel@tonic-gate RW_NEWLINE; 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate no_two: 5500Sstevel@tonic-gate if (!RW_BIT_SET(1)) 5510Sstevel@tonic-gate goto no_one; 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate mdb_printf("%*s%*s -------+%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5540Sstevel@tonic-gate "WRITE_WANTED", RW_BIT(0, " |")); 5550Sstevel@tonic-gate RW_NEWLINE; 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate no_one: 5580Sstevel@tonic-gate if (!RW_BIT_SET(0)) 5590Sstevel@tonic-gate goto no_zero; 5600Sstevel@tonic-gate 5610Sstevel@tonic-gate mdb_printf("%*s%*s --------+", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5620Sstevel@tonic-gate "HAS_WAITERS"); 5630Sstevel@tonic-gate RW_NEWLINE; 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate no_zero: 5660Sstevel@tonic-gate while (rw != NULL) { 5670Sstevel@tonic-gate mdb_printf("%*s", RW_WAITER_OFFSET, ""); 5680Sstevel@tonic-gate RW_NEWLINE; 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate return (DCMD_OK); 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate int 5750Sstevel@tonic-gate mutex(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 5760Sstevel@tonic-gate { 5770Sstevel@tonic-gate mutex_impl_t lock; 5780Sstevel@tonic-gate uint_t force = FALSE; 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 5810Sstevel@tonic-gate return (DCMD_USAGE); 5820Sstevel@tonic-gate } 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate if (mdb_getopts(argc, argv, 5850Sstevel@tonic-gate 'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc) { 5860Sstevel@tonic-gate return (DCMD_USAGE); 5870Sstevel@tonic-gate } 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate if (mdb_vread(&lock, sizeof (lock), addr) == -1) { 5900Sstevel@tonic-gate mdb_warn("failed to read mutex at 0x%0?p", addr); 5910Sstevel@tonic-gate return (DCMD_ERR); 5920Sstevel@tonic-gate } 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 5950Sstevel@tonic-gate mdb_printf("%<u>%?s %5s %?s %6s %6s %7s%</u>\n", 5960Sstevel@tonic-gate "ADDR", "TYPE", "HELD", "MINSPL", "OLDSPL", "WAITERS"); 5970Sstevel@tonic-gate } 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate if (MUTEX_TYPE_SPIN(&lock)) { 6000Sstevel@tonic-gate struct spin_mutex *sp = &lock.m_spin; 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate if (!force && (sp->m_filler != 0 || 6030Sstevel@tonic-gate sp->m_minspl > PIL_MAX || sp->m_oldspl > PIL_MAX || 6040Sstevel@tonic-gate (sp->m_spinlock != 0 && sp->m_spinlock != 0xff))) { 6050Sstevel@tonic-gate mdb_warn("%a: invalid spin lock " 6060Sstevel@tonic-gate "(-f to dump anyway)\n", addr); 6070Sstevel@tonic-gate return (DCMD_ERR); 6080Sstevel@tonic-gate } 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate if (sp->m_spinlock == 0xff) { 6110Sstevel@tonic-gate mdb_printf("%0?p %5s %?s %6d %6d %7s\n", 6120Sstevel@tonic-gate addr, "spin", "yes", sp->m_minspl, sp->m_oldspl, 6130Sstevel@tonic-gate "-"); 6140Sstevel@tonic-gate } else { 6150Sstevel@tonic-gate mdb_printf("%0?p %5s %?s %6d %6s %7s\n", 6160Sstevel@tonic-gate addr, "spin", "no", sp->m_minspl, "-", "-"); 6170Sstevel@tonic-gate } 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate } else { 6200Sstevel@tonic-gate kthread_t *owner = MUTEX_OWNER(&lock); 6210Sstevel@tonic-gate char *waiters = MUTEX_HAS_WAITERS(&lock) ? "yes" : "no"; 6220Sstevel@tonic-gate 6230Sstevel@tonic-gate if (!force && (!MUTEX_TYPE_ADAPTIVE(&lock) || 6240Sstevel@tonic-gate (owner == NULL && MUTEX_HAS_WAITERS(&lock)))) { 6250Sstevel@tonic-gate mdb_warn("%a: invalid adaptive mutex " 6260Sstevel@tonic-gate "(-f to dump anyway)\n", addr); 6270Sstevel@tonic-gate return (DCMD_ERR); 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate if (owner != NULL) { 6310Sstevel@tonic-gate mdb_printf("%0?p %5s %?p %6s %6s %7s\n", 6320Sstevel@tonic-gate addr, "adapt", owner, "-", "-", waiters); 6330Sstevel@tonic-gate } else { 6340Sstevel@tonic-gate mdb_printf("%0?p %5s %?s %6s %6s %7s\n", 6350Sstevel@tonic-gate addr, "adapt", "no", "-", "-", waiters); 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate return (DCMD_OK); 6390Sstevel@tonic-gate } 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate void 6420Sstevel@tonic-gate mutex_help(void) 6430Sstevel@tonic-gate { 6440Sstevel@tonic-gate mdb_printf("Options:\n" 6450Sstevel@tonic-gate " -f force printing even if the data seems to be" 6460Sstevel@tonic-gate " inconsistent\n"); 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate int 6500Sstevel@tonic-gate turnstile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 6510Sstevel@tonic-gate { 6520Sstevel@tonic-gate turnstile_t t; 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate if (argc != 0) 6550Sstevel@tonic-gate return (DCMD_USAGE); 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 6580Sstevel@tonic-gate if (mdb_walk_dcmd("turnstile_cache", "turnstile", argc, argv) 6590Sstevel@tonic-gate == -1) { 6600Sstevel@tonic-gate mdb_warn("can't walk turnstiles"); 6610Sstevel@tonic-gate return (DCMD_ERR); 6620Sstevel@tonic-gate } 6630Sstevel@tonic-gate return (DCMD_OK); 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 6670Sstevel@tonic-gate mdb_printf("%<u>%?s %?s %5s %4s %?s %?s%</u>\n", 6680Sstevel@tonic-gate "ADDR", "SOBJ", "WTRS", "EPRI", "ITOR", "PRIOINV"); 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate if (mdb_vread(&t, sizeof (turnstile_t), addr) == -1) { 6710Sstevel@tonic-gate mdb_warn("can't read turnstile_t at %p", addr); 6720Sstevel@tonic-gate return (DCMD_ERR); 6730Sstevel@tonic-gate } 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate mdb_printf("%0?p %?p %5d %4d %?p %?p\n", 6760Sstevel@tonic-gate addr, t.ts_sobj, t.ts_waiters, t.ts_epri, 6770Sstevel@tonic-gate t.ts_inheritor, t.ts_prioinv); 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate return (DCMD_OK); 6800Sstevel@tonic-gate } 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate /* 6830Sstevel@tonic-gate * Macros and structure definition copied from turnstile.c. 6840Sstevel@tonic-gate * This is unfortunate, but half the macros we need aren't usable from 6850Sstevel@tonic-gate * within mdb anyway. 6860Sstevel@tonic-gate */ 6870Sstevel@tonic-gate #define TURNSTILE_HASH_SIZE 128 /* must be power of 2 */ 6880Sstevel@tonic-gate #define TURNSTILE_HASH_MASK (TURNSTILE_HASH_SIZE - 1) 6890Sstevel@tonic-gate #define TURNSTILE_SOBJ_HASH(sobj) \ 6900Sstevel@tonic-gate ((((int)sobj >> 2) + ((int)sobj >> 9)) & TURNSTILE_HASH_MASK) 6910Sstevel@tonic-gate 6920Sstevel@tonic-gate typedef struct turnstile_chain { 6930Sstevel@tonic-gate turnstile_t *tc_first; /* first turnstile on hash chain */ 6940Sstevel@tonic-gate disp_lock_t tc_lock; /* lock for this hash chain */ 6950Sstevel@tonic-gate } turnstile_chain_t; 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate /* 6980Sstevel@tonic-gate * Given the address of a blocked-upon synchronization object, return 6990Sstevel@tonic-gate * the address of its turnstile. 7000Sstevel@tonic-gate */ 7010Sstevel@tonic-gate 7020Sstevel@tonic-gate /*ARGSUSED*/ 7030Sstevel@tonic-gate int 7040Sstevel@tonic-gate sobj2ts(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 7050Sstevel@tonic-gate { 7060Sstevel@tonic-gate GElf_Sym sym; 7070Sstevel@tonic-gate int isupi; 7080Sstevel@tonic-gate int ttoff; 7090Sstevel@tonic-gate uintptr_t ttable; 7100Sstevel@tonic-gate turnstile_t ts, *tsp; 7110Sstevel@tonic-gate turnstile_chain_t tc; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC) || argc != 0) 7140Sstevel@tonic-gate return (DCMD_USAGE); 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate if (mdb_lookup_by_name("upimutextab", &sym) == -1) { 7170Sstevel@tonic-gate mdb_warn("unable to reference upimutextab\n"); 7180Sstevel@tonic-gate return (DCMD_ERR); 7190Sstevel@tonic-gate } 7200Sstevel@tonic-gate isupi = addr - (uintptr_t)sym.st_value < sym.st_size; 7210Sstevel@tonic-gate ttoff = (isupi ? 0 : TURNSTILE_HASH_SIZE) + TURNSTILE_SOBJ_HASH(addr); 7220Sstevel@tonic-gate 7230Sstevel@tonic-gate if (mdb_lookup_by_name("turnstile_table", &sym) == -1) { 7240Sstevel@tonic-gate mdb_warn("unable to reference turnstile_table"); 7250Sstevel@tonic-gate return (DCMD_ERR); 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate ttable = (uintptr_t)sym.st_value + sizeof (turnstile_chain_t) * ttoff; 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate if (mdb_vread(&tc, sizeof (turnstile_chain_t), ttable) == -1) { 7300Sstevel@tonic-gate mdb_warn("unable to read turnstile_chain_t at %#lx", ttable); 7310Sstevel@tonic-gate return (DCMD_ERR); 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate 7340Sstevel@tonic-gate for (tsp = tc.tc_first; tsp != NULL; tsp = ts.ts_next) { 7350Sstevel@tonic-gate if (mdb_vread(&ts, sizeof (turnstile_t), 7360Sstevel@tonic-gate (uintptr_t)tsp) == -1) { 7370Sstevel@tonic-gate mdb_warn("unable to read turnstile_t at %#p", tsp); 7380Sstevel@tonic-gate return (DCMD_ERR); 7390Sstevel@tonic-gate } 7400Sstevel@tonic-gate if ((uintptr_t)ts.ts_sobj == addr) { 7410Sstevel@tonic-gate mdb_printf("%p\n", tsp); 7420Sstevel@tonic-gate break; 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate } 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate return (DCMD_OK); 7470Sstevel@tonic-gate } 748