14a1c2431SJonathan Adams /* 24a1c2431SJonathan Adams * CDDL HEADER START 34a1c2431SJonathan Adams * 44a1c2431SJonathan Adams * The contents of this file are subject to the terms of the 54a1c2431SJonathan Adams * Common Development and Distribution License (the "License"). 64a1c2431SJonathan Adams * You may not use this file except in compliance with the License. 74a1c2431SJonathan Adams * 84a1c2431SJonathan Adams * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94a1c2431SJonathan Adams * or http://www.opensolaris.org/os/licensing. 104a1c2431SJonathan Adams * See the License for the specific language governing permissions 114a1c2431SJonathan Adams * and limitations under the License. 124a1c2431SJonathan Adams * 134a1c2431SJonathan Adams * When distributing Covered Code, include this CDDL HEADER in each 144a1c2431SJonathan Adams * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154a1c2431SJonathan Adams * If applicable, add the following below this CDDL HEADER, with the 164a1c2431SJonathan Adams * fields enclosed by brackets "[]" replaced with your own identifying 174a1c2431SJonathan Adams * information: Portions Copyright [yyyy] [name of copyright owner] 184a1c2431SJonathan Adams * 194a1c2431SJonathan Adams * CDDL HEADER END 204a1c2431SJonathan Adams */ 214a1c2431SJonathan Adams /* 224a1c2431SJonathan Adams * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 234a1c2431SJonathan Adams * Use is subject to license terms. 244a1c2431SJonathan Adams */ 254a1c2431SJonathan Adams 26*3b6e0a59SMatt Amdur /* 27*3b6e0a59SMatt Amdur * Copyright (c) 2012 by Delphix. All rights reserved. 28*3b6e0a59SMatt Amdur * Copyright (c) 2012 Joyent, Inc. All rights reserved. 29*3b6e0a59SMatt Amdur */ 30*3b6e0a59SMatt Amdur 31*3b6e0a59SMatt Amdur #include <mdb/mdb_modapi.h> 324a1c2431SJonathan Adams #include <mdb/mdb.h> 334a1c2431SJonathan Adams #include <mdb/mdb_io.h> 344a1c2431SJonathan Adams #include <mdb/mdb_module.h> 354a1c2431SJonathan Adams #include <mdb/mdb_string.h> 364a1c2431SJonathan Adams #include <mdb/mdb_whatis.h> 374a1c2431SJonathan Adams #include <mdb/mdb_whatis_impl.h> 384a1c2431SJonathan Adams #include <limits.h> 394a1c2431SJonathan Adams 404a1c2431SJonathan Adams static int whatis_debug = 0; 414a1c2431SJonathan Adams 424a1c2431SJonathan Adams /* for bsearch; r is an array of {base, size}, e points into w->w_addrs */ 434a1c2431SJonathan Adams static int 444a1c2431SJonathan Adams find_range(const void *r, const void *e) 454a1c2431SJonathan Adams { 464a1c2431SJonathan Adams const uintptr_t *range = r; 474a1c2431SJonathan Adams uintptr_t el = *(const uintptr_t *)e; 484a1c2431SJonathan Adams 494a1c2431SJonathan Adams if (el < range[0]) 504a1c2431SJonathan Adams return (1); 514a1c2431SJonathan Adams 524a1c2431SJonathan Adams if ((el - range[0]) >= range[1]) 534a1c2431SJonathan Adams return (-1); 544a1c2431SJonathan Adams 554a1c2431SJonathan Adams return (0); 564a1c2431SJonathan Adams } 574a1c2431SJonathan Adams 584a1c2431SJonathan Adams /* for qsort; simple uintptr comparator */ 594a1c2431SJonathan Adams static int 604a1c2431SJonathan Adams uintptr_cmp(const void *l, const void *r) 614a1c2431SJonathan Adams { 624a1c2431SJonathan Adams uintptr_t lhs = *(const uintptr_t *)l; 634a1c2431SJonathan Adams uintptr_t rhs = *(const uintptr_t *)r; 644a1c2431SJonathan Adams 654a1c2431SJonathan Adams if (lhs < rhs) 664a1c2431SJonathan Adams return (-1); 674a1c2431SJonathan Adams if (lhs > rhs) 684a1c2431SJonathan Adams return (1); 694a1c2431SJonathan Adams return (0); 704a1c2431SJonathan Adams } 714a1c2431SJonathan Adams 724a1c2431SJonathan Adams static const uintptr_t * 734a1c2431SJonathan Adams mdb_whatis_search(mdb_whatis_t *w, uintptr_t base, size_t size) 744a1c2431SJonathan Adams { 754a1c2431SJonathan Adams uintptr_t range[2]; 764a1c2431SJonathan Adams 774a1c2431SJonathan Adams range[0] = base; 784a1c2431SJonathan Adams range[1] = size; 794a1c2431SJonathan Adams 804a1c2431SJonathan Adams return (bsearch(range, w->w_addrs, w->w_naddrs, sizeof (*w->w_addrs), 814a1c2431SJonathan Adams find_range)); 824a1c2431SJonathan Adams } 834a1c2431SJonathan Adams 844a1c2431SJonathan Adams /* 854a1c2431SJonathan Adams * Returns non-zero if and only if there is at least one address of interest 864a1c2431SJonathan Adams * in the range [base, base+size). 874a1c2431SJonathan Adams */ 884a1c2431SJonathan Adams int 894a1c2431SJonathan Adams mdb_whatis_overlaps(mdb_whatis_t *w, uintptr_t base, size_t size) 904a1c2431SJonathan Adams { 914a1c2431SJonathan Adams const uintptr_t *f; 924a1c2431SJonathan Adams uint_t offset, cur; 934a1c2431SJonathan Adams 944a1c2431SJonathan Adams if (whatis_debug && w->w_magic != WHATIS_MAGIC) { 954a1c2431SJonathan Adams mdb_warn( 964a1c2431SJonathan Adams "mdb_whatis_overlaps(): bogus mdb_whatis_t pointer\n"); 974a1c2431SJonathan Adams return (0); 984a1c2431SJonathan Adams } 994a1c2431SJonathan Adams 1004a1c2431SJonathan Adams if (w->w_done || size == 0) 1014a1c2431SJonathan Adams return (0); 1024a1c2431SJonathan Adams 1034a1c2431SJonathan Adams if (base + size - 1 < base) { 1044a1c2431SJonathan Adams mdb_warn("mdb_whatis_overlaps(): [%p, %p+%p) overflows\n", 1054a1c2431SJonathan Adams base, base, size); 1064a1c2431SJonathan Adams return (0); 1074a1c2431SJonathan Adams } 1084a1c2431SJonathan Adams 1094a1c2431SJonathan Adams f = mdb_whatis_search(w, base, size); 1104a1c2431SJonathan Adams if (f == NULL) 1114a1c2431SJonathan Adams return (0); 1124a1c2431SJonathan Adams 1134a1c2431SJonathan Adams cur = offset = f - w->w_addrs; 1144a1c2431SJonathan Adams 1154a1c2431SJonathan Adams /* 1164a1c2431SJonathan Adams * We only return success if there's an address we'll actually 1174a1c2431SJonathan Adams * match in the range. We can quickly check for the ALL flag 1184a1c2431SJonathan Adams * or a non-found address at our match point. 1194a1c2431SJonathan Adams */ 1204a1c2431SJonathan Adams if ((w->w_flags & WHATIS_ALL) || !w->w_addrfound[cur]) 1214a1c2431SJonathan Adams return (1); 1224a1c2431SJonathan Adams 1234a1c2431SJonathan Adams /* Search backwards then forwards for a non-found address */ 1244a1c2431SJonathan Adams while (cur > 0) { 1254a1c2431SJonathan Adams cur--; 1264a1c2431SJonathan Adams 1274a1c2431SJonathan Adams if (w->w_addrs[cur] < base) 1284a1c2431SJonathan Adams break; 1294a1c2431SJonathan Adams 1304a1c2431SJonathan Adams if (!w->w_addrfound[cur]) 1314a1c2431SJonathan Adams return (1); 1324a1c2431SJonathan Adams } 1334a1c2431SJonathan Adams 1344a1c2431SJonathan Adams for (cur = offset + 1; cur < w->w_naddrs; cur++) { 1354a1c2431SJonathan Adams if ((w->w_addrs[cur] - base) >= size) 1364a1c2431SJonathan Adams break; 1374a1c2431SJonathan Adams 1384a1c2431SJonathan Adams if (!w->w_addrfound[cur]) 1394a1c2431SJonathan Adams return (1); 1404a1c2431SJonathan Adams } 1414a1c2431SJonathan Adams 1424a1c2431SJonathan Adams return (0); /* everything has already been seen */ 1434a1c2431SJonathan Adams } 1444a1c2431SJonathan Adams 1454a1c2431SJonathan Adams /* 1464a1c2431SJonathan Adams * Iteratively search our list of addresses for matches in [base, base+size). 1474a1c2431SJonathan Adams */ 1484a1c2431SJonathan Adams int 1494a1c2431SJonathan Adams mdb_whatis_match(mdb_whatis_t *w, uintptr_t base, size_t size, uintptr_t *out) 1504a1c2431SJonathan Adams { 1514a1c2431SJonathan Adams size_t offset; 1524a1c2431SJonathan Adams 1534a1c2431SJonathan Adams if (whatis_debug) { 1544a1c2431SJonathan Adams if (w->w_magic != WHATIS_MAGIC) { 1554a1c2431SJonathan Adams mdb_warn( 1564a1c2431SJonathan Adams "mdb_whatis_match(): bogus mdb_whatis_t pointer\n"); 1574a1c2431SJonathan Adams goto done; 1584a1c2431SJonathan Adams } 1594a1c2431SJonathan Adams } 1604a1c2431SJonathan Adams 1614a1c2431SJonathan Adams if (w->w_done || size == 0) 1624a1c2431SJonathan Adams goto done; 1634a1c2431SJonathan Adams 1644a1c2431SJonathan Adams if (base + size - 1 < base) { 1654a1c2431SJonathan Adams mdb_warn("mdb_whatis_match(): [%p, %p+%x) overflows\n", 1664a1c2431SJonathan Adams base, base, size); 1674a1c2431SJonathan Adams return (0); 1684a1c2431SJonathan Adams } 1694a1c2431SJonathan Adams 1704a1c2431SJonathan Adams if ((offset = w->w_match_next) != 0 && 1714a1c2431SJonathan Adams (base != w->w_match_base || size != w->w_match_size)) { 1724a1c2431SJonathan Adams mdb_warn("mdb_whatis_match(): new range [%p, %p+%p) " 1734a1c2431SJonathan Adams "while still searching [%p, %p+%p)\n", 1744a1c2431SJonathan Adams base, base, size, 1754a1c2431SJonathan Adams w->w_match_base, w->w_match_base, w->w_match_size); 1764a1c2431SJonathan Adams offset = 0; 1774a1c2431SJonathan Adams } 1784a1c2431SJonathan Adams 1794a1c2431SJonathan Adams if (offset == 0) { 1804a1c2431SJonathan Adams const uintptr_t *f = mdb_whatis_search(w, base, size); 1814a1c2431SJonathan Adams 1824a1c2431SJonathan Adams if (f == NULL) 1834a1c2431SJonathan Adams goto done; 1844a1c2431SJonathan Adams 1854a1c2431SJonathan Adams offset = (f - w->w_addrs); 1864a1c2431SJonathan Adams 1874a1c2431SJonathan Adams /* Walk backwards until we reach the first match */ 1884a1c2431SJonathan Adams while (offset > 0 && w->w_addrs[offset - 1] >= base) 1894a1c2431SJonathan Adams offset--; 1904a1c2431SJonathan Adams 1914a1c2431SJonathan Adams w->w_match_base = base; 1924a1c2431SJonathan Adams w->w_match_size = size; 1934a1c2431SJonathan Adams } 1944a1c2431SJonathan Adams 1954a1c2431SJonathan Adams for (; offset < w->w_naddrs && ((w->w_addrs[offset] - base) < size); 1964a1c2431SJonathan Adams offset++) { 1974a1c2431SJonathan Adams 1984a1c2431SJonathan Adams *out = w->w_addrs[offset]; 1994a1c2431SJonathan Adams w->w_match_next = offset + 1; 2004a1c2431SJonathan Adams 2014a1c2431SJonathan Adams if (w->w_addrfound[offset]) { 2024a1c2431SJonathan Adams /* if we're not seeing everything, skip it */ 2034a1c2431SJonathan Adams if (!(w->w_flags & WHATIS_ALL)) 2044a1c2431SJonathan Adams continue; 2054a1c2431SJonathan Adams 2064a1c2431SJonathan Adams return (1); 2074a1c2431SJonathan Adams } 2084a1c2431SJonathan Adams 2094a1c2431SJonathan Adams /* We haven't seen this address yet. */ 2104a1c2431SJonathan Adams w->w_found++; 2114a1c2431SJonathan Adams w->w_addrfound[offset] = 1; 2124a1c2431SJonathan Adams 2134a1c2431SJonathan Adams /* If we've found them all, we're done */ 2144a1c2431SJonathan Adams if (w->w_found == w->w_naddrs && !(w->w_flags & WHATIS_ALL)) 2154a1c2431SJonathan Adams w->w_done = 1; 2164a1c2431SJonathan Adams 2174a1c2431SJonathan Adams return (1); 2184a1c2431SJonathan Adams } 2194a1c2431SJonathan Adams 2204a1c2431SJonathan Adams done: 2214a1c2431SJonathan Adams w->w_match_next = 0; 2224a1c2431SJonathan Adams w->w_match_base = 0; 2234a1c2431SJonathan Adams w->w_match_size = 0; 2244a1c2431SJonathan Adams return (0); 2254a1c2431SJonathan Adams } 2264a1c2431SJonathan Adams 2274a1c2431SJonathan Adams /* 2284a1c2431SJonathan Adams * Report a pointer (addr) in an object beginning at (base) in standard 2294a1c2431SJonathan Adams * whatis-style. (format, ...) are mdb_printf() arguments, to be printed 2304a1c2431SJonathan Adams * after the address information. The caller is responsible for printing 2314a1c2431SJonathan Adams * a newline (either in format or after the call returns) 2324a1c2431SJonathan Adams */ 2334a1c2431SJonathan Adams /*ARGSUSED*/ 2344a1c2431SJonathan Adams void 2354a1c2431SJonathan Adams mdb_whatis_report_object(mdb_whatis_t *w, 2364a1c2431SJonathan Adams uintptr_t addr, uintptr_t base, const char *format, ...) 2374a1c2431SJonathan Adams { 2384a1c2431SJonathan Adams va_list alist; 2394a1c2431SJonathan Adams 2404a1c2431SJonathan Adams if (whatis_debug) { 2414a1c2431SJonathan Adams if (mdb_whatis_search(w, addr, 1) == NULL) 2424a1c2431SJonathan Adams mdb_warn("mdb_whatis_report_object(): addr " 2434a1c2431SJonathan Adams "%p is not a pointer of interest.\n", addr); 2444a1c2431SJonathan Adams } 2454a1c2431SJonathan Adams 2464a1c2431SJonathan Adams if (addr < base) 2474a1c2431SJonathan Adams mdb_warn("whatis: addr (%p) is less than base (%p)\n", 2484a1c2431SJonathan Adams addr, base); 2494a1c2431SJonathan Adams 2504a1c2431SJonathan Adams if (addr == base) 2514a1c2431SJonathan Adams mdb_printf("%p is ", addr); 2524a1c2431SJonathan Adams else 2534a1c2431SJonathan Adams mdb_printf("%p is %p+%p, ", addr, base, addr - base); 2544a1c2431SJonathan Adams 2554a1c2431SJonathan Adams if (format == NULL) 2564a1c2431SJonathan Adams return; 2574a1c2431SJonathan Adams 2584a1c2431SJonathan Adams va_start(alist, format); 2594a1c2431SJonathan Adams mdb_iob_vprintf(mdb.m_out, format, alist); 2604a1c2431SJonathan Adams va_end(alist); 2614a1c2431SJonathan Adams } 2624a1c2431SJonathan Adams 2634a1c2431SJonathan Adams /* 2644a1c2431SJonathan Adams * Report an address (addr), with symbolic information if available, in 2654a1c2431SJonathan Adams * standard whatis-style. (format, ...) are mdb_printf() arguments, to be 2664a1c2431SJonathan Adams * printed after the address information. The caller is responsible for 2674a1c2431SJonathan Adams * printing a newline (either in format or after the call returns) 2684a1c2431SJonathan Adams */ 2694a1c2431SJonathan Adams /*ARGSUSED*/ 2704a1c2431SJonathan Adams void 2714a1c2431SJonathan Adams mdb_whatis_report_address(mdb_whatis_t *w, uintptr_t addr, 2724a1c2431SJonathan Adams const char *format, ...) 2734a1c2431SJonathan Adams { 2744a1c2431SJonathan Adams GElf_Sym sym; 2754a1c2431SJonathan Adams va_list alist; 2764a1c2431SJonathan Adams 2774a1c2431SJonathan Adams if (whatis_debug) { 2784a1c2431SJonathan Adams if (mdb_whatis_search(w, addr, 1) == NULL) 2794a1c2431SJonathan Adams mdb_warn("mdb_whatis_report_adddress(): addr " 2804a1c2431SJonathan Adams "%p is not a pointer of interest.\n", addr); 2814a1c2431SJonathan Adams } 2824a1c2431SJonathan Adams 2834a1c2431SJonathan Adams mdb_printf("%p is ", addr); 2844a1c2431SJonathan Adams 2854a1c2431SJonathan Adams if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, NULL, 0, &sym) != -1 && 2864a1c2431SJonathan Adams (addr - (uintptr_t)sym.st_value) < sym.st_size) { 2874a1c2431SJonathan Adams mdb_printf("%a, ", addr); 2884a1c2431SJonathan Adams } 2894a1c2431SJonathan Adams 2904a1c2431SJonathan Adams va_start(alist, format); 2914a1c2431SJonathan Adams mdb_iob_vprintf(mdb.m_out, format, alist); 2924a1c2431SJonathan Adams va_end(alist); 2934a1c2431SJonathan Adams } 2944a1c2431SJonathan Adams 2954a1c2431SJonathan Adams uint_t 2964a1c2431SJonathan Adams mdb_whatis_flags(mdb_whatis_t *w) 2974a1c2431SJonathan Adams { 2984a1c2431SJonathan Adams /* Mask out the internal-only flags */ 2994a1c2431SJonathan Adams return (w->w_flags & WHATIS_PUBLIC); 3004a1c2431SJonathan Adams } 3014a1c2431SJonathan Adams 3024a1c2431SJonathan Adams uint_t 3034a1c2431SJonathan Adams mdb_whatis_done(mdb_whatis_t *w) 3044a1c2431SJonathan Adams { 3054a1c2431SJonathan Adams return (w->w_done); 3064a1c2431SJonathan Adams } 3074a1c2431SJonathan Adams 3084a1c2431SJonathan Adams /* 3094a1c2431SJonathan Adams * Whatis callback list management 3104a1c2431SJonathan Adams */ 3114a1c2431SJonathan Adams typedef struct whatis_callback { 3124a1c2431SJonathan Adams uint64_t wcb_index; 3134a1c2431SJonathan Adams mdb_module_t *wcb_module; 3144a1c2431SJonathan Adams const char *wcb_modname; 3154a1c2431SJonathan Adams char *wcb_name; 3164a1c2431SJonathan Adams mdb_whatis_cb_f *wcb_func; 3174a1c2431SJonathan Adams void *wcb_arg; 3184a1c2431SJonathan Adams uint_t wcb_prio; 3194a1c2431SJonathan Adams uint_t wcb_flags; 3204a1c2431SJonathan Adams } whatis_callback_t; 3214a1c2431SJonathan Adams 3224a1c2431SJonathan Adams static whatis_callback_t builtin_whatis[] = { 3234a1c2431SJonathan Adams { 0, NULL, "mdb", "mappings", whatis_run_mappings, NULL, 3244a1c2431SJonathan Adams WHATIS_PRIO_MIN, WHATIS_REG_NO_ID } 3254a1c2431SJonathan Adams }; 3264a1c2431SJonathan Adams #define NBUILTINS (sizeof (builtin_whatis) / sizeof (*builtin_whatis)) 3274a1c2431SJonathan Adams 3284a1c2431SJonathan Adams static whatis_callback_t *whatis_cb_start[NBUILTINS]; 3294a1c2431SJonathan Adams static whatis_callback_t **whatis_cb = NULL; /* callback array */ 3304a1c2431SJonathan Adams static size_t whatis_cb_count; /* count of callbacks */ 3314a1c2431SJonathan Adams static size_t whatis_cb_size; /* size of whatis_cb array */ 3324a1c2431SJonathan Adams static uint64_t whatis_cb_index; /* global count */ 3334a1c2431SJonathan Adams 3344a1c2431SJonathan Adams #define WHATIS_CB_SIZE_MIN 8 /* initial allocation size */ 3354a1c2431SJonathan Adams 3364a1c2431SJonathan Adams static int 3374a1c2431SJonathan Adams whatis_cbcmp(const void *lhs, const void *rhs) 3384a1c2431SJonathan Adams { 3394a1c2431SJonathan Adams whatis_callback_t *l = *(whatis_callback_t * const *)lhs; 3404a1c2431SJonathan Adams whatis_callback_t *r = *(whatis_callback_t * const *)rhs; 3414a1c2431SJonathan Adams int ret; 3424a1c2431SJonathan Adams 3434a1c2431SJonathan Adams /* First, handle NULLs; we want them at the end */ 3444a1c2431SJonathan Adams if (l == NULL && r == NULL) 3454a1c2431SJonathan Adams return (0); 3464a1c2431SJonathan Adams if (l == NULL) 3474a1c2431SJonathan Adams return (1); 3484a1c2431SJonathan Adams if (r == NULL) 3494a1c2431SJonathan Adams return (-1); 3504a1c2431SJonathan Adams 3514a1c2431SJonathan Adams /* Next, compare priorities */ 3524a1c2431SJonathan Adams if (l->wcb_prio < r->wcb_prio) 3534a1c2431SJonathan Adams return (-1); 3544a1c2431SJonathan Adams if (l->wcb_prio > r->wcb_prio) 3554a1c2431SJonathan Adams return (1); 3564a1c2431SJonathan Adams 3574a1c2431SJonathan Adams /* then module name */ 3584a1c2431SJonathan Adams if ((ret = strcmp(l->wcb_modname, r->wcb_modname)) != 0) 3594a1c2431SJonathan Adams return (ret); 3604a1c2431SJonathan Adams 3614a1c2431SJonathan Adams /* and finally insertion order */ 3624a1c2431SJonathan Adams if (l->wcb_index < r->wcb_index) 3634a1c2431SJonathan Adams return (-1); 3644a1c2431SJonathan Adams if (l->wcb_index > r->wcb_index) 3654a1c2431SJonathan Adams return (1); 3664a1c2431SJonathan Adams 3674a1c2431SJonathan Adams mdb_warn("whatis_cbcmp(): can't happen: duplicate indices\n"); 3684a1c2431SJonathan Adams return (0); 3694a1c2431SJonathan Adams } 3704a1c2431SJonathan Adams 3714a1c2431SJonathan Adams static void 3724a1c2431SJonathan Adams whatis_init(void) 3734a1c2431SJonathan Adams { 3744a1c2431SJonathan Adams int idx; 3754a1c2431SJonathan Adams 3764a1c2431SJonathan Adams for (idx = 0; idx < NBUILTINS; idx++) { 3774a1c2431SJonathan Adams whatis_cb_start[idx] = &builtin_whatis[idx]; 3784a1c2431SJonathan Adams whatis_cb_start[idx]->wcb_index = idx; 3794a1c2431SJonathan Adams } 3804a1c2431SJonathan Adams whatis_cb_index = idx; 3814a1c2431SJonathan Adams 3824a1c2431SJonathan Adams whatis_cb = whatis_cb_start; 3834a1c2431SJonathan Adams whatis_cb_count = whatis_cb_size = NBUILTINS; 3844a1c2431SJonathan Adams 3854a1c2431SJonathan Adams qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), whatis_cbcmp); 3864a1c2431SJonathan Adams } 3874a1c2431SJonathan Adams 3884a1c2431SJonathan Adams void 3894a1c2431SJonathan Adams mdb_whatis_register(const char *name, mdb_whatis_cb_f *func, void *arg, 3904a1c2431SJonathan Adams uint_t prio, uint_t flags) 3914a1c2431SJonathan Adams { 3924a1c2431SJonathan Adams whatis_callback_t *wcp; 3934a1c2431SJonathan Adams 3944a1c2431SJonathan Adams if (mdb.m_lmod == NULL) { 3954a1c2431SJonathan Adams mdb_warn("mdb_whatis_register(): can only be called during " 3964a1c2431SJonathan Adams "module load\n"); 3974a1c2431SJonathan Adams return; 3984a1c2431SJonathan Adams } 3994a1c2431SJonathan Adams 4004a1c2431SJonathan Adams if (strbadid(name)) { 4014a1c2431SJonathan Adams mdb_warn("mdb_whatis_register(): whatis name '%s' contains " 4024a1c2431SJonathan Adams "illegal characters\n"); 4034a1c2431SJonathan Adams return; 4044a1c2431SJonathan Adams } 4054a1c2431SJonathan Adams 4064a1c2431SJonathan Adams if ((flags & ~(WHATIS_REG_NO_ID|WHATIS_REG_ID_ONLY)) != 0) { 4074a1c2431SJonathan Adams mdb_warn("mdb_whatis_register(): flags (%x) contain unknown " 4084a1c2431SJonathan Adams "flags\n", flags); 4094a1c2431SJonathan Adams return; 4104a1c2431SJonathan Adams } 4114a1c2431SJonathan Adams if ((flags & WHATIS_REG_NO_ID) && (flags & WHATIS_REG_ID_ONLY)) { 4124a1c2431SJonathan Adams mdb_warn("mdb_whatis_register(): flags (%x) contains both " 4134a1c2431SJonathan Adams "NO_ID and ID_ONLY.\n", flags); 4144a1c2431SJonathan Adams return; 4154a1c2431SJonathan Adams } 4164a1c2431SJonathan Adams 4174a1c2431SJonathan Adams if (prio > WHATIS_PRIO_MIN) 4184a1c2431SJonathan Adams prio = WHATIS_PRIO_MIN; 4194a1c2431SJonathan Adams 4204a1c2431SJonathan Adams if (whatis_cb == NULL) 4214a1c2431SJonathan Adams whatis_init(); 4224a1c2431SJonathan Adams 4234a1c2431SJonathan Adams wcp = mdb_zalloc(sizeof (*wcp), UM_SLEEP); 4244a1c2431SJonathan Adams 4254a1c2431SJonathan Adams wcp->wcb_index = whatis_cb_index++; 4264a1c2431SJonathan Adams wcp->wcb_prio = prio; 4274a1c2431SJonathan Adams wcp->wcb_module = mdb.m_lmod; 4284a1c2431SJonathan Adams wcp->wcb_modname = mdb.m_lmod->mod_name; 4294a1c2431SJonathan Adams wcp->wcb_name = strdup(name); 4304a1c2431SJonathan Adams wcp->wcb_func = func; 4314a1c2431SJonathan Adams wcp->wcb_arg = arg; 4324a1c2431SJonathan Adams wcp->wcb_flags = flags; 4334a1c2431SJonathan Adams 4344a1c2431SJonathan Adams /* 4354a1c2431SJonathan Adams * See if we need to grow the array; note that at initialization 4364a1c2431SJonathan Adams * time, whatis_cb_count is greater than whatis_cb_size; this clues 4374a1c2431SJonathan Adams * us in to the fact that the array doesn't need to be freed. 4384a1c2431SJonathan Adams */ 4394a1c2431SJonathan Adams if (whatis_cb_count == whatis_cb_size) { 4404a1c2431SJonathan Adams size_t nsize = MAX(2 * whatis_cb_size, WHATIS_CB_SIZE_MIN); 4414a1c2431SJonathan Adams 4424a1c2431SJonathan Adams size_t obytes = sizeof (*whatis_cb) * whatis_cb_size; 4434a1c2431SJonathan Adams size_t nbytes = sizeof (*whatis_cb) * nsize; 4444a1c2431SJonathan Adams 4454a1c2431SJonathan Adams whatis_callback_t **narray = mdb_zalloc(nbytes, UM_SLEEP); 4464a1c2431SJonathan Adams 4474a1c2431SJonathan Adams bcopy(whatis_cb, narray, obytes); 4484a1c2431SJonathan Adams 4494a1c2431SJonathan Adams if (whatis_cb != whatis_cb_start) 4504a1c2431SJonathan Adams mdb_free(whatis_cb, obytes); 4514a1c2431SJonathan Adams whatis_cb = narray; 4524a1c2431SJonathan Adams whatis_cb_size = nsize; 4534a1c2431SJonathan Adams } 4544a1c2431SJonathan Adams 4554a1c2431SJonathan Adams /* add it into the table and re-sort */ 4564a1c2431SJonathan Adams whatis_cb[whatis_cb_count++] = wcp; 4574a1c2431SJonathan Adams qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), whatis_cbcmp); 4584a1c2431SJonathan Adams } 4594a1c2431SJonathan Adams 4604a1c2431SJonathan Adams void 4614a1c2431SJonathan Adams mdb_whatis_unregister_module(mdb_module_t *mod) 4624a1c2431SJonathan Adams { 4634a1c2431SJonathan Adams int found = 0; 4644a1c2431SJonathan Adams int idx; 4654a1c2431SJonathan Adams 4664a1c2431SJonathan Adams if (mod == NULL) 4674a1c2431SJonathan Adams return; 4684a1c2431SJonathan Adams 4694a1c2431SJonathan Adams for (idx = 0; idx < whatis_cb_count; idx++) { 4704a1c2431SJonathan Adams whatis_callback_t *cur = whatis_cb[idx]; 4714a1c2431SJonathan Adams 4724a1c2431SJonathan Adams if (cur->wcb_module == mod) { 4734a1c2431SJonathan Adams found++; 4744a1c2431SJonathan Adams whatis_cb[idx] = NULL; 4754a1c2431SJonathan Adams 4764a1c2431SJonathan Adams strfree(cur->wcb_name); 4774a1c2431SJonathan Adams mdb_free(cur, sizeof (*cur)); 4784a1c2431SJonathan Adams } 4794a1c2431SJonathan Adams } 4804a1c2431SJonathan Adams /* If any were removed, compact the array */ 4814a1c2431SJonathan Adams if (found != 0) { 4824a1c2431SJonathan Adams qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), 4834a1c2431SJonathan Adams whatis_cbcmp); 4844a1c2431SJonathan Adams whatis_cb_count -= found; 4854a1c2431SJonathan Adams } 4864a1c2431SJonathan Adams } 4874a1c2431SJonathan Adams 4884a1c2431SJonathan Adams int 4894a1c2431SJonathan Adams cmd_whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 4904a1c2431SJonathan Adams { 4914a1c2431SJonathan Adams mdb_whatis_t w; 4924a1c2431SJonathan Adams size_t idx; 4934a1c2431SJonathan Adams int ret; 4944a1c2431SJonathan Adams int keep = 0; 4954a1c2431SJonathan Adams int list = 0; 4964a1c2431SJonathan Adams 4974a1c2431SJonathan Adams if (flags & DCMD_PIPE_OUT) { 4984a1c2431SJonathan Adams mdb_warn("whatis: cannot be output into a pipe\n"); 4994a1c2431SJonathan Adams return (DCMD_ERR); 5004a1c2431SJonathan Adams } 5014a1c2431SJonathan Adams 5024a1c2431SJonathan Adams if (mdb.m_lmod != NULL) { 5034a1c2431SJonathan Adams mdb_warn("whatis: cannot be called during module load\n"); 5044a1c2431SJonathan Adams return (DCMD_ERR); 5054a1c2431SJonathan Adams } 5064a1c2431SJonathan Adams 5074a1c2431SJonathan Adams if (whatis_cb == NULL) 5084a1c2431SJonathan Adams whatis_init(); 5094a1c2431SJonathan Adams 5104a1c2431SJonathan Adams bzero(&w, sizeof (w)); 5114a1c2431SJonathan Adams w.w_magic = WHATIS_MAGIC; 5124a1c2431SJonathan Adams 5134a1c2431SJonathan Adams whatis_debug = 0; 5144a1c2431SJonathan Adams 5154a1c2431SJonathan Adams if (mdb_getopts(argc, argv, 5164a1c2431SJonathan Adams 'D', MDB_OPT_SETBITS, TRUE, &whatis_debug, /* hidden */ 5174a1c2431SJonathan Adams 'b', MDB_OPT_SETBITS, WHATIS_BUFCTL, &w.w_flags, /* hidden */ 5184a1c2431SJonathan Adams 'l', MDB_OPT_SETBITS, TRUE, &list, /* hidden */ 5194a1c2431SJonathan Adams 'a', MDB_OPT_SETBITS, WHATIS_ALL, &w.w_flags, 5204a1c2431SJonathan Adams 'i', MDB_OPT_SETBITS, WHATIS_IDSPACE, &w.w_flags, 5214a1c2431SJonathan Adams 'k', MDB_OPT_SETBITS, TRUE, &keep, 5224a1c2431SJonathan Adams 'q', MDB_OPT_SETBITS, WHATIS_QUIET, &w.w_flags, 5234a1c2431SJonathan Adams 'v', MDB_OPT_SETBITS, WHATIS_VERBOSE, &w.w_flags, 5244a1c2431SJonathan Adams NULL) != argc) 5254a1c2431SJonathan Adams return (DCMD_USAGE); 5264a1c2431SJonathan Adams 5274a1c2431SJonathan Adams if (list) { 5284a1c2431SJonathan Adams mdb_printf("%<u>%-16s %-12s %4s %?s %?s %8s%</u>", 5294a1c2431SJonathan Adams "NAME", "MODULE", "PRIO", "FUNC", "ARG", "FLAGS"); 5304a1c2431SJonathan Adams 5314a1c2431SJonathan Adams for (idx = 0; idx < whatis_cb_count; idx++) { 5324a1c2431SJonathan Adams whatis_callback_t *cur = whatis_cb[idx]; 5334a1c2431SJonathan Adams 5344a1c2431SJonathan Adams const char *curfl = 5354a1c2431SJonathan Adams (cur->wcb_flags & WHATIS_REG_NO_ID) ? "NO_ID" : 5364a1c2431SJonathan Adams (cur->wcb_flags & WHATIS_REG_ID_ONLY) ? "ID_ONLY" : 5374a1c2431SJonathan Adams "none"; 5384a1c2431SJonathan Adams 5394a1c2431SJonathan Adams mdb_printf("%-16s %-12s %4d %-?p %-?p %8s\n", 5404a1c2431SJonathan Adams cur->wcb_name, cur->wcb_modname, cur->wcb_prio, 5414a1c2431SJonathan Adams cur->wcb_func, cur->wcb_arg, curfl); 5424a1c2431SJonathan Adams } 5434a1c2431SJonathan Adams return (DCMD_OK); 5444a1c2431SJonathan Adams } 5454a1c2431SJonathan Adams 5464a1c2431SJonathan Adams if (!(flags & DCMD_ADDRSPEC)) 5474a1c2431SJonathan Adams return (DCMD_USAGE); 5484a1c2431SJonathan Adams 5494a1c2431SJonathan Adams w.w_addrs = &addr; 5504a1c2431SJonathan Adams w.w_naddrs = 1; 5514a1c2431SJonathan Adams 5524a1c2431SJonathan Adams /* If our input is a pipe, try to slurp it all up. */ 5534a1c2431SJonathan Adams if (!keep && (flags & DCMD_PIPE)) { 5544a1c2431SJonathan Adams mdb_pipe_t p; 5554a1c2431SJonathan Adams mdb_get_pipe(&p); 5564a1c2431SJonathan Adams 5574a1c2431SJonathan Adams if (p.pipe_len != 0) { 5584a1c2431SJonathan Adams w.w_addrs = p.pipe_data; 5594a1c2431SJonathan Adams w.w_naddrs = p.pipe_len; 5604a1c2431SJonathan Adams 5614a1c2431SJonathan Adams /* sort the address list */ 5624a1c2431SJonathan Adams qsort(w.w_addrs, w.w_naddrs, sizeof (*w.w_addrs), 5634a1c2431SJonathan Adams uintptr_cmp); 5644a1c2431SJonathan Adams } 5654a1c2431SJonathan Adams } 5664a1c2431SJonathan Adams w.w_addrfound = mdb_zalloc(w.w_naddrs * sizeof (*w.w_addrfound), 5674a1c2431SJonathan Adams UM_SLEEP | UM_GC); 5684a1c2431SJonathan Adams 5694a1c2431SJonathan Adams if (whatis_debug) { 5704a1c2431SJonathan Adams mdb_printf("Searching for:\n"); 5714a1c2431SJonathan Adams for (idx = 0; idx < w.w_naddrs; idx++) 5724a1c2431SJonathan Adams mdb_printf(" %p", w.w_addrs[idx]); 5734a1c2431SJonathan Adams } 5744a1c2431SJonathan Adams 5754a1c2431SJonathan Adams ret = 0; 5764a1c2431SJonathan Adams 5774a1c2431SJonathan Adams /* call in to the registered handlers */ 5784a1c2431SJonathan Adams for (idx = 0; idx < whatis_cb_count; idx++) { 5794a1c2431SJonathan Adams whatis_callback_t *cur = whatis_cb[idx]; 5804a1c2431SJonathan Adams 5814a1c2431SJonathan Adams /* Honor the ident flags */ 5824a1c2431SJonathan Adams if (w.w_flags & WHATIS_IDSPACE) { 5834a1c2431SJonathan Adams if (cur->wcb_flags & WHATIS_REG_NO_ID) 5844a1c2431SJonathan Adams continue; 5854a1c2431SJonathan Adams } else { 5864a1c2431SJonathan Adams if (cur->wcb_flags & WHATIS_REG_ID_ONLY) 5874a1c2431SJonathan Adams continue; 5884a1c2431SJonathan Adams } 5894a1c2431SJonathan Adams 5904a1c2431SJonathan Adams if (w.w_flags & WHATIS_VERBOSE) 5914a1c2431SJonathan Adams mdb_printf("Searching %s`%s...\n", 5924a1c2431SJonathan Adams cur->wcb_modname, cur->wcb_name); 5934a1c2431SJonathan Adams 5944a1c2431SJonathan Adams if (cur->wcb_func(&w, cur->wcb_arg) != 0) 5954a1c2431SJonathan Adams ret = 1; 5964a1c2431SJonathan Adams 5974a1c2431SJonathan Adams /* reset the match state for the next callback */ 5984a1c2431SJonathan Adams w.w_match_next = 0; 5994a1c2431SJonathan Adams w.w_match_base = 0; 6004a1c2431SJonathan Adams w.w_match_size = 0; 6014a1c2431SJonathan Adams 6024a1c2431SJonathan Adams if (w.w_done) 6034a1c2431SJonathan Adams break; 6044a1c2431SJonathan Adams } 6054a1c2431SJonathan Adams 6064a1c2431SJonathan Adams /* Report any unexplained pointers */ 6074a1c2431SJonathan Adams for (idx = 0; idx < w.w_naddrs; idx++) { 6084a1c2431SJonathan Adams uintptr_t addr = w.w_addrs[idx]; 6094a1c2431SJonathan Adams 6104a1c2431SJonathan Adams if (w.w_addrfound[idx]) 6114a1c2431SJonathan Adams continue; 6124a1c2431SJonathan Adams 6134a1c2431SJonathan Adams mdb_whatis_report_object(&w, addr, addr, "unknown\n"); 6144a1c2431SJonathan Adams } 6154a1c2431SJonathan Adams 6164a1c2431SJonathan Adams return ((ret != 0) ? DCMD_ERR : DCMD_OK); 6174a1c2431SJonathan Adams } 6184a1c2431SJonathan Adams 6194a1c2431SJonathan Adams void 6204a1c2431SJonathan Adams whatis_help(void) 6214a1c2431SJonathan Adams { 6224a1c2431SJonathan Adams int idx; 6234a1c2431SJonathan Adams 6244a1c2431SJonathan Adams mdb_printf("%s\n", 6254a1c2431SJonathan Adams "Given a virtual address (with -i, an identifier), report where it came\n" 6264a1c2431SJonathan Adams "from.\n" 6274a1c2431SJonathan Adams "\n" 6284a1c2431SJonathan Adams "When fed from a pipeline, ::whatis will not maintain the order the input\n" 6294a1c2431SJonathan Adams "comes in; addresses will be reported as it finds them. (-k prevents this;\n" 6304a1c2431SJonathan Adams "the output will be in the same order as the input)\n"); 6314a1c2431SJonathan Adams (void) mdb_dec_indent(2); 6324a1c2431SJonathan Adams mdb_printf("%<b>OPTIONS%</b>\n"); 6334a1c2431SJonathan Adams (void) mdb_inc_indent(2); 6344a1c2431SJonathan Adams mdb_printf("%s", 6354a1c2431SJonathan Adams " -a Report all information about each address/identifier. The default\n" 6364a1c2431SJonathan Adams " behavior is to report only the first (most specific) source for each\n" 6374a1c2431SJonathan Adams " address/identifier.\n" 6384a1c2431SJonathan Adams " -i addr is an identifier, not a virtual address.\n" 6394a1c2431SJonathan Adams " -k Do not re-order the input. (may be slower)\n" 6404a1c2431SJonathan Adams " -q Quiet; don't print multi-line reports. (stack traces, etc.)\n" 6414a1c2431SJonathan Adams " -v Verbose output; display information about the progress of the search\n"); 6424a1c2431SJonathan Adams 6434a1c2431SJonathan Adams if (mdb.m_lmod != NULL) 6444a1c2431SJonathan Adams return; 6454a1c2431SJonathan Adams 6464a1c2431SJonathan Adams (void) mdb_dec_indent(2); 6474a1c2431SJonathan Adams mdb_printf("\n%<b>SOURCES%</b>\n\n"); 6484a1c2431SJonathan Adams (void) mdb_inc_indent(2); 6494a1c2431SJonathan Adams mdb_printf("The following information sources will be used:\n\n"); 6504a1c2431SJonathan Adams 6514a1c2431SJonathan Adams (void) mdb_inc_indent(2); 6524a1c2431SJonathan Adams for (idx = 0; idx < whatis_cb_count; idx++) { 6534a1c2431SJonathan Adams whatis_callback_t *cur = whatis_cb[idx]; 6544a1c2431SJonathan Adams 6554a1c2431SJonathan Adams mdb_printf("%s`%s\n", cur->wcb_modname, cur->wcb_name); 6564a1c2431SJonathan Adams } 6574a1c2431SJonathan Adams (void) mdb_dec_indent(2); 6584a1c2431SJonathan Adams } 659