17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*346799e8SJonathan W Adams * Common Development and Distribution License (the "License"). 6*346799e8SJonathan W Adams * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*346799e8SJonathan W Adams * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 277c478bd9Sstevel@tonic-gate #include <sys/types.h> 287c478bd9Sstevel@tonic-gate #include <sys/mutex.h> 297c478bd9Sstevel@tonic-gate #include <sys/thread.h> 307c478bd9Sstevel@tonic-gate #include <sys/condvar.h> 317c478bd9Sstevel@tonic-gate #include <sys/sleepq.h> 327c478bd9Sstevel@tonic-gate #include <sys/sobject.h> 337c478bd9Sstevel@tonic-gate #include <sys/rwlock_impl.h> 347c478bd9Sstevel@tonic-gate #include <sys/turnstile.h> 357c478bd9Sstevel@tonic-gate #include <sys/proc.h> 367c478bd9Sstevel@tonic-gate #include <sys/mutex_impl.h> 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #include <stdio.h> 397c478bd9Sstevel@tonic-gate 40*346799e8SJonathan W Adams struct sobj_type_info { 41*346799e8SJonathan W Adams int sobj_type; 42*346799e8SJonathan W Adams const char *sobj_name; 43*346799e8SJonathan W Adams const char *sobj_ops_name; 44*346799e8SJonathan W Adams } sobj_types[] = { 45*346799e8SJonathan W Adams { SOBJ_MUTEX, "mutex", "mutex_sobj_ops" }, 46*346799e8SJonathan W Adams { SOBJ_RWLOCK, "rwlock", "rw_sobj_ops" }, 47*346799e8SJonathan W Adams { SOBJ_CV, "cv", "cv_sobj_ops" }, 48*346799e8SJonathan W Adams { SOBJ_SEMA, "sema", "sema_sobj_ops" }, 49*346799e8SJonathan W Adams { SOBJ_USER, "user", "lwp_sobj_ops" }, 50*346799e8SJonathan W Adams { SOBJ_USER_PI, "user_pi", "lwp_sobj_pi_ops" }, 51*346799e8SJonathan W Adams { SOBJ_SHUTTLE, "shuttle", "shuttle_sobj_ops" } 52*346799e8SJonathan W Adams }; 53*346799e8SJonathan W Adams #define NUM_SOBJ_TYPES (sizeof (sobj_types) / sizeof (*sobj_types)) 54*346799e8SJonathan W Adams 55*346799e8SJonathan W Adams void 56*346799e8SJonathan W Adams sobj_type_to_text(int type, char *out, size_t sz) 57*346799e8SJonathan W Adams { 58*346799e8SJonathan W Adams int idx; 59*346799e8SJonathan W Adams if (type == SOBJ_NONE) { 60*346799e8SJonathan W Adams mdb_snprintf(out, sz, "<none>"); 61*346799e8SJonathan W Adams return; 62*346799e8SJonathan W Adams } 63*346799e8SJonathan W Adams 64*346799e8SJonathan W Adams for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 65*346799e8SJonathan W Adams struct sobj_type_info *info = &sobj_types[idx]; 66*346799e8SJonathan W Adams if (info->sobj_type == type) { 67*346799e8SJonathan W Adams mdb_snprintf(out, sz, "%s", 68*346799e8SJonathan W Adams sobj_types[idx].sobj_name); 69*346799e8SJonathan W Adams return; 70*346799e8SJonathan W Adams } 71*346799e8SJonathan W Adams } 72*346799e8SJonathan W Adams mdb_snprintf(out, sz, "<unk:%02x>", type); 73*346799e8SJonathan W Adams } 74*346799e8SJonathan W Adams 75*346799e8SJonathan W Adams void 76*346799e8SJonathan W Adams sobj_ops_to_text(uintptr_t addr, char *out, size_t sz) 77*346799e8SJonathan W Adams { 78*346799e8SJonathan W Adams sobj_ops_t ops; 79*346799e8SJonathan W Adams 80*346799e8SJonathan W Adams if (addr == 0) { 81*346799e8SJonathan W Adams mdb_snprintf(out, sz, "<none>"); 82*346799e8SJonathan W Adams return; 83*346799e8SJonathan W Adams } 84*346799e8SJonathan W Adams if (mdb_vread(&ops, sizeof (ops), addr) == -1) { 85*346799e8SJonathan W Adams mdb_snprintf(out, sz, "??", ops.sobj_type); 86*346799e8SJonathan W Adams return; 87*346799e8SJonathan W Adams } 88*346799e8SJonathan W Adams 89*346799e8SJonathan W Adams sobj_type_to_text(ops.sobj_type, out, sz); 90*346799e8SJonathan W Adams } 91*346799e8SJonathan W Adams 92*346799e8SJonathan W Adams int 93*346799e8SJonathan W Adams sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out) 94*346799e8SJonathan W Adams { 95*346799e8SJonathan W Adams int idx; 96*346799e8SJonathan W Adams GElf_Sym sym; 97*346799e8SJonathan W Adams 98*346799e8SJonathan W Adams for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 99*346799e8SJonathan W Adams struct sobj_type_info *info = &sobj_types[idx]; 100*346799e8SJonathan W Adams if (strcasecmp(info->sobj_name, name) == 0) { 101*346799e8SJonathan W Adams if (mdb_lookup_by_name(info->sobj_ops_name, 102*346799e8SJonathan W Adams &sym) == -1) { 103*346799e8SJonathan W Adams mdb_warn("unable to find symbol \"%s\"", 104*346799e8SJonathan W Adams info->sobj_ops_name); 105*346799e8SJonathan W Adams return (-1); 106*346799e8SJonathan W Adams } 107*346799e8SJonathan W Adams *sobj_ops_out = (uintptr_t)sym.st_value; 108*346799e8SJonathan W Adams return (0); 109*346799e8SJonathan W Adams } 110*346799e8SJonathan W Adams } 111*346799e8SJonathan W Adams 112*346799e8SJonathan W Adams mdb_warn("sobj type \"%s\" unknown\n", name); 113*346799e8SJonathan W Adams return (-1); 114*346799e8SJonathan W Adams } 115*346799e8SJonathan W Adams 116*346799e8SJonathan W Adams void 117*346799e8SJonathan W Adams sobj_type_walk(void (*cbfunc)(int, const char *, const char *, void *), 118*346799e8SJonathan W Adams void *cbarg) 119*346799e8SJonathan W Adams { 120*346799e8SJonathan W Adams int idx; 121*346799e8SJonathan W Adams 122*346799e8SJonathan W Adams for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) { 123*346799e8SJonathan W Adams struct sobj_type_info *info = &sobj_types[idx]; 124*346799e8SJonathan W Adams cbfunc(info->sobj_type, info->sobj_name, info->sobj_ops_name, 125*346799e8SJonathan W Adams cbarg); 126*346799e8SJonathan W Adams } 127*346799e8SJonathan W Adams } 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate typedef struct wchan_walk_data { 1307c478bd9Sstevel@tonic-gate caddr_t *ww_seen; 1317c478bd9Sstevel@tonic-gate int ww_seen_size; 1327c478bd9Sstevel@tonic-gate int ww_seen_ndx; 1337c478bd9Sstevel@tonic-gate uintptr_t ww_thr; 1347c478bd9Sstevel@tonic-gate sleepq_head_t ww_sleepq[NSLEEPQ]; 1357c478bd9Sstevel@tonic-gate int ww_sleepq_ndx; 1367c478bd9Sstevel@tonic-gate uintptr_t ww_compare; 1377c478bd9Sstevel@tonic-gate } wchan_walk_data_t; 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate int 1407c478bd9Sstevel@tonic-gate wchan_walk_init(mdb_walk_state_t *wsp) 1417c478bd9Sstevel@tonic-gate { 1427c478bd9Sstevel@tonic-gate wchan_walk_data_t *ww = 1437c478bd9Sstevel@tonic-gate mdb_zalloc(sizeof (wchan_walk_data_t), UM_SLEEP); 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate if (mdb_readvar(&ww->ww_sleepq[0], "sleepq_head") == -1) { 1467c478bd9Sstevel@tonic-gate mdb_warn("failed to read sleepq"); 1477c478bd9Sstevel@tonic-gate mdb_free(ww, sizeof (wchan_walk_data_t)); 1487c478bd9Sstevel@tonic-gate return (WALK_ERR); 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate if ((ww->ww_compare = wsp->walk_addr) == NULL) { 1527c478bd9Sstevel@tonic-gate if (mdb_readvar(&ww->ww_seen_size, "nthread") == -1) { 1537c478bd9Sstevel@tonic-gate mdb_warn("failed to read nthread"); 1547c478bd9Sstevel@tonic-gate mdb_free(ww, sizeof (wchan_walk_data_t)); 1557c478bd9Sstevel@tonic-gate return (WALK_ERR); 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate ww->ww_seen = mdb_alloc(ww->ww_seen_size * 1597c478bd9Sstevel@tonic-gate sizeof (caddr_t), UM_SLEEP); 1607c478bd9Sstevel@tonic-gate } else { 1617c478bd9Sstevel@tonic-gate ww->ww_sleepq_ndx = SQHASHINDEX(wsp->walk_addr); 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate wsp->walk_data = ww; 1657c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate int 1697c478bd9Sstevel@tonic-gate wchan_walk_step(mdb_walk_state_t *wsp) 1707c478bd9Sstevel@tonic-gate { 1717c478bd9Sstevel@tonic-gate wchan_walk_data_t *ww = wsp->walk_data; 1727c478bd9Sstevel@tonic-gate sleepq_head_t *sq; 1737c478bd9Sstevel@tonic-gate kthread_t thr; 1747c478bd9Sstevel@tonic-gate uintptr_t t; 1757c478bd9Sstevel@tonic-gate int i; 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate again: 1787c478bd9Sstevel@tonic-gate /* 1797c478bd9Sstevel@tonic-gate * Get the address of the first thread on the next sleepq in the 1807c478bd9Sstevel@tonic-gate * sleepq hash. If ww_compare is set, ww_sleepq_ndx is already 1817c478bd9Sstevel@tonic-gate * set to the appropriate sleepq index for the desired cv. 1827c478bd9Sstevel@tonic-gate */ 1837c478bd9Sstevel@tonic-gate for (t = ww->ww_thr; t == NULL; ) { 1847c478bd9Sstevel@tonic-gate if (ww->ww_sleepq_ndx == NSLEEPQ) 1857c478bd9Sstevel@tonic-gate return (WALK_DONE); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate sq = &ww->ww_sleepq[ww->ww_sleepq_ndx++]; 1887c478bd9Sstevel@tonic-gate t = (uintptr_t)sq->sq_queue.sq_first; 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * If we were looking for a specific cv and we're at the end 1927c478bd9Sstevel@tonic-gate * of its sleepq, we're done walking. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate if (t == NULL && ww->ww_compare != NULL) 1957c478bd9Sstevel@tonic-gate return (WALK_DONE); 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate /* 1997c478bd9Sstevel@tonic-gate * Read in the thread. If it's t_wchan pointer is NULL, the thread has 2007c478bd9Sstevel@tonic-gate * woken up since we took a snapshot of the sleepq (i.e. we are probably 2017c478bd9Sstevel@tonic-gate * being applied to a live system); we can't believe the t_link pointer 2027c478bd9Sstevel@tonic-gate * anymore either, so just skip to the next sleepq index. 2037c478bd9Sstevel@tonic-gate */ 2047c478bd9Sstevel@tonic-gate if (mdb_vread(&thr, sizeof (thr), t) != sizeof (thr)) { 2057c478bd9Sstevel@tonic-gate mdb_warn("failed to read thread at %p", t); 2067c478bd9Sstevel@tonic-gate return (WALK_ERR); 2077c478bd9Sstevel@tonic-gate } 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate if (thr.t_wchan == NULL) { 2107c478bd9Sstevel@tonic-gate ww->ww_thr = NULL; 2117c478bd9Sstevel@tonic-gate goto again; 2127c478bd9Sstevel@tonic-gate } 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate /* 2157c478bd9Sstevel@tonic-gate * Set ww_thr to the address of the next thread in the sleepq list. 2167c478bd9Sstevel@tonic-gate */ 2177c478bd9Sstevel@tonic-gate ww->ww_thr = (uintptr_t)thr.t_link; 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate /* 2207c478bd9Sstevel@tonic-gate * If we're walking a specific cv, invoke the callback if we've 2217c478bd9Sstevel@tonic-gate * found a match, or loop back to the top and read the next thread. 2227c478bd9Sstevel@tonic-gate */ 2237c478bd9Sstevel@tonic-gate if (ww->ww_compare != NULL) { 2247c478bd9Sstevel@tonic-gate if (ww->ww_compare == (uintptr_t)thr.t_wchan) 2257c478bd9Sstevel@tonic-gate return (wsp->walk_callback(t, &thr, wsp->walk_cbdata)); 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate if (ww->ww_thr == NULL) 2287c478bd9Sstevel@tonic-gate return (WALK_DONE); 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate goto again; 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate /* 2347c478bd9Sstevel@tonic-gate * If we're walking all cvs, seen if we've already encountered this one 2357c478bd9Sstevel@tonic-gate * on the current sleepq. If we have, skip to the next thread. 2367c478bd9Sstevel@tonic-gate */ 2377c478bd9Sstevel@tonic-gate for (i = 0; i < ww->ww_seen_ndx; i++) { 2387c478bd9Sstevel@tonic-gate if (ww->ww_seen[i] == thr.t_wchan) 2397c478bd9Sstevel@tonic-gate goto again; 2407c478bd9Sstevel@tonic-gate } 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate /* 2437c478bd9Sstevel@tonic-gate * If we're not at the end of a sleepq, save t_wchan; otherwise reset 2447c478bd9Sstevel@tonic-gate * the seen index so our array is empty at the start of the next sleepq. 2457c478bd9Sstevel@tonic-gate * If we hit seen_size this is a live kernel and nthread is now larger, 2467c478bd9Sstevel@tonic-gate * cope by replacing the final element in our memory. 2477c478bd9Sstevel@tonic-gate */ 2487c478bd9Sstevel@tonic-gate if (ww->ww_thr != NULL) { 2497c478bd9Sstevel@tonic-gate if (ww->ww_seen_ndx < ww->ww_seen_size) 2507c478bd9Sstevel@tonic-gate ww->ww_seen[ww->ww_seen_ndx++] = thr.t_wchan; 2517c478bd9Sstevel@tonic-gate else 2527c478bd9Sstevel@tonic-gate ww->ww_seen[ww->ww_seen_size - 1] = thr.t_wchan; 2537c478bd9Sstevel@tonic-gate } else 2547c478bd9Sstevel@tonic-gate ww->ww_seen_ndx = 0; 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate return (wsp->walk_callback((uintptr_t)thr.t_wchan, 2577c478bd9Sstevel@tonic-gate NULL, wsp->walk_cbdata)); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate void 2617c478bd9Sstevel@tonic-gate wchan_walk_fini(mdb_walk_state_t *wsp) 2627c478bd9Sstevel@tonic-gate { 2637c478bd9Sstevel@tonic-gate wchan_walk_data_t *ww = wsp->walk_data; 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate mdb_free(ww->ww_seen, ww->ww_seen_size * sizeof (uintptr_t)); 2667c478bd9Sstevel@tonic-gate mdb_free(ww, sizeof (wchan_walk_data_t)); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate struct wcdata { 2707c478bd9Sstevel@tonic-gate sobj_ops_t sobj; 2717c478bd9Sstevel@tonic-gate int nwaiters; 2727c478bd9Sstevel@tonic-gate }; 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2757c478bd9Sstevel@tonic-gate static int 2767c478bd9Sstevel@tonic-gate wchaninfo_twalk(uintptr_t addr, const kthread_t *t, struct wcdata *wc) 2777c478bd9Sstevel@tonic-gate { 2787c478bd9Sstevel@tonic-gate if (wc->sobj.sobj_type == SOBJ_NONE) { 2797c478bd9Sstevel@tonic-gate (void) mdb_vread(&wc->sobj, sizeof (sobj_ops_t), 2807c478bd9Sstevel@tonic-gate (uintptr_t)t->t_sobj_ops); 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate wc->nwaiters++; 2847c478bd9Sstevel@tonic-gate return (WALK_NEXT); 2857c478bd9Sstevel@tonic-gate } 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate static int 2887c478bd9Sstevel@tonic-gate wchaninfo_vtwalk(uintptr_t addr, const kthread_t *t, int *first) 2897c478bd9Sstevel@tonic-gate { 2907c478bd9Sstevel@tonic-gate proc_t p; 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate (void) mdb_vread(&p, sizeof (p), (uintptr_t)t->t_procp); 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate if (*first) { 2957c478bd9Sstevel@tonic-gate *first = 0; 2967c478bd9Sstevel@tonic-gate mdb_printf(": %0?p %s\n", addr, p.p_user.u_comm); 2977c478bd9Sstevel@tonic-gate } else { 2987c478bd9Sstevel@tonic-gate mdb_printf("%*s%0?p %s\n", (int)(sizeof (uintptr_t) * 2 + 17), 2997c478bd9Sstevel@tonic-gate "", addr, p.p_user.u_comm); 3007c478bd9Sstevel@tonic-gate } 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate return (WALK_NEXT); 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3067c478bd9Sstevel@tonic-gate static int 3077c478bd9Sstevel@tonic-gate wchaninfo_walk(uintptr_t addr, void *ignored, uint_t *verbose) 3087c478bd9Sstevel@tonic-gate { 3097c478bd9Sstevel@tonic-gate struct wcdata wc; 3107c478bd9Sstevel@tonic-gate int first = 1; 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate bzero(&wc, sizeof (wc)); 3137c478bd9Sstevel@tonic-gate wc.sobj.sobj_type = SOBJ_NONE; 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate if (mdb_pwalk("wchan", (mdb_walk_cb_t)wchaninfo_twalk, &wc, addr) < 0) { 3167c478bd9Sstevel@tonic-gate mdb_warn("failed to walk wchan %p", addr); 3177c478bd9Sstevel@tonic-gate return (WALK_NEXT); 3187c478bd9Sstevel@tonic-gate } 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate mdb_printf("%0?p %4s %8d%s", addr, 3217c478bd9Sstevel@tonic-gate wc.sobj.sobj_type == SOBJ_CV ? "cond" : 3227c478bd9Sstevel@tonic-gate wc.sobj.sobj_type == SOBJ_SEMA ? "sema" : "??", 3237c478bd9Sstevel@tonic-gate wc.nwaiters, (*verbose) ? "" : "\n"); 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate if (*verbose != 0 && wc.nwaiters != 0 && mdb_pwalk("wchan", 3267c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)wchaninfo_vtwalk, &first, addr) == -1) { 3277c478bd9Sstevel@tonic-gate mdb_warn("failed to walk waiters for wchan %p", addr); 3287c478bd9Sstevel@tonic-gate mdb_printf("\n"); 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate return (WALK_NEXT); 3327c478bd9Sstevel@tonic-gate } 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate int 3357c478bd9Sstevel@tonic-gate wchaninfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3367c478bd9Sstevel@tonic-gate { 3377c478bd9Sstevel@tonic-gate uint_t v = FALSE; 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 3407c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &v, NULL) != argc) 3417c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate if (v == TRUE) { 3447c478bd9Sstevel@tonic-gate mdb_printf("%-?s %-4s %8s %-?s %s\n", 3457c478bd9Sstevel@tonic-gate "ADDR", "TYPE", "NWAITERS", "THREAD", "PROC"); 3467c478bd9Sstevel@tonic-gate } else 3477c478bd9Sstevel@tonic-gate mdb_printf("%-?s %-4s %8s\n", "ADDR", "TYPE", "NWAITERS"); 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 3507c478bd9Sstevel@tonic-gate if (wchaninfo_walk(addr, NULL, &v) == WALK_ERR) 3517c478bd9Sstevel@tonic-gate return (DCMD_ERR); 3527c478bd9Sstevel@tonic-gate } else if (mdb_walk("wchan", (mdb_walk_cb_t)wchaninfo_walk, &v) == -1) { 3537c478bd9Sstevel@tonic-gate mdb_warn("failed to walk wchans"); 3547c478bd9Sstevel@tonic-gate return (DCMD_ERR); 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate return (DCMD_OK); 3587c478bd9Sstevel@tonic-gate } 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate int 3617c478bd9Sstevel@tonic-gate blocked_walk_init(mdb_walk_state_t *wsp) 3627c478bd9Sstevel@tonic-gate { 3637c478bd9Sstevel@tonic-gate if ((wsp->walk_data = (void *)wsp->walk_addr) == NULL) { 3647c478bd9Sstevel@tonic-gate mdb_warn("must specify a sobj * for blocked walk"); 3657c478bd9Sstevel@tonic-gate return (WALK_ERR); 3667c478bd9Sstevel@tonic-gate } 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate wsp->walk_addr = NULL; 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate if (mdb_layered_walk("thread", wsp) == -1) { 3717c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk 'thread'"); 3727c478bd9Sstevel@tonic-gate return (WALK_ERR); 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate return (WALK_NEXT); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate int 3797c478bd9Sstevel@tonic-gate blocked_walk_step(mdb_walk_state_t *wsp) 3807c478bd9Sstevel@tonic-gate { 3817c478bd9Sstevel@tonic-gate uintptr_t addr = (uintptr_t)((const kthread_t *)wsp->walk_layer)->t_ts; 3827c478bd9Sstevel@tonic-gate uintptr_t taddr = wsp->walk_addr; 3837c478bd9Sstevel@tonic-gate turnstile_t ts; 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate if (mdb_vread(&ts, sizeof (ts), addr) == -1) { 3867c478bd9Sstevel@tonic-gate mdb_warn("couldn't read %p's turnstile at %p", taddr, addr); 3877c478bd9Sstevel@tonic-gate return (WALK_ERR); 3887c478bd9Sstevel@tonic-gate } 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate if (ts.ts_waiters == 0 || ts.ts_sobj != wsp->walk_data) 3917c478bd9Sstevel@tonic-gate return (WALK_NEXT); 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate return (wsp->walk_callback(taddr, wsp->walk_layer, wsp->walk_cbdata)); 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate typedef struct rwlock_block { 3977c478bd9Sstevel@tonic-gate struct rwlock_block *rw_next; 3987c478bd9Sstevel@tonic-gate int rw_qnum; 3997c478bd9Sstevel@tonic-gate uintptr_t rw_thread; 4007c478bd9Sstevel@tonic-gate } rwlock_block_t; 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate static int 4037c478bd9Sstevel@tonic-gate rwlock_walk(uintptr_t taddr, const kthread_t *t, rwlock_block_t **rwp) 4047c478bd9Sstevel@tonic-gate { 4057c478bd9Sstevel@tonic-gate turnstile_t ts; 4067c478bd9Sstevel@tonic-gate uintptr_t addr = (uintptr_t)t->t_ts; 4077c478bd9Sstevel@tonic-gate rwlock_block_t *rw; 4087c478bd9Sstevel@tonic-gate int state, i; 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate if (mdb_vread(&ts, sizeof (ts), addr) == -1) { 4117c478bd9Sstevel@tonic-gate mdb_warn("couldn't read %p's turnstile at %p", taddr, addr); 4127c478bd9Sstevel@tonic-gate return (WALK_ERR); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate for (i = 0; i < TS_NUM_Q; i++) { 4167c478bd9Sstevel@tonic-gate if ((uintptr_t)t->t_sleepq == 4177c478bd9Sstevel@tonic-gate (uintptr_t)&ts.ts_sleepq[i] - (uintptr_t)&ts + addr) 4187c478bd9Sstevel@tonic-gate break; 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate if (i == TS_NUM_Q) { 4227c478bd9Sstevel@tonic-gate if ((state = mdb_get_state()) == MDB_STATE_DEAD || 4237c478bd9Sstevel@tonic-gate state == MDB_STATE_STOPPED) { 4247c478bd9Sstevel@tonic-gate /* 4257c478bd9Sstevel@tonic-gate * This shouldn't happen post-mortem or under kmdb; 4267c478bd9Sstevel@tonic-gate * the blocked walk returned a thread which wasn't 4277c478bd9Sstevel@tonic-gate * actually blocked on its turnstile. This may happen 4287c478bd9Sstevel@tonic-gate * in-situ if the thread wakes up during the ::rwlock. 4297c478bd9Sstevel@tonic-gate */ 4307c478bd9Sstevel@tonic-gate mdb_warn("thread %p isn't blocked on ts %p\n", 4317c478bd9Sstevel@tonic-gate taddr, addr); 4327c478bd9Sstevel@tonic-gate return (WALK_ERR); 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate return (WALK_NEXT); 4367c478bd9Sstevel@tonic-gate } 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate rw = mdb_alloc(sizeof (rwlock_block_t), UM_SLEEP | UM_GC); 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate rw->rw_next = *rwp; 4417c478bd9Sstevel@tonic-gate rw->rw_qnum = i; 4427c478bd9Sstevel@tonic-gate rw->rw_thread = taddr; 4437c478bd9Sstevel@tonic-gate *rwp = rw; 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate return (WALK_NEXT); 4467c478bd9Sstevel@tonic-gate } 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate /* 4497c478bd9Sstevel@tonic-gate * > rwd_rwlock::rwlock 4507c478bd9Sstevel@tonic-gate * ADDR OWNER/COUNT FLAGS WAITERS 4517c478bd9Sstevel@tonic-gate * 7835dee8 READERS=1 B011 30004393d20 (W) 4527c478bd9Sstevel@tonic-gate * || 4537c478bd9Sstevel@tonic-gate * WRITE_WANTED -------+| 4547c478bd9Sstevel@tonic-gate * HAS_WAITERS --------+ 4557c478bd9Sstevel@tonic-gate * 4567c478bd9Sstevel@tonic-gate * |--ADDR_WIDTH--| |--OWNR_WIDTH--| 4577c478bd9Sstevel@tonic-gate * |--LBL_OFFSET--||-LBL_WIDTH| 4587c478bd9Sstevel@tonic-gate * |--------------LONG-------------| 4597c478bd9Sstevel@tonic-gate * |------------WAITER_OFFSET------------| 4607c478bd9Sstevel@tonic-gate */ 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate #ifdef _LP64 4637c478bd9Sstevel@tonic-gate #define RW_ADDR_WIDTH 16 4647c478bd9Sstevel@tonic-gate #define RW_OWNR_WIDTH 16 4657c478bd9Sstevel@tonic-gate #else 4667c478bd9Sstevel@tonic-gate #define RW_ADDR_WIDTH 8 4677c478bd9Sstevel@tonic-gate #define RW_OWNR_WIDTH 11 4687c478bd9Sstevel@tonic-gate #endif 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate #define RW_LONG (RW_ADDR_WIDTH + 1 + RW_OWNR_WIDTH) 4717c478bd9Sstevel@tonic-gate #define RW_LBL_WIDTH 12 4727c478bd9Sstevel@tonic-gate #define RW_LBL_OFFSET (RW_ADDR_WIDTH + RW_OWNR_WIDTH - 3 - RW_LBL_WIDTH) 4737c478bd9Sstevel@tonic-gate #define RW_WAITER_OFFSET (RW_LONG + 6) 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate /* Access rwlock bits */ 4767c478bd9Sstevel@tonic-gate #define RW_BIT(n, offon) (wwwh & (1 << (n)) ? offon[1] : offon[0]) 4777c478bd9Sstevel@tonic-gate #define RW_BIT_SET(n) (wwwh & (1 << (n))) 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate /* Print a waiter (if any) and a newline */ 4807c478bd9Sstevel@tonic-gate #define RW_NEWLINE \ 4817c478bd9Sstevel@tonic-gate if (rw != NULL) { \ 4827c478bd9Sstevel@tonic-gate int q = rw->rw_qnum; \ 4837c478bd9Sstevel@tonic-gate mdb_printf(" %?p (%s)", rw->rw_thread, \ 4847c478bd9Sstevel@tonic-gate q == TS_READER_Q ? "R" : q == TS_WRITER_Q ? "W" : "?"); \ 4857c478bd9Sstevel@tonic-gate rw = rw->rw_next; \ 4867c478bd9Sstevel@tonic-gate } \ 4877c478bd9Sstevel@tonic-gate mdb_printf("\n"); 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4907c478bd9Sstevel@tonic-gate int 4917c478bd9Sstevel@tonic-gate rwlock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 4927c478bd9Sstevel@tonic-gate { 4937c478bd9Sstevel@tonic-gate rwlock_impl_t lock; 4947c478bd9Sstevel@tonic-gate rwlock_block_t *rw = NULL; 4957c478bd9Sstevel@tonic-gate uintptr_t wwwh; 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC) || addr == NULL || argc != 0) 4987c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate if (mdb_vread(&lock, sizeof (lock), addr) == -1) { 5017c478bd9Sstevel@tonic-gate mdb_warn("failed to read rwlock at 0x%p", addr); 5027c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5037c478bd9Sstevel@tonic-gate } 5047c478bd9Sstevel@tonic-gate 5057c478bd9Sstevel@tonic-gate if (mdb_pwalk("blocked", (mdb_walk_cb_t)rwlock_walk, &rw, addr) == -1) { 5067c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk 'blocked' for sobj %p", addr); 5077c478bd9Sstevel@tonic-gate return (WALK_ERR); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate mdb_printf("%?s %*s %5s %?s\n", "ADDR", 5117c478bd9Sstevel@tonic-gate RW_OWNR_WIDTH, "OWNER/COUNT", "FLAGS", "WAITERS"); 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate mdb_printf("%?p ", addr); 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate if (((wwwh = lock.rw_wwwh) & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) 5167c478bd9Sstevel@tonic-gate mdb_printf("%*s", RW_OWNR_WIDTH, "1"); 5177c478bd9Sstevel@tonic-gate else if ((wwwh = lock.rw_wwwh) & RW_WRITE_LOCKED) 5187c478bd9Sstevel@tonic-gate mdb_printf("%*p", RW_OWNR_WIDTH, wwwh & RW_OWNER); 5197c478bd9Sstevel@tonic-gate else { 5207c478bd9Sstevel@tonic-gate uintptr_t count = (wwwh & RW_HOLD_COUNT) >> RW_HOLD_COUNT_SHIFT; 5217c478bd9Sstevel@tonic-gate char c[20]; 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate mdb_snprintf(c, 20, "READERS=%ld", count); 5247c478bd9Sstevel@tonic-gate mdb_printf("%*s", RW_OWNR_WIDTH, count ? c : "-"); 5257c478bd9Sstevel@tonic-gate } 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate mdb_printf(" B%c%c%c", 5287c478bd9Sstevel@tonic-gate RW_BIT(2, "01"), RW_BIT(1, "01"), RW_BIT(0, "01")); 5297c478bd9Sstevel@tonic-gate RW_NEWLINE; 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate mdb_printf("%*s%c %c%c%c", RW_LONG - 1, "", 5327c478bd9Sstevel@tonic-gate " |"[(wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK], 5337c478bd9Sstevel@tonic-gate RW_BIT(2, " |"), RW_BIT(1, " |"), RW_BIT(0, " |")); 5347c478bd9Sstevel@tonic-gate RW_NEWLINE; 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate if ((wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) { 5377c478bd9Sstevel@tonic-gate mdb_printf("%*s%*s --+---+", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5387c478bd9Sstevel@tonic-gate "DESTROYED"); 5397c478bd9Sstevel@tonic-gate goto no_zero; 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate if (!RW_BIT_SET(2)) 5437c478bd9Sstevel@tonic-gate goto no_two; 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate mdb_printf("%*s%*s ------+%c%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5467c478bd9Sstevel@tonic-gate "WRITE_LOCKED", RW_BIT(1, " |"), RW_BIT(0, " |")); 5477c478bd9Sstevel@tonic-gate RW_NEWLINE; 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate no_two: 5507c478bd9Sstevel@tonic-gate if (!RW_BIT_SET(1)) 5517c478bd9Sstevel@tonic-gate goto no_one; 5527c478bd9Sstevel@tonic-gate 5537c478bd9Sstevel@tonic-gate mdb_printf("%*s%*s -------+%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5547c478bd9Sstevel@tonic-gate "WRITE_WANTED", RW_BIT(0, " |")); 5557c478bd9Sstevel@tonic-gate RW_NEWLINE; 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate no_one: 5587c478bd9Sstevel@tonic-gate if (!RW_BIT_SET(0)) 5597c478bd9Sstevel@tonic-gate goto no_zero; 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate mdb_printf("%*s%*s --------+", RW_LBL_OFFSET, "", RW_LBL_WIDTH, 5627c478bd9Sstevel@tonic-gate "HAS_WAITERS"); 5637c478bd9Sstevel@tonic-gate RW_NEWLINE; 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate no_zero: 5667c478bd9Sstevel@tonic-gate while (rw != NULL) { 5677c478bd9Sstevel@tonic-gate mdb_printf("%*s", RW_WAITER_OFFSET, ""); 5687c478bd9Sstevel@tonic-gate RW_NEWLINE; 5697c478bd9Sstevel@tonic-gate } 5707c478bd9Sstevel@tonic-gate 5717c478bd9Sstevel@tonic-gate return (DCMD_OK); 5727c478bd9Sstevel@tonic-gate } 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate int 5757c478bd9Sstevel@tonic-gate mutex(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 5767c478bd9Sstevel@tonic-gate { 5777c478bd9Sstevel@tonic-gate mutex_impl_t lock; 5787c478bd9Sstevel@tonic-gate uint_t force = FALSE; 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 5817c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 5857c478bd9Sstevel@tonic-gate 'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc) { 5867c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate if (mdb_vread(&lock, sizeof (lock), addr) == -1) { 5907c478bd9Sstevel@tonic-gate mdb_warn("failed to read mutex at 0x%0?p", addr); 5917c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 5957c478bd9Sstevel@tonic-gate mdb_printf("%<u>%?s %5s %?s %6s %6s %7s%</u>\n", 5967c478bd9Sstevel@tonic-gate "ADDR", "TYPE", "HELD", "MINSPL", "OLDSPL", "WAITERS"); 5977c478bd9Sstevel@tonic-gate } 5987c478bd9Sstevel@tonic-gate 5997c478bd9Sstevel@tonic-gate if (MUTEX_TYPE_SPIN(&lock)) { 6007c478bd9Sstevel@tonic-gate struct spin_mutex *sp = &lock.m_spin; 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate if (!force && (sp->m_filler != 0 || 6037c478bd9Sstevel@tonic-gate sp->m_minspl > PIL_MAX || sp->m_oldspl > PIL_MAX || 6047c478bd9Sstevel@tonic-gate (sp->m_spinlock != 0 && sp->m_spinlock != 0xff))) { 6057c478bd9Sstevel@tonic-gate mdb_warn("%a: invalid spin lock " 6067c478bd9Sstevel@tonic-gate "(-f to dump anyway)\n", addr); 6077c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6087c478bd9Sstevel@tonic-gate } 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate if (sp->m_spinlock == 0xff) { 6117c478bd9Sstevel@tonic-gate mdb_printf("%0?p %5s %?s %6d %6d %7s\n", 6127c478bd9Sstevel@tonic-gate addr, "spin", "yes", sp->m_minspl, sp->m_oldspl, 6137c478bd9Sstevel@tonic-gate "-"); 6147c478bd9Sstevel@tonic-gate } else { 6157c478bd9Sstevel@tonic-gate mdb_printf("%0?p %5s %?s %6d %6s %7s\n", 6167c478bd9Sstevel@tonic-gate addr, "spin", "no", sp->m_minspl, "-", "-"); 6177c478bd9Sstevel@tonic-gate } 6187c478bd9Sstevel@tonic-gate 6197c478bd9Sstevel@tonic-gate } else { 6207c478bd9Sstevel@tonic-gate kthread_t *owner = MUTEX_OWNER(&lock); 6217c478bd9Sstevel@tonic-gate char *waiters = MUTEX_HAS_WAITERS(&lock) ? "yes" : "no"; 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate if (!force && (!MUTEX_TYPE_ADAPTIVE(&lock) || 6247c478bd9Sstevel@tonic-gate (owner == NULL && MUTEX_HAS_WAITERS(&lock)))) { 6257c478bd9Sstevel@tonic-gate mdb_warn("%a: invalid adaptive mutex " 6267c478bd9Sstevel@tonic-gate "(-f to dump anyway)\n", addr); 6277c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6287c478bd9Sstevel@tonic-gate } 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate if (owner != NULL) { 6317c478bd9Sstevel@tonic-gate mdb_printf("%0?p %5s %?p %6s %6s %7s\n", 6327c478bd9Sstevel@tonic-gate addr, "adapt", owner, "-", "-", waiters); 6337c478bd9Sstevel@tonic-gate } else { 6347c478bd9Sstevel@tonic-gate mdb_printf("%0?p %5s %?s %6s %6s %7s\n", 6357c478bd9Sstevel@tonic-gate addr, "adapt", "no", "-", "-", waiters); 6367c478bd9Sstevel@tonic-gate } 6377c478bd9Sstevel@tonic-gate } 6387c478bd9Sstevel@tonic-gate return (DCMD_OK); 6397c478bd9Sstevel@tonic-gate } 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate void 6427c478bd9Sstevel@tonic-gate mutex_help(void) 6437c478bd9Sstevel@tonic-gate { 6447c478bd9Sstevel@tonic-gate mdb_printf("Options:\n" 6457c478bd9Sstevel@tonic-gate " -f force printing even if the data seems to be" 6467c478bd9Sstevel@tonic-gate " inconsistent\n"); 6477c478bd9Sstevel@tonic-gate } 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate int 6507c478bd9Sstevel@tonic-gate turnstile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 6517c478bd9Sstevel@tonic-gate { 6527c478bd9Sstevel@tonic-gate turnstile_t t; 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate if (argc != 0) 6557c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 6567c478bd9Sstevel@tonic-gate 6577c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 6587c478bd9Sstevel@tonic-gate if (mdb_walk_dcmd("turnstile_cache", "turnstile", argc, argv) 6597c478bd9Sstevel@tonic-gate == -1) { 6607c478bd9Sstevel@tonic-gate mdb_warn("can't walk turnstiles"); 6617c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6627c478bd9Sstevel@tonic-gate } 6637c478bd9Sstevel@tonic-gate return (DCMD_OK); 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 6677c478bd9Sstevel@tonic-gate mdb_printf("%<u>%?s %?s %5s %4s %?s %?s%</u>\n", 6687c478bd9Sstevel@tonic-gate "ADDR", "SOBJ", "WTRS", "EPRI", "ITOR", "PRIOINV"); 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate if (mdb_vread(&t, sizeof (turnstile_t), addr) == -1) { 6717c478bd9Sstevel@tonic-gate mdb_warn("can't read turnstile_t at %p", addr); 6727c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6737c478bd9Sstevel@tonic-gate } 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate mdb_printf("%0?p %?p %5d %4d %?p %?p\n", 6767c478bd9Sstevel@tonic-gate addr, t.ts_sobj, t.ts_waiters, t.ts_epri, 6777c478bd9Sstevel@tonic-gate t.ts_inheritor, t.ts_prioinv); 6787c478bd9Sstevel@tonic-gate 6797c478bd9Sstevel@tonic-gate return (DCMD_OK); 6807c478bd9Sstevel@tonic-gate } 6817c478bd9Sstevel@tonic-gate 6827c478bd9Sstevel@tonic-gate /* 6837c478bd9Sstevel@tonic-gate * Macros and structure definition copied from turnstile.c. 6847c478bd9Sstevel@tonic-gate * This is unfortunate, but half the macros we need aren't usable from 6857c478bd9Sstevel@tonic-gate * within mdb anyway. 6867c478bd9Sstevel@tonic-gate */ 6877c478bd9Sstevel@tonic-gate #define TURNSTILE_HASH_SIZE 128 /* must be power of 2 */ 6887c478bd9Sstevel@tonic-gate #define TURNSTILE_HASH_MASK (TURNSTILE_HASH_SIZE - 1) 6897c478bd9Sstevel@tonic-gate #define TURNSTILE_SOBJ_HASH(sobj) \ 6907c478bd9Sstevel@tonic-gate ((((int)sobj >> 2) + ((int)sobj >> 9)) & TURNSTILE_HASH_MASK) 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate typedef struct turnstile_chain { 6937c478bd9Sstevel@tonic-gate turnstile_t *tc_first; /* first turnstile on hash chain */ 6947c478bd9Sstevel@tonic-gate disp_lock_t tc_lock; /* lock for this hash chain */ 6957c478bd9Sstevel@tonic-gate } turnstile_chain_t; 6967c478bd9Sstevel@tonic-gate 6977c478bd9Sstevel@tonic-gate /* 6987c478bd9Sstevel@tonic-gate * Given the address of a blocked-upon synchronization object, return 6997c478bd9Sstevel@tonic-gate * the address of its turnstile. 7007c478bd9Sstevel@tonic-gate */ 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 7037c478bd9Sstevel@tonic-gate int 7047c478bd9Sstevel@tonic-gate sobj2ts(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 7057c478bd9Sstevel@tonic-gate { 7067c478bd9Sstevel@tonic-gate GElf_Sym sym; 7077c478bd9Sstevel@tonic-gate int isupi; 7087c478bd9Sstevel@tonic-gate int ttoff; 7097c478bd9Sstevel@tonic-gate uintptr_t ttable; 7107c478bd9Sstevel@tonic-gate turnstile_t ts, *tsp; 7117c478bd9Sstevel@tonic-gate turnstile_chain_t tc; 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC) || argc != 0) 7147c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("upimutextab", &sym) == -1) { 7177c478bd9Sstevel@tonic-gate mdb_warn("unable to reference upimutextab\n"); 7187c478bd9Sstevel@tonic-gate return (DCMD_ERR); 7197c478bd9Sstevel@tonic-gate } 7207c478bd9Sstevel@tonic-gate isupi = addr - (uintptr_t)sym.st_value < sym.st_size; 7217c478bd9Sstevel@tonic-gate ttoff = (isupi ? 0 : TURNSTILE_HASH_SIZE) + TURNSTILE_SOBJ_HASH(addr); 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("turnstile_table", &sym) == -1) { 7247c478bd9Sstevel@tonic-gate mdb_warn("unable to reference turnstile_table"); 7257c478bd9Sstevel@tonic-gate return (DCMD_ERR); 7267c478bd9Sstevel@tonic-gate } 7277c478bd9Sstevel@tonic-gate ttable = (uintptr_t)sym.st_value + sizeof (turnstile_chain_t) * ttoff; 7287c478bd9Sstevel@tonic-gate 7297c478bd9Sstevel@tonic-gate if (mdb_vread(&tc, sizeof (turnstile_chain_t), ttable) == -1) { 7307c478bd9Sstevel@tonic-gate mdb_warn("unable to read turnstile_chain_t at %#lx", ttable); 7317c478bd9Sstevel@tonic-gate return (DCMD_ERR); 7327c478bd9Sstevel@tonic-gate } 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate for (tsp = tc.tc_first; tsp != NULL; tsp = ts.ts_next) { 7357c478bd9Sstevel@tonic-gate if (mdb_vread(&ts, sizeof (turnstile_t), 7367c478bd9Sstevel@tonic-gate (uintptr_t)tsp) == -1) { 7377c478bd9Sstevel@tonic-gate mdb_warn("unable to read turnstile_t at %#p", tsp); 7387c478bd9Sstevel@tonic-gate return (DCMD_ERR); 7397c478bd9Sstevel@tonic-gate } 7407c478bd9Sstevel@tonic-gate if ((uintptr_t)ts.ts_sobj == addr) { 7417c478bd9Sstevel@tonic-gate mdb_printf("%p\n", tsp); 7427c478bd9Sstevel@tonic-gate break; 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate 7467c478bd9Sstevel@tonic-gate return (DCMD_OK); 7477c478bd9Sstevel@tonic-gate } 748