xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/sobj.c (revision 0:68f95e015346)
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