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