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