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
find_range(const void * r,const void * e)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
uintptr_cmp(const void * l,const void * r)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 *
mdb_whatis_search(mdb_whatis_t * w,uintptr_t base,size_t size)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
mdb_whatis_overlaps(mdb_whatis_t * w,uintptr_t base,size_t size)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
mdb_whatis_match(mdb_whatis_t * w,uintptr_t base,size_t size,uintptr_t * out)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
mdb_whatis_report_object(mdb_whatis_t * w,uintptr_t addr,uintptr_t base,const char * format,...)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
mdb_whatis_report_address(mdb_whatis_t * w,uintptr_t addr,const char * format,...)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
mdb_whatis_flags(mdb_whatis_t * w)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
mdb_whatis_done(mdb_whatis_t * w)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
whatis_cbcmp(const void * lhs,const void * rhs)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
whatis_init(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
mdb_whatis_register(const char * name,mdb_whatis_cb_f * func,void * arg,uint_t prio,uint_t flags)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
mdb_whatis_unregister_module(mdb_module_t * mod)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
cmd_whatis(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
whatis_help(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