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